DOCS

接入文档

把 AI 数字人嵌入你的网站,3 种方式任选,最少 2 行代码即可上线。

工作原理:平台负责呈现,大脑由你接入

平台提供数字人的形象渲染、语音合成(TTS)与口型驱动;对话大脑(LLM)由你的自有系统接入。平台不绑定任何模型,也不代管你的对话逻辑。

用户在你自己的界面输入你的 LLM 生成回复回复经接口传给数字人数字人以语音与口型呈现

「自带 LLM」(BYO-LLM)是实时数字人领域的通行架构,Anam、Tavus、D-ID 等均采用同一模式:平台专注实时呈现,对话智能由接入方自主掌控。用你自己的输入 UI 收集用户问题,调你的 LLM 后用 speak() / lipsync() 驱动数字人开口即可。

查看数字人实时对话效果 —— 前往官网体验

三步快速接入

1创建数字人

进入「数字人管理」→ 新建,选择形象和音色。系统会自动生成一把默认 API Key。

前往创建
2取密钥 + 配白名单

打开该数字人的「嵌入代码」页,复制它的 API Key,并把你的站点域名加进域名白名单。

我的数字人
3复制代码嵌入

下方选择一种接入方式,把 YOUR_API_KEY 替换成自己的即可。

怎么选?

先看这张对比表,再挑最适合自己的方式。

方式适用场景可控性样式隔离
JS SDK单页应用、需要事件订阅与程序化控制⭐⭐⭐
Web ComponentVue / React / 原生 HTML,要求简洁⭐⭐高(Shadow DOM)
iframe静态网页、CMS、公众号文章、零依赖最高(沙箱)

选哪条接入路线?

根据你的 LLM 输出什么决定 — 只出文本走 A,自带音频走 B。两条路线可在同一个 widget 实例里混用。

A · 文本

你的 LLM 只输出文本

你的后端只需把 LLM 文字回复发给我们,音色合成和数字人口型由我们完成。接入最简单,3 行代码上线。

优势
  • 接入最快:只需返回文本
  • 自动多语言(Azure TTS 全语种)
  • 流式输出,首句响应 <800ms
  • 音色由控制台一键切换,无需重新发版
限制
  • 音色限我们 Azure 音色库,无法用自家定制音色
  • 不能复用你已有的 TTS 投入
关键 API
widget.speak(text) // 你的 LLM 文本 → 平台合成 TTS + 口型
适合场景
  • 通用客服 / FAQ 问答
  • 多语言场景(英 / 日 / 韩 / 阿语等)
  • MVP 快速验证,后续可平滑切到路线 B
B · 音频

你的 LLM 输出文本 + 音频

你的后端用自己的 TTS 生成音频,我们只负责把音频对齐成口型。完全掌控音色和发音,适合品牌定制场景。

优势
  • 音色完全可控,自家明星 / IP / 品牌音
  • 可用方言 / 定制发音 / 情感语气
  • 复用你已有的 TTS 平台投入
  • 走 audioUrl 时不消耗我们 TTS 字符额度
限制
  • 需要自备 TTS(成本和延迟你自己控制)
  • 音频必须 WAV 容器、单文件 ≤ 10MB
  • 走 audioUrl 时音频域名需对我们放行 CORS
关键 API
widget.lipsync(audioUrl) // 你的音频(URL) → 平台只做口型widget.lipsync(audioBlob) // 你的音频(Blob) → 平台只做口型
适合场景
  • 品牌 IP / 明星定制音色数字人
  • 方言播报(粤语 / 闽南语 / 特色英语等)
  • 已有 TTS 平台用户,只想加口型
拿不准选哪个?
  • 第一次接入 → 选 A,只要回个字符串就能让数字人开口,3 行代码跑通
  • 必须用自家音色 / 明星 IP → 选 B,你的 TTS 出音频,我们对口型
  • 没想清楚 → 先 A 上线,后面想换 B 不用改架构,同一个 widget 实例换 API 即可

在 React / Vue 里怎么用?

用我们的 npm 包 —— 真正的 React / Vue <AIAvatar> 组件,带 ref 句柄调 speak() / lipsync()。安装:npm i @ai-avatar/embed-sdk

React · A 路线
// 路线 A:你的 LLM 返回文本,平台做 TTS + 口型
// npm i @ai-avatar/embed-sdk
import { useRef, useState } from 'react';
import { AIAvatar, type AvatarInstance } from '@ai-avatar/embed-sdk/react';

