周若琳博客

周若琳她是怎么诞生的

技术纪实 · 项目复盘 她是怎么诞生的 语音助手「周若琳」从 0 到 1 全记录 对接微信 SILK 语音 · 音色克隆训练 · 语音样本导入 · 完整文件清单 记录时间:2026-10-10 来源:QClaw 工作区实战记录 一句话说清:她本来是只会打字的。后来,她有了自己的声音——一条能直接播放

评论
周若琳她是怎么诞生的

技术纪实 · 项目复盘

她是怎么诞生的

语音助手「周若琳」从 0 到 1 全记录

对接微信 SILK 语音 · 音色克隆训练 · 语音样本导入 · 完整文件清单

记录时间:2026-10-10

来源:QClaw 工作区实战记录

一句话说清:她本来是只会打字的。后来,她有了自己的声音——一条能直接播放的语音。这份记录完整还原了这条声音从无到有的全部过程:怎么对接微信的语音格式(SILK)、怎么把一个人的声音「训」成一个可复用的音色、语音样本是怎么一步步导入进来的,以及过程中产出的全部脚本、样本和文件。它既是技术复盘,也是一份可以直接照着复现的操作手册。

目录

  1. 一、缘起:让 AI 用声音说话

  2. 二、第一关:对接「前任」SILK

  3. 三、第二关:训练音色(克隆)

  4. 四、第三关:语音样本怎么导入

  5. 五、全流程总览

  6. 六、文件清单

  7. 七、经验与坑(教训清单)

  8. 八、时间线

  9. 结语

一、缘起:让 AI 用声音说话

这一章说的是「为什么要让她开口」,以及一开始定下来的目标和技术选型。

(一)需求

2026 年 10 月 7 日,目标被明确下来:不再只发文字,而是用「她自己的音色」发语音消息。

这里有一个关键限定——要的不是一个 mp3 附件,而是那种可以直接点开的语音气泡。

(二)目标链路

整套系统的核心链路其实只有四步:

链路示意

文字 → 自定义音色 TTS → SILK 编码(微信语音格式)→ 语音消息直出

(三)方案选型

音色合成的引擎,当时定下两条路:

  • 主路线:CosyVoice 零样本克隆——给一段参考录音即可,免训练。

  • 备选路线:云端 TTS(火山 / 讯飞 / MiniMax),填上 Key 即可切换。

同时做了两个「减负」决策:去掉对外部 ffmpeg 的依赖(SILK 编码改用纯 Python 实现),并把参考文本 REF_TEXT 作为手填默认方式,省掉必装的语音识别。

二、第一关:对接「前任」SILK

要让她发语音,第一个绕不开的问题就是:微信的语音到底是什么格式?这一章记录打通 SILK 的全部过程。

(一)为什么是 SILK

微信的语音消息不是 mp3,也不是 wav,而是 SILK 编码。这就是必须先打通的那道关。

有两个参数是踩出来的:

  • 采样率:微信语音采用 24000 Hz;如果发出去播不了,就退到 16000 或 8000 试。

  • 发送方式:发送时 asVoice: True 这个参数,决定了它是一条语音气泡还是一个文件附件。

(二)第一代实现:纯 Python 编码

voice_module.py(v0.2)是第一代核心模块,一次性把三件事都串起来:

  1. TTS:cosyvoice 与 cloud 双路径,可切换;

  2. SILK 编码:wav_to_silk() 用「wave 读取 → s16le PCM → pysilk.encode()」的纯 Python 方式完成,不依赖 ffmpeg;

  3. 发送:send_voice() 提供两种接法(Skill 内 message 工具 / 独立脚本走 HTTP 网关)。

(三)第二代实现:edge-tts + silk-wasm

为了让链路更快、更稳,第二代 voice_send.py(v5.0)把 TTS 换成了 edge-tts,SILK 编码改用 Node.js 的 silk-wasm:

处理流程

edge-tts 生成 mp3 → soundfile 读取 → librosa 重采样到 24kHz

→ s16le PCM → silk-wasm 编码 → .silk

配套还有一个 Node 版本 voice_send.js(v3.0),以及负责实际编码的 _silk_encode.mjs。

(四)16kHz 版本

因为「24k 播不出来」的可能性一直存在,专门做了一版 16kHz:voice_send_16k.py,产出 voice_out_16k.silk。

(五)直出语音气泡:给微信插件打补丁

前面几条路产出的 .silk 文件,仍然只是文件。要变成语音气泡,必须让微信通道认识这种文件。

