数字人“说话”的声音,乍看像是播放层的小细节,实际却是一个明确的系统边界。
你的应用或 Agent 栈决定说什么,并通过自己的 TTS 层生成语音。Spatius 接收的是这段数字人要说的音频,将其转换为实时动作数据,再由 AvatarKit 在客户端本地渲染数字人。Spatius 不负责你的 ASR、LLM、TTS、用户轮次策略或工具调用。Spatius 开发者文档地图 对这一职责边界有明确说明。
把边界划清后,接入问题会简单很多:发送正确的音频、使用正确的格式、在正确的时间发送,并明确标记这一轮数字人语音何时结束。如果还在决定这条运行时链路应该放在哪里,可以先从 Spatius 集成路径选择指南 开始。
核心结论
- 应发送数字人要说的 TTS 输出,而不是麦克风输入,也不是从实时播放链路里回录出来的音频。
- 在发送前统一为单声道 16 位 PCM(
s16le),并使用当前会话已配置的采样率;Spatius 不会自动重采样。 - TTS 生成一段就发送一段,不要按 1 倍播放速度人为 sleep。
- 一轮语音的最后一个分块要带上 SDK 对应的结束输入标记。它只结束数字人的音频输入,不替代你产品的轮次决策。
- 如果手上只有按播放速度到达的音频,应为每一轮增加预缓冲,并把增加的首响时间当作应用层取舍。
把语音链路和数字人链路分开
在带语音能力的 SaaS 产品里,往往有多个系统参与。把它们统称为“数字人”,会掩盖真正需要测试和负责的边界。
| 环节 | 常见负责人 | 负责什么 |
|---|---|---|
| 用户说话 | 你的产品与 ASR 服务 | 采集并转写用户语音(如果产品支持语音输入) |
| 推理与工具 | 你的应用、Agent 框架或后端 | 上下文、权限、知识检索、工具调用与回复策略 |
| 语音生成 | 你的 TTS 服务或应用 | 生成数字人要说的音频 |
| 动作生成 | Spatius Motion Server | 将数字人语音转为动作数据 |
| 客户端呈现 | 客户端中的 AvatarKit | 基于返回的动作数据渲染数字人 |
Spatius 的输入就在第四行之前:数字人准备说出的音频。在典型语音 Agent 流程里,这通常是应用已经决定回复内容后产生的 TTS 输出,并不是默认意义上的用户麦克风音频。Spatius 的音频说明 对此有直接解释。
关于更广义的流式 TTS 背景,可参考 Stream 对文本转语音系统如何工作的概览、Async 对流式 TTS 系统的工程拆解,以及 Chrome for Developers 的 AudioWorklet 设计模式。它们讨论的是通用音频管线选择;Spatius 的具体输入行为仍以其音频文档为准。
这样划分也让排查更清晰:回复质量差,通常是应用、模型、检索或工具策略的问题;音频格式错误或发送节奏不对,则是集成问题。两者不该混在一起诊断。
使用源音频,而不是播放回传音频
最重要的时序原则很简单:**TTS 生成出数字人语音时,就把它交给数字人链路。**这也是 Spatius 音频概念中说明的发送时机。
如果你的产品已经通过 RTC、浏览器音频元素或 WebSocket 播放声音,这一点可能有些反直觉。这类通道通常按用户听到的速度交付音频,设计目标是播放,而不是作为音频到动作推理的源数据。
Motion Server 能够从慢速到达的有效音频里生成动作,但客户端需要持续拥有足够准备好的音频与动作片段,才能稳定呈现。如果第一个片段已开始播放、下一个片段却还未准备好,数字人就可能卡顿。因此,Spatius 音频说明建议按 TTS 的生成速度发送,而不要按真实播放的墙钟时间节奏发送。
这种投递取舍在流媒体系统中很常见:Spotify Engineering 写过更平滑的流式传输,Mux 解释了如何从 rebuffer 中恢复,Ably 则讨论了实时流中的 backpressure。它们是理解数字人运行时周边“源音频时序与缓冲行为”的有用背景。
| 音频来源模式 | 是否适合作为数字人语音输入 | 原因 |
|---|---|---|
| TTS 服务刚生成的一段 PCM | 是 | 它是源音频,可以在生成后立即转发 |
| 已完整生成并快速重放的音频素材 | 通常可以 | 它仍可作为源素材,而非实时播放回传 |
| 浏览器麦克风音频 | 默认不可以 | 它属于用户输入,而非数字人要说的话 |
| 按 1 倍播放速度到达的 RTC / WebSocket 音频 | 不建议直接使用 | 其节奏可能导致音频与动作储备不足 |
如果系统里混用了多种接入形态,可通过 Spatius 集成路径选择指南确认:在你实际选用的架构中,究竟由哪一层负责发送数字人语音。
不要通过根据分块时长插入 sleep 来解决节奏问题。只要条件允许,应保留从 TTS 源输出到数字人输入的直连路径;这正是 Direct Mode 模型中的源音频路径。
在边界处定义一份音频契约
在接入任意 TTS 服务前,先写清楚一份音频契约:语音产生服务与向 Spatius 发送语音的客户端或后端,都必须遵守它。这个契约应服务于你选择的集成路径,而不是某个临时的浏览器播放实现。
Motion Server 接收单声道 16 位 PCM(s16le)。会话配置的采样率必须是 8000、16000、22050、24000、32000、44100 或 48000 Hz 之一。Spatius 不会自动对输入重采样,因此源音频与会话配置不一致时,必须先转换。最新受支持格式以音频文档为准。
对于通用音频工程背景,ForaSoft 对帧、包与音频分块的解释、Chrome 关于 AudioWorklet 默认可用的说明,以及其 AudioWorklet 设计模式文章都值得参考。它们不会改变 Spatius 发布的 PCM 与采样率契约。
| 契约问题 | 需要记录的决定 |
|---|---|
| 什么进入数字人链路? | 数字人已获批准回复的 TTS 输出 |
| 使用什么格式? | 单声道 PCM16 / s16le |
| 使用哪个采样率? | 每个会话固定使用一种受支持采样率 |
| 在哪里做转换? | 在发送给数字人之前,由清晰归属的服务或客户端适配层处理 |
| 如何标记本轮语音结束? | 最后一个源分块带上平台对应的结束输入标记 |
| 回复被取消时怎么办? | 应用按自己的轮次策略清除或替换待处理回复 |
这不是要你选择一个放之四海皆准的采样率。合适的值取决于 TTS 服务产出的音频和你的整体链路。关键在于:它必须被明确选择、被会话配置使用,并在集成测试中得到验证,而不是从浏览器默认值中“碰巧继承”。
如果浏览器负责音频转换或缓冲,MDN Web Audio API 概览与AudioWorklet 参考可作为实现层面的外部资料。它们不会改变 Spatius 的输入契约:音频必须在进入数字人链路前完成归一化。
以生成速度发送,并正确结束最后一个分块
一个可靠的实现,会把输出视为连续的源音频分块,并有一个明确的结束输入事件。
- 应用开始生成回复并接收 TTS 音频。
- 每个分块都被归一化为会话约定的 PCM 格式。
- 分块生成后立即转发,而不是等待按用户收听速度播放。
- 回复完成时,实际最后一个分块用
end: true或当前 SDK 的等价标记发送。
这个结束标记非常关键。在 Direct Mode 中,结束输入标记代表当前一轮数字人语音输入结束。数字人仍会播放剩余动作,随后回到 idle;之后再发送音频则会开始新一轮。Direct Mode 的会话说明 描述了这一生命周期。
关于相邻的流生命周期模式,可参考 Ably 关于分布式实时系统连续性的文章、其对可恢复 AI Token 流的分析,以及 Async 的流式 TTS 架构拆解。它们能帮助理解干净的回合结束边界,但不是对 Spatius 行为的替代说明。
一个实用的应用侧做法,是保留一个已归一化的待发送分块:下一个分块到来时,把前一个作为非最终分块发送,再保留新的分块;当 TTS 服务确认结束时,再将保留的最后一个分块标记为最终分块。这样不会在还不知道是否有下一段时,过早宣布一段音频是最后一段。
收到 TTS 分块:
pcm = 转换为会话约定的 PCM
若已有待发送分块:
发送待发送分块(end=false)
待发送分块 = pcm
TTS 生成完成:
若存在待发送分块:
发送待发送分块(end=true)
清空待发送分块
具体调用方式应以对应的 AvatarKit Web SDK 参考为准。这个模式强调的不是某个 API 名称,而是职责边界:应用负责判断 TTS 是否完成,数字人接入层接收一段被正确收尾的语音输入。若要把这条边界映射为可见的客户端状态,请参考 Client State & Events。
只有播放速度音频时,诚实地使用预缓冲
有时你拿到的并不是按生成速度输出的 TTS,而只是一个按收听速度推进的流。例如某些第三方服务只提供适合播放的音频流。此时逐段立即转发,可能出现前文提到的播放卡顿。
文档给出的做法是在每一轮数字人语音开始时加入预缓冲:
- 先在本地积累一段按播放速度到达的音频。
- 缓冲覆盖 Motion Server 启动窗口后,再开始向数字人链路发送。
- 发送的同时持续从源流补充缓冲。
- 本轮被打断或下一轮开始时,重置该缓冲。
Spatius 建议从约 3.5 秒音频开始尝试,4 秒会更保守;之后再根据实际卡顿情况在应用中调整。这个时长不是 SDK 承诺,也不是通用延迟指标,而是“更长启动时间”与“更稳定音频/动作供给”之间的明确取舍。选择生产阈值前,请阅读完整的播放速度音频说明。
如果能够直接获得原始 TTS 输出,通常仍是更干净的架构。预缓冲是受限音频来源下的兜底方案,而不是把正常 TTS 输出故意变成播放速度数据的理由。调整这个阈值或音频来源类型时,应再次核对当前的 Spatius 音频说明。
若要从更广义角度理解这项取舍,可对照 Spotify Engineering 关于流式平滑度的讨论、Mux 对rebuffer 恢复的解释,以及 Stream 的 TTS 延迟与部署指南。它们让“更长启动时间”与“较少中断体验”之间的产品取舍更容易被讨论清楚。
把数字人输入结束和用户轮次策略分开
很容易让一个“结束”事件承担所有含义:TTS 已生成完、数字人已播完、用户可以说话、Agent 可以接收新指令。它们有关联,但并不是同一个事件。
最后一个音频分块上的标记,只是告诉数字人链路:当前这轮不再有新的数字人语音输入。以下问题仍由产品或 Agent 层决定:
- 什么时候允许用户打断?
- 未完成的 Agent 回复应如何处理?
- 取消时要停止工具工作、TTS 生成,还是只停止呈现?
- 什么时候把输入焦点还给用户?
- 外部工具或人工同事接手时,界面应展示什么状态?
请在应用或 Agent 层设计这些策略,再向数字人提供清晰的源音频边界。若想讨论产品层面的中断决策,可参考 用户何时应该能够打断 AI 数字人?。
若要梳理现有 Agent 的整体系统边界,可继续阅读如何为现有 SaaS AI Agent 加入 AI 数字人;若是在小范围上线前评估,也可参考如何在 SaaS 产品中试点 AI 数字人。
关于实时 AI 周边的打断与传输,Ably 对专用 AI 流式 SDK的讨论、Daily 对实时媒体浏览器性能的建议,以及 WebRTC.ventures 对语音 AI 架构传输方式的概览,提供了有用的外部背景。它们不会把用户轮次策略变成数字人层的职责。
把它当成产品流程测试,而不只是一次 API 调用
连接成功并不代表体验没有问题。应在真实的源音频条件下,测试一轮语音从开始到结束、再到下一轮的完整过渡。
| 场景 | 需要验证什么 | 有价值的失败信号 |
|---|---|---|
| 正常流式回复 | 分块按生成速度发送,最后一段被正确标记 | 数字人完成回复并回到 idle,不出现无解释卡顿 |
| 格式不匹配 | 输入在进入数字人链路前已转换 | 不支持或不匹配的源格式不会悄悄穿透到运行时 |
| 缓慢的播放速度源 | 只有在无法获取源 TTS 时,才逐轮使用预缓冲 | 播放稳定性成为被明确管理的取舍,而不是上线后的意外 |
| 用户打断 | 应用按策略丢弃或替换待处理语音 | 过期语音不会被误呈现为下一条回复 |
| TTS 在完成前失败 | 待处理状态会被清除,界面进入应用定义的恢复路径 | 不会因为永远等不到最后一段而持续等待 |
对于 Direct Mode,还应把连接失败纳入测试。当前文档说明:如果其 WebSocket 连接在 15 秒内失败,SDK 会进入仅音频回退模式——音频继续播放,动画不可用。这只是一个狭义的连接回退,不替代产品更广义的错误恢复、重试或人工交接设计。
若要超出这条 SDK 专属回退设计测试,Mux 关于媒体处理采样时序的经验、Ably 对 WebSocket 与实时数据流的概述,以及 Chrome 对 Web Audio 自动播放的解释都是有价值的背景。它们支持团队将时序、重连与浏览器条件和数字人接入契约分开测试。
发布前检查
- 已确认数字人的语音来源是 TTS 输出,而不是默认麦克风输入。
- 每个会话都明确使用一种单声道 PCM16 格式和一种受支持采样率。
- 所有重采样都发生在发送给数字人之前,并有测试覆盖。
- 分块会在生成后立即转发,不会按播放速度插入 sleep。
- 已保留足够状态,确保实际最后一个分块被标记为结束输入。
- 应用取消或替换一轮语音时,会重置待发送音频。
- 轮次、打断、权限、工具和人工交接仍由应用负责,而不是由数字人层承担。
- 已测试正常流、播放速度源的预缓冲、格式错误、取消和连接回退。
若要把加载、连接、播放与回到 idle 的客户端状态一并纳入检查,可结合当前的 Spatius Client Lifecycle 指南完成这份音频专项检查。
关于更广义的浏览器音频实现层,Chrome 的 AudioWorklet 设计模式、Stream 的 TTS 管线概览和 Async 对流式 TTS的文章,都是相关的外部资料。当客户端负责 Spatius 输入边界周围的转换、缓冲或播放控制时,它们尤其有参考价值。
常见问题
Spatius 接收的是用户麦克风音频吗?
默认不是。在语音 Agent 场景里,用户麦克风音频通常进入你的 ASR 与 Agent 流程;发送给 Spatius 的是数字人应该说出的 TTS 输出。Spatius 音频文档对两者做了区分。
可以直接发送 MP3 或其他压缩格式吗?
Motion Server 当前文档定义的输入是单声道 16 位 PCM(s16le),采样率需是受支持的值之一。压缩格式应先转换,并确保会话配置匹配;Spatius 不会自动重采样。
为什么不建议直接转发 RTC 音频?
RTC 音频通常按用户收听的 1 倍速度到达,可能让 AvatarKit 在消耗当前片段前拿不到下一段同步音频与动作。能拿到原始 TTS 输出时,应优先转发原始输出;只有播放速度音频时,再按文档中的播放速度音频方案增加每轮预缓冲。
最后的结束输入标记是否决定用户何时能继续说话?
不是。它只标记当前数字人回复的输入音频结束;Direct Mode 会话生命周期定义了这条数字人侧的边界。用户轮次、打断策略、焦点管理以及后续动作,仍由你的产品或 Agent 框架决定。
若要理解如何把流的传输生命周期与产品会话分开,可参考 Ably 对流连续性的说明、Daily 的媒体会话性能建议,以及 Chrome 的 AudioWorklet 后台处理指南。它们是通用工程参考,并非 Direct Mode 的替代文档。
发送的是产品已经决定呈现的语音
优秀的 TTS 到数字人接入,应该看起来并不戏剧化:应用逻辑决定回复,TTS 产生源音频,数字人层收到格式正确且边界清晰的流,客户端在本地渲染结果。
这种分层让每一层都更容易替换、测试和解释。如果你正在评估 SaaS 产品中的实时数字人能力,可以预约 Spatius 演示。
若团队想继续研究,Ably 关于可恢复 AI 流的文章、ForaSoft 对音频帧与数据包的指南,以及 Spotify Engineering 关于更平滑媒体投递的讨论,都是与本文产品专属资料相配套的第三方阅读材料。
参考资料
- Spatius 开发者文档地图
- Spatius 音频概念
- Spatius Direct Mode 集成
- 如何为现有 SaaS AI Agent 加入 AI 数字人
- MDN Web Audio API 概览
- MDN AudioWorklet 参考
- MDN WebSocket 参考
- AvatarKit Web SDK 参考
- Spatius Client State & Events