跳转到内容

生产接入

生产客户端只应知道业务会话、自有网关和获准的业务 profile。供应商密钥、system/developer prompt、模型、voice、temperature、token 上限、reasoning、工具权限、对象存储 Key 和留存策略都必须由服务端控制。

边界 必须落实的控制
浏览器 / App → 网关 鉴权;每次请求都校验会话归属
上游请求 不透传客户端 JSON;只提取用户内容,再由服务端重建 Body
模型与提示词 固定 model 和 system/developer prompt;拒绝客户端特权角色
生成与声音 固定/白名单 voice、speed、temperature、max tokens、reasoning、tools
跨站请求 精确校验 Origin;Cookie 鉴权时配合 SameSite 与 CSRF Token
滥用控制 按用户、会话、profile、IP、并发和供应商成本预算限流
输入 限制请求体和 SDK maxTurnMs;拒绝不支持的 Content-Type
输出 设置上游与总请求超时;客户端断开时取消上游任务

可运行的 Audio LLM-only 示例 的客户端不包含模型、prompt、voice 或生成参数;服务端通过 createOpenRouterGateway() 重建请求、校验 Origin 并限制请求体/历史/文本。请把强制 authorize 钩子接入自己的用户与会话鉴权;示例不是完整账号服务。

标准客户端工厂 Base URL 服务端锁定
createOpenRouterGatewayASR /api/voice/asr ASR model、language 策略
createOpenRouterGatewayAudioLLM /api/voice/audio-llm model、system prompt、voice、temperature、tokens
createOpenRouterGatewayVoiceTurn /api/voice/asr-llm-tts ASR、LLM、TTS 三段 policy;同一 SSE 响应返回输入转写、文本与 MP3 分片

复合语音工厂实现 AudioLLMProvider 并声明 transcribesInput: true。配合 createOtterVoiceSession 时可以省略客户端字幕 asr;但网关仍必须对 asr_llm_tts profile 单独鉴权、限流和记账。

  • 长期 Key 不能进入浏览器代码、App 包、EXPO_PUBLIC_* 或任何 API 响应。
  • 不要把共享“网关密码”放进前端环境变量;客户端应携带当前用户的短期应用登录态,服务端再校验会话归属。
  • 请求/响应类 API 走同源策略网关;客户端必须直连流式供应商时,只发短期且限 route/model/budget 的 Token。
  • 自定义请求头必须先校验为浏览器兼容字符;任意业务元数据放 JSON Body,不要硬塞 Header。
  • Trace 中必须脱敏 authorization、Cookie、签名 URL、供应商原始响应和 Base64 音频。

直接订阅最终音频事件,不再包装 Provider 或用临时变量关联时序:

session.on('user_audio_final', async ({ turnId, audio, format }) => {
await uploadPrivateTurn({ turnId, role: 'user', bytes: audio, format });
});
session.on('assistant_audio', async ({ turnId, audio, mimeType }) => {
if (audio) await uploadPrivateTurn({ turnId, role: 'assistant', bytes: audio, format: mimeType });
});
  • 录音必须进入私有 Bucket;对象 Key 不可猜测,并绑定租户/会话记录。
  • 回放走用户鉴权代理或短时签名 URL,不能把 Bucket 改成公开。
  • 对象 Key、校验和、留存截止时间、turnId 与数据库 Turn 放进同一套可审计流程。
  • 删除会话/账号时,幂等地排队删除对象并记录结果;数据库级联不会自动删除对象存储。
  • 开启录音前取得合规同意并定义留存周期;转写和声音都按敏感个人数据处理。

错误中的 rawcause 可能含上游响应或用户内容,只用于开发诊断。生产日志只记录 codestageproviderhttpStatusretryablefatalsafeMessage 和自有 request id。

只重试 retryable 错误,在网关使用带随机抖动的指数退避并遵守供应商 Retry Header。鉴权、额度、音频解码、参数校验失败不能在请求不变时盲目重试。客户端 audioLlmRetry、滚动 ASR、after_audio 和后端选择都可能增加请求/成本;服务端预算与幂等控制不能依赖这些客户端值。

const session = createOtterVoiceSession({
// 标准客户端默认单次请求;确需重试时仍由网关独立限制预算。
audioLlmRetry: { maxAttempts: 1 },
// ...
});

Core 只会在还没有输出任何文本/音频流时重试当前轮。一旦开始输出,自动重试可能导致重复播报,因此会直接报告失败。启用 continueSessionOnFailure 后,error.fatalfalse,会话恢复监听;否则进入终止性的 error 状态。

  • 分别测试 401/403、402/额度、429、5xx、超时、客户端取消、SSE 中断、解码失败和播放失败。
  • 上报 user_audio_endassistant_audio_start 延迟,以及错误 stage / HTTP 状态。
  • CI 执行 bun run test:smoke:audio-llm;它不使用麦克风,以固定 WebM 覆盖转换、网关、SSE 和最终音频。
  • 告警样本必须隐私安全,不能自动附带完整请求/响应 Body。
  • 上线前明确删除流程、留存周期、供应商区域和事故响应责任人。