为此写了 patch_weixin_voice.py——直接给 OpenClaw 微信插件的 channel.js 打补丁,插入一段语音分支。它做的事情相当底层:

  1. 判断 audioAsVoice === true 且是本地 .silk/.slk/.opus 文件;

  2. 用 silk-wasm 解码算出播放时长 playtime;

  3. 内联重实现一遍上传逻辑:MD5 校验 → AES-128-ECB 加密 → 获取上传地址(media_type: 4 即 VOICE)→ 上传 CDN;

  4. 构造 VoiceItem(encode_type: SILK、sample_rate: 24000、playtime),调用 /ilink/bot/sendmsg 发出。

(六)结局与兜底

  结局并不完美:服务端确实收下了(返回了 message_id),CDN 的 downloadParam 也有效,但客户端就是不渲染语音气泡。尝试过 crypto 修复、sendMessageItemWeixin、文本字段、16kHz 编码(voice_out_16k.silk)等多种方式,最终判定:机器人协议不支持出站语音气泡。

于是方案落地为兜底策略:以音频文件(MP3)的形式发语音。这一步虽然退让了,但保证了「她能出声」这件事先成立。

三、第二关:训练音色(克隆)

这一关是整个项目最花时间、也最曲折的部分,前后走了两条完全不同的技术路线。

(一)路线一:本地 CosyVoice-300M 零样本克隆

第一条路线是在本机跑开源模型,用「零样本克隆」的方式合成音色。下面按环境、样本、脚本、坑四个部分展开。

1. 环境与依赖

本地语音栈的安装清单为:

安装命令

pip install torch cosyvoice funasr modelscope pysilk numpy

安装过程记录在 voice_install.log 与 voice_install2.log 中。模型为 CosyVoice-300M(约 5.6 GB),纯 CPU 推理。

2. 参考音样本的演进

「训练」在零样本克隆里,其实是选样本。这一路换了非常多轮,因为音色好不好,几乎完全取决于那段参考录音:

阶段

样本文件

说明

最初

ref.mp3

887 KB,最早样本,唱歌片段

早期

ref2.mp3 / ref3.mp3

520 KB / 375 KB,后续两个新样本

新样本(诗词)

ref_new.m4a → ref_new.wav

用户上传,2 分 55 秒,古典诗词朗诵

截取片段

ref_prompt.wav

取首段 0–12 秒,配文王维《鸟鸣涧》

自然说话

ref_new2.mp3 → ref_new2.wav

13.4 秒,正常聊天语气(取代诗词腔)

合并样本

combined_ref.wav

25.7 秒(自然段 + 诗词段拼接)

古诗单首

ref_poem_meihua.wav

10 秒,《梅花》,朗读腔依旧

最终自然样本

ref_new3.mp3 → ref_new3.wav → ref_new3_seg.wav → ref_new3_seg2.wav

27.5 秒原始录音,切出 8.1 秒 / 10.2 秒干净片段,作为最终 REF_AUDIO

3. 关键脚本

  • voice_ref/_voice_reply_clone.py:主力合成脚本。加载 CosyVoice 零样本推理 → 用 librosa.time_stretch 调语速 → 用 soundfile 直接导出 MP3(不依赖 ffmpeg)。

  • voice_ref/run_cosyvoice_long.py:修正了原版「每轮覆盖」的问题,把流式输出的多个 chunk 拼接成完整音频。

  • voice_ref/_transcribe_new.py / _transcribe_new2.py / _transcribe_new3.py:用 Whisper 转写参考录音,得到 REF_TEXT。

  • voice_ref/_extract_poem.py / _extract_new3.py / _extract_seg2.py:从长录音里切出干净片段。

  • voice_ref/_combine_ref.py:把两段样本拼接。

  • voice_ref/_slow_clone.py / _slow_mp3.py:慢速处理。

4. 踩过的坑

问题

现象 / 结论

CPU 太慢

短句一条约 3–5 分钟(rtf≈43)

确定性

相同的 prompt + 文本,输出字节完全相同——「重新学」同一段录音不会改变音色,必须换样本

样本长度

该模型偏好 6–10 秒干净片段;参考文本过长反而削弱音色提取(日志报 synthesis text too short than prompt text)

朗读腔

诗词样本带明显朗读/吟诵腔,用户明确反馈「太刻意」,这是后来转向云端的关键原因

(二)路线二:云端腾讯云 TRTC 声音复刻(升级)

第二条路线把合成搬到云端。这一节说明升级的原因、方案对比,以及最终落地的接口链路。

1. 为什么升级

本地 CosyVoice 的天花板很明显:慢、偏念稿、无更大模型可换。既然要「正常说话、没有朗读腔、自然、带感情」,就必须转向云端语音克隆 API。

