Skip to content

WebSocket API 参考

WebSocket API 使用 Bridge 协议 infini.bridge.v1,把自有消息系统连接到 WebSocket 渠道账号。本文说明连接、鉴权、消息、流式回复、中断、心跳和错误处理。创建渠道账号、签发 Token 和验证接入的步骤参见通过 WebSocket 接入智能体

连接和编码

客户端需要按照目标部署的地址、协议版本和 JSON 字段约定建立连接。

  • 连接地址:使用目标部署为 WebSocket 渠道账号提供的地址,端点路径为 /ws/v1
  • 协议版本infini.bridge.v1
  • 帧类型:WebSocket 文本帧。
  • 消息编码:Protocol Buffers JSON 字段约定。
  • JSON 字段名:snake_case。

生产环境应使用目标部署提供的 wss:// 地址。不要根据控制台站点地址自行拼接 WebSocket 主机名。

所有业务帧使用同一种信封:外层 type 指定消息类型,并由同名字段承载数据。例如:

language-json
{
  "type": "message",
  "message": {
    "meta": {
      "msg_id": "3eeec74d-a4d4-4517-89ec-ef8aeb676666",
      "user_id": "user-001"
    },
    "content": "帮我查询订单状态"
  }
}

支持的帧类型:

方向type数据字段用途
客户端到平台authauth在连接建立后完成鉴权
客户端到平台messagemessage发送用户文本或斜杠指令
客户端到平台interrupt_responseinterrupt_response响应确认或用户工具中断
双向PING请求业务层心跳响应
双向PONG响应业务层心跳
平台到客户端auth_okauth_ok确认帧内鉴权成功
平台到客户端message_replymessage_reply返回一条完整文本;/help 使用该帧返回 JSON 命令清单
平台到客户端message_stream_replymessage_stream_reply返回流式增量、工具事件或中断
平台到客户端errorerror返回连接或运行错误

完成鉴权

wst_ Token 鉴权一个 WebSocket 渠道账号,不直接鉴权智能体模板或智能体实例。渠道账号的绑定模式和绑定对象决定消息进入哪个智能体。

使用 Subprotocol 鉴权

推荐在 HTTP Upgrade 请求中传递 Token:

language-text
Sec-WebSocket-Protocol: infini.bridge.v1, bearer.<base64url_token>

base64url_tokenwst_ Token 的无填充 Base64URL 编码。连接成功后,服务端只回显:

language-text
Sec-WebSocket-Protocol: infini.bridge.v1

这种方式在连接建立前完成鉴权,不发送 auth 或接收 auth_ok 帧。

使用帧内鉴权

客户端无法设置自定义 WebSocket 子协议时,可以使用帧内鉴权。建立连接时不要请求 Sec-WebSocket-Protocol;连接建立后,在目标部署规定的鉴权超时内发送:

language-json
{
  "type": "auth",
  "auth": {
    "token": "wst_xxxxxxxxxxxxxxxxxxxx"
  }
}

鉴权成功后收到:

language-json
{
  "type": "auth_ok",
  "auth_ok": {
    "connection_id": "550e8400-e29b-41d4-a716-446655440000",
    "server_time": "1784685600000000000"
  }
}
字段类型说明
connection_idstring当前连接的标识,可用于客户端日志关联
server_timestringUnix 纳秒时间;按字符串或 BigInt 处理

每次重连都必须重新鉴权。不要在日志、错误消息或客户端页面中输出 wst_ 明文。

Subprotocol 鉴权和帧内鉴权是两种独立路径。已经通过 Subprotocol 完成鉴权时,不要再发送 auth 帧;使用帧内鉴权时,不要只请求 infini.bridge.v1 子协议。

设置消息元数据

messagemessage_replymessage_stream_replyinterrupt_response 使用 meta 关联用户、对话和回复。

字段必填说明
msg_id由客户端为每条输入生成的全局唯一 ID,推荐使用 UUID;平台不会用该字段阻止重复请求
user_idmessageinterrupt_response 必填调用方业务系统中的稳定用户标识,用于定位渠道用户、用户绑定、单聊会话和等待中断响应的运行
user_name用于显示的用户名称
chat_typep2pgroup;省略时按 p2p 处理
chat_id群聊必填稳定的业务对话标识;单聊省略时使用 user_id 维持对话连续性

