【免费下载链接】jarvis
J.A.R.V.I.S for automating your daily tasks using Claude Code
J.A.R.V.I.S.(jarvis)是一个跑在浏览器里的钢铁侠风格语音助手:说一声 "Hey Jarvis",它就会醒来、听懂、开口回答,还能调用你本地工具帮你查资料、生成图片。它的语音管线采用双引擎设计——默认的浏览器原生语音,和可选的 ElevenLabs 云端语音,启动时自动探测、全程自动降级,不配任何 API key 也能正常说话和听懂。这篇文章带你搞懂这套设计是怎么工作的。
双引擎架构:一张图看懂
整个系统由两部分组成,通过 WebSocket(ws://localhost:8787)对话:
- 浏览器(脸和嘴):负责唤醒词检测、语音转文字、播放回复声音、渲染全息 UI
- bridge(大脑和手):一个 Node 进程 bridge/server.mjs,以 headless 方式运行 Claude Code,并代理 ElevenLabs 的语音请求
语音相关的两条"引擎轨道":
| 环节 | 默认轨道(零配置) | 增强轨道(有 ElevenLabs key) |
|---|---|---|
| 听(STT) | 浏览器 SpeechRecognition | 本地 VAD + ElevenLabs Scribe |
| 说(TTS) | 浏览器 speechSynthesis | ElevenLabs 云端语音 |
关键点:两条轨道由同一个小探测决定,全程无开关、无手动选择。
启动时的能力探测:一次 /health 请求定乾坤
浏览器在开机序列里会向 bridge 发一次 GET /health 请求,bridge 检查本机有没有 ElevenLabs 的 key,然后返回一个极简的能力报告:
{ "ok": true, "tts": true, "stt": true }
- 有 key →
tts和stt都为true,语音和转写双双升级到云端引擎 - 没有 key(或 bridge 没启动)→ 两个都是
false,安心用浏览器自带语音,功能完整可用
这个探测逻辑集中在 src/lib/capabilities.ts 的 probeCapabilities() 里,它的注释写得很直白:"探测失败就留在浏览器回退方案上——bridge 不可达时这才是正确行为"。探测在开机序列中、第一句 "Hey Jarvis" 之前就完成(见 src/App.tsx),所以你第一次开口时用的就是正确的引擎。
💡 这个设计的哲学:默认值必须"安全可用"。学生党只装了 Claude Code 也能玩;有 key 的人则自动升级,不需要改任何配置项。
听:STT 的两条路径
增强路径:本地 VAD 秒级响应 + 云端转写
有 ElevenLabs key 时,"听"被拆成两步:
- 检测(本地、即时):src/lib/vad.ts 实现了一个基于能量的语音活动检测器——滑动噪声底 + 阈值 + 滞回门。你张嘴的瞬间它就知道,这就是"插话(barge-in)"能这么自然的原因:JARVIS 正在说话时你一开口,它立刻停嘴。
- 转写(云端、可靠):VAD 把一段完整的音频切片打包,POST 到 bridge 的
/stt端点(bridge/server.mjs),由 ElevenLabs Scribe 转成文字再回来。
为什么要这么拆?因为浏览器原生的 SpeechRecognition 在 7×24 常开场景下会无声地死掉——Chrome 会节流它、停止触发事件、且不抛任何可捕获的错误。而"有人在说话"本质是个能量问题,自己每帧都能量一次,不存在黑盒静默失效。
默认路径:SpeechRecognition + 心跳保活
没有 key 时,src/lib/voice.ts 走浏览器自带的 SpeechRecognition,它更弱一些(Chrome 会节流),所以加了一个心跳定时器:每 5 秒检查引擎是否还有"生命迹象",超过 15 秒没任何动静就判定它哑了,强制拆掉重建一个新的识别器。注释里说得很实在:这一个守卫,就是"唤醒词上节课还好好用、这节课突然失灵"和"助手一直在线"的区别。
两条路径共用同一个"语句组装器":你在句子中间停顿想词,它不会把半句话当成完整问题发出去;你说得完整,它就零额外延迟立刻发送。
说:TTS 的两条路径与降级链
云端语音:一句一请求,失败即降级
选择引擎的核心在 src/lib/tts.ts 的 synthesise() 里,优先级是:
- 探测到 ElevenLabs 可用(或你强制开启)→ 每句话 POST 到 bridge 的
/tts(bridge/server.mjs),bridge 持有 key 调 ElevenLabs,返回 22kHz 单声道 mp3 音频 - 配置了
VITE_TTS_ENGINE=kokoro→ 用 src/lib/kokoro.ts 跑一个 82M 参数的本地神经网络 TTS(WebGPU/ONNX,零云端零 key,代价是首次下载约 86MB 模型) - 否则 → 浏览器原生
speechSynthesis
任何一步失败都不会让这句话哑掉:云端请求失败就丢弃回系统声音;音频播不出来就走下一层。
系统语音:细节里全是坑位
原生路径看着简单,src/lib/tts.ts 里其实处理了大量真实世界的坑:
- Chrome 卡死修复:
speechSynthesis.cancel()之后引擎会进入"接受一切 speak 但什么都不说"的僵尸态——这是整个会话突然全程静音的头号元凶。代码在每次发声前先resume()松绑,并配了 700ms 看门狗:start事件迟迟不来就先松绑重试一次,再不行才判定失效 - 长句保活:Chrome 大约 15 秒不说话就掐断,代码用不可闻的 pause/resume 对每 5 秒"挠一下"引擎
- 音色选择:给系统音色打分排序,偏好英式男声(macOS 上 Enhanced 版 Daniel 是免费的最优解),排除各种搞笑音色
- 流式断句:回复按句子边界切分、边写边说,Claude 还在生成 JARVIS 已经在开口;"Mr."、"e.g." 这类缩写不会误切,"sir" 前自动补逗号留出那口标志性的停顿
自动降级:三条兜底路径
这是整条 TTS 链最漂亮的部分:
| 故障场景 | 自动行为 |
|---|---|
| 云端请求失败(网络抖动等) | 本句降级为浏览器系统声音 |
系统声音被证实坏了(onstart 从未触发) | 闩锁:不再反复重试死路,本句由 bridge 的 ElevenLabs 代理"救场"(rescued 计数 +1),后续全部走云端 |
| 云端也没有 | 保持沉默但不卡死队列,诊断面板记录原因 |
注意"闩锁"这个词:系统语音坏了不是瞬态故障,每句都重试只会让回答在一条死路上磕磕绊绊。所以第一次真失败后就永久切换,诊断对象 src/lib/tts.ts 会如实记录 engine、nativeBroken、rescued 等字段。
如何启用 ElevenLabs 增强引擎
不用碰任何 flag,二选一:
- 启动 bridge 前设置环境变量
ELEVENLABS_API_KEY - 或把 key 写进
~/.claude.json里elevenlabsMCP server 的 env——bridge 会自己去那里读(bridge/server.mjs)
key 就位后,/health 开始报告能力,浏览器下一次开机自动拾取,语音和转写同时升级。免费额度做 demo 完全够用。
🔒 安全细节:key 只留在 bridge 进程里,浏览器永远看不到秘密——它只是把文字 POST 给
/tts、拿回音频。
怎么验证当前跑的是哪套引擎
项目内置了诊断能力,不用翻控制台:
- 按 D 打开实时诊断面板:直说它在没在听你、在不在出声,
window.__tts/window.__voice里的计数一清二楚 - 按 T 做单行音频自检
- HUD 上会显示当前引擎标签:
ElevenLabs、ElevenLabs voice或browser speech(由 src/lib/capabilities.ts 的engineLabel()给出)
常见问题的快速排查:完全没声音 → 确认是真实 Chrome/Edge 窗口(内嵌预览窗格会屏蔽麦克风)且已授权麦克风;bridge 连不上 → 检查 npm run bridge 是否还在跑、端口 8787 有没有被占用。
小结
jarvis 的语音管线设计可以浓缩成三句话:
- 默认零门槛:只装 Claude Code + Node.js + Chrome 就能说能听,不花一分钱
- 探测即选择:开机一次
/health探测,自动决定 STT/TTS 各走哪条轨道,无需任何开关 - 故障不静默:云端失败降系统声音、系统声音坏了闩锁到云端救援、识别器心跳超时自动重建——每一句都不允许无声地死掉
对想自己做语音助手的人来说,这套"本地能量检测 + 云端转写 + 多级降级"的分层思路非常值得借鉴:把不可靠的黑盒和可靠的物理量分开处理,再给每一层配好兜底,鲁棒性自然就出来了。更多配置项(如 JARVIS_VOICE_ID、VITE_TTS_ENGINE)见 README.md。
【免费下载链接】jarvis
J.A.R.V.I.S for automating your daily tasks using Claude Code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