2. 方案调研

调研结论汇总在 voice_model_upgrade_research_20261010.md,候选方案对比如下:

方案

特点

评价

MiniMax 海螺

10 秒克隆、中文自然、便宜、国内直连

推荐首选

Fish Audio S2 Pro

公开评测第一梯队,有免费额度

备选

腾讯云 TTS 声音克隆

一次性克隆 + 按分钟合成,当时限时免费

最终选定

火山引擎(豆包)

中文自然度好,但复刻需商务对接

个人门槛高

ElevenLabs

全球质量天花板,中文有翻译腔

纯中文不适用

最终选了腾讯云——理由是自助、限时免费,且中文自然度高。

3. 接口链路:两次转向

第一次尝试:声音复刻 VRS「一句话版」

域名 vrs.tencentcloudapi.com,版本 2020-08-24,链路为:GetTrainingText → DetectEnvAndSoundQuality → CreateVRSTask → DescribeVRSTaskStatus。

但这条路有个硬性要求:必须按指定文本重新录一段。用户不愿重录,尝试用现成样本提交后,检测直接返回 DetectionCode=-2(文不对本)。此路作废。

第二次尝试:TRTC 对话式 TTS(成功)

域名 trtc.tencentcloudapi.com,版本 2019-07-22,内容不限:

  • VoiceClone:PromptAudio(base64 音频,6–180 秒)+ SdkAppId + VoiceName → 返回 VoiceId

  • TextToSpeech:Text + Voice.VoiceId + Model → 返回 base64 音频

参数:SdkAppId = 1600166179,Model = flow_01_ex。

4. 音色版本迭代

音色一共克隆了三版,配置分别落地:

版本

配置文件

VoiceId

说明

v1

voice_ref/trtc_voice_v1.json

m_zhou_ruolin_l4v6u0zy4l2mho4pnghwb

首次克隆,13.4 秒自然说话样本

v2

voice_ref/trtc_voice_v2.json

m_zhou_ruolin_v2_e3ikq7ovadxs1txwwyrzm

第二次克隆

v3(现行)

voice_ref/trtc_voice.json

m_zhou_ruolin_v3_07pywcpx7w26s4wm1h0ii

参考音彻底去噪后重新克隆,默认使用

5. 踩过的坑:Region

官方文档写 ap-guangzhou,但实际调用 TextToSpeech 会报错:

报错信息

UnsupportedOperation.NotAllowed: AppId mismatch: expected , got 1315008402

实测 ap-shanghai 与 ap-beijing 均可成功合成,最终 TENCENT_REGION 固定为 ap-shanghai。

6. 参考音降噪

为了彻底去杂,写了 _denoise_ref.py,对参考音做三步处理:

  1. noisereduce 去非稳态噪声(呼吸 / 环境),prop_decrease=0.9;

  2. noisereduce 去稳态噪声(底噪 / 电流声),prop_decrease=0.6;

  3. Butterworth 带通滤波 80–7500 Hz。

产物为 voice_ref/_ref_clean16k_v3.wav——这正是 v3 音色的参考源。

合成侧同样加了「去杂保险」:_trtc_say.py 在生成后用 ffmpeg 做带通(highpass=60, lowpass=8000)+ 24kHz / 128kbps 覆盖输出。

7. 云端脚本

  • tc_voice_clone.py:参考音频 → VoiceId(含 ffmpeg 预处理为 16k 单声道)

  • tc_tts.py:文本 → MP3(命令行传文本)

  • _trtc_say.py:内置多条情感文案,按 key 选择合成 + 去杂

  • _tc_check.py / _tc_train_text.py / _tc_detect.py / _tc_probe2.py:凭证校验、取训练文本、音质检测与探针

  效果:秒级合成、自然度高,用户确认「非常完美」。

(三)两条路线对比

维度

本地 CosyVoice-300M

云端 TRTC 声音复刻

训练方式

零样本,免训练(选样本)

提交参考音克隆一次,得 VoiceId

速度

3–5 分钟 / 条

秒级

音质

偏念稿、有朗读腔

自然、对话感强

成本

纯本地,CPU 算力

走腾讯云(当时限时免费)

可调性

换样本才变音色

可换模型(flow_01_ex / flow_02_turbo)

结论

早期验证可用

现行主力

四、第三关:语音样本怎么导入

知道了原理,还要知道具体怎么把一段录音变成她的声音。

