构建实时对话式视觉 AI 需要解决一个核心工程瓶颈:延迟。传统的合成视频生成管线多以异步批处理模式运行,渲染、编码并交付一个 MP4 视频文件通常需要数秒到数分钟不等。对于交互式应用场景——例如 AI 销售代表、虚拟医疗助手、客服数字人以及交互式数字化导师——数秒的延迟会彻底破坏对话流畅度并降低用户参与度。
D-ID 通过其实时流式传输基础设施解决了这一挑战,该架构将低延迟视频生成算法与 WebRTC 及 LiveKit 数据传输技术相结合。D-ID 的流式传输引擎无需在磁盘上生成完整的视频文件,而是实时渲染唇形同步的数字人视频帧,并以亚秒级延迟直接流式传输到客户端浏览器。虽然早期的实现依赖于旧版 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 |
+-------------------+
开发者通常根据自身的基础设施需求选择以下两种集成范式之一:
- 托管式 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 自动化这一预配过程。
创建 Agent
向 https://api.d-id.com/agents 发送 POST 请求,并附带 D-ID API 密钥进行身份验证:
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 信令交互。
方式 A:通过 @d-id/client-sdk 进行客户端集成(推荐)
官方 @d-id/client-sdk 将会话协商、会话描述协议(SDP)提议/应答交换以及交互式连通建立(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 Peer 的已附加媒体流
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 offer。ice_servers:用于 NAT 穿透的 STUN 和 TURN 服务器凭据列表。
-
初始化本地
RTCPeerConnection: 客户端创建一个配置了 D-IDice_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 Answer 提交回 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 提供的两种核心方法触发数字人动画。
模式 1:自主对话轮次(chat)
如果智能体配置了内置 LLM 逻辑,可直接向智能体的对话线程发送文本消息:
// 用户通过 UI 提交提示词
async function handleUserSubmit(userText) {
if (!agentManager) return;
// 将用户消息发送给智能体;智能体查询 LLM、生成语音并流式传输视频
await agentManager.chat(userText);
}
D-ID 智能体会自动处理用户轮次跟踪、LLM 推理、TTS 生成和视频合成,并在解析 token 和消息时触发 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 连通性检查)通常需要 800 毫秒到 2,500 毫秒不等,具体取决于网络环境。
- 反模式:仅在用户提交第一个问题后才触发
agentManager.connect()。 - 最佳实践:在用户打开应用界面、聚焦聊天输入框或完成身份验证时,立即在后台预热 WebRTC 连接。当用户发送第一条消息时,数字人已处于连接状态并可立即渲染响应。
2. 针对长 LLM 响应的分块流式传输
在调用 speak() 之前等待 LLM 生成完整的多句段落会引入显著的延迟(首字时间 Time-to-First-Token 加上完整生成时间)。
为了减少感知延迟:
- 在后端对 LLM 的 Server-Sent Events(SSE)token 流进行缓冲。
- 在自然句子边界处(如
.、?、!,或中文标点。、?、!)切分接收到的文本。 - 立即将第一句分发给 D-ID,与此同时 LLM 继续生成后续句子。
- 排队发送后续句子以保持连续不间断的语音输出。
3. 打断与插话处理
自然的对话需要允许用户在数字人说话中途进行打断。在 D-ID 的现代流式架构中,通过流畅的流式控制原生支持打断能力:
// 检测到数字人正在说话时用户开口发声
function onUserSpeechDetected() {
if (agentManager) {
// 中断当前的语音播放并将数字人重置为空闲聆听状态
agentManager.interrupt();
}
}
当触发打断事件时,D-ID 会截断服务端活跃的语音队列并停止视频传输,确保数字人不会继续覆盖用户的声音。
4. 基于 LiveKit 的表达型数字人(V4)
对于高度重视自然临场感的应用,D-ID 的 V4 Expressive Avatars 使用 LiveKit 传输替代了标准的点对点 WebRTC。表达型数字人在对话轮次之间保持持续的空闲动作(微妙的眨眼、头部微动和呼吸起伏),消除了早期数字人引擎中常见的画面定格感。
资源生命周期管理与计费成本控制
在大规模部署实时视频数字人时,必须严格遵守会话销毁规则和额度核算要求。
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 视频额度。
- 优化建议:为了实现成本效益最大化,应避免将极短的单字确认(如“Yes”、“好的”、“明白”)作为独立的
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() |
| 体验优化 | 支持用户插话打断与后台连接预热 | 麦克风活动时调用 agentManager.interrupt() |
| 生命周期 | 在页面退出或空闲超时后关闭 WebRTC 流 | agentManager.disconnect() / DELETE /streams/{id} |
通过将 D-ID 的实时流式传输引擎与结构化的 LLM 提示词编排及客户端 WebRTC 生命周期管理相结合,开发团队能够构建并在 Web 与移动平台上交付具备企业级性能、反应敏捷且表达自然的对话式 AI 数字人。