ローカルで日本語を発話するようになった
This commit is contained in:
@@ -130,9 +130,9 @@ app.post('/speak', async function(req, res) {
|
|||||||
|
|
||||||
console.log(' -> ' + target.accountName + ' (type=' + target.deviceType + ', serial=' + target.serialNumber + ')');
|
console.log(' -> ' + target.accountName + ' (type=' + target.deviceType + ', serial=' + target.serialNumber + ')');
|
||||||
|
|
||||||
// locale: '' はローカルPCでは日本語発話成功(サーバーからは要検証)
|
// ★ 重要: sequenceJson の non-ASCII(日本語等)を \uXXXX エスケープに変換してから送る
|
||||||
// Alexa.SpeakSsml 系は全滅のため Alexa.Speak に戻す
|
// raw UTF-8 のまま送ると Amazon 側でフィルタリングされ日本語が発話されない(解決済み 2026-03-03)
|
||||||
var sequenceObj = {
|
var sequenceObj = {
|
||||||
'@type': 'com.amazon.alexa.behaviors.model.Sequence',
|
'@type': 'com.amazon.alexa.behaviors.model.Sequence',
|
||||||
startNode: {
|
startNode: {
|
||||||
'@type': 'com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode',
|
'@type': 'com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode',
|
||||||
@@ -141,20 +141,24 @@ var sequenceObj = {
|
|||||||
deviceType: target.deviceType,
|
deviceType: target.deviceType,
|
||||||
deviceSerialNumber: target.serialNumber,
|
deviceSerialNumber: target.serialNumber,
|
||||||
customerId: customerId,
|
customerId: customerId,
|
||||||
locale: '',
|
locale: 'ja-JP',
|
||||||
textToSpeak: text
|
textToSpeak: text
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var rawSequenceJson = JSON.stringify(sequenceObj).replace(
|
||||||
|
/[\u0080-\uffff]/g,
|
||||||
|
function(c) { return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4); }
|
||||||
|
);
|
||||||
|
|
||||||
var bodyStr = JSON.stringify({
|
var bodyStr = JSON.stringify({
|
||||||
behaviorId: 'PREVIEW',
|
behaviorId: 'PREVIEW',
|
||||||
sequenceJson: JSON.stringify(sequenceObj),
|
sequenceJson: rawSequenceJson,
|
||||||
status: 'ENABLED',
|
status: 'ENABLED',
|
||||||
});
|
});
|
||||||
|
|
||||||
// リクエストボディをログ出力(認証・パラメータ確認用)
|
console.log('[DEBUG] textToSpeak:', text);
|
||||||
console.log('[DEBUG] sequenceJson:', JSON.stringify(sequenceObj, null, 2));
|
|
||||||
|
|
||||||
var ttsRes = await httpsRequest('/api/behaviors/preview', {
|
var ttsRes = await httpsRequest('/api/behaviors/preview', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ async function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// プレハブを探す
|
// プレハブを探す
|
||||||
const target = devices.find(d => d.serialNumber === 'G0922H08525302K5');
|
const target = devices.find(d => d.serialNumber === 'G0922H08525302K5'); // オフィスの右エコー(以前成功したデバイス)
|
||||||
console.log('\nTarget device:', target ? `${target.accountName}` : 'NOT FOUND');
|
console.log('\nTarget device:', target ? `${target.accountName}` : 'NOT FOUND');
|
||||||
|
|
||||||
if (!target) { process.exit(1); }
|
if (!target) { process.exit(1); }
|
||||||
@@ -72,7 +72,7 @@ async function main() {
|
|||||||
const customerId = bootstrap.authentication?.customerId;
|
const customerId = bootstrap.authentication?.customerId;
|
||||||
console.log(' Customer ID:', customerId);
|
console.log(' Customer ID:', customerId);
|
||||||
|
|
||||||
// 3. TTSリクエスト
|
// 3. TTSリクエスト(新Cookie + Alexa.Speak + locale:'ja-JP' + 日本語テキスト)
|
||||||
const sequenceObj = {
|
const sequenceObj = {
|
||||||
'@type': 'com.amazon.alexa.behaviors.model.Sequence',
|
'@type': 'com.amazon.alexa.behaviors.model.Sequence',
|
||||||
startNode: {
|
startNode: {
|
||||||
@@ -83,19 +83,30 @@ async function main() {
|
|||||||
deviceSerialNumber: target.serialNumber,
|
deviceSerialNumber: target.serialNumber,
|
||||||
customerId: customerId,
|
customerId: customerId,
|
||||||
locale: 'ja-JP',
|
locale: 'ja-JP',
|
||||||
textToSpeak: 'テストです。聞こえますか',
|
textToSpeak: '\u3053\u308c\u306f\u65e5\u672c\u8a9e\u306e\u30c6\u30b9\u30c8\u3067\u3059', // 「これは日本語のテストです」
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// non-ASCII を \uXXXX に強制エスケープ
|
||||||
|
// Amazon のパーサーが sequenceJson 内の raw UTF-8 を処理できない場合の回避策
|
||||||
|
const rawSequenceJson = JSON.stringify(sequenceObj).replace(
|
||||||
|
/[\u0080-\uffff]/g,
|
||||||
|
c => '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4)
|
||||||
|
);
|
||||||
const bodyObj = {
|
const bodyObj = {
|
||||||
behaviorId: 'PREVIEW',
|
behaviorId: 'PREVIEW',
|
||||||
sequenceJson: JSON.stringify(sequenceObj),
|
sequenceJson: rawSequenceJson,
|
||||||
status: 'ENABLED',
|
status: 'ENABLED',
|
||||||
};
|
};
|
||||||
const body = JSON.stringify(bodyObj);
|
const body = JSON.stringify(bodyObj);
|
||||||
|
|
||||||
console.log('\n[3] TTS送信...');
|
console.log('\n[3] TTS送信...');
|
||||||
|
// 送信内容確認(textToSpeakの部分が\uXXXXエスケープになっているか)
|
||||||
|
const ttsIdx = body.indexOf('textToSpeak');
|
||||||
|
console.log(' textToSpeak部分:', body.substring(ttsIdx, ttsIdx + 80));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ttsRes = await makeRequest('https://alexa.amazon.co.jp/api/behaviors/preview', {
|
const ttsRes = await makeRequest('https://alexa.amazon.co.jp/api/behaviors/preview', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
137
docs/alexa-api/12_ローカルで試したこと.md
Normal file
137
docs/alexa-api/12_ローカルで試したこと.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# Alexa 日本語 TTS 問題 試行記録
|
||||||
|
|
||||||
|
最終更新: 2026-03-03
|
||||||
|
担当: akira + AI (Antigravity)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 現在の問題
|
||||||
|
|
||||||
|
`/api/behaviors/preview` + `Alexa.Speak` を使って日本語テキストを TTSで発話させようとしているが、
|
||||||
|
**日本語Unicode文字だけが Amazon 側でフィルタリングされ、発話されない。**
|
||||||
|
ASCII文字(英語)は正常に発話される。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 環境
|
||||||
|
|
||||||
|
- テスト用スクリプト: `alexa-api/test_tts.js`(ローカルPCから直接 alexa.amazon.co.jp を叩く)
|
||||||
|
- 本番: `alexa-api/server.js`(VPS上のDockerコンテナ)
|
||||||
|
- テストデバイス: オフィスの右エコー (serial: G0922H08525302K5, type: A4ZXE0RM7LQ7A)
|
||||||
|
- Alexaアプリでデバイス言語設定: **日本語** に設定済み(確認済み)
|
||||||
|
- VPS IP: 162.43.33.56(大阪・Xserver Inc. = 日本国内 ✅ )
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 試行ログ(時系列)
|
||||||
|
|
||||||
|
### 【サーバー側での試行】(ChatGPT との会話ログより、2026-03-02〜03)
|
||||||
|
|
||||||
|
#### ❌ `speakType: 'ssml'` を `operationPayload` に追加
|
||||||
|
```json
|
||||||
|
"type": "Alexa.Speak",
|
||||||
|
"operationPayload": { ..., "speakType": "ssml" }
|
||||||
|
```
|
||||||
|
→ 変化なし。`Alexa.Speak` はSSML非対応のため無効。
|
||||||
|
|
||||||
|
#### ❌ `type: 'Alexa.SpeakSsml'` に変更 + `textToSpeak` にSSMLなし
|
||||||
|
```json
|
||||||
|
"type": "Alexa.SpeakSsml",
|
||||||
|
"operationPayload": { ..., "textToSpeak": text }
|
||||||
|
```
|
||||||
|
→ 英語も含めて完全無音(LEDも反応なし)。
|
||||||
|
|
||||||
|
#### ❌ `Alexa.SpeakSsml` + `textToSpeak: '<speak>'+text+'</speak>'`
|
||||||
|
→ 英語も無音。`Alexa.SpeakSsml` は `textToSpeak` ではなく別キーを要求する模様。
|
||||||
|
|
||||||
|
#### ❌ `Alexa.SpeakSsml` + `ssml: ssml`(キー名を変更)
|
||||||
|
→ 英語も発話せず。
|
||||||
|
|
||||||
|
**ChatGPTの最終見解:** `/api/behaviors/preview` では `Alexa.SpeakSsml` は動作しない(APIの癖)。`Alexa.Speak` に戻すしかない。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 【ローカルPCでの試行】(2026-03-03 午前)
|
||||||
|
|
||||||
|
#### ❌ `locale: 'ja-JP'` + 日本語テキスト(test_tts.js デフォルト)
|
||||||
|
```js
|
||||||
|
locale: 'ja-JP',
|
||||||
|
textToSpeak: 'テストです。聞こえますか'
|
||||||
|
```
|
||||||
|
→ 「エ」だけ発話(最初の「テ」の母音のみ)。
|
||||||
|
|
||||||
|
#### ✅ `locale: ''` + ASCII: `'hello'`
|
||||||
|
→ 「ハロー」と正常発話。英語は問題なし。
|
||||||
|
|
||||||
|
#### ❌ `locale: ''` + 日本語: `'テストです。聞こえますか'`
|
||||||
|
→ 「エ」のみ。デバイス言語が英語設定ならこの動作になるが、日本語設定確認済みのため別原因。
|
||||||
|
|
||||||
|
#### ❌ `locale: 'ja-JP'` + 日本語: `'テストです。これは日本語のテストです'`
|
||||||
|
→ 「えんえ」のような音のみ(断片的な音)。
|
||||||
|
|
||||||
|
#### ❌ `locale: 'ja-JP'` + ひらがな: `'あいうえお'`
|
||||||
|
→ 無音(LEDは点滅 = 通知は届いている)。
|
||||||
|
|
||||||
|
#### 🔍 `locale: 'ja-JP'` + 混在: `'あいうえおThis is Testあいうえお'`
|
||||||
|
→ 「ディスイズテスタ」のみ発話。
|
||||||
|
**重要: 日本語部分は無音、ASCII部分のみ日本語アクセントで読まれる。**
|
||||||
|
→ Amazon側で日本語Unicodeを除去している証拠。
|
||||||
|
|
||||||
|
#### ❌ `locale: 'ja-JP'` + Unicodeエスケープ: `'\u3053\u308c\u306f\u30c6\u30b9\u30c8\u3067\u3059'`
|
||||||
|
→ 無音。ファイルエンコード問題ではない(Unicodeエスケープ = `これはテストです` と同一)。
|
||||||
|
**→ 文字コードの問題ではないことが確定。**
|
||||||
|
|
||||||
|
#### ❌ `type: 'AlexaAnnouncement'` + locale:`'ja-JP'` + content[].speak構造
|
||||||
|
```json
|
||||||
|
"type": "AlexaAnnouncement",
|
||||||
|
"operationPayload": {
|
||||||
|
"content": [{ "locale": "ja-JP", "speak": { "type": "text", "value": "日本語のテストです" } }],
|
||||||
|
"target": { "devices": [...] }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
→ 「えんえせんと」("AlexaAnnouncement" を日本語発音で読んだもの)。
|
||||||
|
コンテンツではなくノード型名が読まれた → このノードタイプは別用途。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 確定した事実
|
||||||
|
|
||||||
|
| 事実 | 根拠 |
|
||||||
|
|------|------|
|
||||||
|
| 通知自体は届いている | LEDが点滅する |
|
||||||
|
| 英語ASCIIは正常発話 | "hello" → 「ハロー」、"This is Test" → 「ディスイズテスタ」 |
|
||||||
|
| 日本語Unicodeのみ除去される | 混在テキストで確認。Unicodeエスケープでも同じ |
|
||||||
|
| デバイス言語設定は日本語 | Alexaアプリで確認済み |
|
||||||
|
| サーバーIPは日本(大阪) | ipinfo.io で確認: Xserver Inc., JP |
|
||||||
|
| 文字コードは問題なし | Unicodeエスケープテストで確定 |
|
||||||
|
| `Alexa.SpeakSsml` 系は全て失敗 | 英語含め無音 |
|
||||||
|
| `AlexaAnnouncement` は別用途 | ノード型名が読まれた |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 仮説(現在)
|
||||||
|
|
||||||
|
Amazon の `/api/behaviors/preview` エンドポイントが、
|
||||||
|
何らかの理由で `textToSpeak` 内の日本語Unicodeを除去している。
|
||||||
|
|
||||||
|
考えられる原因:
|
||||||
|
1. **セッション/Cookie が古くなりJapanese TTS権限が変わった**(Cookie の再生成で解消する可能性)
|
||||||
|
2. **Amazonが API の挙動を変更した**(非公開APIのためいつでも変更しうる)
|
||||||
|
3. **別のAPIエンドポイントが必要**(未探索のルートがある可能性)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 次に試すべきこと
|
||||||
|
|
||||||
|
- [ ] `auth4.js` で Cookie を新規取得してテスト(セッションリセット)
|
||||||
|
- [ ] `/api/behaviors/preview` 以外のエンドポイントを探す(例: `/api/ap/d-notification`など)
|
||||||
|
- [ ] `Alexa.TextCommand` ノードタイプ(「テキストで命令を送る」別ルート)
|
||||||
|
- [ ] ローカルブリッジ方式(ローカルPCをプロキシにしてAmazonにリクエストを転送する)
|
||||||
|
- [ ] alexa-cookie / alexa-remote2 のソースコードから別APIを調査する
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- 実装記録: `docs/alexa-api/10_Alexa TTS API 実装記録 (2026-03-02).md`
|
||||||
|
- 上記ファイルに記録されていた未解決事項がこのファイルに続く
|
||||||
Reference in New Issue
Block a user