Files
AI-VideoAssistant/engine/docs/ws_v1_schema_zh.md

15 KiB
Raw Blame History

WS v1 协议完整说明(中文)

本文档描述 /ws 端点的 WebSocket v1 协议,覆盖:

  • 客户端输入JSON 文本消息 + 二进制音频);
  • 服务端输出JSON 事件 + 二进制音频);
  • 每个参数的类型、约束、含义与使用方式;
  • 握手顺序、状态机、错误语义与实现细节。

实现对照来源:

  • models/ws_v1.py
  • core/session.py
  • core/duplex_pipeline.py
  • app/main.py

1. 传输与基础规则

  • 连接地址:ws://<host>/ws
  • 单连接双通道承载:
    • 文本帧JSON 控制消息(严格校验 schema
    • 二进制帧:原始 PCM 音频
  • JSON 校验策略:
    • 所有已定义客户端消息都 extra="forbid",即不允许未声明字段;
    • hello.version 固定必须是 "v1"
    • 缺失 type 或未知 type 会返回协议错误。

2. 状态机与消息顺序

2.1 服务端状态

  • WAIT_HELLO:等待 hello
  • WAIT_START:已通过握手,等待 session.start
  • ACTIVE:会话运行中,可收发文本/音频
  • STOPPED:会话结束

2.2 正确顺序

  1. 客户端发送 hello
  2. 服务端返回 hello.ack
  3. 客户端发送 session.start
  4. 服务端返回 session.started
  5. 客户端可持续发送:
  • 二进制音频
  • input.text(可选)
  • response.cancel(可选)
  • tool_call.results(可选)
  1. 客户端发送 session.stop 或直接断开连接

顺序错误会返回 errorcode = "protocol.order"


3. 客户端 -> 服务端消息(输入)

3.1 hello

示例:

{
  "type": "hello",
  "version": "v1"
}

字段说明:

字段 类型 必填 约束 含义 使用说明
type string 固定 "hello" 消息类型 握手第一条消息
version string 固定 "v1" 协议版本 版本不匹配会 protocol.version_unsupported 并断开

3.2 session.start

示例:

{
  "type": "session.start",
  "audio": {
    "encoding": "pcm_s16le",
    "sample_rate_hz": 16000,
    "channels": 1
  },
  "metadata": {
    "appId": "assistant_123",
    "channel": "web",
    "configVersionId": "cfg_20260217_01",
    "client": "web-debug",
    "output": {
      "mode": "audio"
    },
    "systemPrompt": "你是简洁助手",
    "greeting": "你好,我能帮你什么?",
    "dynamicVariables": {
      "customer_name": "Alice",
      "plan_tier": "Pro"
    }
  }
}

字段说明:

字段 类型 必填 约束 含义 使用说明
type string 固定 "session.start" 启动会话 握手后第二阶段消息
audio object | null 仅支持固定值 音频格式描述 仅用于声明MVP 实际只接受固定 PCM
audio.encoding string 固定 "pcm_s16le" 编码格式 非该值会在模型校验层报错
audio.sample_rate_hz number 固定 16000 采样率 16kHz
audio.channels number 固定 1 声道数 单声道
metadata object | null 任意对象(会被白名单过滤) 运行时配置 用于 app/channel/提示词/输出模式等覆盖

metadata 白名单策略(关键):

  • 允许透传的标识字段ID 类):
    • appId / app_id
    • channel
    • configVersionId / config_version_id
  • 允许透传的覆盖字段:
    • firstTurnMode
    • greeting
    • generatedOpenerEnabled
    • systemPrompt
    • output
    • bargeIn
    • knowledge
    • knowledgeBaseId
    • openerAudio
    • history
    • userId
    • assistantId
    • source
    • dynamicVariables
  • 客户端传入 metadata.services 会被忽略(服务端会记录 warning服务配置由后端/环境变量决定。

metadata.dynamicVariables 规则:

  • 可选;必须是 object<string,string>
  • key 正则:^[a-zA-Z_][a-zA-Z0-9_]{0,63}$
  • 最多 30 个变量,单个 value 最长 1000 字符。
  • systemPrompt / greeting 中支持占位符语法:{{variable_name}}
  • 内置系统变量(始终可用):{{system__time}}{{system_utc}}{{system_timezone}}
    • system__time:会话开始时的本地时间(YYYY-MM-DD HH:mm:ss
    • system_utc:会话开始时的 UTC 时间(YYYY-MM-DD HH:mm:ss
    • system_timezone:会话开始时的本地时区
  • 若模板引用了缺失变量,session.start 会被拒绝,错误码 protocol.dynamic_variables_missing
  • dynamicVariables 结构/内容非法,session.start 会被拒绝,错误码 protocol.dynamic_variables_invalid

output.mode 用法:

  • "audio"(默认语音输出)
  • "text"(纯文本输出)
    • 纯文本模式下仍会收到 assistant.response.delta/final
    • 不会收到 TTS 音频帧与 output.audio.start/end

3.3 input.text

示例:

{
  "type": "input.text",
  "text": "你能做什么?"
}

字段说明:

字段 类型 必填 约束 含义 使用说明
type string 固定 "input.text" 文本输入 跳过 ASR直接触发 LLM 回答
text string 非空字符串为佳 用户文本 用于文本聊天或调试

3.4 response.cancel

示例:

{
  "type": "response.cancel",
  "graceful": false
}

字段说明:

字段 类型 必填 默认值 含义 使用说明
type string - 固定 "response.cancel" 请求中断当前回答
graceful boolean false 取消方式 false 立即打断;true 当前实现主要用于记录日志,不强制中断

3.5 tool_call.results

仅在工具执行端为客户端时使用(assistant.tool_call.executor == "client")。

示例:

{
  "type": "tool_call.results",
  "results": [
    {
      "tool_call_id": "call_abc123",
      "name": "weather",
      "output": { "temp_c": 21, "condition": "sunny" },
      "status": { "code": 200, "message": "ok" }
    }
  ]
}

字段说明:

字段 类型 必填 约束 含义 使用说明
type string 固定 "tool_call.results" 工具执行回传 客户端工具结果上送
results array 默认为空数组 多个工具结果 可批量回传
results[].tool_call_id string 任意字符串 工具调用ID 必须与 assistant.tool_call.tool_call_id 对应
results[].name string 任意字符串 工具名 建议与请求一致
results[].output any 任意 JSON 工具输出 供模型后续组织回答
results[].status object 包含 codemessage 执行状态 用于判定成功/失败
results[].status.code number HTTP 风格状态码 状态码 200-299 判定成功
results[].status.message string 任意字符串 状态描述 例如 "ok" / "timeout"

处理规则:

  • 未请求过的 tool_call_id 会被忽略(防止伪造/串话);
  • 重复回传会被忽略;
  • 超时未回传会由服务端合成超时结果(504)。

3.6 session.stop

示例:

{
  "type": "session.stop",
  "reason": "client_disconnect"
}

字段说明:

字段 类型 必填 约束 含义 使用说明
type string 固定 "session.stop" 结束会话 正常结束推荐发送
reason string | null 任意字符串 结束原因 服务端会回传到 session.stopped.reason

4. 二进制音频输入(客户端 -> 服务端)

session.started 之后可持续发送二进制音频。

固定格式MVP

  • 编码:pcm_s16le
  • 采样率:16000
  • 声道:1
  • 帧长20ms = 640 bytes

分包规则:

  • 单个 WebSocket 二进制消息可包含 1 帧或多帧;
  • 长度必须是 640 的整数倍;
  • 不是 640 倍数会触发 audio.frame_size_mismatch,该消息整包丢弃;
  • 奇数字节长度会触发 audio.invalid_pcm

5. 服务端 -> 客户端事件(输出)

所有 JSON 事件都包含统一包络字段。

5.1 统一包络Envelope

{
  "type": "event.name",
  "timestamp": 1730000000000,
  "sessionId": "sess_xxx",
  "seq": 42,
  "source": "asr",
  "trackId": "audio_in",
  "data": {}
}

字段说明:

字段 类型 含义 使用说明
type string 事件类型 见下方事件清单
timestamp number 事件时间戳(毫秒) ev() 生成
sessionId string 会话ID 同一连接固定
seq number 单会话递增序号 可用于重放、去重、排序
source string 事件来源 常见:asr/llm/tts/tool/system/client/server
trackId string 事件轨道 常用:audio_in/audio_out/control
data object 结构化数据 顶层业务字段会镜像进 data 以兼容旧客户端

关联IDdata 内自动注入,存在时):

  • turn_id:一次用户-助手对话轮次
  • utterance_id:一次用户语音话语
  • response_id:一次助手生成响应
  • tool_call_id:一次工具调用
  • tts_id:一次 TTS 播放段

5.2 事件类型与参数

5.2.1 会话与控制类

  1. hello.ack
  • 关键字段:version
  • 含义:握手成功,应紧接着发送 session.start
  1. session.started
  • 关键字段:
    • trackId
    • tracks.audio_in
    • tracks.audio_out
    • tracks.control
    • audio(回显客户端声明的音频元信息)
  • 含义:会话进入 ACTIVE可发音频/文本
  1. config.resolved
  • 关键字段:
    • config.channel
    • config.output.mode
    • config.tools.enabled
    • config.tools.count
    • config.tracks
  • 含义服务端公开配置快照SaaS 安全),便于前端展示与排错
  • 发送策略:可选调试事件,默认关闭(ws_emit_config_resolved=false
  • 不应返回:
    • assistantId / appId / configVersionId
    • servicesprovider/model/baseUrl 等内部运行细节)
    • 系统提示词原文及其它内部编排细节
  1. heartbeat
  • 关键字段:无业务字段(仅 envelope
  • 含义:保活心跳
  • 默认间隔:heartbeat_interval_sec(默认 50s
  1. session.stopped
  • 关键字段:reason
  • 含义:会话结束确认
  1. error
  • 关键字段:
    • sender
    • code
    • message
    • stage
    • retryable
    • trackId
    • data.error(结构化错误镜像)
  • 含义:统一错误事件

5.2.2 识别与输入侧ASR/VAD

  1. input.speech_started
  • 字段:probability
  • 含义:检测到语音开始
  1. input.speech_stopped
  • 字段:probability
  • 含义:检测到语音结束
  1. transcript.delta
  • 字段:text
  • 含义ASR 增量识别文本(节流发送)
  1. transcript.final
  • 字段:text
  • 含义ASR 最终识别文本

5.2.3 输出侧LLM/TTS/Tool

  1. assistant.response.delta
  • 字段:text
  • 含义:助手增量文本输出(节流发送)
  1. assistant.response.final
  • 字段:text
  • 含义:助手完整文本输出
  1. assistant.tool_call
  • 字段:
    • tool_call_id
    • tool_name
    • arguments(对象)
    • executorclientserver
    • timeout_ms
    • tool_call(完整工具调用对象)
  • 含义:通知客户端发生工具调用(用于可视化或客户端执行)
  1. assistant.tool_result
  • 字段:
    • sourceclientserver
    • tool_call_id
    • tool_name
    • okboolean
    • error(失败时 {code,message,retryable}
    • result(原始结果对象)
  • 含义:工具调用结果回执
  1. output.audio.start
  • 含义TTS 音频输出开始边界
  1. output.audio.end
  • 含义TTS 音频输出结束边界
  1. response.interrupted
  • 含义当前回答被打断barge-in 或 cancel
  1. metrics.ttfb
  • 字段:latencyMs
  • 含义首包音频时延TTFB

5.2.4 工作流扩展事件(可选)

metadata.workflow 生效,会额外出现:

  • workflow.started
  • workflow.node.entered
  • workflow.edge.taken
  • workflow.tool.requested
  • workflow.human_transfer
  • workflow.ended

这些事件用于外部可视化工作流状态,不影响基础语音会话协议。


6. 服务端二进制音频输出(服务端 -> 客户端)

  • 音频为 PCM 二进制帧;
  • 发送单位对齐到 640 bytes(不足会补零后发送);
  • 前端通常结合 output.audio.start/end 做播放边界控制;
  • 收到 response.interrupted 后应丢弃队列中未播放完的旧音频。

7. 错误模型与常见错误码

统一结构(error 事件):

{
  "type": "error",
  "sender": "client",
  "code": "protocol.invalid_message",
  "message": "Invalid message: ...",
  "stage": "protocol",
  "retryable": false,
  "trackId": "control",
  "data": {
    "error": {
      "stage": "protocol",
      "code": "protocol.invalid_message",
      "message": "Invalid message: ...",
      "retryable": false
    }
  }
}

字段语义:

  • sender:错误来源角色(如 client / server
  • code:机器可读错误码
  • message:人类可读描述
  • stage:阶段(protocol|audio|asr|llm|tts|tool
  • retryable:是否建议重试
  • trackId:错误归属轨道

常见错误码:

  • protocol.invalid_json
  • protocol.invalid_message
  • protocol.order
  • protocol.version_unsupported
  • protocol.unsupported
  • audio.invalid_pcm
  • audio.frame_size_mismatch
  • audio.processing_failed
  • server.internal

8. 心跳与超时

服务端后台任务逻辑:

  • 每隔约 5 秒检查一次连接;
  • 超过 inactivity_timeout_sec(默认 60 秒)未收到任何客户端消息则关闭会话;
  • 每隔 heartbeat_interval_sec(默认 50 秒)发送一次 heartbeat

客户端建议:

  • 持续上行音频或定期发送轻量文本消息,避免被判定闲置;
  • heartbeat + seq 检测连接活性和事件乱序。

9. 实战接入建议

  1. 建连后立即发送 hello,收到 hello.ack 后再发 session.start
  2. 语音输入严格按 16k/16bit/mono并保证每个 WS 二进制消息长度是 640*n
  3. UI 层把 assistant.response.delta 当作流式显示,把 assistant.response.final 当作收敛结果。
  4. 播放器用 output.audio.start/end 管理一轮播报生命周期。
  5. 工具调用场景下,若 executor=client,务必按 tool_call_id 回传 tool_call.results
  6. 出现 error 时优先按 code 分流处理,而不是仅看 message

10. 最小完整时序示例

Client -> hello
Server <- hello.ack
Client -> session.start
Server <- session.started
Server <- (optional) config.resolved
Client -> (binary pcm frames...)
Server <- input.speech_started / transcript.delta / transcript.final
Server <- assistant.response.delta / assistant.response.final
Server <- output.audio.start
Server <- (binary pcm frames...)
Server <- output.audio.end
Client -> session.stop
Server <- session.stopped