不要为一次重试重复使用 msg_id 并假设平台会自动去重。会产生业务副作用的工具仍应在业务系统中使用幂等键。

wst_ Token 鉴权的是 WebSocket 渠道账号;user_id 负责在该账号内标识实际发送消息的业务用户。user_id 不是平台生成的用户 ID,也不是凭证。请使用业务系统中不包含敏感信息的稳定标识,不要按消息随机生成,也不要用可能变化的 user_name 代替。发送消息时,客户端应按 msg_id 保存完整的原始 meta,直到收到对应的 message_replydone: true,或者已经按照故障处理流程确认结束。

同一个 WebSocket 渠道账号可以有多个客户端连接。客户端应使用 msg_idchat_id 和自身连接上下文筛选并关联回复,不要假设账号上的每条回复只会发送给发起请求的连接。

发送用户消息

language-json
{
  "type": "message",
  "message": {
    "meta": {
      "msg_id": "3eeec74d-a4d4-4517-89ec-ef8aeb676666",
      "user_id": "user-001",
      "user_name": "Alice",
      "chat_type": "p2p",
      "chat_id": "chat-001"
    },
    "content": "帮我查询订单状态"
  }
}
字段类型必填说明
message.metaobject消息元数据
message.contentstring用户文本或斜杠指令

模板绑定需要按用户创建专属实例时,使用稳定的 user_id 发送 /init。实例绑定可以直接发送普通消息。

WebSocket 接入以文本消息为受支持的输入路径。需要上传文件并把文件作为智能体输入时,使用 Invoke API 的文件上传接口,除非目标部署另行提供 WebSocket 文件输入约定。

接收回复

接收完整文本

message_reply 返回一条完整文本,常用于斜杠指令等非流式回复:

language-json
{
  "type": "message_reply",
  "message_reply": {
    "meta": {
      "msg_id": "3eeec74d-a4d4-4517-89ec-ef8aeb676666",
      "chat_type": "p2p",
      "chat_id": "chat-001"
    },
    "content": "已创建智能体实例"
  }
}

/help 是一个需要单独处理的结构化回复。外层仍是 message_reply,但 message_reply.content 是 JSON 文本,而不是已经解析的 JSON 对象。解析该字符串后得到包含 itemstotal 的对象。下面是只有一个命令条目的简化示例:

language-json
{
  "items": [
    {
      "name": "help",
      "description": "查看所有可用命令,包含命令说明、使用示例、子命令与参数。",
      "example": "/help - 查看所有可用命令",
      "scope": "system"
    }
  ],
  "total": 1
}

items 是命令描述数组,total 是命令数量。每个命令可以包含 namedescriptionexamplescoperequires_initargssub_commands;Protocol Buffers JSON 会省略未设置的可选字段,因此客户端不应假定每个字段都存在。例如,JavaScript 客户端可以使用 JSON.parse(frame.message_reply.content) 后再渲染命令清单。系统命令的用途参见使用系统斜杠命令

接收流式事件

message_stream_reply 返回智能体运行中的增量事件:

language-json
{
  "type": "message_stream_reply",
  "message_stream_reply": {
    "meta": {
      "msg_id": "3eeec74d-a4d4-4517-89ec-ef8aeb676666",
      "chat_type": "p2p",
      "chat_id": "chat-001"
    },
    "content": {
      "content": "订单正在处理中"
    }
  }
}
字段类型说明
thinking.contentstring思考文本增量
content.contentstring最终回答文本增量
tool_callobject工具调用;包含 idname 和 JSON 字符串 arguments
tool_call_resultobject工具结果;包含 idresultstatus
interruptobject中断;包含 typeid 和 JSON 字符串 data
statusobject尽力而为的进度事件;包含 seqsourcephase、JSON 字符串 detailstimestamp 和可选 context_key
donebooleantrue 表示本次智能体运行结束

status.seqstatus.timestamp 在 Protocol Buffers 中是 int64,JSON 帧将它们编码为十进制字符串;timestamp 使用 Unix 毫秒。客户端应按字符串或 BigInt 处理,不要先转换为 JavaScript Number。例如:

