13 KiB
Alexa TTS API マスタードキュメント
最終更新: 2026-03-03 状態: サーバーからの日本語TTS未解決(調査中)
2026/03/03 10:24 akira記録 akiraが下記の変更をしましたので、内容を読んでください。
-
構成とサーバーへのファイル受け渡し方法を変更しました /home/claude/windmill_workflow に、https://gitea.keinafarm.net/akira/windmill_workflow.gitをcloneしました これにより、 C:\Users\akira\Develop\windmill_workflow とのやり取りはgiteaを使って出来るようになります。
-
docker compose up -dで、「 Docker 内部の IP アドレスが変わり、Traefik のルーティングが古い IP を参照したまま 502/504 エラーになる」のは、原因があるはずです。(他のコンテナで、問題になっていないので) 調査して、Traefik 再起動が不必要になるようにしたいです
概要 Windmill から家中の Echo デバイスに任意のテキストを読み上げさせる API サーバー。Node.js + Express で実装し、Docker コンテナとして Windmill サーバー上で稼働する。
⚠ 現在の問題: ローカル PC からは日本語TTS動作確認済み。しかしサーバー(keinafarm.net)のコンテナからリクエストすると日本語文字が Amazon 側で除去されて発話されない。原因は Amazon の IP ベースフィルタリング(海外IPからは日本語 textToSpeak を無視する模様)。調査継続中。
ファイル構成 ファイル 場所 役割 備考 server.js alexa-api/(リポジトリ) Express API サーバー本体 本番コード。変更したらビルド・再デプロイが必要 Dockerfile alexa-api/(リポジトリ) Docker イメージ定義 node:20-alpine ベース。server.js と package*.json をコピー docker-compose.yml alexa-api/(リポジトリ) コンテナ起動設定 windmill_windmill-internal ネットワーク接続。外部ポート非公開 package.json / package-lock.json alexa-api/(リポジトリ) npm 依存関係 本番: express のみ。devDeps に alexa-remote2(不使用) .env.example alexa-api/(リポジトリ) 環境変数テンプレート ALEXA_COOKIE=xxx の形式 .env alexa-api/(リポジトリ、.gitignore 対象) 実際の Cookie 保管 Git にコミットしない。ローカル作業後に scp でサーバーへ転送 auth4.js alexa-api/(リポジトリ) Amazon 認証・Cookie 取得スクリプト ローカルのみで実行(Windows PC) auth.js / auth2.js / auth3.js alexa-api/(リポジトリ) auth4.js の旧バージョン 参考用。実際は auth4.js を使う test_tts.js alexa-api/(リポジトリ) ローカルテスト用スクリプト 直接 alexa.amazon.co.jp を叩いて動作確認 サーバー上のファイル場所: /home/claude/alexa-api/(git リポジトリとは別にコピーして管理)
サーバーへのデプロイ手順 server.js や Dockerfile、package.json を変更した場合は以下の手順でサーバーに反映する。
Step 1: ローカルでファイルを編集 リポジトリ(c:\Users\akira\Develop\windmill_workflow\alexa-api\)でファイルを編集する。
Step 2: scp でサーバーに転送 変更したファイルをサーバーに scp で転送する:
server.js を変更した場合 scp alexa-api/server.js keinafarm-claude:/home/claude/alexa-api/server.js # Dockerfile や package.json を変更した場合 scp alexa-api/Dockerfile keinafarm-claude:/home/claude/alexa-api/Dockerfile scp alexa-api/package.json keinafarm-claude:/home/claude/alexa-api/package.json scp alexa-api/package-lock.json keinafarm-claude:/home/claude/alexa-api/package-lock.json # .env を更新した場合(Cookie 更新時など) scp alexa-api/.env keinafarm-claude:/home/claude/alexa-api/.env
Step 3: サーバーでビルドして再起動 ⚠ 重要: docker compose restart はイメージをリビルドしない。server.js 等を変更した場合は必ず build + up -d を実行すること。
SSH でサーバーに接続してビルド+起動 ssh keinafarm-claude cd /home/claude/alexa-api # イメージをビルド(server.js 等の変更を反映) sudo docker compose build # コンテナを再作成して起動 sudo docker compose up -d # Traefik を再起動(コンテナ再作成後は必須) sudo docker restart traefik
Step 4: 動作確認
ヘルスチェック(Windmill ワーカーコンテナ内から) curl http://alexa_api:3500/health # ログ確認 sudo docker logs alexa_api -f # デバイス一覧確認 curl http://alexa_api:3500/devices
Cookie だけ更新する場合(server.js 変更なし)
.env をサーバーに転送 scp alexa-api/.env keinafarm-claude:/home/claude/alexa-api/.env # コンテナを再起動(restart で OK → イメージのリビルド不要) ssh keinafarm-claude 'sudo docker compose -f /home/claude/alexa-api/docker-compose.yml restart' # ※ Traefik の再起動は不要(コンテナ再作成しないため)
Traefik 再起動が必要な理由 docker compose up -d はコンテナを「再作成」する(docker compose restart は既存コンテナを再起動するだけ)。コンテナが再作成されると Docker 内部の IP アドレスが変わり、Traefik のルーティングが古い IP を参照したまま 502/504 エラーになる。
対処: sudo docker restart traefik で Traefik に新しい IP を再検出させる。
この問題は Traefik の設定で watch: true にすれば自動解消できるが、現状はコンテナ再作成のたびに手動で Traefik を再起動する運用としている。
docker-compose.yml の内容 services: alexa-api: build: . container_name: alexa_api restart: unless-stopped env_file: - .env environment: - PORT=3500 networks: - windmill_windmill-internal # 外部には公開しない(Windmill ワーカーから内部ネットワーク経由でのみアクセス) # デバッグ時は以下のコメントを外す: # ports: # - "127.0.0.1:3500:3500" networks: windmill_windmill-internal: external: true
認証方法(auth4.js) Amazon Japan OpenID フローを自前で実装。ローカル PC(Windows)でのみ実行する:
ローカルPC の alexa-api ディレクトリで実行 cd alexa-api AMAZON_EMAIL="メールアドレス" AMAZON_PASSWORD="パスワード" node auth4.js
成功すると alexa-api/.env が生成または更新される。
ログインフローの概要:
GET https://www.amazon.co.jp/ap/signin?openid.assoc_handle=amzn_dp_project_dee_jp hidden フィールド(anti-csrftoken-a2z, appActionToken, workflowState 等)を抽出 POST でメール/パスワードを送信 alexa.amazon.co.jp/api/apps/v1/token へのリダイレクトをたどる 取得した Cookie(at-acbjp, sess-at-acbjp, sst-acbjp, session-token 等)を .env に保存 TTS の仕組み(server.js) alexa-remote2 は使わない直接 API 実装。Endpoints:
POST /speak — { device: "デバイス名 or serial", text: "しゃべる内容" } GET /devices — デバイス一覧 GET /health — ヘルスチェック 内部の API 呼び出し順序:
GET /api/language → Set-Cookie: csrf=XXXXX を取得(毎リクエストごと) GET /api/bootstrap → customerId を取得(キャッシュ: 永続)(A1AE8HXD8IJ61L) GET /api/devices-v2/device → デバイス一覧(5分キャッシュ) POST /api/behaviors/preview にシーケンス JSON を送信 POST /api/behaviors/preview のボディ構造:
{ behaviorId: "PREVIEW", sequenceJson: JSON.stringify({ "@type": "com.amazon.alexa.behaviors.model.Sequence", startNode: { "@type": "com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode", type: "Alexa.Speak", operationPayload: { deviceType: "...", deviceSerialNumber: "...", customerId: "A1AE8HXD8IJ61L", locale: "ja-JP", // ← 重要(下記参照) textToSpeak: "発話内容" } } }), status: "ENABLED" }
ヘッダーに csrf: XXXXX と Cookie に csrf=XXXXX の両方が必要。Content-Length は Buffer.byteLength で計算(マルチバイト文字対応)。
⚠ locale パラメータについて(重要・未解決) locale 値 ローカル PC から サーバー(keinafarm.net)から ""(空文字) ✅ 日本語・英語・漢字全て発話 ❌ 英語TTSになり日本語部分が発話されない "ja-JP" ❌ 一瞬音が出るだけ(失敗) ❌ 日本語文字が Amazon 側で除去され英字のみ発話 現在 server.js では locale: "ja-JP" に設定している。
仮説: Amazon が海外IP(keinafarm.net = 非日本IP)からのリクエストを IP ベースでフィルタリングし、textToSpeak の日本語文字を除去している。Alexa.TextCommand は同じ問題がない(異なる API パス)。
確認済み事実: alexa_api の server.js ログには日本語テキストが正しく届いている。除去は Amazon サーバー側で発生。
次の調査候補:
SSML の タグで強制的に日本語 TTS を指定できるか Alexa.TextCommand で「次を読み上げて:{text}」形式が使えるか ローカルブリッジ方式(ユーザーのローカル PC で小さなプロキシサーバーを動かし、クラウドサーバーからローカル経由で alexa.amazon.co.jp を叩く) デバイス一覧(Echo デバイスのみ) 名前 deviceType serialNumber プレハブ A4ZXE0RM7LQ7A G0922H085165007R リビングエコー1 ASQZWP4GPYUT7 G8M2DB08522600RL リビングエコー2 ASQZWP4GPYUT7 G8M2DB08522503WF オフィスの右エコー A4ZXE0RM7LQ7A G0922H08525302K5 オフィスの左エコー A4ZXE0RM7LQ7A G0922H08525302J9 寝室のエコー ASQZWP4GPYUT7 G8M2HN08534302XH Windmill スクリプト(u/admin/alexa_speak) export async function main(device: string, text: string) { const res = await fetch("http://alexa_api:3500/speak", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ device, text }), }); if (!res.ok) throw new Error("alexa-api error " + res.status); return res.json(); }
device はデバイス名(日本語)またはシリアル番号で指定可能。Windmill ワーカーから http://alexa_api:3500 でアクセス(windmill_windmill-internal ネットワーク経由)。
Cookie の更新手順 Cookie は数日〜数週間で期限切れ。切れたら:
1. ローカル PC で Cookie を取得 cd alexa-api AMAZON_EMAIL="メールアドレス" AMAZON_PASSWORD="パスワード" node auth4.js # → alexa-api/.env が更新される # 2. サーバーに .env を転送 scp alexa-api/.env keinafarm-claude:/home/claude/alexa-api/.env # 3. コンテナを再起動(restart で OK、リビルド不要) ssh keinafarm-claude 'sudo docker compose -f /home/claude/alexa-api/docker-compose.yml restart' # ※ Traefik 再起動は不要(コンテナ再作成なし)
既知の問題・落とし穴 docker compose restart ≠ リビルド: server.js を変更しても restart ではコンテナ内のコードは古いまま。build + up -d が必要。 コンテナ再作成後は Traefik 再起動必須: up -d でコンテナ再作成すると Docker 内部 IP が変わり Traefik が 502/504 を返す。sudo docker restart traefik で解消。 alexa-remote2 は使えない: 取得した Cookie 文字列を受け付けない(内部で再認証しようとして失敗)。直接 API 実装が必要。 CSRF トークンは Cookie と ヘッダーの両方に必要: csrf ヘッダーだけ、または Cookie だけでは認証失敗。 operationPayload に customerId 必須: ないと 400 エラー。 レート制限: 短時間に連続リクエストすると HTTP 429 または 200 で音が出ない。通常の通知用途では問題なし。 git push がブロックされる: Gitea の pre-receive フック(remote: Gitea: User permission denied for writing)で push が失敗する。根本原因は未調査。ファイル転送は scp で行っている。 firstRunCompleted: false はデバイス設定の未完了フラグ: TTS には直接影響しない(root cause ではなかった)。 サーバー上の運用コマンド一覧