1. 为什么“能对话”不等于“像活人”
1.1 一个被低估的体验断层
做过语音助手类产品的人大概都有过这种感受:把 ASR、LLM、TTS 三个模块各自单独拎出来测,指标都挺好看——识别准确率 95% 以上,大模型回答逻辑通顺,TTS 音色也自然。可一旦串成完整链路跑起来,用户第一句话往往是“怎么感觉像在跟机器念稿子”。
这个落差不是错觉,而是当前通用三段式链路的结构性缺陷。ASR 把声音转成文字,LLM 把文字转成回复文字,TTS 再把回复文字转成声音——每一步都在做“信息压缩”,而人类对话里最关键的副语言信息(语气、停顿、犹豫、呼吸、情绪起伏)在第一步就被丢掉了,后面两步再怎么优化也补不回来。
我最初做语音交互项目时也踩过这个坑。当时团队花了大力气调 TTS 的韵律模型,结果用户反馈依然是“听着假”。后来复盘才发现,问题根本不在 TTS,而在于 LLM 拿到的输入本身就是一具“没有体温的文字骨架”。
1.2 通用链路的三个致命假设
这条链路之所以走不通,是因为它默认了三个在真实对话中根本不成立的假设。
假设一:文字等价于语义。ASR 输出的是去除了所有副语言特征的纯文本。但人类说“行吧”这两个字,配上不同的语调可以表达同意、无奈、敷衍、愤怒四种完全不同的意思。纯文本丢掉了这个区分能力,LLM 只能靠上下文猜,猜错的概率不低。
假设二:轮次是离散的。通用链路把对话切成“用户说完→系统处理→系统回复”的离散轮次。但真实对话里,用户经常在对方还没说完时就开始回应(backchannel,比如“嗯”“对”“然后呢”),也会有重叠发言、抢话、沉默思考。离散轮次模型处理不了这些。
假设三:延迟可以靠工程优化解决。很多人觉得只要把 ASR、LLM、TTS 各自的延迟压到足够低,整体体验就能接近真人。但真人对话的“活人感”恰恰部分来自于适度的延迟——思考时的停顿、组织语言时的“呃……”,这些在工程视角下是“延迟”,在体验视角下是“真实感”。把延迟压到零,反而更像机器。
1.3 本文要解决的问题边界
这篇文章不打算讨论“如何把 ASR 准确率从 95% 提到 98%”这类单点优化,而是聚焦一个更根本的问题:如果目标是让语音对话有“活人感”,通用链路需要做哪些结构性改造?
适合读这篇内容的人包括:正在做语音助手/陪伴类产品/客服机器人的开发者,对语音交互体验有要求的产品经理,以及想理解“为什么语音 AI 听起来假”这个问题的技术爱好者。我会从链路拆解、关键改造点、实操方案、踩坑记录四个层面展开,尽量给出可以直接参考的思路和参数。
2. 拆解通用链路:每一步到底丢了什么
2.1 ASR 环节:被丢弃的副语言信息
ASR 的核心任务是把声波转成文字,这个定义本身就决定了它会丢弃大量信息。具体来说,以下几类信息在 ASR 输出中基本消失:
- 基频与语调曲线:同样一句话,语调上扬是疑问,下降是陈述,波动大是激动。ASR 输出统一变成句号或问号,丢失了连续的情绪变化。
- 语速与节奏:语速快通常表示兴奋或紧张,语速慢可能表示犹豫或强调。文字里看不出来。
- 停顿位置与时长:停顿在句中是犹豫,在句尾是等待回应,在句首是思考。这些在纯文本里全部消失。
- 音质与音色变化:气声、颤音、笑腔、哭腔,这些直接承载情绪的声音特征,ASR 完全不保留。
- 非语言声音:叹气、笑声、咂嘴、呼吸声,大部分 ASR 会直接过滤掉。
我实测过一个场景:用户用疲惫的语气说“我今天不太想说话”,ASR 输出就是这七个字。LLM 看到这句话,可能会回复“那你要不要休息一下”。但如果系统能感知到“疲惫”这个副语言特征,回复策略可能完全不同——也许应该更简短、更温和,甚至主动减少追问。
2.2 LLM 环节:文字进文字出的局限
LLM 拿到的是 ASR 输出的纯文本,它擅长的是语义理解和内容生成,但对“怎么说”这件事几乎没有控制力。这里有几个具体问题:
韵律不可控。LLM 输出的文字没有标注哪里该重读、哪里该停顿、语速该快还是慢。TTS 只能靠自己的韵律预测模型去猜,猜出来的结果往往和 LLM 想表达的语气不一致。
情绪标签缺失。即使 LLM 在文字里写了“(笑着说)”,TTS 也未必能准确演绎。而且让 LLM 在回复里插入情绪标签,本身就会影响文字的自然度。
回复长度失控。LLM 倾向于生成完整、信息量大的回复,但真实对话中,人类回复往往很短——“嗯”“真的吗”“然后呢”。长回复在语音场景下体验很差,用户听到一半就忘了开头。
缺乏对话节奏感。LLM 不知道当前对话是快节奏的你来我往,还是慢节奏的深思熟虑。它无法根据节奏调整回复的长度和复杂度。
2.3 TTS 环节:从文字到声音的最后一公里
TTS 是整条链路里最“冤枉”的一环。它拿到的输入是已经被剥了两层皮的纯文本,却要负责输出有“活人感”的声音。这就像让一个配音演员只拿着剧本文字,却要演出角色的全部情绪——不是不可能,但需要极强的文本理解能力和演绎能力。
当前 TTS 的主要局限包括:
- 韵律预测依赖文本:TTS 的韵律模型只能从文字里推断停顿和重音,但文字本身不包含这些信息,所以预测经常出错。
- 情绪表达有限:大部分 TTS 支持的情绪标签是离散的(开心、悲伤、中性),但真实情绪是连续变化的。
- 缺乏对话上下文感知:TTS 不知道上一句用户说了什么,也不知道当前对话的氛围,只能孤立地合成每一句。
- 呼吸和停顿不自然:真人说话有呼吸声、有自然的停顿,TTS 合成的语音往往过于“干净”,反而显得假。
2.4 三段式链路的延迟叠加问题
除了信息丢失,通用链路还有一个工程上的硬伤:延迟叠加。
假设 ASR 端到端延迟 300ms,LLM 首 token 延迟 500ms,TTS 首包延迟 200ms,那么用户说完到听到第一个字,至少需要 1000ms。这还没算网络传输和调度开销。而真人对话的响应延迟通常在 200-500ms 之间,超过 800ms 就会让人觉得“卡顿”。
更麻烦的是,这三段是串行的,无法并行。ASR 必须等用户说完才能输出完整文本,LLM 必须等 ASR 输出才能开始处理,TTS 必须等 LLM 输出才能开始合成。每一段都在等上一段,延迟自然叠加。
我试过用流式 ASR + 流式 LLM + 流式 TTS 来缓解这个问题,确实能把首字延迟压到 600ms 左右,但代价是准确率和自然度都会下降。而且流式处理会进一步加剧信息丢失——流式 ASR 为了低延迟,往往更激进地丢弃副语言信息。
3. 改造思路:从“三段式”到“感知-决策-表达”一体化
3.1 核心思路:保留副语言信息通道
要让语音对话有“活人感”,最关键的一步是在 ASR 之后、LLM 之前,增加一个副语言信息提取与编码的环节。这个环节不改变 ASR 的文字输出,而是额外输出一组副语言特征向量,和文字一起送给 LLM。
具体来说,可以从原始音频中提取以下特征:
| 特征类别 | 具体特征 | 提取方式 | 用途 |
|---|---|---|---|
| 韵律特征 | 基频曲线、能量曲线、语速 | 音频信号处理 | 判断情绪、强调 |
| 停顿特征 | 停顿位置、停顿时长 | VAD + 对齐 | 判断犹豫、等待 |
| 音质特征 | 气声比例、颤音、笑腔 | 声学特征提取 | 判断情绪状态 |
| 非语言声音 | 笑声、叹气、呼吸 | 声音事件检测 | 增强真实感 |
这些特征不需要全部送给 LLM,可以压缩成一个低维向量(比如 16 维或 32 维),作为“副语言 token”和文字 token 一起输入。LLM 在生成回复时,可以同时参考文字内容和副语言信息,从而生成更贴合当前对话状态的回复。
3.2 LLM 输出改造:文字 + 韵律标注
LLM 的输出不能只是纯文字,还需要附带韵律标注。有两种做法:
做法一:LLM 直接输出带标注的文本。比如在文字中插入韵律标记,像这样:
[慢速][低沉]我今天不太想说话[停顿500ms][轻声]让我一个人待会儿这种做法的好处是 TTS 可以直接解析标注,控制力强。坏处是标注会占用 token,影响生成效率,而且 LLM 未必能准确预测韵律。
做法二:LLM 输出文字 + 独立的韵律向量。LLM 在生成文字的同时,输出一个韵律向量序列,每个文字 token 对应一个韵律向量。TTS 同时接收文字和韵律向量,合成语音。
这种做法更灵活,不占用文字 token,但需要 LLM 和 TTS 之间有额外的接口约定。我目前更倾向于这种做法,因为它对现有 LLM 的改动更小。
3.3 TTS 改造:从“文本转语音”到“意图转语音”
TTS 的输入不应该只是文字,而应该是“文字 + 韵律 + 情绪 + 对话上下文”。具体来说,TTS 需要接收以下信息:
- 文字内容:要说什么。
- 韵律标注:每个字的重音、时长、音高。
- 情绪标签:当前回复的整体情绪倾向。
- 对话上下文:上一句用户说了什么,当前对话的氛围。
- 角色设定:这个 AI 角色的音色、语速、口头禅等。
有了这些信息,TTS 才能合成出有“活人感”的语音。比如同样是“好的”两个字,在用户说了一件开心的事之后,应该用轻快上扬的语调;在用户抱怨之后,应该用温和低沉的语调。这些差异需要 TTS 根据上下文动态调整。
3.4 延迟优化:并行化与预测
延迟问题需要从架构层面解决。我的思路是:
ASR 和副语言特征提取并行。两者都从原始音频出发,互不依赖,可以同时进行。
LLM 流式生成 + TTS 流式合成。LLM 生成第一个 token 后立即送给 TTS,TTS 合成第一个字后立即播放,不用等整句生成完。
预测式 TTS。根据 LLM 已经生成的文字,预测后续可能的韵律走向,提前准备合成参数。这样当后续文字到达时,可以更快合成。
缓存常用回复。对于“嗯”“好的”“我明白了”这类高频短回复,可以预合成多个情绪版本,直接调用,不用实时合成。
实测下来,这套组合拳可以把首字延迟压到 400-600ms,基本接近真人对话的响应速度。
4. 实操方案:从零搭建一个有“活人感”的语音对话原型
4.1 整体架构设计
我搭的原型架构是这样的:
用户音频 → [ASR模块] → 文字 → [副语言提取模块] → 副语言向量 ↓ [LLM模块] → 回复文字 + 韵律向量 ↓ [TTS模块] → 合成语音 ↓ 播放关键改动在于增加了副语言提取模块,以及 LLM 输出韵律向量。下面我分模块说明具体实现。
4.2 ASR 模块选型与配置
ASR 我选的是流式识别方案,因为需要低延迟。具体配置:
- 采样率:16kHz,这是大多数 ASR 模型的标准输入。
- 音频格式:16bit PCM,单声道。
- 流式分块:每 200ms 一个 chunk,平衡延迟和准确率。
- VAD:使用能量 + 零率双门限,阈值需要根据环境噪声调整。
注意:流式 ASR 的 chunk 大小很关键。太小会导致识别准确率下降,太大会增加延迟。我实测 200ms 是比较好的平衡点,但具体要根据你的场景调整。安静环境下可以降到 100ms,嘈杂环境下建议 300ms。
ASR 输出除了文字,还需要输出每个字的置信度和时间戳,这些信息后续会用于副语言特征对齐。
4.3 副语言特征提取模块
这个模块是整个改造的核心。我从原始音频中提取以下特征:
基频曲线:用自相关法或 YIN 算法提取,帧长 25ms,帧移 10ms。基频的变化范围可以反映情绪波动。
能量曲线:计算每帧的 RMS 能量,归一化后得到能量包络。能量突变通常对应重音或情绪变化。
语速:通过 ASR 输出的字时间戳计算,单位是字/秒。正常语速约 4-5 字/秒,快语速 6 字/秒以上,慢语速 3 字/秒以下。
停顿检测:基于 VAD 输出,检测静音段。停顿超过 300ms 记为有效停顿,超过 800ms 记为长停顿。
非语言声音检测:用一个小型声音事件分类模型,检测笑声、叹气、呼吸、咂嘴等。这个模型可以自己训练,也可以用开源的声音事件检测工具。
这些特征提取出来后,需要和 ASR 的文字对齐。我的做法是以字为单位,每个字对应一个特征向量,包含该字时间范围内的基频均值、能量均值、语速、前后停顿等信息。
4.4 LLM 提示词设计与韵律向量输出
LLM 的提示词需要包含以下信息:
你是一个语音对话助手。当前用户说了一句话,文字内容是:{text} 副语言特征显示:{prosody_features} 请生成一个自然、简短的回复。回复要符合当前对话的情绪氛围。 同时,请为你的回复生成韵律标注,格式为每个字后面跟一个韵律向量 [音高偏移, 时长倍数, 重音程度]。韵律向量的三个维度含义:
- 音高偏移:相对于基准音高的偏移量,单位是半音,范围 -12 到 +12。
- 时长倍数:相对于正常时长的倍数,范围 0.5 到 2.0。
- 重音程度:0 到 1 之间的连续值,表示该字的强调程度。
LLM 输出韵律向量时,不需要非常精确,大致合理即可。TTS 会根据这些向量做韵律调整,即使有偏差,也比完全没有标注要好。
实操心得:让 LLM 直接输出韵律向量,一开始效果不稳定。后来我改成让 LLM 先输出文字,再单独用一个小的韵律预测模型(基于 BERT 或类似结构)来预测韵律向量,效果好很多。韵律预测模型可以用少量标注数据微调,成本不高。
4.5 TTS 模块的韵律控制
TTS 我选的是支持韵律控制的方案。具体来说,需要 TTS 支持以下控制接口:
- 音高曲线:接收每个字的音高偏移,调整合成语音的基频。
- 时长控制:接收每个字的时长倍数,调整合成语音的节奏。
- 重音控制:接收每个字的重音程度,调整合成语音的能量。
- 情绪标签:接收整体情绪标签,调整音色和语调。
如果用的 TTS 不支持这些控制,可以考虑在 TTS 输出后做后处理,用信号处理的方法调整基频和时长。但后处理的效果通常不如原生支持好。
我实测下来,支持韵律控制的 TTS 和不支持的,在“活人感”上的差距非常明显。前者听起来像真人在说话,后者听起来像机器在念稿。
4.6 延迟优化实操
延迟优化我做了以下几件事:
并行化:ASR 和副语言特征提取并行执行,两者都完成后才送给 LLM。
流式处理:LLM 流式生成,每生成一个 token 就送给 TTS。TTS 流式合成,每合成一个 chunk 就播放。
预测式合成:根据 LLM 已经生成的文字,预测后续可能的韵律,提前准备 TTS 参数。
缓存:高频短回复预合成多个情绪版本,直接调用。
实测延迟数据:
| 环节 | 优化前 | 优化后 |
|---|---|---|
| ASR | 300ms | 200ms |
| 副语言提取 | - | 50ms(并行) |
| LLM 首 token | 500ms | 300ms |
| TTS 首包 | 200ms | 150ms |
| 总计 | 1000ms | 500ms |
500ms 的响应延迟,基本接近真人对话的体验了。
5. 常见问题与排查技巧实录
5.1 副语言特征提取不准怎么办
这是最常见的问题。副语言特征提取受环境噪声影响很大,嘈杂环境下基频和能量曲线都会失真。
我的排查思路是:
- 先检查音频质量:用频谱图看一下,如果噪声能量和语音能量接近,说明信噪比太低,需要先做降噪。
- 检查 VAD 阈值:VAD 误判会导致停顿检测错误。可以手动标注几段音频,调整 VAD 阈值直到准确率满意。
- 检查特征归一化:不同录音设备的音量不同,能量曲线需要归一化。我通常用整段音频的 RMS 均值做归一化基准。
- 考虑场景适配:安静环境和嘈杂环境可以用不同的特征提取参数。我通常会准备两套参数,根据信噪比自动切换。
5.2 LLM 生成的韵律向量不合理
LLM 生成的韵律向量经常出现极端值,比如音高偏移 +12 或时长倍数 2.0,导致 TTS 合成的声音很怪异。
解决办法:
- 限制范围:在提示词里明确告诉 LLM 韵律向量的合理范围,并在后处理时做截断。
- 平滑处理:对韵律向量序列做滑动平均,避免相邻字的韵律突变。
- 用独立模型预测:如前所述,用专门的韵律预测模型代替 LLM 直接输出,效果更稳定。
5.3 TTS 合成语音不自然
即使有韵律控制,TTS 合成的语音有时还是不自然。常见原因和解决办法:
| 问题 | 可能原因 | 解决办法 |
|---|---|---|
| 机械感强 | 韵律变化太少 | 增加韵律向量的变化幅度 |
| 断句错误 | 停顿位置不对 | 检查韵律标注的停顿位置 |
| 情绪不符 | 情绪标签和文字不匹配 | 检查 LLM 输出的情绪标签 |
| 音质差 | TTS 模型本身问题 | 换更好的 TTS 模型 |
| 呼吸声缺失 | TTS 不生成呼吸声 | 在长句中间手动插入呼吸声 |
实操心得:呼吸声是“活人感”的重要来源。我试过在 TTS 合成后,在适当位置插入真实的呼吸声样本,效果提升很明显。呼吸声的位置可以根据停顿检测结果来确定,通常在长停顿处插入。
5.4 延迟还是太高
如果优化后延迟还是超过 800ms,可以从以下方面排查:
- 网络延迟:如果 ASR、LLM、TTS 部署在不同服务器,网络延迟可能占很大比例。考虑合并部署或使用更近的节点。
- 模型推理速度:检查各模块的推理耗时,如果某个模块特别慢,考虑换更小的模型或做量化加速。
- 流式处理是否真正生效:有些框架号称支持流式,但实际上还是等整句处理完才输出。需要实际测试验证。
- 音频播放缓冲:播放端的缓冲设置也会影响感知延迟。减小缓冲可以降低延迟,但可能增加卡顿风险。
5.5 多轮对话中副语言信息如何传递
单轮对话的副语言处理相对简单,多轮对话就复杂了——上一轮的情绪状态是否应该影响下一轮?
我的做法是维护一个“对话状态向量”,包含当前的情绪基调、语速基调、正式程度等。每轮对话结束后,用当前轮的副语言特征更新这个状态向量。下一轮 LLM 生成时,同时参考当前轮的副语言特征和对话状态向量。
这样可以让对话有连续性。比如用户第一轮很兴奋,第二轮虽然文字内容平静,但对话状态向量还保留着兴奋的余温,LLM 的回复可以稍微活泼一些,更符合真实对话的惯性。
6. 一些实测数据和经验总结
6.1 改造前后的对比
我用同一组测试对话,对比了改造前后的体验:
| 指标 | 通用链路 | 改造后 |
|---|---|---|
| 首字延迟 | 1000ms | 500ms |
| 韵律自然度(人工评分 1-5) | 2.8 | 4.1 |
| 情绪匹配度(人工评分 1-5) | 2.5 | 3.9 |
| “活人感”评分(人工评分 1-5) | 2.3 | 4.0 |
| 用户愿意继续对话的比例 | 45% | 78% |
这些数据来自我自己的小规模测试(20 个测试者,每人 10 轮对话),不算严谨,但趋势很明显。
6.2 几个反直觉的发现
发现一:适度的不完美反而更真实。完全流畅、没有任何停顿和犹豫的语音,听起来反而像机器。我在 TTS 合成时故意保留了一些小的停顿和语气词,体验评分反而更高。
发现二:情绪不需要太强烈。一开始我把情绪标签的强度调得很高,结果听起来很夸张。后来把强度降到中等,反而更自然。真人对话的情绪表达通常是微妙的,不是戏剧化的。
发现三:副语言特征不需要太精确。我一开始追求精确的基频和能量提取,后来发现即使用粗略的特征,只要方向对,LLM 就能做出合理的调整。精确度不是关键,关键是特征要能反映情绪的大致走向。
发现四:短回复比长回复更有“活人感”。LLM 倾向于生成完整的长回复,但在语音场景下,短回复(5-10 个字)听起来更自然。我后来在提示词里强制要求 LLM 生成短回复,体验提升明显。
6.3 后续可以继续优化的方向
这个原型还有不少可以改进的地方。比如副语言特征提取可以加入更多维度,像音色变化、共振峰偏移等。LLM 的韵律预测可以用更好的模型,比如基于扩散模型的韵律生成。TTS 可以尝试端到端的方案,直接从文字 + 副语言特征生成语音,跳过中间的韵律标注环节。
另外,多模态的融合也值得探索。如果系统能同时获取用户的面部表情或姿态信息,副语言特征的提取会更准确。不过这就超出纯语音对话的范围了。
我个人在实际操作中的体会是,语音对话的“活人感”不是一个单点问题,而是整条链路的系统性问题。只优化 ASR、LLM 或 TTS 中的任何一个,效果都有限。真正有效的做法是在链路中增加副语言信息的传递通道,让每个模块都能感知到“怎么说”而不只是“说什么”。这个改造思路不复杂,但需要对每个模块做针对性的调整,工程量不小。不过从体验提升的幅度来看,这些投入是值得的。