思维输入法:用语音识别+大模型补全你的内心独白
我平时脑子里戏特别多。坐在工位上写代码,脑海里已经自动排练起同事可能的反驳;洗漱的时候,先把当天要讲的话完整彩排一遍;甚至刷到一条新闻,脑子里都能冒出三条角度不同的评论。以前我觉得这是噪音,直到有一天我换了个思路——与其消灭这些内心独白,不如给它们装一个 autocomplete。所以我做了个有点奇怪的小工具:捕捉我脑子里那半截没说完的句子,用语言模型补出后半段,再录成我自己的 voice 念出来。它不是语音助手,也不是备忘录,更像一个“思维输入法”。
这个项目最让我兴奋的地方在于,以前我们用的输入法补全的是“手指打出来的字”,而这次补全的是“脑子里还没成型的念头”。整条链路我拆成了四段:录音、识别、补全、回放。每一段都有现成的开源工具,但把它们串起来、调出真正能用的体验,踩的坑比我预想的多得多。如果你也经常被自己的内心独白打断,或者对“语音+大模型”的落地玩法感兴趣,这篇内容应该能帮你省掉不少弯路。
1. 这个项目到底想解决什么问题
1.1 内心独白为什么值得被“补全”
先说一个很实际的现象:我们每天产生的内心独白,其实绝大多数都是半成品。比如我上班路上看到一辆刹车灯坏了的车,脑海里飘过的是“完了这车后面要出事”,但这个念头往往到这里就断了,既没有形成完整的判断,也没有进一步推导“如果我跟在后面应该怎么处理”。这种片段式的自言自语,恰恰是语言模型最擅长的接话对象。
一开始我想做的其实是一个“思维放大器”。不是让模型替我想,而是让它把我的话头接着往下推半步。比如我自言自语说“这个接口的鉴权感觉写得有点松”,模型接一句“松的地方可能在 token 过期校验,建议先看 resolve 函数里的分支”。这句话换我自己想可能要想十秒,但模型一秒钟就给我递过来了,我来判断它靠不靠谱就行。
还有一个很重要的原因:人的短时记忆在工作记忆被塞满的时候,很容易丢信息。自动补全相当于把我的“思维草稿”往前推,让我能够更快进入判断环节,而不是卡在组织语言的原型阶段。这个体验很像写代码时用了补全插件——你不是不写那些代码,而是不用再费力把每个括号和函数名都敲完,可以把脑子省下来想架构。
1.2 从输入法联想到的思维辅助工具
我最早开始想这个项目,是因为某天用手机输入法时发现,它的上下文预测越来越懂我了。有些长句我还没打完,它已经把整句给出来了。那时候我脑海里冒出一个念头:如果输入法补全的是“手打出来的字”,那能不能对“脑子里冒出来的话”做同样的事?
顺着这个念头往下想,问题就变得非常具体:内心独白是语音形式的,得先把语音转成文本;然后需要一个模型理解我这句话的语境和表达习惯;最后还要把补全结果用声音反馈给我。而且这里有个别人可能觉得多余、但我坚持要做的点——补全结果必须用我自己的声音念出来。原因很简单:如果我的“思维输入法”跳出来一个别人声音的提示,那种感觉太像有人在旁边打断我思考了。只有使用我自己的声音,这个反馈才会被大脑识别为“自己产生的延伸想法”,而不是外来干扰。
这里面其实涉及一个认知心理学的基本原理:人对自己声音的熟悉度和接纳程度远高于陌生声音。所以我从一开始就决定,这个工具不能做成一个“第三方语音助手”,它得是一个“第二大脑”的外设,说话的声音必须让我的大脑感到安全、熟悉,才会真正接受它提供的思路。
2. 系统整体设计与关键技术选型
2.1 完整链路:录音、识别、补全、回放
整套系统的实时性链路是这样的:我身上带一个领夹麦克风,连接到手机端的低延迟录音 App,这个 App 会把音频持续写入一个循环缓存。当我想要获得补全时,我会在说完前半句话后轻按一下耳机上的按键,App 就取最近 3 秒的音频,通过 flutter 或者本地脚本发送给语音识别模块。识别出的文本作为 prompt 的一部分,送给大语言模型补全后半段。模型输出的文本再交给 TTS 模块,用我的克隆声音生成一段 1 到 5 秒的音频,通过蓝牙耳机播放出来。
这个流程听起来不长,但每一步的延迟都是要命的事情。我给自己定的目标是从“按按钮”到“听到自己的声音”不超过 1.5 秒。如果超过这个时间,大脑会觉得这个反馈和当前思维已经断开连接了,不但不会帮助思考,反而会打断思路。为了达到这个目标,我不得不重新调整了原本“先完整录音再离线处理”的思路,改成纯流式的管道。
流式处理的思路很直白:不是在录音结束才开始处理,而是音频边录边切。我用了 20 毫秒一帧的音频缓冲,当检测到按键信号时,直接把最近 150 帧(也就是 3 秒)合并成一个音频块。语音识别模块被设计成一个常驻进程,拿到音频块就立刻推理,不用重新加载模型,所以单次识别时间被我压到了 300 毫秒左右。接下来文本生成就用流式接口,第一个 token 到达后就启动 TTS,保证了整体延迟控制在 1.5 秒以内。
2.2 为什么选择本地方案为主
这里有一个容易让人纠结的地方:大语言模型的文本补全,到底是用云端 API 还是本地模型?我的结论是:能本地跑的尽量本地跑。不是因为我有多重视隐私,而是延迟才是这个项目的命门。云端的 API 质量确实更好,比如各家大模型都很擅长续写句子,但网络往返一次要 300 到 800 毫秒,再加上排队时间,整体延迟很容易超过两秒。
本地方案虽然模型的“聪明程度”不如云端 API,但我发现这个场景根本用不上多聪明,它只需要完成“顺着我的话头往前推半步”。这件事一个小尺寸模型完全够用。我测试过 7B 到 9B 参数量级的开源模型,在普通消费级显卡上跑 int8 量化之后的生成速度,已经能够做到每秒钟吐出 20 到 30 个 token。对补全一句 20 到 50 个字的内心独白来说,生成时间不到两秒,配合流式 TTS 足够用。
还有一个原因是音频数据本身很敏感。我曾经试过用云端语音识别服务,但因为网络波动,出现过一次音频块上传失败的情况。之后我就彻底转向本地方案,把 whisper 或 faster-whisper 跑在本地。这里顺便说一下,我用的是 faster-whisper,因为它把 openai 原版 whisper 换成了 CTranslate2 的实现,速度能快三四倍,在相同显卡上跑 tiny 或 base 模型时,300 毫秒内出一个识别结果是很稳的。
2.3 几个关键选型的横向对比
项目里最核心的三个模块是语音识别、文本补全和声音克隆,我分别做了横向对比,结果供你参考。
语音识别部分,我对比了 faster-whisper、funASR 和 macOS 自带的 Speech framework(我是 Windows 用户,所以最后把 macOS 方案排除了)。funASR 在中文上的表现其实略好,但它的模型部署要复杂一些,我最后留在 faster-whisper,因为它支持流式推理、CTranslate2 优化和多种语言,在 Windows 下一条 pip install 就能跑起来。
文本补全部分,我对比了 Llama 3.1 8B、Qwen2.5 7B 和 Gemma 2 9B。用我自己二十来条内心独白测试,Qwen2.5 7B 给出的补全明显更贴合中文口语习惯,Llama 3.1 8B 则更擅长英文续写。考虑到我的内心独白是夹杂中英文的,最后选了 Qwen2.5 7B,并且用 int4 量化把显存占用压到 6GB 以内。
声音克隆部分,开源工具里最省事的是 Coqui TTS 的 XTTS v2,只需要 5 到 10 秒的参考音频就能克隆出一个能用的声音。热门的另一个选项是 GPT-SoVITS,它微调之后的声音相似度更高,但需要更多训练数据。因为这个项目里声音是“自己听”,不需要达到以假乱真的程度,所以我选择了 XTTS v2。
下表是我当时记录的一个选型对比,直接贴出来给同好参考:
| 模块 | 对比方案 | 最终选择 | 核心原因 |
|---|---|---|---|
| 语音识别 | faster-whisper / funASR / 系统自带 | faster-whisper | 延迟低、部署快、中英混说能扛 |
| 文本补全 | Llama 3.1 8B / Qwen2.5 7B / Gemma 2 9B | Qwen2.5 7B | 中文口语续写更自然,显存友好 |
| 声音克隆 | Coqui XTTS v2 / GPT-SoVITS | Coqui XTTS v2 | 训练成本低,单凭参考音频就能用 |
3. 核心环节的落地细节
3.1 语料库的采集:先攒 2000 条自己的话
这里想多说一句,虽然我的系统用的是通用语言模型接话,但我一开始还是坚持攒了一些个人语料。原因在于,一个训练时见过大量书籍、新闻、对话的通用模型,补出来的话往往“太正常”了,没有我自己的语气和用词习惯。比如我平时喜欢说“这不扯吗”,模型倾向于补成“这不是合理的选择”,听感就很别扭。
我的采集办法很笨:拿录音笔在电脑前挂了一周,凡是自己自言自语、跟同事开小会、甚至一个人背稿子的内容,全部录下来,然后跑了一遍带说话人分离的转写脚本,把只有我一个人说话的时间段抽出来,变成一句句文本。这个过程大概产出了两千多条独立句子,每句平均 20 到 80 字。
这些语料我用得最狠的地方不是训练大模型,而是做两件事。第一件,是提取了一批典型表达习惯,比如我常用的口头禅、承接词、句尾习惯。第二件,是选了 20 到 30 条音质最好、情绪最平稳的句子,用来做声音克隆的参考音频。注意,参考音频一定要挑情绪平稳的,不要在情绪高涨或低落的时候录,不然克隆出来的声音会带着那种情绪腔调,平时听起来很怪。
3.2 补全模型的两种路子:微调与 Few-shot
把个人语料变成模型能力,我有两条路可以走。第一条路是 LoRA 微调。我试过用那两千条自己的话去微调 Qwen2.5 7B,训练轮次只设置了 1 到 2 个 epoch,学习率取 2e-4,结果模型确实变得爱用我的用语习惯。但因为语料量不大,微调的效果并不稳定,有时候会过度拟合,补全出来的话和我自己说的几乎一样,反而失去了“帮我延展思考”的作用。
于是我又试了第二条路,也就是 Few-shot 加 Prompt 约束,不微调模型。具体做法是在提示词里固定放上五六条“我说话风格的例句”,然后让模型模仿这些句子的语气去补全。比如我会把“做人不能太较真,但写代码必须较真”“我估计这功能后端要返工”“先跑通再优化”这些句子作为风格样本。这条路的优点是灵活,换一次 Prompt 就能调整风格;缺点是每轮都要多输入几百个 token,会稍微增加一点生成时间。
最后我的方案是两条路结合起来:先用少量语料做一次低步数的 LoRA 微调,把模型的口语习惯拉向“像我说话”,然后在推理时依然给一两个示范例句。这个过程实测下来,补全的效果最自然,既保留了我的语气,又不会因为语料太少而过拟合。如果你不想折腾微调,直接从 Few-shot 开始也完全可行,效果差距没有你想象中那么大。
3.3 用自己的声音:声音克隆与 TTS 接入
声音克隆是整个项目里最让我着迷,也最折腾的一环。Coqui XTTS v2 的用法非常直接,把参考音频和待合成的文本丢进去,它就能吐出一段语音。但我一开始犯了一个错误,直接拿一长段录音的中间片段做参考,结果出来声音非常闷,像嘴里含着东西。后来我仔细研究了它的文档,发现参考音频最好切成 6 到 10 秒,背景干净,人声占比高,且不要带大喘气或重的气息音。
关于声音相似度,我个人的体验是:XTTS v2 在中文上的表现已经足够用了。和原声放在一起对比,至少能让人一听就觉得“这是同一个人”,虽然细听还是能听出一点点“合成味”。但因为我们这个场景是自己给自己听,所以那一点塑料感影响不大。真正影响听感的是语调。补全的句子如果短,TTS 很容易把语调处理得过于平淡,像一个没有感情的朗读机器人。我的解决办法是在合成文本前面加标点符号或者一些语气词,比如“嗯,”“其实吧”,让模型在句首就有重音和停顿。
接入 TTS 的代码路径也很简单,核心调用大致是这样:
from TTS.api import TTS tts = TTS(model_name="tts_models/multilingual/multi-dataset/xtts_v2") tts.tts_to_file( text="其实吧,我觉得这个地方改成缓存会更好。", speaker_wav="ref_audio/my_normal_voice.wav", language="zh-cn", file_path="output.wav" )实际运行的时候我会把模型常驻内存,而不是每次现场加载,因为第一次加载要花好几秒钟,完全不能接受。常驻内存之后,单句合成时间在 0.5 秒到 1 秒之间,非常合适。
3.4 触发方式与交互细节
这里要重点说一下怎么触发整个流程,因为这是影响“顺手程度”的关键。我先后试过三种方案。第一种是纯语音热词唤醒,在循环监听里检测到我说“想想”两个字就启动补全;结果误触率很高,日常对话中很容易带出这两个字,而且热词监听本身就要占用不少 CPU。第二种是检测停顿,等我说完一句话后停顿超过 800 毫秒就自动补全;结果在正常演讲节奏里也经常误触发,搞得我说话都不敢停。
最后我选了物理按键方案。我把一个蓝牙快门按钮改装成了按键触发器,连到手机之后,按键会向处理服务发送一个 UDP 消息。实际使用中,我习惯在脑子里冒出前半句之后,用拇指轻轻一按,然后继续在心里默念这个半句话,下一秒耳机里就会传来我自己的声音,把后半句接上。这个物理按键的好处是明确可控,只有在我想让系统介入时才介入,不会像热词方案那样在不想被打扰的时候突然开口。
另外一个重点是较长的内心独白怎么处理。我的方案是只补全最近 3 秒的音频,也就是最关键的“当前念头”,而不是把前面一大段自言自语都放给模型。原因有两个:一个是要控制延迟,音频段越长识别越慢;另一个是我的目标本来就是“半步补全”,而不是全文重写。只要系统知道上一句大概说什么,再加上当前这半句,就足够给出有价值的补充了。
4. 实操过程中踩过的坑
4.1 语音识别中的自我纠正陷阱
我第一个想吐槽的坑是语音识别对“我自己说话”的识别率。用 faster-whisper 跑中英混说的时候,最大的问题不是识别错误,而是它自作聪明地帮我“改语法”。比如我自言自语时经常说“这个方案,怎么说呢,不太行”,faster-whisper 会把它转写成“这个方案怎么说呢不太行”,丢掉了句子里本来的停顿和语气。可这种停顿和语气恰恰是我内心独白的组成部分,丢掉之后也没办法精准补全。
解决办法是我放弃了原生的分段策略,强制 whisper 按句子边界输出时间戳,并且把 do_uppercase 关掉,保留小写和空格。然后我再写了一个后处理脚本,把停顿时间大于 0.3 秒的地方替换成逗号或者省略号。这一步对后续补全影响很大,模型看到带标点的前半句,比看到一句话糊在一起时,补全质量明显高一个档次。
还有一个更隐蔽的问题,就是自动纠错会把我说的“半句话”给修正成完整句。比如我心里想的是“如果我是用户,我绝”,这种明显没说完的句子,ASR 收到后往往会补一个“对”,变成“如果我是用户,我绝队”。这种纠错在普通语音助手场景下是优点,在“捕念头”场景反而是致命伤。我最后的处理方式是在 prompt 里明确告诉模型:以下文本可能是不完整的语音识别结果,不要修正它,只需延续;同时在 ASR 侧把 suppress_tokens 列表里加了常见的补全词,让识别模块尽量不要吐多余的字。
4.2 模型补全“太标准”导致没个性
另一个让我反复纠结的问题是,无论我怎么调 Prompt,纯通用模型补出来的话总是带着一股“微博大 V 说教风”。比如我说“今天这个 bug 有点怪”,它会接“需要从多个角度分析原因找出根本解决方案”,一听就是标准答案,没有思考价值。我要的是“怪在它只在线上环境出现,你看是不是缓存节点的问题”,这种具体推进感。
后来我发现问题出在模型太想迎合“用户”了。我原来的 Prompt 写的是“请以作者的风格补全以下句子”,模型就会倾向输出置信度高、绝对正确的大白话。后来我把 Prompt 改成“请作为作者的好友,用随意、略带吐槽的语气,接一句真实可操作的建议”,效果立刻不一样了。这说明在这个场景里,角色设定比具体规则重要得多。
另外,补全的长度也要做限制。我一开始没有给 max_new_tokens 设置合理值,模型一口气补出两百多个字,听起来像一场独角戏。后来我把补全长度限制在 15 到 60 个汉字之间,让系统只说最必要的半句话。这样既不会打断思路,也能让 TTS 的合成时间保持在 1 秒以内。
4.3 声音克隆不够像的排查
声音克隆出来不像,大部分人第一反应是换模型、增加参考音频时长。但我的经验是先检查两个基础设施。第一是参考音频的响度,我最初用 Audacity 导出 wav 时没有做响度归一化,导致 TTS 推理时把音量特征也学进去了,合成出来的声音忽大忽小。后来我把参考音频统一用 loudness_normalize 到 -16 LUFS,问题立刻好了很多。
第二是采样率。Coqui XTTS v2 默认要求 22050Hz 的采样率,但我的录音 App 默认输出是 48kHz。让它自己重采样也不是不行,但会损失不少高频细节,声音听起来发闷。我的解决方法是统一用 FFmpeg 把参考音频和待合成文本都处理成 22050Hz 单声道,再用原样交给模型。这里还要注意不要开启任何“降噪增强”之类的音效,过度降噪会让声纹特征丢失。
如果你感觉克隆出来的声音“有点像我,但不像我在近距离说话”,那多半是参考音频离麦克风太远,环境混响太重。我最终选定的参考音频是领夹麦克风离嘴巴 10 厘米左右录的一段话,吐字清晰,没有明显的回声。
4.4 延迟比想象中重要
这个项目里最让我反复调整的,不是识别准确率也不是模型质量,而是端到端延迟。一开始我用的是“录音停了再处理”这种方式,每次触发后要等语音识别加载模型、处理完整音频、生成文本、再 TTS,整体耗时在三到五秒。这个延迟在技术演示上能看,但在实际思考里完全不可用,因为你心里的一句话早就过去了,耳机才冒出后半句,感觉就像 AI 在对我进行延迟反馈一样。
后来我把整个流程改成常驻进程加合理流水线,延迟从三秒降到了 1.2 秒左右,才真正觉得顺滑。优化点主要集中在这几个地方:第一,ASR 模型用的是 faster-whisper,并且只加载一次;第二,TTS 模型常驻显存,不做重复初始化;第三,在 TTS 生成音频时,先用“首个分句”触发播放,后续分句边生成边播放。通过这种边生成边播放的方式,体验上会再快半秒。
5. 常见问题速查表与避坑清单
5.1 问题与解决方案速查表
如果你打算复刻这个项目,我把路上遇到最多的问题整理成了速查表,直接对照处理就行:
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 识别结果缺字 / 多字 | ASR 的自动纠错 | 关闭大写化,设置 suppress_tokens,不要让它补全 |
| 补全内容太官方 | 模型角色设定太“正经” | 把角色改成“好友”或“毒舌同事”,并限制输出长度 |
| 克隆声音不像本人 | 参考音频混响重 / 采样率不对 | 统一 22050Hz 单声道,16 LUFS 响度,选近讲录音 |
| 声音像机器人口吻 | 缺少语气词和标点 | 在文本前加“嗯,”“其实吧”等语气引导 |
| 端到端延迟太高 | 模型反复加载 / 等待完整输出 | 常驻进程,流式生成,分句播放 |
| 蓝牙耳机声音发闷 | 音频传输编码影响 | 优先使用支持 AAC 的耳机,避免 SBC 低码率 |
5.2 一些提升体验的小技巧
除了上面那些问题,还有几个我试过之后觉得非常值得分享的细节。先说 Prompt 设计。我现在的 system prompt 里加了一句很重要的话:“如果前半句已经完整,就补一句相关联的延伸;如果前半句不完整,就把它补成完整的建议。”这个规则让我在两种使用场景——给自己打气、给自己找方案——之间切换得很自然。
另一个技巧是定期更新参考音频。声音不是一成不变的,熬夜和感冒都会导致音色变化。我原来一直用同一段声音样本,结果系统在感冒期间听起来特别违和。后来我改成每个月录一段新参考音频,放到固定的 ref_audio 目录,TTS 常驻会话会自动加载新文件,听感一直保持 “像此刻的我”。
还有一个很实用的习惯是给补全结果打标点。TTS 对逗号和句号的处理差异很大,逗号会带来短暂停顿,句号则会带来明显的降调。我发现补全文本里如果句号太多,听起来像在开小会;逗号太多则显得拖沓。我的策略是给补全结果设置一个长度阈值,短句子只用逗号,长句子在切分点插入句号,听感会自然很多。
6. 一些未必会写在文档里的经验
最后分享一个我自己摸索出来的经验,可能对想做类似工具的人很重要:这个工具最难的其实不是技术,而是“适应大脑的节奏”。最初几天我很兴奋,几乎每句话都要按一下按键,让系统接话,结果一天下来大脑非常累,因为你的思维一直被打断去听反馈。后来我改变了用法,只在两种场景触发:一个是头脑风暴时,想快速得到替代思路;另一个是写重要回复前,用补全来预演可能被追问的角度。
这个开关式的使用方式,让工具的定位从“始终在场的旁白”变成了“随叫随到的思维练习伙伴”。如果你也想折腾这类项目,我建议你先不要追求全自动化,哪怕是手动按键触发,只要能在一分钟内完成一次完整的“自我对话接龙”,它就已经有不错的实用价值了。我自己做到这一步之后,明显感到在写方案、开讨论会时,脑子里的“卡壳感”少了很多,因为我已经提前用系统把很多思维岔路走了一遍。
整个项目最让我意外的收获是:用自己的声音听模型补全,会慢慢改变你思考时的语言习惯。因为你反复听到“你接下来大概会这么想”,下一次你在思考时,真的会潜意识地采用那些更清晰的表达方式。好比用输入法久了,你打字的句式风格也会被输入法影响一样。工具这东西,用久了就不再是工具,它会变成你思考过程的一部分。