背景痛点:为什么想“动嘴写代码”
- 双手被键盘“焊死”:写业务代码时,经常一边查文档、一边回消息,还要在 IDE 和浏览器之间来回切,手指快抽筋。
- 重复模板写到吐:CRUD、单元测试、日志打印,每天复制粘贴,脑力消耗在毫无营养的“体力活”上。
- 灵感稍纵即逝:脑子突然冒出一段算法,等双手搭好脚手架,思路已经凉了。
- 远程会议&代码评审:一边投屏一边改代码,键盘声“啪啪”响,同事听不清你说什么,自己也手忙脚乱。
一句话:如果能让“说话”直接变成“可编译的代码”,就能把认知资源集中在真正的创造性工作上。
技术选型:为什么 Whisper 比“别人家”更适合写代码
- 离线可用:模型文件一次下载,断网也能跑,公司内网数据不外泄。
- 热词定制:通过
initial_prompt把“async/await、lambda、decorator”等开发关键词喂进去,专有名词识别率瞬间提升 15%。 - 多语言混合:注释写中文、代码写英文,Whisper 自动切换,不需要手动指定语言码。
- 时间戳对齐:返回的
segments带毫秒级时间戳,方便后续做“语音→代码”高亮映射。 - 对比云 API:Google Speech-to-Text 按 15s 切片计费,一小时会议 20 块;Whisper 在 4 核笔记本上实时率 0.3,成本≈0。
系统总览:一张图看懂数据流
整个流程分三层:
- 语音采集层:PyAudio 循环读麦克风,检测到能量阈值>0.02 自动开录,静音 1s 自动停录,生成 WAV 片段。
- 转写&理解层:Whisper 把语音→文本,ChatTTS 做“意图分类+实体抽取”,判断是“生成代码”“写注释”还是“跑测试”。
- 代码生成层:本地缓存的代码模板+大模型 API(可选)拼装片段,最终写入剪贴板或直接插入 IDE(通过 Language Server Protocol)。
核心实现:30 行 Python 搭出 MVP
下面代码全部通过 Python 3.10 测试,依赖见文末。
1. 语音采集&端点检测
import pyaudio, wave, webrtcvad, collections class VoiceTrigger: def __init__(self, aggressiveness=2, frame_duration_ms=30): self.vad = webrtcvad.Vad(aggressiveness) self.ring = collections.deque(maxlen=30) self.speaking = False def is_speech(self, buf): self.ring.append(self.vad.is_speech(buf, 16000)) active = sum(self.ring) / len(self.ring) # 超过 50% 帧为语音,认为开始说话 if not self.speaking and active > 0.5: self.speaking = True return "start" # 连续 1s 无声,认为结束 if self.speaking and active < 0.2: self.speaking = False return "end" return None2. Whisper 本地推理
import whisper, tempfile, os model = whisper.load_model("base") # 第一次会下载 150 MB def wav_to_text(path: str) -> str: result = model.transcribe(path, language="zh", initial_prompt="以下是 Python 代码") return result["text"].strip()3. ChatTTS 意图解析
ChatTTS 官方目前只放出推理接口,这里用“零样本提示”方式:
import ChatTTS, torch chat = ChatTTS.Chat() chat.load(compile=False) # 开发机没显卡,用 CPU 模式 def nl_to_intent(text: str) -> dict: prompt = f"请提取下面句子的意图和参数,输出 JSON:\n{text}" params_refine_text = chat.infer(prompt, skip_refine_text=False) # 返回类似 {"intent":"write_function","name":"quick_sort","args":["arr"]} return json.loads(params_refine_text[0])4. 代码片段拼装
from string import Template TEMPLATES = { "write_function": Template( "def $name($args):\n \"\"\"TODO\"\"\"\n pass\n" ), "write_test": Template( "def test_$name():\n assert $name($args) == $expected\n" ) } def generate_code(intent: dict) -> str: tpl = TEMPLATES[intent["intent"]] return tpl.substitute(intent)5. 把片段写进 IDE
VS Code 已内置 LSP 端口:
import socket, json def paste_to_vscode(code: str): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("127.0.0.1", 8080)) sock.send(json.dumps({"command": "insert", "text": code}).encode()) sock.close()把上面 5 段拼到一条 asyncio 流水线,就能“边说边出代码”。
性能优化:让延迟<300 ms
- 模型裁剪:Whisper
base→tiny识别率下降 3%,但首字延迟从 450 ms 降到 180 ms。 - 流式 VAD:把帧长从 30 ms 改成 10 ms,端点检测提前 200 ms 触发,录音结束即可开始推理。
- 并发队列:用
asyncio.Queue缓存语音片段,Whisper 与 ChatTTS 跑在独立线程池,防止 GPU 互相抢占。 - 模板缓存:代码模板提前渲染为字节码,substitute 阶段省掉 10% CPU。
- 热加载:监控
templates/目录,文件变动自动重载,无需重启服务。
避坑指南:踩过的坑都在这
- 采样率不匹配:Whisper 训练用 16 kHz,PyAudio 默认 44.1 kHz,不一致性会掉 8% 识别率,记得
rate=16000。 - 中文标点全角:ChatTTS 会把全角括号当特殊 token,导致 JSON decode 失败,提前
unicodedata.normalize("NFKC", text)。 - 显存爆炸:ChatTTS 默认 batch_size=8,笔记本 4 GB 显存直接 OOM,改
batch_size=1并开启compile=False。 - 多进程 CUDA 死锁:Whisper 与 ChatTTS 同时用
torch.multiprocessing,需在主入口加if __name__ == "__main__": multiprocessing.set_start_method("spawn")。 - VS Code 端口占用:LSP 监听 8080,和前端 React 冲突,改成
socket.getrandomavailableport()动态分配。
扩展思考:大模型加持后的下一步
- 让 Whisper 输出“代码级时间戳”,结合 AST 把语音直接 patch 到函数级别,实现“口头重构”。
- 引入 CodeT5+/StarCoder 做“续写”,把 ChatTTS 的意图向量当 prefix,生成整文件而非片段。
- 建立私有知识库:把公司内部的 utils 函数向量化,语音说“发邮件”就能自动插入内部 SDK 调用。
- 语音回放:用 ChatTTS 把代码注释读出来,Code Review 变成“听书”,通勤路上也能审稿。
- 低代码混合:拖拽逻辑视图后,用语音描述分支条件,直接生成 YAML 供 CI/CD 消费。
实践小结
整套方案从“录音”到“代码上屏”平均 1.2 s,日常写样板代码的效率至少翻倍。最重要的是:思路不再被键盘打断,可以像“自言自语”一样完成原型。
下一步,我打算把 pipeline 封装成 VS Code 插件,放到市场让同事内测;再把 Whisper 蒸馏成 50 MB 的 int8 模型,树莓派也能跑,真正做到“随时开口,代码就有”。
进一步学习资源
- Whisper 官方仓库:github.com/openai/whisper
- ChatTTS 文档:chattts.org 的“推理加速”章节
- 《Voice-Driven Development》演讲:PyCon 2023 视频,讲语音 IDE 交互设计
- 实时 VAD 方案:github.com/wiseman/py-webrtcvad 示例合集
把键盘留给真正的创造,剩下的交给嘴巴吧。