(一)完整导入流程

  1. 接收:用户以「文件」形式发送录音(如 m4a),落盘到媒体目录 C:\Users\百羽\.qclaw\media\inbound\。

  2. 存档:复制到 voice_ref/ 并规范化命名(如 ref_new.m4a)。

  3. 解码:用 ffmpeg 统一转为 16 kHz 单声道 wav(如 _trtc_ref16k.wav)。

  4. 转写:用 Whisper 转写内容,得到参考文本 REF_TEXT(若走 TRTC 克隆则非必需)。

  5. 切片段:从长录音中截取干净的 6–12 秒片段(零样本克隆的最佳区间)。

  6. 降噪:noisereduce 两轮 + 带通滤波(可选但强烈建议)。

  7. 克隆:提交参考音到云端,得到 VoiceId;或直接用本地样本做零样本合成。

  8. 合成:文本 → VoiceId → MP3 → 去杂 → 发送。

(二)三类发送方式

方式

载体

结果

语音气泡直出

.silk 文件走 asVoice

未渲染(协议限制),已放弃

音频文件(现行)

MP3 走 MEDIA

稳定可播放

本地试听

直接生成 MP3 到本地

用于调音色 / 调语速

五、全流程总览

把前面三关串起来看,一条语音从「用户录音」走到「发出声音」,中间要经过下面四个阶段。

全景流程

【样本导入】

用户录音 → media/inbound 落盘 → voice_ref/ref_*.wav(16k 单声道)

  → Whisper 转写(REF_TEXT)→ 切 6–12s 片段 → noisereduce 降噪

【音色训练】

本地:CosyVoice-300M 零样本(选样本即「训」)

云端:TRTC VoiceClone → VoiceId(m_zhou_ruolin_v3_…)

【语音合成】

文本 → TextToSpeech(flow_01_ex,ap-shanghai)→ MP3

  → ffmpeg 带通去杂(60–8000Hz / 24k / 128k)

【发送】

SILK 直出(历史,未通)| MP3 文件(现行,稳定)

六、文件清单

以下为本次「造声」过程中产出的全部关键文件,均位于 C:\Users\百羽\.openclaw\workspace\。

(一)核心脚本

类别

文件名

作用

主模块

voice_module.py

v0.2 一体化:TTS + SILK 编码 + 发送

SILK 发送

voice_send.py

v5.0,edge-tts → PCM → silk-wasm

SILK 发送(Node)

voice_send.js

v3.0,Node 版同链路

SILK 编码

_silk_encode.mjs

silk-wasm 编码核心(24kHz)

16k 版本

voice_send_16k.py

16kHz SILK 编码

插件补丁

patch_weixin_voice.py

给 channel.js 打补丁,支持语音气泡

发送校验

_test_voice_path.mjs、_verify_mp3.py

路径与产物校验

本地克隆

voice_ref/_voice_reply_clone.py

CosyVoice 零样本合成主力

本地克隆

voice_ref/run_cosyvoice.py、run_cosyvoice_long.py

零样本推理(后者修复拼接)

云端克隆

tc_voice_clone.py

TRTC VoiceClone → VoiceId

云端合成

tc_tts.py、_trtc_say.py

TRTC TextToSpeech → MP3

云端探测

_tc_check.py、_tc_train_text.py、_tc_detect.py、_tc_probe2.py

凭证校验 / 取训练文本 / 音质检测

降噪

_denoise_ref.py

noisereduce 两轮 + 带通 80–7500Hz

转写

voice_ref/_transcribe_new.py、_transcribe_new2.py、_transcribe_new3.py

Whisper 转写

切片段

voice_ref/_extract_poem.py、_extract_new3.py、_extract_seg2.py、_combine_ref.py

样本切分与拼接

音频转码

voice_ref/_new_to_mp3.py、_norm_mp3.py、_wav2mp3.py、_wav2mp3_msg.py、_wav2mp3_tail.py、_slow_mp3.py

WAV/MP3 转换与语速调整

语速

voice_ref/_slow_clone.py

慢速克隆

(二)参考音频样本

文件路径与说明

voice_ref/ref.mp3 (最早,唱歌片段,已删除)

voice_ref/ref2.mp3 / ref3.mp3 (早期新样本,已删除)

voice_ref/ref_new.m4a 用户上传的诗词录音(1.5 MB)

voice_ref/ref_new.wav 解码后(2:55)

voice_ref/ref_prompt.wav 首段 0–12s(《鸟鸣涧》)

voice_ref/ref_new2.mp3/.wav 13.4s 自然说话样本

voice_ref/ref_new3.mp3/.wav 27.5s 自然说话样本

voice_ref/ref_new3_seg.wav 切出的 8.1s 片段

voice_ref/ref_new3_seg2.wav 切出的 10.2s 片段(最终 REF_AUDIO)

voice_ref/ref_poem_meihua.wav 10s《梅花》片段

