Agent Zero Whisper STT 语音转文字插件深度解析:架构、配置与端到端转写流程
【免费下载链接】agent-zeroAgent Zero AI framework项目地址: https://gitcode.com/GitHub_Trending/ag/agent-zero
导读
本文以 Agent Zero 内置的_whisper_stt插件(plugins/_whisper_stt)为对象,完整讲解这套基于 OpenAI Whisper 的语音转文字(Speech-to-Text)能力的职责划分、六大配置参数、两个插件 API、模型运行时原理与前端麦克风状态机。读完本文,你将掌握:如何在 Agent Zero 中启用并调优 Whisper STT、send/draft两种消息投递模式的区别、静音检测到转写派发的完整数据链路,以及该插件如何与sttService服务、Web UI 扩展点协同工作,为后续二次开发或故障排查提供源码级依据。
一、插件定位:职责边界清晰的 STT 提供者
从 plugins/_whisper_stt/README.md 与 plugins/_whisper_stt/AGENTS.md 可以看出,该插件遵循"一个插件只负责一件事"的设计原则,其核心职责是:
- 在插件启用时,把 Whisper 注册为当前活跃的 STT 提供者;
- 拥有麦克风运行时(microphone runtime)、设备选择 UI、消息投递模式与插件 API;
- 将依赖安装与模型引导(bootstrap)约束在 Docker/启动路径上,插件本身不负责安装依赖。
与之配套,_kokoro_tts插件负责文本转语音(TTS),二者共同构成 Agent Zero 的语音能力双子插件。这种拆分也体现在目录结构上:语音能力从核心代码库迁出为插件后,旧的核心语音文件(如api/transcribe.py、helpers/whisper.py、webui/css/speech.css等)已被移除,并由 tests/test_speech_plugin_split.py 中的test_legacy_core_speech_artifacts_are_removed测试用例逐一断言其不存在。
模块归属(Ownership)
根据 plugins/_whisper_stt/AGENTS.md 中声明的所有权边界,各文件分工如下:
| 模块 | 归属职责 |
|---|---|
api/(status.py、transcribe.py) | 转写与状态端点 |
helpers/(runtime.py、migration.py) | 运行时与迁移行为 |
hooks.py | 提供者注册与生命周期行为 |
webui/ | 设置页、主语音 UI、Store(状态仓库)与样式 |
default_config.yaml、plugin.yaml、README.md | 默认值、元数据与行为说明 |
插件元数据
plugins/_whisper_stt/plugin.yaml 声明了插件的基本信息:
name: _whisper_stt title: Whisper STT description: Built-in Whisper speech-to-text plugin. version: 1.0.0 always_enabled: false settings_sections: - agent per_project_config: false per_agent_config: false要点:always_enabled: false表示插件默认不启用,需要在设置中显式打开;配置作用于全局(per_project_config与per_agent_config均为false),不会按项目或按 Agent 隔离;settings_sections: [agent]表明其配置入口挂在 Agent 设置分区下,Web UI 通过 extensions/webui/voice-settings-main/whisper-card.html 在voice-settings-main扩展点渲染设置卡片。
二、配置详解:六大参数与取值约束
插件默认配置定义在 plugins/_whisper_stt/default_config.yaml:
model_size: base language: en message_mode: send silence_threshold: 0.3 silence_duration: 1000 waiting_timeout: 2000同时,plugins/_whisper_stt/helpers/runtime.py 中维护了一份同值的DEFAULT_CONFIG字典,并在normalize_config()函数中实现了完整的参数校验与钳制(clamping)逻辑,这正是"配置参数说明"的权威来源:
| 参数 | 默认值 | 含义 | normalize 约束(源码行为) |
|---|---|---|---|
model_size | base | Whisper 模型变体 | 仅接受集合{"tiny", "base", "small", "medium", "large", "turbo"}中的值,非法值回退默认 |
language | en | 语言提示,auto表示自动检测 | 非空字符串即可;auto在运行时被解析为None(交给 Whisper 自动识别) |
message_mode | send | 最终转写结果的投递方式 | 仅接受{"send", "draft"},小写归一化,非法值回退send |
silence_threshold | 0.3 | 触发录音的最低信号门限 | 强制转 float 并钳制到[0.0, 1.0] |
silence_duration | 1000 | 进入等待阶段所需的静音毫秒数 | 转 int,必须> 0 |
waiting_timeout | 2000 | 静音后等待多久才派发转写(毫秒) | 转 int,必须> 0 |
注意:
normalize_config只做"防御性"校验,任何非法或缺失的键都会被安全地替换为默认值,不会抛异常——例如float("abc")会触发TypeError/ValueError捕获后静默保留默认值(见 runtime.py)。
配置的两个入口
- YAML 默认值:default_config.yaml,作为新环境的种子值来源;
- 运行时持久化配置:由
migration.ensure_config_seeded()生成/校验的 JSON 配置文件,实际写入路径由plugins.determine_plugin_asset_path决定(见 migration.py)。Web UI 的 config.html 提供图形化编辑入口,其中:- Model Size下拉框列出全部六种模型(Tiny / Base / Small / Medium / Large / Turbo);
- Language为文本输入框,占位符
en,文档明确提示"Useautoto let Whisper detect it"; - Voice Message Handling二选一:
Send immediately/Draft in composer; - Silence Threshold为 0~1 的 range 滑条(step 0.01);
- Silence Duration与Waiting Timeout为数字输入框(min 100、step 100 毫秒)。
配置保存在 Web UI 中修改后,会经hooks.py的save_plugin_config走一次normalize_config,保证落盘数据始终合法(见 hooks.py)。
三、后端 API:状态查询与音频转写
插件向外部暴露两个 REST 端点,均基于helpers.api.ApiHandler实现:
1.POST /api/plugins/_whisper_stt/status— 状态查询
实现见 plugins/_whisper_stt/api/status.py。该端点会先调用migration.ensure_config_seeded()确保配置已初始化,然后返回结构化状态:
{ "plugin": "_whisper_stt", "enabled": true, "config": { "model_size": "base", "language": "en", "message_mode": "send", "silence_threshold": 0.3, "silence_duration": 1000, "waiting_timeout": 2000 }, "model": { "ready": false, "loading": false, "loaded_model": "" }, "package": { "version": "20250625", "error": "" } }其中model.ready对应运行时_model is not None、model.loading对应is_updating_model(即模型正在预加载),package.version通过importlib.metadata.version("openai-whisper")读取,若依赖缺失则error字段携带异常信息——这也印证了"依赖安装走启动路径"的约束:插件运行时只负责读取已安装好的包。
2.POST /api/plugins/_whisper_stt/transcribe— 音频转写
实现见 plugins/_whisper_stt/api/transcribe.py,请求体为 JSON,关键字段:
| 字段 | 必填 | 说明 |
|---|---|---|
audio | 是 | 音频的 Base64 编码字符串(前端为 WAV Blob 转换而来) |
ctxid | 否 | 上下文 ID,传入后调用self.use_context(ctxid)关联上下文 |
处理流程与返回约定:
- 插件未启用 → 返回
409,body 为"Whisper STT plugin is disabled"; audio缺失或为空 → 返回400,body 为"Missing audio";- 正常情况调用
runtime.transcribe(audio),返回:
{ "success": true, "text": "转写文本", "language": "en" }- 转写过程抛异常 → 返回
{"success": false, "error": "<异常信息>", "text": ""},错误不会以 500 形式打断前端状态机。
四、运行时原理:模型加载、语言解析与转写链路
模型预加载与全局单例
runtime.py 的preload()/_preload()是模型管理核心:
- 使用模块级全局变量
_model/_model_name缓存已加载模型,避免每次转写重复加载; is_updating_model作为互斥锁,_preload会while is_updating_model: await asyncio.sleep(0.1)自旋等待其他加载完成,防止并发加载同一模型;- 仅在"模型未加载或模型名变化"时才真正执行
whisper.load_model; - 模型下载根目录固定在
files.get_abs_path("/tmp/models/whisper"),这也解释了"模型 bootstrap 走 Docker/启动路径"的约定; - 加载过程中通过
NotificationManager向界面推送"Loading Whisper model...",加载完成后推送"Whisper model loaded."(2 秒展示),并同步PrintStyle控制台日志。
hooks.py的save_plugin_config中还有一个贴心细节:当用户把model_size从 A 改成 B 时,会立即DeferredTask().start_task(runtime.preload, next_model)异步预热新模型(见 hooks.py),切换模型无需等到首次点击麦克风。
转写调用链
runtime.transcribe()→_transcribe()的完整链路(runtime.py):
- 归一化配置(未传则读取已保存配置);
_resolve_language()解析语言:空字符串或auto→None(不传language参数,交给 Whisper 自动检测);否则原样小写传递;await _preload(model_name)确保模型就绪;base64.b64decode还原音频字节,写入tempfile.NamedTemporaryFile(suffix=".wav")临时文件;- 调用
_model.transcribe(temp_path, fp16=False, language=...)——fp16: False确保在无 CUDA 或需要高精度的环境中也能稳定转写; finally中删除临时文件,避免磁盘残留。
启用状态判定
is_globally_enabled()通过plugins.determined_toggle_from_paths(True, reversed(plugins.get_plugin_roots(PLUGIN_NAME)))判定全局开关(runtime.py),保证插件目录被多次叠加时,最终以最上层启停状态为准。
五、旧配置迁移:从核心设置到插件配置
该插件从核心代码库拆分出来后,需要把旧版核心配置(存放在usr/settings.json)平滑迁移到插件自己的配置文件,这部分由 plugins/_whisper_stt/helpers/migration.py 完成:
ensure_config_seeded():若插件配置尚不存在,则基于旧设置构建种子配置并落盘;build_seed_config()读取旧键:stt_model_size、stt_language、stt_silence_threshold、stt_silence_duration、stt_waiting_timeout,逐项映射到新键,缺失项使用默认值;_coerce_float/_coerce_int提供容错转换,旧值为非法类型时回退默认值。
这也与测试 tests/test_speech_plugin_split.py 中的断言相呼应:stt_model_size等旧键已从核心设置中移除,settings.get_default_settings()的输出中不再出现任何 legacy 语音键。
六、前端运行时:麦克风状态机与端到端流程
STT 提供者注册机制
Web 前端通过 webui/js/stt-service.js 暴露全局sttService(单例,继承EventTarget),以"提供者注册表"模式管理多个 STT 实现:插件启用时registerProvider("_whisper_stt", {...})注入handleMicrophoneClick、requestMicrophonePermission、updateMicrophoneButtonUI、stop、getStatus五个钩子;插件禁用或状态拉取失败时unregisterProvider清理并停止当前录音(见 whisper-stt-store.js)。麦克风按钮由此实现"谁启用谁接管"的解耦。
插件的前端状态仓库 webui/whisper-stt-store.js 定义了完整状态机:
| 状态 | 含义 | 按钮颜色(whisper-stt.css) |
|---|---|---|
inactive | 麦克风待机 | grey |
activating | 麦克风激活中 | silver(图标 0.8s 脉冲动画) |
listening | 侦听语音 | red |
recording | 正在录音 | green |
waiting | 等待最终静音 | teal |
processing | 转写中 | darkcyan(图标脉冲动画) |
按钮本体由 extensions/webui/chat-input-box-end/microphone-button.html 通过x-teleport注入到聊天输入区(#chat-buttons-wrapper),并随状态切换mic-*class 与aria-label(无障碍标签),同时以sttService.emitStatusChange(micStatus)广播状态。
静音检测算法(核心逻辑)
MicrophoneInput类(whisper-stt-store.js)使用 Web Audio API 的AnalyserNode做实时音量分析:
- 采样时域数据,计算RMS 幅度
sqrt(sum((sample-128)/128)² / N); - 门限并非直接用
silence_threshold,而是经过densify(value) = Math.exp(-5 * (1 - value))非线性映射后比较——这使得 0~1 的滑条值在实际声学感知上更均匀; - RMS 超过门限(且 TTS 未在播报)→ 从
listening进入recording,MediaRecorder.start(1000)按 1 秒分片采集; - 录音中 RMS 持续低于门限达到
silence_duration毫秒 → 进入waiting; waiting阶段再等waiting_timeout毫秒 → 进入processing,触发转写派发;- 若
silence_duration到达前声音恢复,silenceStartTime重置,继续录音。
整个状态推进由requestAnimationFrame驱动的analyzeFrame循环完成(whisper-stt-store.js),并且在dispose()时彻底清理 MediaRecorder、MediaStream、AudioContext,避免浏览器资源泄漏。
转写结果投递:send 与 draft 模式
process()把录音分片合成Blob(type: "audio/wav"),转 Base64 后 POST 到/plugins/_whisper_stt/transcribe。返回文本先经过filterResult()过滤——若文本整体被{}、()或[]包裹(疑似模型输出异常内容),直接丢弃并记日志,避免把非语音噪声当消息发送。
随后sendVoiceMessage(text)(whisper-stt-store.js)按message_mode分流:
send模式:updateChatInput(message)填充输入框后立即sendMessage()发送;draft模式:只updateChatInput(message)把文本留在编辑器中,由用户审阅后再手动发送,sendsImmediately为false时不触发发送。
此外,前端还实现了两个易用性细节:通过navigator.mediaDevices.enumerateDevices()枚举音频输入设备,选择结果存入localStorage(键whisperSttSelectedDevice),并监听devicechange事件在插拔设备时刷新列表;同时订阅ttsService的statechange,一旦 TTS 开始播报且麦克风处于活动状态,立即stop()停止录音,避免语音助手自说自话被误转写。
状态页与配置页
- webui/main.html 提供状态面板:Provider State(启用/模型就绪/已加载模型/Package 版本)、Resolved Config(六项归一化后的实际配置)、Microphone(当前状态、设备下拉选择),以及"Request Mic Permission / Open Settings / Refresh"三个操作按钮;
- extensions/webui/voice-settings-main/whisper-card.html 在设置页展示精简卡片(模型、语言、消息模式、麦克风),可直接跳转配置或状态面板。
七、依赖与验证:如何确认插件工作正常
依赖声明
仓库 requirements.txt 固定了openai-whisper==20250625,插件运行所需的 Whisper 依赖由此声明。如前所述,依赖安装与模型下载(/tmp/models/whisper)均发生在 Docker/启动路径,插件运行时只做加载与推理。
测试覆盖
tests/test_speech_plugin_split.py 是理解该插件行为契约的最佳测试入口,重点覆盖:
test_builtin_speech_plugins_are_discoverable_and_toggleable:_whisper_stt可被发现、可切换,always_enabled=False且挂载agent设置分区;test_plugin_owned_voice_files_exist:断言plugin.yaml、api/transcribe.py、三个 WebUI 扩展点文件与whisper-stt-store.js均存在;test_whisper_message_mode_defaults_to_send_and_supports_draft:验证message_mode默认send、draft大小写归一化(DRAFT→draft)、非法值回退send,并逐文件核对 UI 选项与 Store 行为;test_chat_bar_keeps_existing_send_and_mic_icon_contract:对七个mic-*状态类在 Store、CSS、按钮扩展点三处的同步存在性做了全量断言。
快速自检清单
按 plugins/_whisper_stt/AGENTS.md 的 Verification 指引,改动后应冒烟测试以下链路:
- 状态端点:
/api/plugins/_whisper_stt/status返回enabled、config与model信息正确; - 转写链路:录音→Base64→
/transcribe返回text与language; - 模型/语言设置:切换
model_size触发预热,language=auto时后端不传 language 参数; - send/draft 模式:
send立即发送、draft仅留在编辑器; - 静音处理:
silence_threshold/silence_duration/waiting_timeout三参数联动符合预期。
八、扩展与定制方向
基于上述架构,开发者可以低成本地扩展语音能力:
- 替换模型:在设置中把
model_size改为tiny(最快)到turbo(质量最高),模型变更后hooks.py会自动异步预加载; - 多语言支持:将
language设为具体语言代码(如zh、ja、de)提升识别准确率,或保持auto交给 Whisper 自动检测; - 消息审阅流程:将
message_mode设为draft,让用户在任何转写结果进入对话前拥有最终确认权; - 二次开发:若要接入其他 STT 引擎,可仿照
_whisper_stt实现一套api/+helpers/+ WebUI 扩展点,并复用sttService.registerProvider接口(webui/js/stt-service.js)完成提供者注册,前端无需改动即可无缝切换。
结语
_whisper_stt插件是 Agent Zero"核心瘦身、能力插件化"架构的典型样本:后端把 Whisper 的模型管理、配置归一化、迁移与推理封装在helpers/中,api/仅暴露status与transcribe两个薄端点,前端则通过sttService提供者模式与 Web UI 扩展点实现麦克风状态机与消息投递。理解这层分工,无论是调参、排障还是接入新语音引擎,你都能在 plugins/_whisper_stt、webui/js/stt-service.js 与 tests/test_speech_plugin_split.py 之间快速定位问题根源。
【免费下载链接】agent-zeroAgent Zero AI framework项目地址: https://gitcode.com/GitHub_Trending/ag/agent-zero
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考