WebSocket API 协议

适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-12 | 来源:crates/cheng-api/src/ws/protocol.rscrates/cheng-api/src/ws/manager.rscrates/cheng-api/src/rest/routes.rs

工作流运行通过 WebSocket 流式推送。轮询 GET /executions/:id 告诉你结果;而 socket 告诉你正在发生什么——逐节点进度、逐 token 的模型输出、智能体迭代和审批请求。

连接

接口 说明
/ws/executions 顶层
/api/v1/ws/executions 同一个处理器,带版本前缀的别名

三种认证方式任选其一:

Authorization: Bearer <jwt>          # 推荐
/ws/executions?token=<jwt>           # 无法设置请求头时
/ws/executions?ticket=chtk_…         # 渠道客户端

票据形式的存在,是为了让 chk_ 渠道密钥永远不出现在 URL 中。票据由已认证的渠道 REST 接口签发,并在升级时一次性消费——票据不是可重复使用的凭证。

协议版本

WS_PROTOCOL_VERSION = 1

事件结构发生任何不兼容变更时都会递增。外部客户端——cheng CLI 就是这么做的——在依赖事件语义之前,会通过 CLI 会话 API 比对该版本号。请检查它,不要假设;这个常量存在的意义就在于此。

消息信封

每条消息都被包裹:

{
  "messageId": "uuid",
  "timestamp": "2026-08-12T10:30:00Z",
  "type": "NODE_COMPLETE",
  "...": "各类型专有字段"
}

messageId 用于去重——重连之后你完全可能合理地收到同一条消息两次,因此请以它为键,而不要假设投递恰好一次。载荷被扁平化进信封,所以 typemessageId 平级,而不是嵌套在里面。

消息类型是 SCREAMING_SNAKE_CASE,字段名是 camelCase。

订阅

仅仅连接并不会收到事件;你需要订阅某个 scope。共三种粒度:

{ "type": "SUBSCRIBE", "scope": { "type": "workspace",    "workspaceId": "…" } }
{ "type": "SUBSCRIBE", "scope": { "type": "conversation", "conversationId": "…" } }
{ "type": "SUBSCRIBE", "scope": { "type": "execution",    "executionId": "…" } }

服务端以 SUBSCRIBED 确认。UNSUBSCRIBE 使用相同的 scope 结构,以 UNSUBSCRIBED 确认。

请选择能覆盖需求的最小 scope:观察单次运行用 execution;每轮都是一次新执行的对话界面用 conversation;看板用 workspace。为了观察一次运行而订阅整个工作区,意味着要在客户端过滤掉所有其他人的流量。

PING 保持连接活跃,服务端以 PONG 回应。两者都接受可选的 datatimestamp

执行事件

类型 含义
EXECUTION_START 运行开始——携带 executionIdworkflowIdstartedAtinputs
EXECUTION_PROGRESS 总体进度
EXECUTION_STATE_CHANGED 状态机转换
NODE_COMPLETE 某个节点成功,附带其输出
NODE_FAILED 某个节点失败,附带错误
EXECUTION_COMPLETE 终态:成功
EXECUTION_FAILED 终态:失败
EXECUTION_CANCELLED 终态:已取消
ERROR 协议或服务端错误

流式节点输出

类型 含义
NODE_STREAM_START 某个节点开始流式输出
NODE_STREAM_CHUNK 一段增量输出
NODE_STREAM_COMPLETE 流式输出结束
NODE_STREAM_FAILED 流式输出中途失败

请把 chunk 累积在与已完成消息分开的缓冲区里;若干 chunk 之后出现 *_FAILED,意味着你必须丢弃或标记这段不完整文本,而不能把它当作完整答案呈现。

智能体事件

智能体运行要健谈得多,而这正是它可调试的原因:

类型 含义
AGENT_ITERATION_COMPLETED 一轮「推理—行动—观察」循环结束
AGENT_TURN_STARTED / AGENT_TURN_COMPLETED 对话轮次边界
TOOL_EXECUTION_STARTED / _PROGRESS / _COMPLETED / _FAILED 工具调用生命周期
CONTEXT_WINDOW_STATUS_UPDATED 上下文用量——留意压缩时机
AGENT_PAUSED_FOR_REVIEW 正在等待人工

审批事件

APPROVAL_REQUESTED 宣告需要一个决策;APPROVAL_RESOLVED 宣告决策已作出。请通过 REST(POST /executions/:id/approve)作答,而不是通过 socket——在这里 WebSocket 是通知通道,不是控制通道。

会话与消息事件

CONVERSATION_CREATEDMESSAGE_CREATEDWORKFLOW_TRIGGEREDMESSAGE_COMPLETED 是对话界面所消费的事件:用户消息触发工作流,输出流式返回,轮次结束。

容器与循环事件

CONTAINER_STARTED / CONTAINER_COMPLETED 包裹子流程和批量子流程;LOOP_ITERATION_STARTED / _COMPLETED / _FAILED 上报每次迭代,因此 100 个条目的批处理是逐条可观测的,而不是一个不透明的节点。

文档与表格事件

SHADOW_EVENTTABLE_EVENT 承载实时的文档与电子表格变更,这正是工作流写入时协同编辑器能保持同步的原因。

客户端本地工作区事件

这些实现的是反方向——服务端请求客户端做事,也正是 cheng back 让浏览器会话操作你本机文件的机制:

类型 方向
CLIENT_WORKSPACE_READY 客户端宣告其本地工作区
WORKSPACE_TOOL_REQUEST 服务端请求客户端执行一个工具
WORKSPACE_TOOL_RESULT 客户端返回结果
WORKSPACE_TOOL_CANCEL 服务端取消一个待处理请求
CLIENT_WORKSPACE_DISCONNECTED 本地工作区已断开

WORKSPACE_TOOL_RESULT 的错误除消息外还带有稳定的 LOCAL_EXECUTOR_* 错误码,因此客户端可以基于错误码分支,而不必解析文字描述。

重连

请实现带抖动的退避、重连后恢复订阅,并基于 messageId 去重。用 close code 区分主动关闭与网络故障——重试一次干净的关闭是白费功夫。内置的 @chengflow/chat WebSocket 客户端已经全部实现了这些;参见 SDK 快速开始

如果事件流静默,请与 GET /executions/:id 对账,而不要无限等待。CLI 正是为此使用了 30 秒的静默阈值。

下一步

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容