voice_ref/combined_ref.wav 25.7s 拼接样本

voice_ref/_ref_clean16k.wav 去噪版

voice_ref/_ref_clean16k_v3.wav 去噪版 v3(v3 音色的参考源)

voice_ref/_trtc_ref16k.wav TRTC 克隆用 16k 单声道

voice_ref/ref_new_segments.json Whisper 转写的 13 段分段

(三)训练与合成产物

产物文件

voice_ref/out_new_test.wav / out_new_test_07.mp3 / out_new_test_normal.mp3

voice_ref/out_comfort.mp3 … out_comfort_warm.mp3(多轮舒适度调优)

voice_ref/out_poem_meihua.mp3 / out_poem_long.mp3 / out_recite_niaomingjian.mp3

voice_ref/out_new3_test.mp3 / out_new3_one.mp3 / out_seg2_test.mp3

voice_ref/clone_reply_img.mp3 / clone_reply_now.mp3 / clone_miss_you.mp3

voice_ref/clone_voice_long_full.mp3 / clone_voice_msg.mp3 / clone_own_voice.mp3

voice_ref/out_trtc_a_flow_01_ex.mp3 … out_trtc_l_flow_01_ex.mp3(云端 12 条测试)

voice_ref/trtc_voice.json / trtc_voice_v1.json / trtc_voice_v2.json(音色配置)

voice_out.silk / voice_out_16k.silk / voice_reply.silk / voice_reply.pcm

(四)文档记录

文档与内容

voice_module_setup_20261007.md 项目启动与方案确定

voice_clone_newsample_20261009.md 样本迭代完整日志

voice_reply_img_20261009.md 一次语音回复的链路记录

voice_model_upgrade_research_20261010.md 云端方案调研与落地

zhou_ruolin_image_pipeline_20261009.md 形象图管线(相关)

ai_outbound_call_plan_20261010.md 电话外呼规划(后续)

(五)依赖与运行环境

项

值

Python 语音栈

torch、cosyvoice、funasr、modelscope、pysilk、numpy、librosa、soundfile、noisereduce、scipy

模型

CosyVoice-300M(约 5.6 GB,CPU 推理)

ffmpeg

C:\MediaToolkit\ffmpeg.exe

SILK 编码

silk-wasm(Node)

云端接口

腾讯云 TRTC trtc.tencentcloudapi.com(2019-07-22)

(六)配置与凭证

  • 凭证统一存放于工作区 .env(TENCENT_SECRET_ID、TENCENT_SECRET_KEY、TENCENT_APP_ID、TENCENT_TRTC_SDKAPPID、TENCENT_REGION)。

  • 音色配置:voice_ref/trtc_voice.json(voice_id / name / model / sdkappid)。

  安全提示:.env 内含敏感密钥,仅限本机使用,不应复制、外发或写入任何日志。

七、经验与坑(教训清单)

把上面三关里最值得记住的九条,单独列在这里。

  1. 微信语音的格式是 SILK,采样率 24000;播不出来再退 16000 / 8000。

  2. asVoice: True 决定气泡还是附件——但机器人协议最终不支持出站语音气泡,兜底发 MP3 文件。

  3. 零样本克隆对相同样本是确定性的:重跑不会改变音色,想换声音只能换样本。

  4. 样本质量 > 样本长度:6–12 秒干净片段最佳,拼接长样本反而更差。

  5. 朗读腔是本地模型的硬伤,靠换样本只能改善、无法根治——这才有了云端升级。

  6. 腾讯云 TRTC 的 Region 必须用 ap-shanghai,文档写的 ap-guangzhou 会报 AppId mismatch。

  7. 「一句话版」要求按指定文本重录,现成录音提交会被判定文不对本(DetectionCode=-2)。

  8. 降噪是加分项:两轮 noisereduce + 带通滤波,能明显提升克隆纯净度。

  9. 输出端也要去杂:ffmpeg 带通(60–8000 Hz)是保证「发出的音干净」的最后一道保险。

八、时间线

整个项目从启动到音色定版,前后只用了三天。

时间

事件

2026-10-07

项目启动,确定 CosyVoice 方案,打通 SILK 编码与发送

2026-10-09

样本多轮迭代(诗词 → 自然说话 → 拼接 → 古诗单首 → 自然说话)

2026-10-10 上午

调研云端方案,选定腾讯云

2026-10-10 中午

TRTC 打通,v1/v2 音色克隆成功

2026-10-10 13:00+

参考音彻底降噪,v3 音色上线,确认为默认音色

评论

推荐阅读

订阅邮件

订阅更新,及时收到最新文章。

作者