language-json
{
  "type": "message_stream_reply",
  "message_stream_reply": {
    "meta": {
      "msg_id": "3eeec74d-a4d4-4517-89ec-ef8aeb676666",
      "chat_type": "p2p",
      "chat_id": "chat-001"
    },
    "status": {
      "seq": "12",
      "source": "loop_telemetry",
      "phase": "start",
      "details": "{\"iteration\":1,\"phase\":\"start\"}",
      "timestamp": "1784685600123"
    }
  }
}

该示例展示一种可能返回的循环进度。读取 details 时,请按字段名处理,不依赖字段顺序。

同一帧最多包含一个增量字段;非终止帧通常省略 done,终止帧中的 donetruedone 可以单独出现,也可以与最后一个文本增量同时出现。客户端应分别累积 thinkingcontent,按工具调用 ID 关联工具事件,并在收到 done: true 后结束本次运行。

status 用于进度展示,可以按 seq 排序和去重,也可以忽略。它可能缺失或重复,details 结构随 source 变化。请以 done: true 判断运行结束,以 Trace 进行审计,并由客户端另行保存重连恢复所需的状态。

响应中断

中断出现在 message_stream_reply.interrupt。客户端应保存该帧的 meta 和中断 ID,并通过 msg_id 找到发送原消息时保存的完整 meta。完成用户确认或业务工具执行后,在同一 WebSocket 连接发送:

language-json
{
  "type": "interrupt_response",
  "interrupt_response": {
    "meta": {
      "msg_id": "3eeec74d-a4d4-4517-89ec-ef8aeb676666",
      "user_id": "user-001",
      "chat_type": "p2p",
      "chat_id": "chat-001"
    },
    "payload": {
      "interrupt_id": "interrupt-001",
      "action": "RESPOND",
      "response_data": "{\"ticket_id\":\"INC-1024\",\"status\":\"处理中\"}"
    }
  }
}
动作用途response_data
APPROVE批准确认型中断通常为空
REJECT拒绝确认型中断通常为空
RESPOND返回用户输入或用户工具包执行结果使用中断所需的 JSON 字符串
ERROR表示调用方无法返回有效结果提供可供智能体处理的错误数据

interrupt_response.meta 必须标识触发中断的原消息,并包含其稳定 user_id。在协议的正常行为中,服务端中断帧会返回这份消息上下文;服务端修复上线前的兼容处理见下面的说明。用户工具包的执行和参数校验参见定义用户工具包

注意

服务端修复上线前,中断帧可能缺少 user_id

当前已确认一个 WebSocket HITL 缺陷:服务端返回的 message_stream_reply.interrupt.meta 可能没有原消息的 user_id。如果客户端直接原样返回这份不完整的 meta,平台可能无法定位原来等待响应的运行;WebSocket 连接仍会保持在线并交换 PINGPONG,但工具结果、最终回复和 done: true 不会继续返回,原 Trace 也不会出现决定后的执行事件。

请按 msg_id 查找当前客户端保存的原始消息 meta,并只从该记录补回缺少的 user_id。如果当前客户端没有对应的待处理消息,不要响应该中断;同一渠道账号上的其他连接可能会收到并处理它。客户端确认中断属于自己发出的消息,却已经丢失原记录,或者中断帧的消息上下文与原记录不一致时,请停止发送决定,保存原始帧并联系平台支持。不要根据 chat_iduser_name、当前界面用户或另一条待处理消息猜测 user_id

下面的代码展示补偿逻辑;pendingMessages 表示客户端在发送消息时保存的 msg_id 到完整 meta 的映射:

language-javascript
const interruptMeta = frame.message_stream_reply.meta;
const originalMeta = pendingMessages.get(interruptMeta.msg_id);

if (!originalMeta) {
  return; // 不是当前客户端保存的待处理消息
}
if (!originalMeta?.user_id) {
  throw new Error("无法确定原消息的 user_id;停止发送中断决定");
}
if (
  interruptMeta.user_id &&
  interruptMeta.user_id !== originalMeta.user_id
) {
  throw new Error("中断 user_id 与原消息不一致;停止发送中断决定");
}
if (
  interruptMeta.chat_id &&
  originalMeta.chat_id &&
  interruptMeta.chat_id !== originalMeta.chat_id
) {
  throw new Error("中断 chat_id 与原消息不一致;停止发送中断决定");
}

