Web 浏览器接入
对应示例:examples/web
完整示例可在 UI 中切换两条服务端通路:原生 Audio LLM,以及
ASR → LLM → TTS 级联。两者在浏览器里都实现为一个 audio-turn Provider,
因此共享同一套 Session、事件、播放和 barge-in 生命周期。只需要原生音频
输入/输出时,可从更小的 examples/web-audio-llm-only
开始;它不配置文本 LLM 和 TTS。
cd examples/webbun run start # http://localhost:5173线上 Demo 即本示例打包产物:ottervoice.vercel.app
示例当前通路
Section titled “示例当前通路”| UI 选择 | 服务端实现 | 输入字幕 | 联网搜索 |
|---|---|---|---|
| 原生 · GPT Audio Mini | OpenRouter openai/gpt-audio-mini |
并行 Qwen ASR | 不支持 |
| 原生 · Gemini Live | Google gemini-3.1-flash-live-preview |
并行 Qwen ASR | 可选 Google Search Grounding |
| 级联 | Qwen ASR → Gemini Flash Lite → MiniMax Speech | 同一复合响应 | 可选 OpenRouter 搜索路由 |
这些模型、prompt、voice、搜索工具和预算都只存在于服务端。UI 的“输入 / 输出 文本”开关只控制字幕是否可见,不会关闭服务器需要的转写步骤。
import { createOtterVoiceSession } from '@ottervoice/core';import { createOpenRouterGatewayVoiceTurn } from '@ottervoice/provider-openrouter';import { createWebRuntime, prepareBrowserAudio } from '@ottervoice/runtime-web';
const runtime = createWebRuntime({ mimeType: 'audio/webm;codecs=opus', timesliceMs: 100, volumePollMs: 50, bargeInPreRollMs: 500,});
// 浏览器只知道自有复合路由,不包含模型、prompt、voice 或 Provider Key。const voiceTurn = createOpenRouterGatewayVoiceTurn({ baseUrl: '/api/voice/asr-llm-tts', requireDoneSentinel: true, prepareAudio: (audio, format) => prepareBrowserAudio(audio, format, { sampleRate: 16_000, maxDurationMs: 60_000, }),});
const session = createOtterVoiceSession({ mode: 'full_duplex', // 这里描述客户端与 Provider 的契约;服务端仍可组合 ASR → LLM → TTS。 audioLlmStartTiming: 'after_audio', runtime, // voiceTurn.transcribesInput === true,因此不需要额外的 ASR 或文本 LLM。 providers: { audioLlm: voiceTurn }, turnDetection: { strategy: 'volume', minSpeechMs: 180, silenceTimeoutMs: 450, volumeThreshold: 0.025, }, policy: { autoStartListening: true, allowInterruption: true, },});
await session.start();服务端用 createOpenRouterGateway() 为复合 asr_llm_tts profile 的三段
policy 固定模型、系统提示词、声音、temperature、token 上限与 reasoning,并在
authorize 中校验用户和会话归属。原生 Audio LLM 路线则使用
createOpenRouterGatewayAudioLLM;若它不返回权威输入转写,再并行配置一个
createOpenRouterGatewayASR。不要在浏览器 Session 中设置
audioLlmSystemPrompt;该字段只适合完全运行在可信 Node/服务端的 Session。
客户端只有一种 audio-turn 契约,不代表网关后面只能有一个模型。
createOpenRouterGatewayVoiceTurn 把服务端复合实现包装成 AudioLLMProvider,并通过
transcribesInput: true 把同一响应中的输入转写交给 Core,避免再发一次 ASR 请求。
VAD 默认值与起步配置
Section titled “VAD 默认值与起步配置”Core 默认值是 hybrid、minSpeechMs: 500、silenceTimeoutMs: 1200、maxTurnMs: 120000、volumeThreshold: 0.02,属于偏保守的跨运行时配置。产品调优可从下表起步,并用真实设备和声学环境验证:
| 浏览器 | 策略 | minSpeechMs |
silenceTimeoutMs |
volumeThreshold |
采集建议 |
|---|---|---|---|---|---|
| 桌面 Chrome | hybrid |
180 | 650 | 0.02–0.03 | 100 ms timeslice;500 ms 插话 pre-roll |
| Android Chrome | hybrid |
220 | 850 | 0.025–0.04 | 100 ms timeslice;600 ms pre-roll;单轮限制 60–90 秒 |
| Safari(macOS/iOS) | hybrid |
250 | 900 | 0.025–0.04 | WebM 不可用时让 MediaRecorder 选择受支持 MIME |
环境噪声导致误触发时提高 volumeThreshold,轻声用户漏检时降低它。普通 VAD 与 interruptionDetection 要独立调参,插话检测应更严格。上线前必须覆盖内置麦克风、耳机、外放回声,以及权限/自动播放恢复场景。
浏览器注意点
Section titled “浏览器注意点”- 页面需 HTTPS 或
localhost - 在用户手势里请求麦克风,并尽量
audioOutput.unlock?.() - autoplay 解锁失败不应阻断 ASR
- 离开页面时
await session.dispose()
模型锁定、录音存储与删除、安全日志、重试策略见生产接入。
选项说明见 @ottervoice/runtime-web。