AIアバターSDKは「どこまで切り離せるか」で選ぶ — Anamの3モードと、切り離すほど増える責任
full・custom_llm・passthroughを組んで引っかかったこと — 400エラー、PCM形式、そしてAPIキーはサーバーに
AIアバターサービスを選ぶとき、つい顔の品質から比べてしまう。実際に組み込んでみて考えが変わった。先に見るべきはどこまで切り離せるかだ。そして切り離すほど、こちら側のコードが重くなる。
この記事を読めば、Anamの3モードのどれを選ぶか、選んだあと何を自分で面倒見るかが整理できる。もうひとつ。APIキーはブラウザに置かない。
始まりは400エラーだった
app10にAnamのテスターを作るとき、最初はアバターだけを指定してみた(avatarId のみ)。標準のWebRTC接続では 400 avatar_only_requires_integration で弾かれる。顔だけ使いたいなら、声を決めるか、音声を自分で流し込む(passthrough)と宣言する必要がある。ここで「モード」という考え方が見えてきた。
3つのモード
| モード | personaConfig | Anamが持つもの | こちらが持つもの |
|---|---|---|---|
| full | voiceId + llmId + systemPrompt |
STT・LLM・TTS・顔 | なし |
| custom_llm | llmId: CUSTOMER_CLIENT_V1 |
STT・TTS・顔 | 返答生成 → talk() / createTalkMessageStream() |
| passthrough | enableAudioPassthrough: true |
顔だけ | STT・LLM・TTSすべて。PCMを sendAudioChunk() で |
custom_llm は MESSAGE_HISTORY_UPDATED を受けて自前のLLMを呼ぶ。ユーザーが割り込んだら、そのLLMのストリームもこちらで切る。Anamは代わりに切ってくれない。
passthrough は音声形式を合わせる仕事が増える。PCM s16le・16kHz・monoをbase64のチャンクで送り、endSequence() で終わりを伝える。マイク側も、ブラウザが出すwebm/opusをWAVに変換するコードを自分で書いた。思ったより手間がかかる。
いつどれを選ぶか
とにかく会話する顔が欲しく、返答はAnam側のLLMで足りる → full
自社データ・自社プロンプトで答えさせたい(社内文書検索、相談フロー) → custom_llm。たいていはここが現実的なラインだ
声まで自前にしたい(クローンした声、別のTTS) → passthrough。音声形式・割り込み・同期を全部引き受ける
他のアバターサービス(HeyGen、Tavusなど)もLLMやTTSの差し替えに対応しているかは、今回ドキュメントで確認できていない。比べるときは同じ質問ひとつで足りる。「どの段階からこちらで持てて、そのとき何がこちらに来るのか」。
APIキーはサーバーに
Anam公式の JavaScript quickstart はローカルのHTMLデモなので、APIキーをブラウザのスクリプトに書いてセッショントークンを取りに行く。デモとしては楽だ。そのまま公開すると、キーが誰にでも見える。
app10ではこう分けた。
APIキーはサーバーにだけ置く
サーバーがpersonaConfigを詰めた短命のセッショントークンを発行し、ブラウザはそのトークンでWebRTCを開くだけにする
モードもサーバーが決める。ブラウザがpersonaConfigを勝手に書き換えられないようにし、知らないmodeが来たらfullに落とす
モードごとのpersonaConfigが正しく出ているかはrequest spec(WebMock)で確認した。実際のキーで3モードすべてをつなぐのはまだだ。passthroughに16kHzではなく24kHzのPCMを入れたらどう崩れるかは、実際に見てから追記する。
アバターSDKの組み込みで、モード選びやキー管理の構成に迷っているなら、negabaroに相談してほしい(お問い合わせ)。
キーコンセプト
アバターだけだと400 — 声を決めるかpassthroughを宣言する
たいていはcustom_llm — 自前のLLMを使い、割り込み時はストリームを自分で切る
passthroughはPCM s16le・16kHz・monoをbase64チャンクで、終わりはendSequence()
APIキーはサーバーに、ブラウザにはセッショントークンだけ。モードもサーバーが決める