codev / docs /voice /overview.md
chenbhao's picture
chore: rename VersperClaw to Codev, update org to chenbhao
96f34e3
|
Raw
History Blame Contribute Delete
9.98 kB

语音系统

概述

Codev 的语音系统支持语音转文字(STT, Speech-to-Text)文字转语音(TTS, Text-to-Speech) 功能。语音录制使用底层音频捕获库(cpal),含有 SoX 和 arecord 回退方案。语音识别支持多个提供商,包括云端 API 和本地模型;语音合成同样支持多个引擎,涵盖 Edge TTS 和 DashScope TTS。

注意:部分功能受构建特性门控(feature gate)限制,仅在 ant-internal 构建中可用。


语音录制(Voice Recording)

核心文件:src/services/voice.ts

录音模块使用分层策略:

层级 方案 适用平台
原生 audio-capture-napi(cpal) macOS, Linux, Windows
回退 1 SoX rec Linux (有 SoX)
回退 2 arecord (ALSA) Linux (有 ALSA)

关键特征:

  • 懒加载原生模块loadAudioNapi() 在首次按键时异步加载 audio-capture-napi,避免启动时阻塞(dlopen 可能耗时 1-8 秒)。
  • 静音检测:使用 SoX 或 arecord 时启用静音检测(阈值 3%,持续时间 2 秒),自动结束录音。
  • 录音常量:采样率 16000 Hz,单声道,16-bit PCM。
  • 依赖检查checkVoiceDependencies() 探测可用性,显示每个方案的可用状态。

STT 提供商(Speech-to-Text)

抽象接口

文件:src/services/voice/providers.ts

interface TranscriptionProvider {
  name: string
  transcribe(wavPath: string, language?: string): Promise<TranscriptionResult>
}

所有 STT 提供商实现此接口,LocalWhisperSTTDoubaoSTTProvider 是内置实现。


1. Groq Whisper(云端)

文件:src/services/voice/groqSTT.ts

通过官方 groq-sdk npm 包调用 Groq LPU API,使用 Whisper 模型。

模型回退

  1. 优先使用 whisper-large-v3
  2. 遇到 429(限速)或 5xx(服务端错误)时自动回退到 whisper-large-v3-turbo
  3. 其他错误(4xx)直接抛出,不重试

API 密钥解析(优先级从高到低):

  1. 显式传入的 apiKey 参数
  2. friend.json 中的 groqApiKey 配置
  3. 环境变量 GROQ_API_KEY
  4. ~/.claude/settings.json 中的 env.groqApiKey

核心流程

  • connectGroqStream() 返回 VoiceStreamConnection 接口
  • 接收 PCM 音频块并缓冲
  • finalize() 时将 PCM 转换为 WAV(pcmToWav(),16-bit 单声道,16000 Hz)
  • 通过 File API 上传 WAV 到 Groq

2. Local Whisper(本地模型)

文件:src/services/voice/whisperSTT.ts

基于 Python openai-whisper 的本地部署方案。

