Skip to content

Commit b0a9d95

Browse files
docs: 更新 Voice Mode 文档,添加豆包 ASR 后端说明和致谢
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent dce10e6 commit b0a9d95

2 files changed

Lines changed: 173 additions & 31 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
| **Poor Mode** | 穷鬼模式,关闭记忆提取和键入建议,大幅度减少并发请求 | /poor 可以开关 |
2828
| **Channels 频道通知** | MCP 服务器推送外部消息到会话(飞书/Slack/Discord/微信等),`--channels plugin:name@marketplace` 启用 | [文档](https://ccb.agent-aura.top/docs/features/channels) |
2929
| **自定义模型供应商** | OpenAI/Anthropic/Gemini/Grok 兼容 | [文档](https://ccb.agent-aura.top/docs/features/custom-platform-login) |
30-
| Voice Mode | Push-to-Talk 语音输入 | [文档](https://ccb.agent-aura.top/docs/features/voice-mode) |
30+
| Voice Mode | 语音输入,支持豆包语言输入(`/voice doubao` | [文档](https://ccb.agent-aura.top/docs/features/voice-mode) |
3131
| Computer Use | 屏幕截图、键鼠控制 | [文档](https://ccb.agent-aura.top/docs/features/computer-use) |
3232
| Chrome Use | 浏览器自动化、表单填写、数据抓取 | [自托管](https://ccb.agent-aura.top/docs/features/chrome-use-mcp) [原生版](https://ccb.agent-aura.top/docs/features/claude-in-chrome-mcp) |
3333
| Sentry | 企业级错误追踪 | [文档](https://ccb.agent-aura.top/docs/internals/sentry-setup) |
@@ -233,6 +233,10 @@ TUI (REPL) 模式需要真实终端,无法直接通过 VS Code launch 启动
233233
</picture>
234234
</a>
235235

236+
## 致谢
237+
238+
- [doubaoime-asr](https://github.com/starccy/doubaoime-asr) — 豆包 ASR 语音识别 SDK,为 Voice Mode 提供无需 Anthropic OAuth 的语音输入方案
239+
236240
## 许可证
237241

238242
本项目仅供学习研究用途。Claude Code 的所有权利归 [Anthropic](https://www.anthropic.com/) 所有。

docs/features/voice-mode.md

Lines changed: 168 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
11
# VOICE_MODE — 语音输入
22

33
> Feature Flag: `FEATURE_VOICE_MODE=1`
4-
> 实现状态:完整可用(需要 Anthropic OAuth)
4+
> 实现状态:完整可用(双后端:Anthropic OAuth / 豆包 ASR
55
> 引用数:46
66
77
## 一、功能概述
88

9-
VOICE_MODE 实现"按键说话"(Push-to-Talk)语音输入。用户按住空格键录音,音频通过 WebSocket 流式传输到 Anthropic STT 端点(Nova 3),实时转录显示在终端中。
9+
VOICE_MODE 实现"按键说话"(Push-to-Talk)语音输入。用户按住空格键录音,音频流式传输到 STT 后端,实时转录显示在终端中。支持两个后端:
10+
11+
- **Anthropic STT(默认)**:通过 WebSocket 流式传输到 Nova 3 端点,需要 Anthropic OAuth
12+
- **豆包 ASR(Doubao)**:通过 `doubaoime-asr` 包的 AsyncGenerator 协议流式识别,使用独立凭证文件,无需 Anthropic OAuth
1013

1114
### 核心特性
1215

1316
- **Push-to-Talk**:长按空格键录音,释放后自动发送
1417
- **流式转录**:录音过程中实时显示中间转录结果
1518
- **无缝集成**:转录文本直接作为用户消息提交到对话
19+
- **双后端切换**:通过 `/voice` 命令参数选择 STT 后端,持久化到 settings.json
1620

1721
## 二、用户交互
1822

1923
| 操作 | 行为 |
2024
|------|------|
2125
| 长按空格 | 开始录音,显示录音状态 |
22-
| 释放空格 | 停止录音,等待最终转录 |
23-
| 转录完成 | 自动插入到输入框并提交 |
24-
| `/voice` 命令 | 切换语音模式开关 |
26+
| 释放空格 | 停止录音,转录结果自动提交 |
27+
| `/voice` | 切换语音模式开关(默认使用 Anthropic 后端) |
28+
| `/voice doubao` | 启用语音模式并使用豆包 ASR 后端 |
29+
| `/voice anthropic` | 切换回 Anthropic STT 后端 |
2530

2631
### UI 反馈
2732

@@ -35,26 +40,37 @@ VOICE_MODE 实现"按键说话"(Push-to-Talk)语音输入。用户按住空
3540

3641
文件:`src/voice/voiceModeEnabled.ts`
3742

38-
三层检查
43+
两层检查函数
3944

4045
```ts
46+
// Anthropic 后端(需要 OAuth)
4147
isVoiceModeEnabled() = hasVoiceAuth() && isVoiceGrowthBookEnabled()
48+
49+
// 豆包后端 / 通用可用性检查(不需要 OAuth)
50+
isVoiceAvailable() = isVoiceGrowthBookEnabled()
4251
```
4352

4453
1. **Feature Flag**`feature('VOICE_MODE')` — 编译时/运行时开关
4554
2. **GrowthBook Kill-Switch**`!getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_quartz_disabled', false)` — 紧急关闭开关(默认 false = 未禁用)
46-
3. **Auth 检查**`hasVoiceAuth()` — 需要 Anthropic OAuth token(非 API key)
55+
3. **Auth 检查(仅 Anthropic)**`hasVoiceAuth()` — 需要 Anthropic OAuth token(非 API key)
56+
4. **Provider 检查**`voiceProvider` 设置决定使用哪个后端,豆包后端跳过 OAuth 检查
4757

4858
### 3.2 核心模块
4959

5060
| 模块 | 职责 |
5161
|------|------|
5262
| `src/voice/voiceModeEnabled.ts` | Feature flag + GrowthBook + Auth 三层门控 |
53-
| `src/hooks/useVoice.ts` | React hook 管理录音状态和 WebSocket 连接 |
54-
| `src/services/voiceStreamSTT.ts` | WebSocket 流式传输到 Anthropic STT |
63+
| `src/hooks/useVoice.ts` | React hook 管理录音状态和后端连接 |
64+
| `src/services/voiceStreamSTT.ts` | Anthropic WebSocket 流式 STT |
65+
| `src/services/doubaoSTT.ts` | 豆包 ASR 适配器(AsyncGenerator → VoiceStreamConnection) |
66+
| `src/commands/voice/voice.ts` | `/voice` 命令实现,处理后端选择和持久化 |
67+
| `src/hooks/useVoiceEnabled.ts` | 语音启用状态 hook,根据 provider 决定是否跳过 OAuth |
68+
| `src/utils/settings/types.ts` | `voiceProvider: 'anthropic' | 'doubao'` 设置类型定义 |
5569

5670
### 3.3 数据流
5771

72+
#### Anthropic 后端
73+
5874
```
5975
用户按下空格键
6076
@@ -79,47 +95,169 @@ WebSocket 连接到 Anthropic STT 端点
7995
转录文本 → 插入输入框 → 自动提交
8096
```
8197

98+
#### 豆包 ASR 后端
99+
100+
```
101+
用户按下空格键
102+
103+
104+
useVoice hook 激活(检测到 voiceProvider === 'doubao')
105+
106+
107+
macOS 原生音频 / SoX 开始录音
108+
109+
110+
connectDoubaoStream() 创建 AudioChunkQueue + VoiceStreamConnection
111+
112+
├──→ onReady 立即触发(无需等待握手)
113+
114+
115+
音频数据通过 AudioChunkQueue 传入 transcribeRealtime()
116+
117+
├──→ INTERIM_RESULT → 实时显示中间转录
118+
├──→ FINAL_RESULT → 显示最终转录
119+
120+
121+
用户释放空格键
122+
123+
124+
finalize() 立即返回(豆包在录音过程中已返回结果,无需等待)
125+
126+
127+
转录文本 → 插入输入框 → 自动提交
128+
```
129+
82130
### 3.4 音频录制
83131

84-
支持两种音频后端:
132+
支持两种音频后端(两个 STT 后端共享)
85133
- **macOS 原生音频**:优先使用,低延迟
86134
- **SoX(Sound eXchange)**:回退方案,跨平台
87135

88-
音频流通过 WebSocket 发送到 Anthropic 的 Nova 3 STT 模型。
136+
### 3.5 豆包 ASR 适配器设计
137+
138+
文件:`src/services/doubaoSTT.ts`
139+
140+
豆包后端使用适配器模式,将 `doubaoime-asr` 的 AsyncGenerator 协议桥接到 `VoiceStreamConnection` 接口:
141+
142+
**AudioChunkQueue** — push 式异步队列:
143+
- 实现 `AsyncIterable<Uint8Array>` 接口
144+
- `push(chunk)` 将音频数据入队,`push(null)` 发送结束信号
145+
- 内部维护等待者(waiting)和缓冲队列(chunks)两个状态
146+
147+
**connectDoubaoStream()** — 连接入口:
148+
- 动态导入 `doubaoime-asr`(optionalDependencies)
149+
-`~/.claude/tts/doubao/credentials.json` 加载凭证
150+
- 创建 AudioChunkQueue 和 VoiceStreamConnection
151+
- 立即触发 `onReady`(避免与 useVoice 的音频缓冲死锁)
152+
- `finalize()` 立即返回(豆包在录音过程中已返回结果)
153+
- 后台 async IIFE 消费 `transcribeRealtime` generator,映射响应类型到回调
154+
155+
**响应类型映射**
156+
157+
| doubaoime-asr ResponseType | 回调映射 |
158+
|----------------------------|----------|
159+
| SESSION_STARTED | 日志记录 |
160+
| VAD_START | 日志记录 |
161+
| INTERIM_RESULT | `onTranscript(text, false)` |
162+
| FINAL_RESULT | `onTranscript(text, true)` |
163+
| ERROR | `onError(errorMsg)` |
164+
| SESSION_FINISHED | 日志记录 |
165+
166+
### 3.6 后端选择逻辑
167+
168+
文件:`src/hooks/useVoice.ts`
169+
170+
```ts
171+
// 判断当前 provider
172+
isDoubaoProvider() → 读取 settings.voiceProvider
173+
174+
// handleKeyEvent 中的可用性检查
175+
const sttAvailable = isDoubaoProvider()
176+
? isDoubaoAvailableSync() // 乐观检查(首次返回 true)
177+
: isVoiceStreamAvailable() // Anthropic WebSocket 检查
178+
179+
// attemptConnect 中的连接函数选择
180+
const connectFn = isDoubaoProvider()
181+
? connectDoubaoStream
182+
: connectVoiceStream
183+
```
184+
185+
豆包后端的特殊处理:
186+
- 跳过 `getVoiceKeyterms()` 调用(豆包无需关键词提示)
187+
- 跳过 Focus Mode(`if (!enabled || !focusMode || isDoubaoProvider())`
89188

90189
## 四、关键设计决策
91190

92-
1. **OAuth 独占**:语音模式使用 `voice_stream` 端点(claude.ai),仅 Anthropic OAuth 用户可用。API key、Bedrock、Vertex 用户无法使用
93-
2. **GrowthBook 负向门控**`tengu_amber_quartz_disabled` 默认 `false`,新安装自动可用(无需等 GrowthBook 初始化)
94-
3. **Keychain 缓存**`getClaudeAIOAuthTokens()` 首次调用访问 macOS keychain(~20-50ms),后续缓存命中
95-
4. **独立于主 feature flag**`isVoiceGrowthBookEnabled()` 在 feature flag 关闭时短路返回 `false`,不触发任何模块加载
191+
1. **双后端共存**:豆包后端作为独立适配器与 Anthropic 后端并存,不替换原有流程,通过 `voiceProvider` 设置切换
192+
2. **设置持久化**`voiceProvider` 存储在 `settings.json`,通过 `/voice` 命令修改,跨会话生效
193+
3. **OAuth 独占(Anthropic)**:Anthropic 后端使用 `voice_stream` 端点(claude.ai),仅 OAuth 用户可用
194+
4. **豆包无需 OAuth**:豆包后端使用独立凭证文件,不依赖 Anthropic 认证,通过 `isVoiceAvailable()` 放宽门控
195+
5. **GrowthBook 负向门控**`tengu_amber_quartz_disabled` 默认 `false`,新安装自动可用
196+
6. **onReady 立即触发**:豆包后端在连接建立后立即触发 `onReady`,避免与 useVoice 音频缓冲的时序死锁(Anthropic 需要等待 WebSocket 握手)
197+
7. **finalize() 立即返回**:豆包在录音过程中已返回所有结果,用户抬手时无需等待处理
198+
8. **乐观可用性检查**`isDoubaoAvailableSync()` 在首次调用时返回 `true`,实际导入错误在 `connectDoubaoStream` 中处理
199+
9. **optionalDependencies**`doubaoime-asr` 作为可选依赖,安装失败不影响 Anthropic 后端
96200

97201
## 五、使用方式
98202

99203
```bash
100204
# 启用 feature
101205
FEATURE_VOICE_MODE=1 bun run dev
102206

103-
# 在 REPL 中使用
207+
# 在 REPL 中使用 Anthropic 后端
104208
# 1. 确保已通过 OAuth 登录(claude.ai 订阅)
105-
# 2. 按住空格键说话
106-
# 3. 释放空格键等待转录
107-
# 4. 或使用 /voice 命令切换开关
209+
# 2. 输入 /voice 启用
210+
# 3. 按住空格键说话
211+
# 4. 释放空格键等待转录
212+
213+
# 在 REPL 中使用豆包 ASR 后端
214+
# 1. 确保 doubaoime-asr 已安装(bun add doubaoime-asr)
215+
# 2. 配置凭证文件:~/.claude/tts/doubao/credentials.json
216+
# 3. 输入 /voice doubao 启用
217+
# 4. 按住空格键说话
218+
# 5. 释放空格键,转录结果即刻显示
219+
220+
# 切换后端
221+
/voice doubao # 切换到豆包 ASR
222+
/voice anthropic # 切换回 Anthropic STT
223+
/voice # 关闭语音模式
224+
```
225+
226+
### 豆包凭证配置
227+
228+
凭证文件路径:`~/.claude/tts/doubao/credentials.json`
229+
230+
```json
231+
{
232+
"deviceId": "...",
233+
"installId": "...",
234+
"cdid": "...",
235+
"openudid": "...",
236+
"clientudid": "...",
237+
"token": "..."
238+
}
108239
```
109240

110241
## 六、外部依赖
111242

112-
| 依赖 | 说明 |
113-
|------|------|
114-
| Anthropic OAuth | claude.ai 订阅登录,非 API key |
115-
| GrowthBook | `tengu_amber_quartz_disabled` 紧急关闭 |
116-
| macOS 原生音频 或 SoX | 音频录制 |
117-
| Nova 3 STT | 语音转文本模型 |
243+
| 依赖 | 说明 | 适用后端 |
244+
|------|------|----------|
245+
| Anthropic OAuth | claude.ai 订阅登录,非 API key | Anthropic |
246+
| GrowthBook | `tengu_amber_quartz_disabled` 紧急关闭 | 通用 |
247+
| macOS 原生音频 或 SoX | 音频录制 | 通用 |
248+
| Nova 3 STT | Anthropic 语音转文本模型 | Anthropic |
249+
| doubaoime-asr | 豆包 ASR SDK(optionalDependencies) | 豆包 |
250+
| 凭证文件 | `~/.claude/tts/doubao/credentials.json` | 豆包 |
118251

119252
## 七、文件索引
120253

121-
| 文件 | 行数 | 职责 |
122-
|------|------|------|
123-
| `src/voice/voiceModeEnabled.ts` | 54 | 三层门控逻辑 |
124-
| `src/hooks/useVoice.ts` || React hook(录音状态 + WebSocket) |
125-
| `src/services/voiceStreamSTT.ts` || STT WebSocket 流式传输 |
254+
| 文件 | 职责 |
255+
|------|------|
256+
| `src/voice/voiceModeEnabled.ts` | 三层门控逻辑 + `isVoiceAvailable()` |
257+
| `src/hooks/useVoice.ts` | React hook(录音状态 + 后端选择 + 连接管理) |
258+
| `src/hooks/useVoiceEnabled.ts` | 语音启用状态 hook(按 provider 决定 OAuth 检查) |
259+
| `src/services/voiceStreamSTT.ts` | Anthropic STT WebSocket 流式传输 |
260+
| `src/services/doubaoSTT.ts` | 豆包 ASR 适配器(AudioChunkQueue + connectDoubaoStream) |
261+
| `src/commands/voice/voice.ts` | `/voice` 命令(开关 + 后端选择) |
262+
| `src/commands/voice/index.ts` | 命令注册(去除 availability 限制) |
263+
| `src/utils/settings/types.ts` | `voiceProvider` 类型定义 |

0 commit comments

Comments
 (0)