发送和接收:单接口、分离接口与 WebSocket
run 表示“发起一轮”;subscribe 表示“建立一个会话的持续接收通道”。订阅不依赖本页面先发送消息,也可以接收其他客户端或后端主动发起的轮次。函数个数由交互方式决定,与 HTTP 或 WebSocket 的名称无关。
生命周期
- ChatKit 需要建立会话接收通道时调用 subscribe;已有有效订阅则复用。函数返回时监听必须已经建立,并能缓冲事件。底层可以复用已有 WebSocket,不要求每次新建连接。
- 本地发送先等待订阅就绪再调用 run;订阅已缓冲且身份匹配的事件可在受理响应之前到达。OpenAI 工具循环也可能继续调用 run。
- 每条事件带可靠 runId;后端开始事件可登记本地没有发起的轮次,交错事件分别进入对应轮次。
- 回答结束、用户停止一轮,都不会关闭会话订阅。切换会话保留后台监听。
- 释放运行时,SDK 取消 Response reader 或调用迭代器 return();清理该会话监听,不能顺手关掉其他会话共用的连接。
重新打开页面时,history 提供基线,resume 补收游标之后的输出。subscribe 负责持续接收,不自动保证重放;可靠恢复需要后端提供重放或完整历史。
使用 useChatSessions 或 useChatRuntime 时,Hook 在初始化真实会话的运行时时自动建立订阅。“打开会话”是这套默认管理方式的调用时机,不是 subscribe 只在打开页面时才能使用的限制。只使用 Core 时,runtime.connect() 可显式建立监听,不必先发送;首次 send 也会等待连接就绪。断线后重新连接会重新调用 subscribe;这不代表 SDK 提供无限自动重连。
自定义事件流在迭代器 return() 中移除监听并唤醒 pending next();具体用法与停止一轮的区别见能力函数 API。
可运行的独立订阅
先点按钮、不发送用户消息,也会看到主 Agent 回答;随后可以在输入框正常发送。
示例的内存事件总线用于演示,不承担持久化。真实 WS 客户端将监听到的事件放进同样的异步序列,数据仍按所选协议解析。连接重连、鉴权、消息分发由接入方负责。
订阅事件身份
表中“结束”指关闭本轮读取器,不一定是执行完成:A2A 的 final 与 Hermes 的 done 本身不能替代执行结论。OpenAI 的 finish_reason 还可能要求进入工具循环。
这些独立会话事件约定不代表原始协议天然提供广播。没有身份或开始标记时,在接入方代理中补齐;不能按最近发送的一轮猜测。自定义协议通过 identifyEvent 定义自己的身份规则。只讨论主 Agent 的消息,子任务状态由业务组件自行查询或订阅。
独立订阅恢复时,resume 必须让同一会话通道从 eventId 之后完整、有序地重放,再接入实时输出。不能让重放与实时增量乱序混入同一轮;SDK 不比较不透明游标的大小。历史活动轮次在恢复前收到的尾帧不作为重放替代。
当前接入边界
- 本地命令的订阅事件必须使用与命令相同的路由 runId。ADK 的 invocationId、A2A 的 taskId/id、Hermes 的 run_id 若另行生成,需要代理统一编号;受理响应中的新编号不会自动绑定到订阅分发器。
- A2A 的本地发送和被动接收使用同一个事件解码入口:HTTP SSE 通常携带 JSON-RPC 信封;异步序列可直接给 result 内的事件,也可保留已有信封,不会被二次包装。事件带 contextId 时必须与当前 sessionId 相同,错会话事件会被拒绝。
- HTTP subscribe 由公共 SSE 读取器解析 JSON;event 行会写入对象的 event 字段。它不复用各协议直接 run 的全部解析规则。ADK 的 [DONE]、OpenAI 的无身份结束标记不能用来路由订阅;结束和错误事件也须带轮次身份。
- 订阅层只按外层 StreamEvent.eventId 去重,HTTP 对应 SSE id。ADK 仅给 data.id 或 Hermes 仅给 data.seq 不会启用这一层去重;必要时同时提供外层编号。
- OpenAI 被动轮次没有本地发起时的工具循环上下文,不能据此承诺执行浏览器工具。
- 断线后再次 connect 可以建连接,但空闲会话订阅没有自动重连定时器;应用需要管理重连。已有断线轮次的有限恢复见恢复页面。