Agent 语音引擎换心记:从「够用」到「稳且好」

Agent 语音引擎换心记:从「够用」到「稳且好」

一句话:给 Agent 做固定角色语音回复,先用 CosyVoice2 zero-shot 调到「够用」,再用一套测量脚本量出它的天花板(说话人余弦 0.06),最后换成 GPT-SoVITS 微调(余弦 0.89、合成快 5 倍、常驻内存从 5GB 压到 150MB)。这不是踩坑救火,是用量化提前换掉一个到顶的方案。

起因

给 QQ 上的 Agent 接语音回复,交互闭环是 STT 听懂 → LLM 想怎么回 → TTS 用角色音色说出来。第一个硬要求是:声音得是固定那个角色,不是每次随机一个近似音。这个角色原型选了 doro------一个公开的虚拟主播角色------音轨从她公开视频里扒(具体做法见幕一)。

最开始走 CosyVoice2 zero-shot------推理时贴一段参考音频,临时捏出音色。调完频响补偿(EQ)之后,日常用其实够用、基本能听清。

但「够用」底下到底稳不稳,耳朵说了不算,得量。于是我写了一套评估脚本,把同一段参考音频的合成结果量化成三个数:人声厚度(500--1000Hz 能量占比)、底噪、说话人相似度余弦。量完才发现,旧方案的「够用」坐在一个不稳定的天花板上。

幕一:把声音做到「够用」

音源。 最早用 QQ 语音(SILK 二次转码),合成底噪 p10 只有 -29dB,听着发闷。换成 B 站原视频音轨(一次解码、真数字静音),底噪直接到 -52.6dB,差 24dB。根因不是编码差,是参考音频的停顿段带底噪,模型把它当环境声一起克隆了。

EQ 修听感。 CosyVoice2 在 24kHz 宽带生成,参考只有 7kHz 带宽,于是 4--8kHz 被凭空补出来、500--1000Hz 反而塌陷------听感像老式收音机。加一条通用频响补偿:500--1000Hz 从 11.4% 拉到 36.0%,4--8kHz 从 13% 压到 2.8%。这一步让旧方案从「发闷」变成「够用」。

幕二:测量暴露天花板

量着量着,三个教训比结论更重要:

  • 单次采样不可信。 同音色同文本合成 3 次,底噪 p10 最大差 30dB。任何音质评估必须多次采样取均值,否则会把运气好的一次当成改进(这次就误判过一次:单测到 -69dB 便宣布「与 demo 持平」)。
  • 参考音频越长越糟。 10s 带停顿的参考合成后中频只有 11.4%(塌陷),5.5s 紧凑句能到 22%。给 CosyVoice2 的参考取 4--6s、完整句、紧凑。
  • 静音门破坏性极大。 曾用能量门控削弱起尾音,合成断断续续无法收听,已回滚。参考音频只允许谱减,不做能量门控。

把旧方案按这套脚本和 GPT-SoVITS 微调做同脚本对照,差距才彻底显形:

样本 500--1000Hz 底噪 p10 说话人余弦
参考音频(doro 音轨) 45.8% -90.6dB 1.000
GS 微调(短句) 42.0% 数字静音 0.894
CosyVoice2 zero-shot(对照) 11.1% -5.7dB 0.091

余弦用 ERes2NetV2 说话人嵌入,和早期 campplus 基准尺度不同,这里只做同脚本相对比较。

CosyVoice2 zero-shot 的余弦只有 0.06--0.09,正好落在跨音色区间------模型在说话人空间里不认为那是同一个人。注意:这是裸 zero-shot、无 EQ 的同脚本对照;实际上线版带 EQ,体验是「够用」,但测量说明它的「够用」底下是每次临时捏音色 + 变声器分布外,EQ 修的是听感,治不了根因。

幕三:换心

用同一条 B 站音轨的 ~102s 素材对 GPT-SoVITS v2 微调(18 段清洗后数据,s2 12 epoch + s1 30 epoch),把音色从「每次赌运气」变成「训练进权重」。参考音频只管韵律,不再决定音色------zero-shot 的三个问题(底噪抖动、中频塌陷、音色漂移)同时消失。

项 GPT-SoVITS v2 微调 CosyVoice2 zero-shot
说话人余弦 0.85--0.89 0.06--0.09
500--1000Hz 42--45% 11%
RTF(热合成) 0.32(3.4s 合 10.8s) ~1.6
种子稳定性 余弦波动 ~0.03 底噪抖 30dB
听感验收 比 CV 最好时好太多 够用(带 EQ)

