リアルタイム対話型ビジュアル AI の構築には、根本的なエンジニアリングのボトルネックである「レイテンシ」の解決が不可欠です。従来の合成動画生成パイプラインは非同期のバッチ処理モードで動作するため、MP4 動画ファイルのレンダリング、エンコード、配信までに数秒から数分を要します。AI 営業担当者、仮想医療アシスタント、カスタマーサポートアバター、対話型デジタル家庭教師といった対話型ユースケースでは、数秒の遅延が会話のテンポを崩し、ユーザーエンゲージメントを低下させてしまいます。
D-ID は、低遅延動画生成アルゴリズムと WebRTC および LiveKit データ転送を組み合わせたリアルタイムストリーミングインフラストラクチャによって、この課題に対処しています。D-ID のストリーミングエンジンは、ディスク上に完全な動画ファイルを生成する代わりに、リップシンクされたアバター動画フレームをリアルタイムに生成し、1秒未満の応答速度でクライアントブラウザに直接ストリーミングします。初期の実装はレガシーな Talks および Clips ストリーミングエンドポイント(/talks/streams)に依存していましたが、D-ID の最新アーキテクチャは D-ID Agents フレームワークと公式の @d-id/client-sdk を中心に構築されています。
本ガイドでは、D-ID Streaming API を大規模言語モデル(LLM)と統合するための完全なアーキテクチャパイプラインについて解説します。これには、バックエンドエージェントの設定、WebRTC セッションネゴシエーション、フロントエンドメディアストリームのアタッチ、対話インタラクションパターン、レイテンシ最適化、およびクレジットライフサイクル管理が含まれます。
アーキテクチャの概要:対話型アバターパイプライン
リアルタイム対話型アバターアプリケーションは、ユーザー入力、言語推論、音声合成、顔アニメーション、およびピアツーピアメディア配信にまたがる連携ループとして機能します。
+-------------+ Audio / Text +-------------------+
| | ---------------------> | Application Logic |
| User Client | | (STT / LLM / RAG) |
| (Browser) | <--------------------- +-------------------+
| | WebRTC Video / Audio |
| | <---------------------------+ | Text / Audio Chunks
+-------------+ | v
+-------------------+
| D-ID Streaming |
| Engine & Agents |
+-------------------+
開発者は通常、インフラストラクチャの要件に応じて以下の2つの統合パターンのいずれかを採用します。
- マネージド D-ID Agent ワークフロー:D-ID が LLM システムプロンプト、外部ナレッジベース(検索拡張生成 / RAG)、およびテキスト読み上げ(TTS)音声プロバイダーを内部で管理します。クライアントアプリケーションは
@d-id/client-sdkを使用してエージェントに直接接続し、agentManager.chat()経由でユーザープロンプトを送信します。 - カスタムバックエンドオーケストレーションワークフロー:開発者のバックエンドが、会話状態、ユーザー認証、独自ツール、ベクターデータベース、および LLM 呼び出し(OpenAI GPT-4o、Anthropic Claude、セルフホストモデルなど)の完全な制御を保持します。その後、バックエンドは生成されたテキストまたは合成音声を
agentManager.speak()または REST ストリーミングエンドポイント経由で D-ID にストリーミングし、アバターをアニメーション化します。
ステップ 1:REST API による Agent のプロビジョニングと設定
リアルタイムストリームを開始する前に、開発者はアバターのペルソナ、音声特性、およびインテリジェンス層を設定します。エージェントは D-ID Studio のインターフェースから作成することもできますが、本番環境のアプリケーションでは通常、REST API を介してこのプロビジョニングを自動化します。
エージェントの作成
D-ID API キーで認証を行い、https://api.d-id.com/agents に POST リクエストを送信します。
curl -X POST "https://api.d-id.com/agents" \
-H "Authorization: Basic <YOUR_BASE64_ENCODED_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"presenter": {
"type": "clip",
"presenter_id": "v2_public_Amber@0zSz8kflCN",
"voice": {
"type": "microsoft",
"voice_id": "en-US-JennyMultilingualV2Neural"
}
},
"llm": {
"type": "openai",
"provider": "openai",
"model": "gpt-4o",
"instructions": "You are an AI customer success specialist for an enterprise SaaS platform. Keep responses under two sentences, maintain an empathetic and professional tone, and guide users to relevant product features."
}
}'
主要な設定パラメータ
| 設定オブジェクト | パラメータ | 説明 |
|---|---|---|
presenter | type | アバターのレンダリングタイプ:talk(写真ベースの V2)、clip(事前レンダリングされたスタジオプレゼンター V3)、または expressive(LiveKit ベースの V4 アバター)。 |
presenter | presenter_id または source_url | 標準の D-ID プレゼンターの識別子、またはカスタム写真アバター用の一般公開されている HTTPS 画像 URL。 |
presenter.voice | type | 音声プロバイダーエンジン。一般的には microsoft(Azure Neural)または elevenlabs。 |
presenter.voice | voice_id | プロバイダーカタログに一致する特定の音声モデル識別子(例:en-US-JennyMultilingualV2Neural)。 |
llm | type および provider | ネイティブマネージド補完の場合は openai、外部バックエンドルーティングの場合は custom。 |
llm | instructions | アバターのペルソナ、制約、会話の境界を定義するシステム手順。 |
API は一意の agentId を含む JSON レスポンスを返します。この識別子をアプリケーションのデータベースに保存してください。
クライアント認証情報の保護
本番環境の Web アプリケーションやモバイルアプリケーションでは、クライアント側のコードにメインの D-ID API キーを露出させてはなりません。代わりに、バックエンド経由でスコープを絞った Client Key を生成します。
curl -X POST "https://api.d-id.com/agents/<AGENT_ID>/client-key" \
-H "Authorization: Basic <YOUR_BASE64_ENCODED_API_KEY>" \
-H "Content-Type: application/json"
D-ID Studio のダッシュボードで、Allowed Domains 設定を使用してこのクライアントキーを本番ドメインに制限し、不正な利用を防止します。
ステップ 2:低遅延 WebRTC ストリームの確立
リアルタイムの対話を行うには、クライアントブラウザと D-ID のレンダリングクラスタ間で WebRTC ピア接続を確立する必要があります。D-ID は、自動化された @d-id/client-sdk(強く推奨)と、REST ベースの直接的な WebRTC シグナリングという2つの統合ルートを提供しています。
方法 A:@d-id/client-sdk によるクライアント統合(推奨)
公式の @d-id/client-sdk は、セッションネゴシエーション、Session Description Protocol(SDP)のオファー/アンサー交換、および Interactive Connectivity Establishment(ICE)候補の収集を、洗練された JavaScript インターフェースに抽象化します。
1. SDK のインストール
npm install @d-id/client-sdk
2. AgentManager の初期化とメディアトラックのバインド
import * as did from "@d-id/client-sdk";
// DOM 要素
const videoElement = document.getElementById("avatar-video");
const statusElement = document.getElementById("connection-status");
const chatInputElement = document.getElementById("chat-input");
const sendButton = document.getElementById("send-btn");
let agentManager = null;
async function initializeAvatar() {
const agentId = "agt_xxxxxxxxxxxxxxxx";
const clientKey = "YOUR_CLIENT_KEY";
agentManager = await did.createAgentManager(agentId, {
auth: {
type: "key",
clientKey: clientKey
},
callbacks: {
// D-ID WebRTC ピアから受信したアタッチ済みメディアストリーム
onSrcObjectReady(mediaStream) {
videoElement.srcObject = mediaStream;
videoElement.play().catch((err) => console.error("Video autoplay error:", err));
},
// 接続状態の遷移を追跡:connecting、connected、disconnected、closed
onConnectionStateChange(state) {
statusElement.textContent = `Status: ${state}`;
console.log("WebRTC Connection State:", state);
},
// 受信した対話ターンの処理
onNewMessage(messages) {
const latest = messages[messages.length - 1];
console.log(`[${latest.role}]: ${latest.content}`);
},
// アバターの発話ライフサイクルを監視
onVideoStatusChange(status) {
console.log("Avatar Video Status:", status); // 例: "playing", "idle"
},
onError(error, errorData) {
console.error("D-ID Agent Error:", error, errorData);
}
}
});
// WebRTC セッションに接続
await agentManager.connect();
}
initializeAvatar().catch(console.error);
方法 B:手動 WebRTC シグナリングハンドシェイク(内部構造)
ヘッドレス環境、ブラウザ以外のクライアント(ネイティブモバイルアプリや組み込みデバイスなど)、またはレガシーシステムの場合、開発者は D-ID の REST エンドポイントを使用して直接 WebRTC ハンドシェイクを実行できます。
-
ストリームセッションの初期化:
https://api.d-id.com/agents/{agentId}/streams(またはレガシーな/talks/streams)にPOSTリクエストを送信します。D-ID は以下を返します。id:一意のストリームセッション ID。session_id:セッション追跡識別子。offer:D-ID の WebRTC メディアサーバーによって生成された SDP オファー。ice_servers:NAT 越えに必要な STUN および TURN サーバーの認証情報リスト。
-
ローカル
RTCPeerConnectionの初期化: クライアントは D-ID のice_serversで設定されたRTCPeerConnectionインスタンスを作成します。const pc = new RTCPeerConnection({ iceServers: response.ice_servers }); -
リモート記述の設定とローカルアンサーの作成:
await pc.setRemoteDescription(new RTCSessionDescription(response.offer)); const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); // SDP アンサーを D-ID に送信 await fetch(`https://api.d-id.com/agents/${agentId}/streams/${response.id}/sdp`, { method: "POST", headers: { "Authorization": `Basic ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ answer: answer, session_id: response.session_id }) }); -
ICE 候補の交換: クライアントピア接続の
onicecandidateイベントをリッスンし、各候補を D-ID の ICE エンドポイントに転送します。pc.onicecandidate = (event) => { if (event.candidate) { fetch(`https://api.d-id.com/agents/${agentId}/streams/${response.id}/ice`, { method: "POST", headers: { "Authorization": `Basic ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ candidate: event.candidate.candidate, sdpMid: event.candidate.sdpMid, sdpMLineIndex: event.candidate.sdpMLineIndex, session_id: response.session_id }) }); } }; -
メディアトラックの受信: メディアトラックを受信したら、リモートストリームを DOM にバインドします。
pc.ontrack = (event) => { videoElement.srcObject = event.streams[0]; };
ステップ 3:対話インタラクション:chat() と speak()
WebRTC セッションがアクティブになると、開発者は SDK が提供する2つの主要なメソッドを使用してアバターのアニメーションをトリガーできます。
パターン 1:自律的な対話ターン(chat)
エージェントに組み込みの LLM ロジックが設定されている場合、エージェントの会話スレッドに直接テキストメッセージを送信します。
// ユーザーが UI 経由でプロンプトを送信
async function handleUserSubmit(userText) {
if (!agentManager) return;
// ユーザーメッセージをエージェントに送信。エージェントは LLM を照会し、音声を生成して動画をストリーミング
await agentManager.chat(userText);
}
D-ID エージェントは、ユーザーターンの追跡、LLM 推論、TTS 生成、動画合成を自動的に処理し、トークンやメッセージの確定に応じて onNewMessage コールバックを発火します。
パターン 2:カスタムバックエンドオーケストレーション(speak)
エンタープライズデータベースに対するマルチターン RAG クエリ、ツール呼び出しを伴う LangChain エージェント、カスタム LLM エンドポイントなど、独自のワークフローが必要な場合、開発者は speak() メソッドを使用します。これにより、バックエンドで個別にレスポンスを生成し、アバターに特定のコンテンツを発話させることができます。
テキスト入力の送信
// エージェントに設定された TTS 音声を使用して、任意テキストを発話するようアバターに指示
await agentManager.speak({
type: "text",
input: "Welcome back, Sarah. I have pulled your quarterly analytics report."
});
事前合成された音声の送信
特殊な音声エンジン(カスタムクローン音声や専用音声モデルなど)を利用するアプリケーションでは、公開されている HTTPS 音声 URL を直接渡すことができます。
// 提供された音声と同期してアバターの口と表情がアニメーション
await agentManager.speak({
type: "audio",
audio_url: "https://storage.yourdomain.com/audio/response_chunk_01.mp3"
});
本番環境におけるレイテンシ最適化とパフォーマンス戦略
対話型 AI において、人間の遅延に対する許容度は非常に低く、1,000〜1,500ミリ秒を超える沈黙は不自然に感じられます。流暢な対話を実現するには、パイプラインの各フェーズを最適化する必要があります。
1. 接続の事前ウォームアップ
WebRTC 接続の確立(SDP 交換、STUN/TURN 候補の検出、ICE 接続チェック)には、ネットワーク状況に応じて通常 800ms から 2,500ms かかります。
- アンチパターン:ユーザーが最初の質問を送信した後にのみ
agentManager.connect()をトリガーする。 - ベストプラクティス:ユーザーがアプリケーション画面を開いたとき、チャット入力にフォーカスしたとき、または認証を完了した直後に、バックグラウンドで WebRTC 接続を事前ウォームアップします。ユーザーが最初のメッセージを送信する頃にはアバターはすでに接続されており、即座にレスポンスを描画できます。
2. 長文 LLM レスポンスのストリームチャンク化
LLM が完全な複数文の段落を生成するのを待ってから speak() を呼び出すと、大幅な遅延(最初のトークン生成時間+全体の完了時間)が発生します。
体感ラグを最小限に抑えるには:
- バックエンドで LLM の Server-Sent Events(SSE)トークンストリームをバッファリングします。
- 受信テキストを自然な文の区切り(
.、?、!、句点など)で分割します。 - LLM が後続の文を生成し続ける間に、最初の文を直ちに D-ID にディスパッチします。
- 後続の文をキューに入れ、途切れのない発話を維持します。
3. バージイン(割り込み)処理
自然な会話には、ユーザーがアバターの発話途中で割り込めることが求められます。D-ID の最新ストリーミングアーキテクチャでは、流暢なストリーミングコントロールによって割り込み機能がネイティブにサポートされています。
// アバターが話している最中にユーザーの発話を検出
function onUserSpeechDetected() {
if (agentManager) {
// 現在の音声再生を中断し、アバターをアイドル状態(傾聴状態)に戻す
agentManager.interrupt();
}
}
割り込みイベントが発生すると、D-ID はサーバー上のアクティブな発話キューを切り捨てて動画送信を停止し、アバターがユーザーの発言に被せて話し続けるのを防ぎます。
4. LiveKit による Expressive アバター(V4)
自然な存在感を重視するアプリケーション向けに、D-ID の V4 Expressive Avatars は標準のポイントツーポイント WebRTC ではなく LiveKit トランスポートを使用します。Expressive アバターは、会話ターンの間にも継続的なアイドル動作(微細なまばたき、頭部の微動、呼吸のリズム)を維持し、従来のエンジンでよく見られた静止画のような不自然さを解消します。
リソースライフサイクル管理と課金コストの考慮事項
リアルタイム動画アバターを大規模に展開するには、セッション終了ルールとクレジット計算の厳格な遵守が必要です。
1. 適切なセッション終了処理
終了されなかった WebRTC セッションはサーバーリソースを消費し続け、予期しないクレジットの枯渇につながる可能性があります。コンポーネントのアンマウントやウィンドウのアンロードイベント時に、明示的な切断ハンドラーを必ず実装してください。
// ウィンドウアンロード時にセッションをクリーンアップ
window.addEventListener("beforeunload", () => {
if (agentManager) {
agentManager.disconnect().catch(console.error);
}
});
// シングルページアプリケーションでのクリーンアップ(React / Vue / Svelte のアンマウント時)
function cleanupAvatarSession() {
if (agentManager) {
agentManager.disconnect();
agentManager = null;
}
}
REST 実装の場合は、セッション終了時に https://api.d-id.com/agents/{agentId}/streams/{streamId} へ DELETE リクエストを送信してください。
2. クレジット消費と 15 秒切り上げルール
D-ID は、アカウントの統一分数残高(Creative Reality™ Studio と API で共有)から動画生成の利用量を直接差し引きます。
- 15 秒切り上げインターバル:Streaming API を介して生成された動画の長さは、リクエストごとに最も近い 15 秒単位に切り上げられます。たとえば、4 秒の音声応答を生成すると、15 秒分の API 動画クレジットが消費されます。
- 最適化のポイント:コスト効率を最大化するため、「はい」「了解」「わかりました」といった極端に短い一言の確認を単独の
speak()リクエストとして送信することは避けてください。簡潔な肯定表現は後続の案内と組み合わせるか、UI のテキスト表示で微細な確認を行うようにします。
3. アイドルタイムアウトと非アクティブ保護
ユーザーがブラウザタブを開いたまま放置した際の孤立セッションを防ぐため、非アクティブタイマーを実装してください。90〜120秒間ユーザーのインタラクションがない場合は、ユーザーに確認を促すか、agentManager.disconnect() を使用して WebRTC 接続を自動終了します。
統合チェックリストのまとめ
| フェーズ | 主要アクション | 推奨ツール / エンドポイント |
|---|---|---|
| プロビジョニング | 音声、ペルソナ、LLM 指示を設定してエージェントを作成 | POST /agents |
| セキュリティ | ドメイン許可リストを適用したスコープ付きクライアントキーを発行 | POST /agents/{agentId}/client-key |
| 接続確立 | WebRTC ピアセッションを確立し、<video> ストリームをバインド | @d-id/client-sdk(agentManager.connect()) |
| 対話・通信 | 対話プロンプトまたは外部 LLM のテキストチャンクを送信 | agentManager.chat() または agentManager.speak() |
| UX 最適化 | ユーザーの割り込み(バージイン)とバックグラウンド接続事前ウォームアップを有効化 | マイクアクティビティ時に agentManager.interrupt() |
| ライフサイクル | ページ離脱時またはアイドルタイムアウト時に WebRTC ストリームを切断 | agentManager.disconnect() / DELETE /streams/{id} |
D-ID のリアルタイムストリーミングエンジンを、構造化された LLM プロンプトオーケストレーションおよびクライアント側 WebRTC ライフサイクル管理と組み合わせることで、開発チームは Web やモバイルプラットフォーム全体でエンタープライズグレードのパフォーマンスを発揮する、応答性に優れ自然な対話型 AI アバターを構築・提供できます。