export function AvatarCanvas() {
  const avatar = useRef<AvatarInstance>(null);
  const [ready, setReady] = useState(false);

  // 你自己的输入框提交 → 调你的 LLM → 驱动数字人开口
  async function onUserSend(text: string) {
    const reply = await myLLM(text);              // 你自己的 LLM
    const r = await avatar.current?.speak(reply); // 平台 TTS + 口型
    // r?.usage?.chars, r?.interrupted ...
  }

  return (
    <>
      {/* 你的容器 —— 数字人 100% 撑满它;尺寸 / 位置 / 圆角用你自己的 CSS */}
      <div id="avatar-box" style={{ width: 360, height: 480, borderRadius: 16, overflow: 'hidden' }} />
      <AIAvatar
        ref={avatar}
        apiKey="YOUR_API_KEY"
        mount="#avatar-box"        // 纯展示画布(传了 mount 即自动 inline)
        onAvatarLoaded={() => setReady(true)}      // 就绪后再放开你的输入框
        onSpeakingChange={(speaking) => {/* 同步你的 UI */}}
      />
      {/* ...你自己的输入框;提交时调 onUserSend,就绪前禁用... */}
    </>
  );
}

// ↓ 换成你自己的 LLM 调用
async function myLLM(text: string) {
  const res = await fetch('/api/my-llm', { method: 'POST', body: JSON.stringify({ text }) });
  return (await res.json()).reply as string;
}

完整参数参考

所有可用参数 + 类型 + 默认值 + 说明,按接入方式切换查看。

apiKeystring必填

用于鉴权、授权本次请求的密钥。

格式: 格式 'sk-<48位>'

API Key,随数字人自动生成,在该数字人的「嵌入代码」页复制。建议一站点一把 Key 顺便加 Origin 白名单,泄漏时方便定向回收

mountstring | HTMLElement可选默认:

把数字人挂成一块撑满你自己容器的画布。

格式: CSS 选择器或 DOM 元素,如 '#ai-avatar'

mount: '#ai-avatar'(或传入元素)。displayMode 为 'inline' 时必填

appearance.displayModeenum可选默认: 'bubble'

选择撑满容器的画布,还是悬浮气泡。

可选值
'inline'会说话的画布:数字人撑满你的 mount 容器(推荐的纯展示范式),常驻画面、嵌进你自己的布局
'bubble'固定在视口角落的悬浮气泡(默认),点击展开

数字人在页面上的放置方式。'inline' 需要配 mount 容器;'bubble' 是自管理的悬浮 widget

appearance.positionenum可选默认: 'bottom-right'

将组件固定在视口的某个角落。

可选值
'bottom-right'右下角(默认)
'bottom-left'左下角
'top-right'右上角
'top-left'左上角

传入 appearance.position, SDK 自动应用到 wrapper 的 fixed left/right + top/bottom

appearance.sizeenum可选默认: 'normal'

设置数字人的展示尺寸。

可选值
'compact'340 × 55vh,移动端友好
'normal'400 × 66vh(默认)
'large'480 × 80vh,大屏 demo

传入 appearance.size, SDK 展开时把对应像素值套到 wrapper width/height

appearance.scalenumber可选默认: 1

等比缩放数字人。

格式: 推荐范围 0.5 ~ 1.5

传入 appearance.scale: 0.9, SDK 展开 panel 时给 iframe 加 transform: scale(0.9)

appearance.zIndexnumber可选默认: 2147483647

控制数字人相对页面元素的层叠顺序。

格式: 任意整数;你的 UI 被遮挡时调低 (如 1) 让你的元素盖在数字人之上

传入 appearance.zIndex: 1, SDK 应用到 wrapper

appearance.interactiveboolean可选默认: false(指针事件默认穿透)

决定数字人捕获鼠标事件,还是让事件穿透。

可选值
trueiframe 接收鼠标 / 触摸事件
false整片区域点击穿透到下层你的 UI

传入 appearance.interactive: false, SDK 给 wrapper 加 pointer-events: none

appearance.left / right / top / bottomnumber | string可选默认:

以像素级偏移精确定位组件。

格式: number 自动加 px (24 → '24px'); string 原样透传 ('10vw' / '5%' / '50px')

传入 appearance.left: 24 等, SDK 把对应字段写到 wrapper inline style (未指定的边保持 auto)

appearance.width / heightnumber | string可选默认:

