引子
文字聊天机器人已经很不稀奇了,但如果你想让自己的人设角色在群里用特定的声音说话,链路会突然长出一大截:协议端、机器人框架、大模型、语音合成,少一环都不行。
最近「大肥鱼」人设和「鲸鱼娘」桌宠在 DeepSeek 生态里火了一把,很多人第一次意识到:这些角色如果只会打字,乐趣少一半;真让它们用自己的声音在群里接话,才算「活过来」。
这篇文章是完整的一次搭建实录:从技术选型、端口规划,到语音编码绕不开的坑,再到把默认 TTS 换成支持零样本音色克隆的 GPT-SoVITS;最后还包括跑起来之后的那些问题——消息上下文怎么隔离、群消息历史留多少条、怎么让它主动接话又不招人烦、以及延迟该从哪一层下手。全程本地部署,不依赖任何商业语音服务。
一、先把架构拆成四层
语音聊天机器人不是「接个 LLM 就行了」,它是一条四段式的流水线:
QQ 客户端
│ (QQ 协议)
▼
① 协议端 ── OneBot 11 ──▶ ② 机器人框架 ──▶ ③ 大模型
│
▼
④ 语音合成 ──▶ 转码 ──▶ 回传
| 层 | 职责 | 常见实现 |
|---|---|---|
| 协议端 | 收发 QQ 消息,暴露标准接口 | NapCat(对外提供 OneBot 11) |
| 机器人框架 | 事件分发、插件、人设、会话管理 | AstrBot |
| 大模型 | 生成回复文本 | 任意兼容 OpenAI 协议的服务 |
| 语音合成 | 文本转音频 | Edge-TTS / GPT-SoVITS / 云端 TTS |
理解这条链路的价值在于:出问题时要能判断是「收不到消息」「消息没被处理」还是「处理了但发不出去」。这三类故障的现象完全不同,排查入口也完全不一样。
从桌宠到群成员:差的不只是「会不会发声」
最近 DeepSeek 生态里最出圈的两样东西,一个是「大肥鱼」人设,一个是「鲸鱼娘」桌宠。前者是一份人设插件,决定它怎么说话、什么语气、什么口头禅;后者是本地桌宠,负责在桌面上晃、冒泡、偶尔吐槽。
这两类插件都是社区产物,共同点是观赏向、单人向:只有你一个人看、一个人对话,没有并发、没有社交压力、也没有「什么时候该闭嘴」的问题。
但一旦把它搬进群里,性质就变了:
| 维度 | 桌宠 / 本地人设 | 群机器人 |
|---|---|---|
| 受众 | 只有你 | 一群人,且各有偏好 |
| 记忆 | 单一会话 | 私聊按人隔离、群聊共享 |
| 说话时机 | 你点它才动 | 需要 @、关键词或主动插话策略 |
| 表达形式 | 文字气泡 | 文字 + 语音,会有「像不像」的问题 |
所以「大肥鱼能不能在群里说话」这件事,真正难的不是接一个大模型,而是把它的人设、记忆边界和发言节奏搬到多用户场景里——这也是后面几节要解决的东西。
二、选型:官方路线还是第三方协议端
2026 年的 QQ 机器人基本是两条路:
官方开放平台
- 优点:合规、无封号风险、官方已放开语音收发能力;
- 缺点:权限和场景受限,很多群能力拿不到,个性化程度低。
第三方协议端(如 NapCat)+ 开源框架
- 优点:能力完整、可以做到高度个性化的角色扮演;
- 缺点:属于模拟登录,存在风控与封号风险,需要自己承担。
如果只是做「群里的个性化 AI 角色」,第二条路几乎是唯一选择。但必须配套几条纪律:
- 用不常用的小号,绝不拿主号跑;
- 控制发言频率,别做主动刷屏;
- 不加入大群、不群发;
- 私聊优先验证,稳定后再考虑群聊。
这些不是「建议」,是这条路线能否长期跑下去的前提。
三、连接方式:反向 WebSocket
协议端和框架之间用 OneBot 11 标准接口通信。推荐反向 WebSocket:由协议端主动连到框架,框架只监听本地端口。
好处是配置更简单,也不需要在协议端做鉴权白名单。端口规划建议提前定好,避免和本机其他服务打架:
# 框架侧(示意)
platform:
type: aiocqhttp # OneBot 11 适配器
ws_reverse:
host: 127.0.0.1
port: 6199
path: /ws
webui:
port: 6185 # 管理后台
协议端侧填同一个地址:
ws://127.0.0.1:6199/ws
连上之后,日志里出现适配器连接成功的记录,就说明「协议端 → 框架」这一段通了。
排查提示:私聊不需要 @,群聊必须 @ 才会触发回复。很多人第一次测试收不到回复,是因为在群里直接说话没 @ 机器人。另外二维码登录有有效期,过期重新生成即可,不要反复重启进程。
四、语音:绕不开的 SILK v3
这是整条链路上最容易卡住的一步。QQ 的语音消息不是普通的 mp3/ogg,它要求 SILK v3 编码。框架生成的是 wav/mp3,直接塞进语音接口大概率发不出去或者对方听不到。
所以链路上必须有一个转码环节,通常依赖 ffmpeg:
文本 → TTS(返回 wav/mp3) → ffmpeg 转 SILK v3 → 语音消息发送
配置层面要做两件事:
- 确认
ffmpeg在 PATH 里,且框架能调用到(容器/守护进程环境下经常是这一条没满足); - 明确开启语音输出,让框架走「语音消息」而不是「音频文件」的发送通道。
调试顺序建议从简到繁:先让机器人回文字,再让它回语音,最后才调音色。一次只改一个变量,出问题才定位得动。
五、音色不达标:从 Edge-TTS 换到 GPT-SoVITS
Edge-TTS 的优点是零配置、开箱可用、延迟低;缺点是音色是固定的。它只能在你选定的声音里挑一个,没有办法复刻某个特定角色的声线。
问题就出在这里。「大肥鱼」之所以是「大肥鱼」,一半靠语气和口头禅,另一半靠那个辨识度极高的声线;「鲸鱼娘」桌宠也是同理——文字能模仿,声音模仿不了,人设就散了。你可以在提示词里写满「说话慢一点、尾音拖长一点」,但只要输出是 Edge-TTS 那几个标准播音腔,听起来就不是那个角色。
这条需求没法靠调参数解决,只能换引擎:需要能克隆音色的 TTS。GPT-SoVITS 是目前本地部署里比较成熟的选择,关键优势是零样本推理:
- 不需要训练模型;
- 只需要 3~10 秒干净的参考音频 + 对应的文本;
- 推理后即可得到接近参考音色的输出。
资源上,一台 64GB 统一内存的 Apple Silicon 机器满速跑是够的,这也是本地部署相对云端方案的优势——没有额度、没有延迟抖动、隐私可控。
部署成一个本地 HTTP 服务后(默认端口 9880),在框架里把 TTS 提供商切过去就行:
tts:
provider: gsv_tts
endpoint: http://127.0.0.1:9880
ref_audio: ./voices/ref.wav # 3~10s 干净人声
ref_text: "参考音频对应的文本"
prompt_language: zh
text_language: zh
准备参考音频时,有三条经验值得提前知道:
- 干净比长更重要:有背景音乐、混响、多人说话的素材,克隆出来会「串味」;
- 文本要对得上:参考文本和音频内容不一致,韵律会明显跑偏;
- 情绪要一致:用平静语气做参考,就别指望它输出激动语气,想覆盖多情绪就准备多份参考。
六、TTS 接上之后,为什么还是收不到回复
把本地 TTS 服务接进来、模型权重也加载成功之后,遇到的问题很典型:链路看起来全绿,但发消息就是没回复。
这时候最忌讳的是「到处改配置」。正确做法还是分层验证,只是这次要验证到「消息有没有走到合成那一步」:
- 协议端 → 框架:日志里有事件推送记录;
- 框架 → 大模型:有请求发出、有文本返回;
- 文本 → TTS:本地服务是否收到请求、返回的音频是否非空;
- 音频 → QQ:转码后的 SILK 文件大小是否正常(几十字节基本就是失败)。
这次最后落在第 3、4 步之间,重连后恢复,本质是TTS 加载完成与消息通道就绪之间存在时序差:服务已经起来了,但语音通道还没真正可用,此时进来的消息会被直接吞掉。稳定之后的表现就是秒回。
还有一个容易忽略的「假成功」:参考音用的是占位音频。链路全通、语音也能发出去,但听起来完全是另一个人的音色。这不是 bug,是素材没换——先跑通链路,再替换 3~10 秒无背景音的干音,这个顺序不要颠倒,否则音色不对和链路不通会混在一起排查。
七、上下文与记忆:私聊是独立房间,群聊是大厅
这一点直接决定了机器人「记不记得事」,也最容易出现「怎么突然失忆了」的困惑。
框架的上下文划分规则可以概括成三段式键:
平台 : 消息类型 : 会话 ID
由此得到两条结论:
| 场景 | 会话 ID | 上下文表现 |
|---|---|---|
| 私聊 | 对方账号 | 每人一个独立对话,互不影响 |
| 群聊 | 群号 | 整群共用一个上下文,所有人共享记忆 |
几个实践要点:
- 私聊和群聊的记忆完全不互通,不会跨场景传递;
- 清空上下文是在对应会话里发
/reset,只清当前会话,不影响别人; - 群聊里可以开「每人独立记忆」,但代价是机器人会明显变健忘——每个人只有极短的上下文窗口,人设连续性反而变差。一般不建议开,宁可用一个共享上下文 + 更长的保留条数。
如果群聊只有被 @ 的消息才进历史,那机器人自然「只记得别人点它说过的话」。想让它在群里更连贯,需要把群消息历史打开,并把保留条数调到一个合理值(几百条量级),否则上下文太短,接话会显得突兀。
八、主动发言:三种机制,别混为一谈
「让机器人自己说话」有完全不同的三种实现,选错方向会白折腾很久:
| 机制 | 触发条件 | 特点 | 适用 |
|---|---|---|---|
| 概率插话 | 群里有消息时按概率接话 | 原生能力,配置简单 | 群聊里有存在感 |
| 定时主动 | cron 定时任务 | 可控、可预测 | 早晚问候、定时播报 |
| 事件触发 | 插件 hook 响应事件/关键词 | 精准、零延迟误触 | 入群欢迎、关键词唤醒 |
8.1 概率插话的「节制」比概率更重要
一个常见的错误配置是:概率设得很低(比如 0.1)就觉得「已经很克制了」。但群里消息一多,0.1 的累计触发率其实相当高,一晚上能接话几十次。
真正让拟人感成立的,是这几项配套约束:
- 活跃时段:夜里不接话,否则很像「一直盯着屏幕的机器人」;
- 冷却时间:一次发言后强制静默 N 分钟;
- 白名单:只在指定群生效,别全局打开;
- 上下文长度:至少留几十上百条,否则接话会答非所问;
- 人设一致性:大肥鱼会在你加班时阴阳你两句,鲸鱼娘会突然冒出气泡吐槽——这种「性格」来自人设提示词和语音的双重叠加。如果文字像、声音不像,或者反过来,拟人感立刻垮掉,所以人设和音色必须一起调。
顺带说一句:同一个插件生态里也有做「自动刷视频、自动评论」那一类的东西。它们和桌宠、人设不是一回事,属于典型的滥用型自动化,别碰。能力越大,越需要克制,这句话在群机器人上尤其成立。
8.2 需要 100% 触发时,用关键词而不是调概率
测试阶段经常需要「发消息必回」。把概率调到 1.0 是最差的做法——它会让机器人对每条消息都插话,既吵又难验证。
更合适的是关键词唤醒:插件里维护一个关键词元组,命中才回,同时把概率插话关掉避免重复回复。
# 关键词唤醒插件(示意)
KEYWORDS = ("机器人昵称", "自定义暗号")
def match(event) -> bool:
text = event.message_str or ""
return event.is_at_bot or any(k in text for k in KEYWORDS)
两个必须知道的细节:
- 改完要重启才生效,热改配置不会重新加载模块;
- 关键词要和「@ 机器人」并列判断,否则群里没人 @ 时永远不触发。
九、延迟:先调推理强度,再换模型
语音机器人的延迟是叠加的:LLM 首字延迟 + TTS 合成耗时 + 转码 + 网络。很多人第一反应是换更小的模型,但往往第一步该调的是推理强度。
现在不少模型支持多档 reasoning effort(low / high / max),默认值常常是 high。这在写代码、做分析时是好事,放到群聊闲聊里就是纯延迟:
provider:
model: deepseek-flash
reasoning_effort: low # 群聊闲聊场景,改 low 通常质量损失很小
经验判断:
- 一对一的深度问答:保持
high,值得等; - 群聊接话、语音回复:改
low,用户对「快」的感知远大于对「多思考两秒」的感知; - 语音链路:TTS 合成本身就是固定开销,给 LLM 留的预算要更紧。
调完之后再看是否换模型,顺序反了会浪费很多时间。
十、聊天记录去哪了:审计与复盘
机器人跑起来之后,很快会需要回答「它昨天在群里说了什么」。记录其实不止一处,按用途选:
| 途径 | 位置 | 适用 |
|---|---|---|
| 框架 WebUI 会话管理 | 图形界面 | 快速翻看、单条定位 |
| 本地数据库 | data/data_v4.db 的 platform_message_history 表 | 批量导出、做分析 |
| 协议端接口 | OneBot 拉取 QQ 侧原始消息 | 需要原始数据结构 |
| QQ 客户端 | 聊天窗口 | 只做肉眼确认 |
两个配置层面的坑:
- 默认只存被 @ 的群消息,所以「没存到」往往不是 bug,是本来就没开;
- 打开全群历史后要注意存储增长,几百人的群一天能堆出可观的记录量,定期清理或加个保留天数。
十一、排查清单
把这次踩过的坑整理成一份顺序清单,按这个顺序走基本不会绕路:
- 框架能起来吗 —— WebUI 能打开,说明服务和配置没问题;
- 协议端连上了吗 —— 日志里能看到反向 WS 连接成功;
- 能收到事件吗 —— 私聊发一条消息,框架日志里是否有事件记录;
- 能回文字吗 —— 先把 LLM 链路跑通,别急着上语音;
- 能回语音吗 —— 检查
ffmpeg、SILK 转码、语音通道; - 音色对不对 —— 前五步全通之后再动 TTS 参数;
- 延迟能接受吗 —— 先降推理强度,再考虑换模型;
- 是否在说话太多 —— 检查概率插话的冷却、时段和白名单。
另外几个高频细节:
- 人设配置放在框架侧,不要写死在代码里,方便热更新;
- 测试用小号、控制在私聊,确认稳定再放进群,避免频繁触发风控;
- 框架的 WebUI 有默认初始账号,首次登录第一件事就是改密码,别让管理后台裸奔在本地端口上。
十二、合规与风险,必须写在前面
第三方协议端模拟登录这件事,本质上是踩在平台规则的边缘。技术上能做,不代表可以随便用。几个底线:
- 只用小号,主号不参与;
- 不做主动营销、不群发、不刷屏;
- 不进大群,控制消息频率;
- 主动发言能力要慎用:它是最容易从「有个机器人」滑向「骚扰」的功能,冷却和时段限制不是可选项;
- 随时准备停用,不做依赖它运转的业务。
如果这些前提不能接受,那还是老老实实走官方开放平台,牺牲一部分能力换取稳定性。
结语
这条链路的价值不只是「让机器人说话」,而是它把几个平时割裂的领域串起来了:即时通讯协议、事件驱动的机器人框架、LLM 接入、语音合成与音频编码。每一段都不算深,但拼在一起就会冒出各种「看起来该通却不通」的问题。
回过头看,真正花时间的往往不是「让它说话」,而是「让它记得」和「让它少说」——上下文按私聊/群聊怎么隔离、群消息历史留多少条、主动接话的冷却怎么配,这些都不属于任何单个组件的文档,只能自己一层层试出来。
而「像不像」这件事,最后拼的也不是某个单点技术:人设插件给了性格,桌宠给了陪伴感,GPT-SoVITS 给了声线,机器人框架给了记忆和分寸。四样凑齐,大肥鱼或鲸鱼娘才算真正从你的桌面走进了群里。
最容易踩的坑反而不是大模型,而是最不起眼的那一层:语音编码格式。同样地,第二常见的坑是时序——服务起来了不等于通道就绪,重连一次往往比改十行配置有效。搭复杂链路时,先按层验证,再谈效果。