迁移旧版智能体接入
将旧 Direct Invoke 或 Bridge v1 客户端迁移到智能体开发服务平台(AgentWorks)2026-09-16 发布的接口:用 HTTP 提交消息和工具结果,用 WebSocket 订阅回复。本页已合并 2026-09-15 和 2026-09-16 的迁移要求,无需先实现中间版本。
确认原有接入方式
根据现有代码选择准备方式,然后继续完成同一页中的提交、订阅和恢复步骤。
| 现有代码的特征 | 原有接入方式 | 准备方式 |
|---|---|---|
调用 /api/agents/Invoke,提交 prompt,同步返回答案或接收 SSE | Direct Invoke | 创建渠道账号和新凭证 |
使用 infini.bridge.v1,通过 WebSocket 发送 message,凭证为 wst_ Token | Bridge v1 | 保留渠道账号,调整握手鉴权 |
旧接口和旧帧格式已移除,不能只替换地址或子协议名称。首次开发客户端、不涉及旧代码迁移时,使用接入指南;已经适配 Bridge v2 的客户端按智能体接入 API 更新日志核对后续变化。
准备渠道账号和凭证
先按原有接入方式准备账号。两条路径最终都使用绑定到 WebSocket 渠道账号的 wst_ Token;HTTP 基址和 WebSocket 地址使用服务提供方给出的值,不要从控制台主机名推导。
从 Direct Invoke 迁移
为目标智能体实例创建 WebSocket 渠道账号,选择实例绑定,再为业务服务创建 wst_ Token。原实例的 API 调用开关、Invoke 地址和 agt_、dbg_ Token 不能继续用于新接入,也不能只改 Token 前缀。
创建入口和操作步骤见准备 WebSocket 接入。新接入验证通过后,再停止旧调用方并收回旧凭证。
从 Bridge v1 迁移
保留原渠道账号和仍然有效的 wst_ Token。将子协议改为 infini.bridge.v2,删除连接后发送 auth、等待 auth_ok 的代码;不需要换成 IAM 用户 Token,也不需要使用 infini.bff.v1。
完成握手鉴权
两种客户端都在 WebSocket 握手时鉴权。例如:
Sec-WebSocket-Protocol: infini.bridge.v2, bearer.<base64url_token>base64url_token 是完整 wst_ Token 的无填充 Base64URL 编码;编码不等于加密。能够设置请求头的服务端客户端也可使用 Authorization: Bearer <wst_token>。握手成功后收到 hello,再进行订阅;不再等待 auth_ok。
/ws/v1 路径没有改变,hello.d.protocol_version 中的 v1 也不表示应使用旧子协议。握手后的时间字段从 auth_ok.server_time(Unix 纳秒)变为 hello.d.server_time(Unix 毫秒)。
wst_ Token 仅保存在你的应用后端,网页和移动应用通过后端访问。带有 Origin 的握手还需符合部署允许的来源配置。
通过 HTTP 提交消息
用 POST /api/bridge/v1/SendMessage 替换原提交方式。请求体是 JSON,不能继续发送旧对象的包装字段。
| 要调整的内容 | 旧 Direct Invoke | 旧 Bridge v1 | 新请求 |
|---|---|---|---|
| 用户输入 | prompt | message.content | 根部的 content |
| 用户、会话和消息标识 | 原调用方需补齐业务映射 | message.meta 内的字段 | 根部的 user_id、chat_id、chat_type、msg_id |
| 输出方式 | stream 控制同步或 SSE | WebSocket 广播回复 | 删除 stream;提交后订阅会话 |
| 请求包装 | 旧 Invoke 请求字段 | type、message | 删除旧包装和无对应含义的字段,未知字段会被拒绝 |
以下示例使用 group 会话模式,表示按业务 chat_id 组织会话,不要求创建飞书或 QQ 群聊。原来使用 p2p 或 /session 的客户端,请先核对会话范围。
向部署提供的 Bridge HTTP 基址发送请求,使用以下请求头:
Authorization: Bearer <wst_token>
Content-Type: application/jsonPOST /api/bridge/v1/SendMessage 的请求体:
{
"user_id": "user-17",
"chat_id": "ticket-session-42",
"chat_type": "group",
"msg_id": "request-42",
"content": "查询工单 INC-1024 的状态。"
}user_id 由你的后端根据已认证用户确定。继续同一业务会话时复用 chat_id,每条新消息使用新的 msg_id;重试同一条消息时复用原 msg_id。
同时检查 HTTP 状态和 success_response。以下成功响应只表示消息已接收,不是最终答案:
{
"code": 0,
"http_status_code": 200,
"success_response": true,
"data": {
"msg_id": "request-42",
"context_key": "account-1_ticket-session-42"
}
}保存返回的 msg_id 和 context_key。需要继续向业务系统提供同步接口时,由你的后端接收后续事件、处理超时,再返回最终结果;不要直接返回上述提交响应作为回答。
原 Invoke 的文件上传入口和 files 数组不能搬到 SendMessage,该方法接收文本。具体限制与文件处理方式见能否像旧 Invoke 一样,通过调用接口直接提交文件?。
订阅会话并接收回复
新接入不再返回 SSE,也不向渠道账号内所有连接广播回复。客户端必须订阅会话,再把收到的事件关联到对应请求。
明确会话范围
业务后端保存已认证用户、业务 chat_id 与服务端 context_key 的映射。不同业务会话使用不同 chat_id;不要把旧 Invoke 的 session_id 直接填入新请求,也不要自行拼接 context_key。
旧 Bridge 默认 chat_type 为 p2p,新接口默认值为 group。迁移时明确填写所需类型,避免因省略字段而改变会话归属。
注意
原客户端使用 p2p 或 /session 时
本页的完整示例使用 group。p2p 不会在提交结果中返回可直接用于订阅的 context_key;本页不覆盖保留 p2p 的迁移。切换正式流量前,请与服务提供方确认会话标识的获取、切换和恢复方式并完成验证;不能只补上 chat_type: "p2p",也不能忽略原有会话行为直接切换为 group。
订阅、查询历史和执行工具前,由业务后端检查用户权限。同一渠道账号的 Token 不等于最终用户权限;分开会话也不会自动分开智能体实例使用的记忆库或沙箱。旧历史数据需要另外安排读取或迁移,协议升级不会自动转换原有会话标识。
建立会话订阅
WebSocket 握手收到 hello 后,用 SendMessage 返回的 context_key 填写 session_id:
{
"t": "sub",
"d": {
"session_id": "account-1_ticket-session-42",
"since_seq": 0,
"mode": "full"
}
}检查订阅结果中的 subd.d.ok,成功示例如下:
{"t":"subd","sid":"account-1_ticket-session-42","d":{"session_id":"account-1_ticket-session-42","ok":true,"cursor":0,"replaying":true}}首次订阅使用 since_seq: 0,补齐提交消息与订阅之间已经产生、仍在保留窗口内的事件;省略它只会收到新事件。重连时使用该会话最后已处理并保存的外层 seq,不要使用 d.seq 或 d.server_seq。
同一连接可以订阅多个会话,不要重复发送未变化的订阅。需要审批或用户工具时使用 mode: "full"。不再关注会话时发送:
{"t":"unsub","d":{"session_id":"account-1_ticket-session-42"}}解析回复和运行状态
用外层 sid 关联会话、mid 关联提交时的 msg_id。不要按事件到达顺序猜测它属于哪次请求,也不要继续解析旧 SSE chunk 或旧 type 帧。
回复文本示例:
{
"t": "ev",
"sid": "account-1_ticket-session-42",
"mid": "request-42",
"seq": 184713,
"d": {
"kind": "message_delta",
"message_delta": {
"kind": "text",
"message_id": "message-1",
"text": "工单正在处理中。"
}
}
}按 message_id 追加文本;thinking 内容单独处理。历史回放可能把同一消息的多个相邻文本增量合并为一帧,合并帧的外层 seq 是其中最后一个原始事件的游标。不要按帧数计算消息数,也不要要求序号连续。
旧 Bridge 工具调用参数由 tool_call.arguments 改为 tool_call.args;结果由 tool_call_result 改为 message_delta.tool_result,用 tool_call_id 对应调用。
以下事件表示 request-42 对应的主智能体运行成功结束:
{
"t": "ev",
"sid": "account-1_ticket-session-42",
"mid": "request-42",
"seq": 184715,
"d": {
"kind": "lifecycle",
"lifecycle": { "phase": "success" }
}
}将旧 done 判断改为主智能体对应请求的 success、error 或 cancelled 终态。has_pending_interrupt 表示等待审批或工具结果,不是完成;外层 sub 非空时属于子代理,不能用子代理终态结束主智能体请求。
需要展示定时任务或子代理新会话时,处理 opened 通知,用其中的 d.session_id 发送 sub。通知本身不重放,也不替代订阅;业务服务应保存已经关注的会话标识。完整事件字段见 API 参考。
回传审批和工具结果
旧中断响应改为 HTTP RespondInterrupt。从 ev.d.kind = pending、pending_phase = created 的事件取得待处理项,例如:
{
"t": "ev",
"sid": "account-1_ticket-session-42",
"mid": "request-42",
"seq": 184714,
"d": {
"kind": "pending",
"pending_phase": "created",
"pending": {
"id": "interrupt-1",
"tool_call_id": "call-1",
"type": "respond",
"name": "get_ticket_status",
"arguments": "{\"ticket_id\":\"INC-1024\"}",
"meta": {}
}
}
}pending.type = hitl 时由应用提供审批,使用 APPROVE 或 REJECT;respond 时由你的应用执行工具,成功返回 RESPOND,失败返回 ERROR。不要把用户工具的执行请求只当作审批,也不要在事件重放时重复执行业务写入。
使用与 SendMessage 相同的鉴权和 JSON 请求头,向 POST /api/bridge/v1/RespondInterrupt 提交:
{
"chat_type": "group",
"chat_id": "ticket-session-42",
"user_id": "user-17",
"msg_id": "request-42",
"context_key": "account-1_ticket-session-42",
"interrupt_id": "interrupt-1",
"action": "RESPOND",
"response_data": "{\"status\":\"处理中\",\"team\":\"网络支持组\"}"
}msg_id 使用产生中断的帧 mid,context_key 使用帧 sid,interrupt_id 使用 pending.id;chat_id 仍是原业务会话标识。response_data 是字符串,结构化结果先序列化为 JSON;失败只返回安全的错误信息,不泄露凭证。
检查 HTTP 状态和 success_response。收到 pending_phase = resolved 后移除对应待处理项,继续等待运行终态。保存中断 ID 和业务幂等记录,避免重连或重试造成重复操作。
处理错误和断线恢复
消息提交成功、订阅成功和智能体运行成功是三个不同结果。分别处理失败,并让断线恢复只补齐状态,不重新执行业务。
区分请求、订阅和运行失败
按以下位置判断错误,不再只监听一个旧 error 帧:
| 失败阶段 | 判断方式 | 客户端处理 |
|---|---|---|
| 握手或 HTTP 请求 | 实际 HTTP 状态码 | 401 检查凭证,停止盲目重试;503 退避重试 |
| HTTP 业务请求 | success_response = false、data.reason | 即使 HTTP 为 200 也按失败处理;按 reason 分支,不依赖错误文案 |
| 会话订阅 | subd.d.ok = false | 检查该帧的 reason 和 message,不要误以为已开始接收事件 |
| WebSocket 连接 | err.d.reason | 连接数超限时减少连接;backpressure 时检查接收速度,再携带游标重连 |
| 智能体运行 | lifecycle.phase = error 或 cancelled | 结束对应请求的等待,展示失败或取消,而不是成功 |
同一会话的上行请求串行发送。重试消息提交时复用原 msg_id;服务端的短期去重不能替代业务系统的幂等控制。其他错误值见 API 错误参考。
断线后按游标续传
保持客户端库对 WebSocket 协议层 ping 的正常响应。若使用应用层心跳,将旧 {"type":"PING"} 改为 {"t":"PING"},接收 {"t":"PONG"}。
断线后退避重连,重新鉴权并订阅;每个会话的 since_seq 填最后已处理并持久保存的外层 seq。不要因为断线就重新提交原消息或再次执行工具。
游标失效时重建状态
收到 reset 表示旧游标不能继续使用,例如超出保留范围或大于服务端现有事件位置。不能继续用旧的较大游标过滤新事件,也不能把它当成任务完成。
清除该会话的失效游标,以
reset.d.cursor ?? 0作为恢复起点;省略cursor时使用0。暂存随后收到的事件,同时查询消息和待处理中断。两个请求都使用 HTTP
POST及前面的鉴权请求头:/api/bridge/v1/ListMessages:{"context_key":"account-1_ticket-session-42","limit":200}。/api/bridge/v1/ListPendingInterrupts:{"context_key":"account-1_ticket-session-42"}。
根据查询结果重建历史和待处理状态,按消息、工具调用和中断标识合并暂存事件;不要把历史全文和同一消息的回放文本重复拼接。继续处理并保存外层
seq,保留已执行业务工具的去重记录。查询失败时保留本地记录并提示恢复失败,不假装任务已完成。
收到中断时要保存 pending.id 与原请求 mid 的对应关系。ListPendingInterrupts 不返回原 mid;如果无法从本地记录或回放事件恢复它,暂停该中断的自动应答,不要新建 msg_id 或用 tool_call_id 代替。
ListMessages 按新到旧返回,limit 默认 20、上限 200。旧 Invoke 的任务状态查询和重置请求不能照搬,正常运行状态改由生命周期事件维护,这两个查询接口只用于历史展示或状态补偿。
服务端发出 reset 后会在当前订阅继续回放,不需要每次都重新发送 sub。若连接或订阅也需要恢复,使用重置后最后已处理的外层 seq;尚未处理新事件时使用上述恢复起点。不要因 cursor 缺失而省略 since_seq,否则只会接收新事件。
验证并切换业务流量
在测试会话中确认以下结果,再让正式业务流量使用新客户端:
- 握手后收到
hello,HTTP 提交成功,订阅返回subd.d.ok = true。 - 回复对应正确的
sid和mid,只有主智能体终态结束当前请求的等待。 - 两个业务会话的回复不混在一起,继续同一会话时保留预期上下文。
- 审批、拒绝、工具成功和工具失败都能通过
RespondInterrupt完成处理,智能体随后继续运行。 - 断线恢复不导致重复提交、重复工具写入;实时回复和历史回放的文本一致。
- 收到
reset后,即使没有cursor,也能补齐状态并继续接收事件;中断无法关联原mid时暂停自动应答,失败或取消不会显示为成功。
确认通过后切换业务流量,停止旧客户端;从 Direct Invoke 迁移的应用再收回旧凭证。已有用户工具应用可结合工单助手实战验证完整业务闭环。