以显式像素尺寸覆盖 size 预设。

格式: number 自动加 px (320 → '320px'); string 原样透传 ('40vw' / '60%' / '500px')

传入 appearance.width / height, SDK 展开时写到 wrapper inline style

behavior.lazyLoadboolean可选默认: false

延迟加载数字人至用户打开时,加快首屏速度。

可选值
falseinit 立即创建 iframe + 加载 GLB + 建 session (默认)
trueinit 不创建, 等你调 widget.show() 才触发

传入 behavior.lazyLoad: true, init 只占位 wrapper, widget.show() 调用时才真正下载 GLB + 建 session

运行时控制 API

加载完成后,通过实例方法以编程方式驱动数字人:SDK 调用 init() 返回的实例,Web Component 直接调用 <ai-avatar> 元素。speak() 与 lipsync() 为异步方法,返回 Promise<SpeakResult>。

适用接入方式: JS SDK Web Component
调用形式
SDKconst widget = AIAvatar.init({...}); await widget.speak('你好')
WCawait document.querySelector('ai-avatar').speak('你好')
返回值:SpeakResultspeak() 与 lipsync() 所返回 Promise 的解析值,字段均为可选。
audioDurationnumber
本次播报的音频时长,单位为秒。
usage{ chars?: number; seconds?: number }
本次调用的计量用量:合成字符数(chars)与音频秒数(seconds)。
interruptedboolean
为 true 时表示本次播报被新的驱动调用或 stop() 中断。
方法签名说明
speak()speak(text, options?): Promise<SpeakResult>A 路线:传文本,平台合成 TTS 并驱动口型。Promise 在整段播完(或被打断)时 resolve、失败时 reject。await 它读 SpeakResult(时长 + 用量)。
lipsync()lipsync(audio, text?): Promise<SpeakResult>B 路线:传音频(WAV 链接,或 Blob / File ≤ 10MB),平台只驱动口型(不做 TTS)。Promise<SpeakResult> 语义同 speak()。
waitAvatarLoaded()waitAvatarLoaded(): Promise<void>数字人 GLB + 口型引擎就绪时 resolve(已加载则立即 resolve)。纯展示客户在放开自己的输入框前 await 它。
isAvatarLoaded()isAvatarLoaded(): boolean同步检查数字人 GLB 是否已加载完。
示例: 用自己的 UI 驱动数字人并 await 结果 (JS SDK)

纯展示范式 —— 输入框和 LLM 都由你自带,数字人只是一块画布。speak() / lipsync() 在数字人说完时 resolve;读 SpeakResult 拿用量,被新调用打断时 interrupted 为 true。

JS SDK
// HTML: <div id="ai-avatar" style="width:360px;height:480px;border-radius:16px;overflow:hidden"></div>

const avatar = AIAvatar.init({
  apiKey: 'sk-...',
  mount: '#ai-avatar',                                          // 你的容器; 数字人 100% 撑满它
  appearance: { displayMode: 'inline' },                       // 纯展示画布
  onAvatarLoaded() { /* 数字人就绪 — 放开你的输入框 */ },
  onSpeakingChange(speaking) { /* 说话时同步你自己的 UI */ },
});

// 也可以用 await 等就绪, 替代上面的回调
await avatar.waitAvatarLoaded();

// 你自己的输入框提交 → 调你的 LLM → 驱动数字人
async function onUserSend(text) {
  const reply = await myLLM(text);          // 你自己的 LLM
  const result = await avatar.speak(reply); // 平台 TTS + 口型
  if (!result.interrupted) {
    console.log('说完了, TTS 用了字符:', result.usage?.chars);
  }
}

回调 / 事件

在 init() 里作为回调传入(SDK),或作为事件监听 —— Web Component 的 avatar-* CustomEvent / iframe 的 postMessage AVATAR_*。

onAvatarLoaded() => void

数字人 GLB + 口型引擎就绪时触发。纯展示客户在这里放开自己的输入框。

SDK: onAvatarLoaded() · WC: avatar-loaded · iframe: AVATAR_LOADED
onSpeakingChange(speaking: boolean) => void

数字人开始 / 停止说话时触发。同步你自己的 UI —— 比如说话时禁用输入框。

SDK: onSpeakingChange(speaking) · WC: avatar-speaking (e.detail.speaking) · iframe: AVATAR_SPEAKING
onError(err: { code: string; message: string }) => void

