本文以 2026-10-18 工作区实现为基准。OpenRealtime 是可定制、可打断的实时语音 Agent 应用,数字人为可选输出方式。代码能力、部署配置和真实供应商的可用性分别验证;演示回声或静音适配器不能证明真实语音可用。
本文是当前实现的架构总入口。详细的服务与数据边界、消息处理与生命周期和架构阅读导航按职责展开;目标方案与外部产品调研单独维护,不作为默认实现。
面向产品使用者,先阅读产品功能架构图:按体验入口、传输接入、交互控制、三段式语音能力和外接能力分层,说明各模块的功能及可替换边界。
功能地图
| 功能 | 当前职责与实现边界 |
|---|---|
| 实时语音与文字 | 浏览器采集音频,流式 ASR、话轮判停、回答、TTS 与播放;支持文字入口 |
| 追问、插话与改口 | 协调输入归属、打断与附和;按输入版本、回复版本及播放许可隔离旧输出 |
| 定制助手 | 角色、人设、语言、声音及 ASR / LLM / TTS 实例选择;平台与个人凭据分别授权 |
| 工具与 Agent | 模型工具规划、权限检查、有界执行、结果回填;兼容外部 Chat Agent,不能据此认定任意办事 Agent 已完成验收 |
| 知识资料 | 导入、绑定、检索与资料问答;默认关键词检索,其他检索策略按配置选择 |
| 记忆与历史 | 会话上下文、长期记忆、历史续聊、删除与恢复;三者有不同的数据生命周期 |
| 数字人 | 形象、素材、动作、离线视频或实时服务;不是普通语音通话的强制依赖 |
| 管理与诊断 | 登录、供应商配置、额度准入、会话时间线、延迟、播放回执、录音与保留策略;录音依赖存储配置 |
| 插件扩展 | VAD、ASR、话轮管理、Brain、语音渲染、TTS 与工具能力的协议和适配器 |
服务与数据架构图
flowchart TB
U[用户] --> W[Web 前端 Next.js / 浏览器]
W --> UI[角色 / 知识 / 历史 / 设置 / 诊断页面]
W --> A[AudioWorklet 采集 / WebAudio 播放]
W -->|HTTPS /api| API[会话管理服务 Node.js apps/backend]
A <-->|WebSocket /voice| V[语音交互服务 Python]
API --> AUTH[登录 / 授权 / 额度 / 准入票据]
API --> CONFIG[角色 / 供应商实例 / 知识 / 记忆 / 历史]
API <-->|受限内部控制连接| V
API --> DB[(PostgreSQL)]
API --> FS[(共享目录 / 资源与资料)]
V --> DB
V --> FS
V --> R[VoiceSessionRuntime / EndpointBridge]
R --> E[CascadedRealtimeEngine]
E --> IN[VAD + ASR]
IN --> TM[CoordinatedTurnManager 会话 actor]
TM --> B[Brain / Agent / 工具规划]
B --> G[权限与预算 / 工具执行 / 结果回填]
G --> B
B --> OUT[语音渲染 / 分句 / TTS]
IN <-->|识别| P[外部供应商 / 自托管服务]
B <-->|模型| P
OUT <-->|合成| P
OUT --> A
A -.->|播放回执| R
E -.-> OBS[Journal / Telemetry / 诊断 / 可选录音]
OBS --> DB
OBS --> OBJ[(可选对象存储)]
A --> AV[可选数字人播放 / 实时服务]
前端可通过 OpenNext 部署至 Cloudflare;同源入口将 /api/* 路由到会话管理服务,将 /voice/* 路由到实时语音服务。Next.js 是框架,不代表所有 Next.js 代码都属于前端:apps/backend 运行服务端业务接口。
会话管理服务发放授权配置与短期准入票据;浏览器不取得数据库访问或长期供应商密钥。语音服务核验准入并绑定供应商实例版本。两项后端目前仍依赖共享存储和内部控制连接,跨机器部署需要处理这些依赖。端口、认证、媒体挂载与构建方式见部署说明。
当前运行时与代码入口
当前启动入口 demos/voice_web_server.py 的 _build_realtime_session() 默认选择 cascaded,通过 CascadedPipeline.assemble()、RealtimeRuntimeFactory 和 EndpointBridge 运行。native 分支明确拒绝作为当前生产引擎。旧 Kernel / Pipeline 及参考研究实现不能作为默认运行链路来描述。
| 层 | 主要位置 | 职责 |
|---|---|---|
| 页面与编排 | apps/web/hooks/use-voice-session.ts、apps/web/voice/session/ | 准入、连接状态、会话事件与重连 |
| 浏览器音频 | apps/web/voice/audio/input-runtime.ts、output-runtime.ts | 输入采集、缓冲、播放、停播与回执 |
| 管理接口 | apps/backend/src/app/api/、apps/web/lib/voice-admission.ts | 身份、配置与数据、准入票据 |
| 端点生命周期 | server/voice_session/endpoint_bridge.py、runtime.py | 启动就绪、失败、恢复与帧桥接 |
| 实时引擎 | server/realtime/cascaded/engine.py、pipeline.py | 输入、生成、语音输出和能力生命周期 |
| 话轮权威 | server/realtime/capabilities/coordinated_turn_manager.py | actor 串行修改生命周期;模型与计时器返回带版本消息 |
| 模型与工具 | server/brain/、server/tools/ | 上下文、工具规划、权限、执行与续答 |
| 数据与诊断 | server/conversation_journal/、server/persistence/、server/observability/ | 持久化、追踪、播放事实与诊断 |
正常消息处理流程图
sequenceDiagram
actor U as 用户
participant W as 浏览器
participant A as 会话管理 API
participant V as VoiceSessionRuntime
participant I as VAD / ASR
participant T as 话轮协调 actor
participant B as Brain / LLM
participant S as TTS
participant J as Journal / Telemetry
W->>A: 登录态、角色与供应商选择,申请准入
A-->>W: 短期 ticket 与能力选择
W->>V: 建立 WebSocket,hello / 输入能力 / 恢复信息
V->>V: 核验绑定并打开实时能力
alt 初始化成功
V-->>W: session_ready / 能力与会话状态
else ASR 等必需能力不可用
V-->>W: 明确失败事件与恢复状态
end
U->>W: 讲话
W->>V: input_epoch_started / 输入音频帧
V->>I: 持续音频输入
I-->>W: 经运行时返回识别增量与修订
I->>T: 讲话、停顿、候选边界与文本版本
T->>T: 输入归属、语义完整性、话权与候选有效性
Note over T,B: 可预取候选响应;新证据到达必须撤销失效候选
T->>J: 已接受的输入证据与话轮
T->>B: 提交有效话轮与上下文
B-->>V: 正文输出(工具分支见下节)
V->>S: 可播文本 / 分句
S-->>W: 经运行时传送文本与音频增量
W->>U: 排队播放
W-->>V: playback_progress_v2 / 停播与质量回执
V->>J: 回复完成、已播放事实与诊断
WebSocket 打开、session_ready、模型生成完成、音频发送完成、浏览器实际播放完成是不同状态。展示和诊断必须分别记录。输入音频持续读取,不能等待模型或工具执行结束后才继续收音。
工具消息流程
flowchart LR
Q[已提交用户话轮] --> M[LLM 生成工具调用]
M --> C[完整参数 / 调用 ID / 权限 / 预算校验]
C -->|允许| X[执行工具并记录真实结果]
C -->|拒绝 / 待确认 / 失败| F[结构化反馈]
X --> F
F --> R[关联原调用回填模型]
R --> A[生成正式回答]
A --> S[可播文本 → TTS → 播放]
X -.-> P[可选确认语 / 真实进度]
P -.-> S
普通聊天支持文本增量进入 TTS。工具规划与执行仍需等待完整、有效的参数;工具结果续答已支持 StreamingToolCallingLlmClient,由 ToolCallingBrain.continue_turn_stream() 向运行时交付可播片段,不必等待整个上游 SSE 结束。兼容客户端使用有界队列,普通文本按可播语句输出,结构化答案按完整语音记录校验后输出;参数碎片和 reasoning 不进入 TTS。未实现流式契约的客户端仍返回完整结果。
流式校验以已交付记录为边界:后续非法记录、缺失完成标记或清理错误会传播为失败,但已播放的合法片段不能撤回。工具等待确认语、正式答案、生成完成和实际播放完成各自记录,不能把确认语首声当作正式答案首声。工具决策能力的 shadow 模式只用于观察,不能据此宣称已替代正式工具规划。
打断、改口与恢复
sequenceDiagram
participant W as 浏览器
participant T as 话轮协调 actor
participant E as 实时引擎
participant B as Brain / TTS
participant J as Journal / 播放记录
W->>T: 播放期间的新语音和输入版本
T->>T: 区分附和、续说、纠正与有效打断
alt 有效打断或回复失效
T->>E: 失效旧回复 / 取消旧输出许可
E->>B: 传播取消并有界清理
E-->>W: 停播旧输出
W-->>J: 实际播放位置与停止事实
T->>J: 按已听内容和输入修订建立上下文
T->>E: 按新输入生成或修复回复
else 附和或证据不足
T->>T: 保持输出或等待更多有效证据
end
opt 网络断开
W->>E: 重连 / resume 信息
E->>J: 校验恢复身份与已确认位置
E-->>W: 恢复快照 / 事件 / 能力状态或明确失败
end
隔离依赖 input_epoch、ASR 修订、generation、回复版本和句子播放许可的配合;客户端与服务端都要拒绝旧数据。上下文不能把“生成过但没有播放”的内容当作用户已听见。停止播报也不等于撤销已经发生的外部工具写入。
验证与维护
HTTP 400 通常需要检查参数与协议,401/403 检查身份和权限,429 检查容量和配额,500 检查未处理异常,502/503/504 检查入口、应用与供应商依赖。一次错误需要结合 route、错误 code、session、generation、调用 ID、耗时和对应依赖判断;不能只按 HTTP 状态归因。
/api/health 返回 200 不证明一次通话能启动;WebSocket 内的 ASR、TTS、判停和播放失败也可能完全没有 HTTP 5xx。验证应覆盖实际输入、识别、判停、回答、可听音频、播放回执、打断和恢复。可靠性与运维、话轮协调、语音插件提供进一步说明。