听感验收后拍板 CosyVoice2 退役。余弦从 0.06 到 0.89,合成快 5 倍,这不是「修好一个坏的」,是「突破一个到顶的」。

顺手做的:内存隔离

语音引擎权重 ~4GB,常驻会吃掉 backend 近 5GB 内存。实测同进程卸载(del + gc + empty_cache)后内存只回 11%------显存能回收,内存不能(torch CPU 分配器 + glibc 碎片 + ONNX arena 都不还给系统)。唯一彻底归还的办法是杀进程。

于是把引擎挪进可回收子进程:backend 只留 145MB 的转发层,收到语音消息转写成功后后台预热,合成时子进程加载 ~4.8GB 权重,空闲 600s 自动自杀。

css 复制代码
graph LR
    A[backend 145MB] -->|stdin/stdout 一行一个 JSON| B[tts_worker 子进程]
    B -->|加载引擎 4.8GB| C[合成 WAV]
    A -->|空闲 600s| D[杀子进程 内存全还]

效果:纯文字聊天时语音子进程根本不存在(内存 0);用过语音后空闲回收,backend 常驻回到 145MB。和换心一起,把一个「够用但吃 5GB」的语音模块,变成「稳且好、常驻 150MB」。

几点说明(边界,老实说)

  • 旧方案其实够用。 CosyVoice2 + EQ 日常能用、基本能听清,只是测量暴露它坐在不稳定天花板上。换引擎是「够用 → 稳且好」,不是「坏 → 修好」。
  • GS 仍有 seed 抖动。 短句的频率占比在 26%--43% 间跳(3 seed),余弦稳(0.854--0.885)但频率会晃,没到完美。
  • 唱歌另走 RVC。 TTS 不做旋律控制,唱歌需求单独管线,音色同源。
  • 关于试听。 本文发布平台(掘金 / 小红书)均不支持直接挂音频文件,故不嵌入试听片段。固定角色音色的最终听感以文字描述:声音像 doro、清晰稳定,明显优于 CosyVoice2 最好时;量化对比见上文测量表(余弦 0.06→0.89、RTF 0.32 vs 1.6)。如需亲耳试听,GS 成品音频可随仓库发布或转短视频另行展示。

收尾

整套改动收敛在 plugins/voice/:删掉 CosyVoice2 适配(cosyvoice2_rt),新增 gptsovits_rt/ 薄适配(三方法契约 __init__ / list_voices() / synthesize(text, voice) 不变,调用方无感)+ tts_worker.py 子进程隔离 + eq.py 频响补偿。GPT-SoVITS 与 CosyVoice2 的推理代码都不进仓库,直接复用 external/GPT-SoVITS 上游------所谓「换心」,换的是适配层与微调权重,不是把引擎搬进搬出。仓库 momo-null/omniagent。

相关推荐
lgcgkCQ1 小时前
【AI 转型实战 1】SpringAI 入门:搭建基础大模型对话能力
java·ai·agent·rag·spring ai·mcp·langchain4j
王中阳Go1 小时前
5.27 分钟干完后端和全部测试:一场三小时笔试的五个失分点,四个不在代码上
大数据·数据库·人工智能·后端·大语言模型
goehou1 小时前
ReAct 循环跑偏的三种样子,以及怎么在工程上兜住
llm·agent·react·工具调用·工程实践
70后写java的老码农1 小时前
Ollama 大模型私有化部署与彻底卸载指南(Windows/Linux)
人工智能
清如许.1 小时前
如何评估 AI 智能体(AI Agent)?指标、方法与最佳实践
人工智能
猪八宅百炼成仙1 小时前
让 AI 帮你清理电脑而不翻车:一个 Windows 体检调优 Skill 的工程化设计
人工智能·windows
宋艳_AI应用开发1 小时前
五个阶段跑完,它交了一份日期错两年的投资报告——深思熟虑的Agent最危险的地方,是想得太像
人工智能
用户1357153998191 小时前
凌晨三点,第 4 个反编译窗口
人工智能
Flynn Liang1 小时前
RAG 答非所问/幻觉怎么破?Spring AI QuestionAnswerAdvisor 召回优化实战(TopK/重排/混合检索)
人工智能·算法·spring·rag·幻觉·springai