架构

  • 启动一个长期运行的 Python 子进程(whisper_server.py
  • 通过 stdin/stdout 的 JSON 行协议通信
  • 支持预加载模型(preloadWhisperModel()

通信协议

  • {"type":"load","model":"small"} —— 加载模型
  • {"type":"transcribe","wav":"/path/to/audio.wav","language":"en"} —— 转写
  • 服务端以 {"type":"result","text":"...","language":"..."}{"type":"error","message":"..."} 响应

进程管理

  • 进程崩溃后自动重启
  • 30 秒超时保护
  • 临时文件自动清理

可用性检查:使用 Python importlib.util.find_spec("whisper") 探测 whisper 模块是否可导入,避免加载 PyTorch 的耗时。


3. Anthropic Voice Stream(WebSocket)

文件:src/services/voiceStreamSTT.ts

通过 Anthropic 的 voice_stream WebSocket 端点传输语音,仅在 ant-internal 构建中可用(由 feature('VOICE_MODE') 门控)。

WebSocket 协议

  • 端点:wss://api.anthropic.com/api/ws/speech_to_text/voice_stream
  • 认证:OAuth Bearer Token(与 Claude Code 共享凭证)
  • 消息类型:
    • KeepAlive —— 每 8 秒发送保持连接
    • CloseStream —— 结束流
    • 服务端推送 TranscriptTextTranscriptEndpointTranscriptError

连接生命周期

  1. connectVoiceStream() 建立 WebSocket 连接
  2. send(audioChunk) 发送二进制音频帧
  3. finalize() 发送 CloseStream,等待服务端返回 TranscriptEndpoint
  4. FinalizeSource 枚举标识解析路径:post_closestream_endpointno_data_timeoutsafety_timeoutws_closews_already_closed

Deepgram Nova 3 门控:通过 GrowthBook 特性标记 deepgram_nova_3_gate 控制是否使用 Deepgram Nova 3 模型。

Voice Keyterms:通过查询参数传递关键词列表,提高领域术语的识别准确率。


4. Doubao(豆包 STT)

文件:src/services/voice/doubaoSTT.ts

此文件是一个自动生成的存根(stub),对应 ant-internal 的 feature() 门控模块。外部构建中所有代码路径在 DCE(死代码消除)后不会实际执行。

存根使用 JavaScript Proxy 将任何属性访问、函数调用、构造操作映射到无操作(noop)处理器。导出 connectDoubaoStreamnormalizeLanguageForSTT 等函数作为占位符。

DoubaoSTTProvider(在 providers.ts 中)封装了此存根的调用逻辑,通过动态导入(import('./doubaoSTT.js'))在运行时解析。


TTS 提供商(Text-to-Speech)

抽象接口

文件:src/services/voice/providers.ts

interface TTSProvider {
  name: string
  synthesize(text: string): Promise<SynthesisResult>
}

EdgeTTSProviderCommandTTSProvider 是内置实现。


1. Edge TTS(微软神经网络语音)

文件:src/services/voice/providers.ts(类 EdgeTTSProvider

实现路径一(Provider 接口)

  • 直接调用 edge-tts 命令行工具
  • 使用 Node.js child_process.spawn 执行子进程
  • 通过 --voice--text--write-media 参数控制输出
  • 默认语音:en-US-AriaNeural

实现路径二(独立函数): 文件:src/services/voice/edgeTTS.tsspeakWithEdgeTTS()

  • 调用 scripts/speak.py Python 脚本
  • 依赖 .venv/bin/python 或系统 Python
  • 返回标准化的 TTSResult 接口

实现路径三(Friend 模块): 文件:src/friend/tts.tsedgeTts()

  • 使用 node-edge-tts npm 包(Node.js 原生实现,无需 Python)
  • 默认语音:zh-CN-XiaoxiaoNeural(中文语音)
  • 输出为 MP3 文件

播放功能src/services/voice/edgeTTS.ts playAudioFile()):

平台 播放器 说明
macOS afplay 原生
Linux ffplay pkill 已有进程,再启动新进程
Windows start 系统默认播放器

2. Qwen DashScope TTS(通义千问语音合成)

文件:src/friend/tts.tsqwenTts()

调用阿里云 DashScope API 进行语音合成。

API 信息

  • 国内端点:https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation
  • 国际端点:https://dashscope-intl.aliyuncs.com/...
  • 默认模型:qwen3-tts-flash
  • 默认语音:Cherry
  • 认证:Bearer Token(apiKey

参数

  • voice:语音角色(如 Cherry, Polly 等)
  • model:模型版本
  • language:语言(zhen),决定端点选择和 language_type

流程

  1. POST 请求获取合成音频的 URL
  2. 从 URL 下载音频数据
  3. 保存为 WAV 临时文件
  4. 支持 30 秒超时保护

音频文件注册registerAudioFile() 将文件路径注册到内存映射,5 分钟后自动过期,用于跨模块引用。


3. Command TTS(命令模板回退)

文件:src/services/voice/providers.ts(类 CommandTTSProvider

通用 shell 命令 TTS,支持模板占位符:

  • {input} / {input_path} —— 输入文本文件路径
  • {output_path} —— 输出音频文件路径

适用于调用任意外部 TTS 命令行工具。


Voice Stream 协议

文件:src/services/voiceStreamSTT.ts

Voice Stream 是 Anthropic 的 WebSocket 协议,用于实时语音识别。

消息类型

方向 类型 说明
客户端 → 服务端 KeepAlive 心跳,每 8 秒
客户端 → 服务端 CloseStream 结束音频流
客户端 → 服务端 二进制帧 PCM 音频数据
服务端 → 客户端 TranscriptText 转写文本片段
服务端 → 客户端 TranscriptEndpoint 转写结束标记
服务端 → 客户端 TranscriptError 错误信息

FinalizeSource 枚举

finalize() 方法的解析路径:

说明
post_closestream_endpoint 正常流程:发送 CloseStream 后收到 TranscriptEndpoint
no_data_timeout 发送 CloseStream 后 1.5 秒无响应
safety_timeout WebSocket 挂起超过 5 秒
ws_close WebSocket 连接关闭
ws_already_closed 已关闭的连接被重复调用

Keyterms(关键词)

通过 WebSocket 查询参数 keyterms 传递关键词列表,格式为逗号分隔的 URL 编码值。关键词可提高模型对特定术语的识别准确率。


Voice Mode(语音模式)

语音模式是 Push-to-Talk(按住说话)的实现:

  1. 开始录音:用户按下语音快捷键
  2. 音频采集:底层 cpal 或回退方案开始采集 16kHz 单声道 PCM 音频
  3. 音频传输:音频块通过 Voice Stream WebSocket 实时发送
  4. 释放停止:用户松开快捷键,发送 CloseStream
  5. 等待转写:接收服务端返回的 TranscriptText 和 TranscriptEndpoint
  6. 提交文本:转写文本进入对话输入流

当使用本地 Whisper 时,流程类似但使用子进程通信而非 WebSocket。


提供商注册与选择

文件:src/services/voice/providers.ts

系统通过 TranscriptionProviderTTSProvider 接口实现多提供商支持。每个提供商有自己的名称(name 属性)和实现逻辑。选择策略在调用方(如 useVoice hook)中决定,根据可用性和用户配置选择合适的提供商。