const responseMeta = {
  ...originalMeta,
  ...interruptMeta,
  user_id: originalMeta.user_id,
};

服务端修复后,相同逻辑仍可核对返回的 user_id 是否与原消息一致。

注意

收到 done: true 后再确认运行结束

发送 APPROVEREJECT 后,请继续等待原 meta 对应的 done: true,再确认本次运行结束。决定已经发出或收到 tool_call 都不表示决定已经送达原运行,也不证明工具已经执行。

如果连接仍正常但没有收到 done: true,请把本次结果记为未知,保留原始消息和中断的 meta、中断 ID 与发生时间。在原 Trace 中检查决定后的执行事件,并在 MCP 服务或实际写入的业务系统中核对工具是否执行;没有新的独立“审批 Trace”不表示记录丢失。不要重复发送决定或原消息;没有幂等保证时,也不要重试有副作用的操作。生产接入前,分别使用单项、无副作用的调用验证批准和拒绝都能收到 done: true;任一测试未通过时,请联系平台支持并改用已经完成审批验收的入口。

如果一个待审批中断同时包含多个 tool_calls,请保存原始帧和 meta,停止有副作用的处理并联系平台支持。当前接口一次只提交一个决定,无法保证批内每项决定都被保留,也未规定断线后的处理方式;不要逐项响应或使用内部 EDIT 动作。

处理心跳和重连

Bridge 使用 JSON 文本帧实现业务层心跳,不使用 WebSocket 协议控制帧:

language-json
{"type":"PING"}

收到 PING 后发送:

language-json
{"type":"PONG"}

客户端也可以发送 PING 并等待 PONG。心跳间隔、允许丢失次数、鉴权超时和连接数限制由目标部署配置。

断线恢复时:

  1. 使用带抖动的指数退避建立新连接。
  2. 重新鉴权。
  3. 恢复客户端自己的消息关联状态。
  4. 只重试能够安全重复执行的业务请求。

处理错误

HTTP Upgrade 错误

连接建立前的错误使用 HTTP 状态和 JSON 响应:

language-json
{
  "code": "unauthorized",
  "message": "token invalid"
}
HTTP 状态code处理
400missing_protocol_version添加 infini.bridge.v1 子协议
400invalid_token_encoding使用无填充 Base64URL 重新编码 wst_ Token
401unauthorized检查、轮换或重新启用 Token
401account_mismatch使用绑定到目标 WebSocket 渠道账号的 Token
502 或 503gateway_unavailable按退避策略重试,并检查目标部署状态
503pre_auth_pool_full延迟后重试帧内鉴权,或改用 Subprotocol 鉴权

WebSocket 错误帧

连接建立后的错误使用:

language-json
{
  "type": "error",
  "error": {
    "code": "invalid_message",
    "message": "message parse error"
  }
}
code连接影响处理
duplicate_auth关闭建立新连接并只鉴权一次
auth_required可能保持或关闭完成鉴权;连接已关闭时重新连接
auth_errorunauthorizedaccount_mismatch关闭修正 Token 或渠道账号后重新连接
auth_timeout关闭在目标部署的时限内发送 auth,或使用 Subprotocol 鉴权
max_connections_exceeded关闭复用或关闭闲置连接,再按退避策略重试
server_restart关闭按退避策略重新连接和鉴权
invalid_message保持修正 JSON 信封或字段后发送新请求
meta_user_id_required保持添加稳定且非空的 meta.user_id
agent_error保持将当前运行标记为失败,并结合 Trace 排查智能体实例

连接还可能以标准 WebSocket Close Code 结束:1000 表示正常结束或心跳超时,1001 表示服务端关闭,1003 表示鉴权或帧类型不可接受,1008 表示策略限制。客户端应同时记录最后一个 error 帧和 Close Code,但不要记录 Token 明文。

鉴权和账号错误在配置修正前不应无界重试。连接暂时不可用、服务重启和网络中断可以按有上限的退避策略恢复。