出错时触发 —— 加载失败、驱动(speak / lipsync)失败、鉴权 / 配额问题。

SDK: onError(err) · WC: avatar-error (e.detail) · iframe: ERROR

组件控制

控制组件的显隐、形态与生命周期。show() / hide() 控制整体显隐,expand() / collapse() 在气泡与展开形态间切换 —— 二者为相互独立的维度。

方法签名说明
show()show(): void显示 widget。lazyLoad 模式下第一次调用会触发 iframe 创建 + GLB 下载 + session 创建;之后调用只切换可见性,不重新下载。
hide()hide(): void隐藏整个 widget(数字人 + 气泡),状态保留,再调 show() 可恢复。
expand()expand(): voidbubble → full 展开。仅当组件以气泡形态启动时有意义。
collapse()collapse(): voidfull → bubble 折叠。仅当组件有气泡形态时有意义。
toggle()toggle(): voidexpand / collapse 之间切换。
reloadConfig()reloadConfig(): void你在控制台改完配置后,通知嵌入端重拉(无需刷新页面)。
destroy()destroy(): void彻底销毁(移除 DOM + 解绑事件),不可恢复。Web Component 同步把 <ai-avatar> 元素自身从 DOM 移除;SDK 要重新调 AIAvatar.init()。
iframe 接入下没有运行时实例, 用以下等价方式
  • speak (文本→TTS+口型): postMessage `{ __avatar: true, id: String(Date.now()), type: 'SPEAK_TEXT', payload: { text } }` 给 iframe.contentWindow
  • lipsync (音频→口型): postMessage `{ __avatar: true, id, type: 'SPEAK_AUDIO', payload: { audioUrl, text } }`(URL 需 CORS 放行嵌入域)或 `payload: { audio, text }`(audio 为 Blob/File, 跨 origin 透传)
  • 两者都监听回传的 `SPEAK_RESULT` 消息(按发出时的 id 关联)拿 SpeakResult
  • show / hide: 父页改 iframe.style.display = 'block' / 'none' 即可
  • expand / collapse: postMessage `{ __avatar: true, type: 'EXPAND' / 'COLLAPSE' }` 给 iframe.contentWindow, 配合 HEIGHT_CHANGE bridge 接收 PureJS 回的尺寸
  • destroy: iframe.remove()

常见错误码

状态码含义排查建议
400请求参数错误对照接口文档检查必填字段(如 text 长度、voiceId 是否合法)。
401Token 无效或已禁用检查 API Key 是否拼错、是否已在控制台禁用。
402额度耗尽前往「计费与用量」升级套餐或购买流量包。
403域名未在白名单到项目的「嵌入代码」页,把当前域名加进域名白名单。
404资源不存在核对 sessionId 拼写,以及是不是别的用户的资源。
422文本与音色语言不匹配 / 合成音频为空比如给中文文本配了非中文音色。到项目设置把音色换成与你内容语言一致的。
429速率超限客户端降低调用频率,或升级套餐提升上限。

LLM 失败兜底:别让数字人卡在转圈

数字人只会说你回传的文本。你的 LLM 超时或挂了时必须在你自己的 UI 里兜底,否则数字人没有回复、用户一直干等。

  • 超时 / 出错时,调 speak('抱歉,出错了') 让数字人说句兜底,别冷场。
  • 用户连续发问:speak / lipsync 会自动中断上一条播放,但你自己的 UI 要自己去重 / 排队。
  • 必须等 onAvatarLoaded / avatar-loaded 信号后再放行用户输入,否则首句会「听到声音却看不到数字人、且没口型」。
JS
// 你自己的输入处理 → 你的 LLM → 驱动数字人
async function onUserSend(text) {
  try {
    const reply = await myCustomerLLM(text);
    widget.speak(reply);
  } catch (e) {
    widget.speak('抱歉,我这边出了点问题,请稍后再试');  // 兜底回复
  }
}

安全:务必配置域名白名单

前端嵌入场景 API Key 不可避免会暴露给浏览器,白名单是最有效的防盗用手段。

在项目的 「嵌入代码」 页找到「域名白名单」,添加允许的来源域名(如 https://your-site.com)。后端会根据请求的 Origin 头校验,非白名单直接拒绝。

  • 支持精确匹配(含协议和端口),也支持 *.example.com 通配子域;一行一个
  • 开发环境单独加 http://localhost:3000,不要用通配符
  • 一把 Key 只服务一个站点,泄漏时方便精准回收
返回首页