🎤 从踩坑到恍然大悟:我做了一个没必要的 VS Code 语音插件

🎤 从踩坑到恍然大悟:我做了一个没必要的 VS Code 语音插件

"给他 up 烤盘子"

在测试语音识别功能时,我对着麦克风说了一句"GitHub Copilot"。

屏幕上显示的是:给他 up 烤盘子

那一刻,我对语音识别技术产生了深深的敬畏——敬畏它在某些时刻的离谱程度。

但更离谱的事情还在后面:我花了一整天开发的语音插件,在即将完成时,发现 Copilot 本身就已经支持语音输入和朗读了。

这是一篇关于"白忙活"的记录。但正如老话说的——过程比结果重要(至少我是这么安慰自己的)。


项目背景:Hackathon 的冲动

公司举办内部 Hackathon,主题是围绕 AI 编程工具做创新。我脑海里冒出一个"很酷"的想法:

如果能用语音和 Copilot 对话,然后让它把回答念出来,岂不是很方便?

想象一下:

  • 🎤 对着麦克风说:"帮我解释这段代码"
  • 🤖 Copilot 分析后给出解释
  • 🔊 解释内容被朗读出来

解放双手,闭眼编程,完美!

于是,VoicePilot 诞生了——一个旨在为 GitHub Copilot Chat 添加语音交互能力的 VS Code 扩展。


技术探索之旅

架构设计

经过一番 brainstorming,我确定了技术栈:

┌─────────────────────────────────────────────────────────────┐
│                      VoicePilot 架构                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   🎤 语音输入                    🔊 语音输出                  │
│   ─────────                    ─────────                   │
│   Azure Speech SDK             Azure Speech SDK             │
│   (STT: 语音转文字)              (TTS: 文字转语音)             │
│         │                            ▲                      │
│         ▼                            │                      │
│   ┌─────────────────────────────────────────────┐          │
│   │           @voice Chat Participant            │          │
│   │         (VS Code Chat API 集成)              │          │
│   └─────────────────────────────────────────────┘          │
│                        │                                    │
│                        ▼                                    │
│              GitHub Copilot 处理                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

核心技术选型:

  • Azure Speech SDK (microsoft-cognitiveservices-speech-sdk):负责语音识别和语音合成
  • VS Code Chat Participant API (vscode.chat.createChatParticipant):创建 @voice 聊天参与者
  • esbuild:打包依赖,避免 node_modules 体积问题

第一个坑:window is not defined

满怀信心地写完代码,打包,安装,运行——

Error: window is not defined

怎么回事?

原来 Azure Speech SDK 的麦克风输入 API (AudioConfig.fromDefaultMicrophoneInput()) 依赖浏览器环境的 window 对象。而 VS Code 扩展运行在 Node.js 环境中,根本没有 window

这是一个典型的"环境不匹配"问题。SDK 文档里那些漂亮的示例代码,都是给浏览器用的。

解决方案尝试

  1. 使用文件输入替代麦克风(录音 → 识别)—— 太麻烦
  2. 创建 WebView 来捕获音频 —— 复杂度爆炸
  3. 寻找替代方案...

第二个坑:VS Code Speech 扩展

在搜索解决方案时,我发现了 VS Code Speech 扩展 (ms-vscode.vscode-speech)——微软官方出品,提供本地语音识别能力。

更关键的是,我检查了一下自己的 VS Code:

code --list-extensions | grep -i speech

输出:

ms-vscode.vscode-speech
ms-vscode.vscode-speech-language-pack-zh-cn

我早就安装了它,只是从来没用过。

这让我开始怀疑:Copilot 是不是已经集成了语音功能?


恍然大悟时刻

抱着试试看的心态,我按下了 Cmd+I(macOS)。

一个语音输入界面出现了。我说了句话,文字自动出现在 Copilot Chat 的输入框里。

然后我又搜索了设置,找到了这个:

"accessibility.voice.autoSynthesize": "on"

开启后,当我用语音提问时,Copilot 的回复会自动被朗读出来

我坐在电脑前,沉默了三秒。

我花了一天时间开发的功能,VS Code 早就有了。


收获与反思

虽然 VoicePilot 最终被判定为"没必要",但这次探索让我收获颇丰:

📚 技术收获

  1. VS Code 扩展开发全流程

    • TypeScript 项目结构
    • package.json 中的 contributes 配置
    • 命令注册、状态栏管理、快捷键绑定
  2. Chat Participant API

    • 创建自定义的 @xxx 聊天参与者
    • 与 Copilot 模型交互
    • 处理流式响应
  3. esbuild 打包

    • 将 node_modules 打包进扩展
    • 解决 .vsix 文件过大问题
    • 处理外部依赖的 require
  4. Azure Speech SDK 的局限性

    • 麦克风 API 需要浏览器环境
    • 文件输入是 Node.js 环境的替代方案

💡 经验教训

做项目前,先调研现有方案!

如果我一开始就搜索"VS Code Copilot voice",可能 5 分钟就能发现答案,而不是花一天去重复造轮子。

但话说回来,如果不亲自踩坑,我也不会真正理解 VS Code 扩展开发的方方面面。有时候,弯路也是一种学习。


附录:Copilot 语音功能完全指南

既然 VoicePilot 没必要做了,那就把 Copilot 自带的语音功能整理清楚,造福后人:

前置条件

  1. 安装 VS Code Speech 扩展
  2. 安装对应语言包(如中文:ms-vscode.vscode-speech-language-pack-zh-cn
  3. 确保麦克风权限已授予 VS Code

语音输入

功能Mac 快捷键Windows 快捷键
语音输入到 ChatCmd+I(按住说话,松开提交)Ctrl+I
编辑器听写模式Cmd+Alt+VCtrl+Alt+V

使用技巧:按住 Cmd+I 不放,对着麦克风说话,松开后自动提交到 Copilot。

语音输出(朗读)

在设置中开启:

// 当用语音输入时,自动朗读 Copilot 的回复
"accessibility.voice.autoSynthesize": "on",

// 朗读时跳过代码块(推荐开启)
"accessibility.voice.ignoreCodeBlocks": true

语言设置

// 设置语音识别的语言
"accessibility.voice.speechLanguage": "zh-CN"

支持 26 种语言,包括中文、英文、日文、法文等。


尾声

VoicePilot 项目虽然"失败"了,但我收获了:

  • 一套完整的 VS Code 扩展开发经验
  • 对 Azure Speech SDK 的深入理解
  • 以及这篇博文

最重要的是,我终于知道怎么让 Copilot "开口说话"了。

下次 Hackathon,我一定会先花 10 分钟调研。

——大概吧。 😅

留言与讨论