Skip to content

迁移旧版智能体接入

连接 AI 助手

将旧 Direct Invoke 或 Bridge v1 客户端迁移到智能体开发服务平台(AgentWorks)2026-09-16 发布的接口:用 HTTP 提交消息和工具结果,用 WebSocket 订阅回复。本页已合并 2026-09-15 和 2026-09-16 的迁移要求,无需先实现中间版本。

确认原有接入方式

根据现有代码选择准备方式,然后继续完成同一页中的提交、订阅和恢复步骤。

现有代码的特征原有接入方式准备方式
调用 /api/agents/Invoke,提交 prompt,同步返回答案或接收 SSEDirect Invoke创建渠道账号和新凭证
使用 infini.bridge.v1,通过 WebSocket 发送 message,凭证为 wst_ TokenBridge 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 握手时鉴权。例如:

language-text
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新请求
用户输入promptmessage.content根部的 content
用户、会话和消息标识原调用方需补齐业务映射message.meta 内的字段根部的 user_idchat_idchat_typemsg_id
输出方式stream 控制同步或 SSEWebSocket 广播回复删除 stream;提交后订阅会话
请求包装旧 Invoke 请求字段typemessage删除旧包装和无对应含义的字段,未知字段会被拒绝

以下示例使用 group 会话模式,表示按业务 chat_id 组织会话,不要求创建飞书或 QQ 群聊。原来使用 p2p/session 的客户端,请先核对会话范围

向部署提供的 Bridge HTTP 基址发送请求,使用以下请求头:

language-text
Authorization: Bearer <wst_token>
Content-Type: application/json

POST /api/bridge/v1/SendMessage 的请求体:

language-json
{
  "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。以下成功响应只表示消息已接收,不是最终答案:

language-json
{
  "code": 0,
  "http_status_code": 200,
  "success_response": true,
  "data": {
    "msg_id": "request-42",
    "context_key": "account-1_ticket-session-42"
  }
}

保存返回的 msg_idcontext_key。需要继续向业务系统提供同步接口时,由你的后端接收后续事件、处理超时,再返回最终结果;不要直接返回上述提交响应作为回答。

原 Invoke 的文件上传入口和 files 数组不能搬到 SendMessage,该方法接收文本。具体限制与文件处理方式见能否像旧 Invoke 一样,通过调用接口直接提交文件?

订阅会话并接收回复

新接入不再返回 SSE,也不向渠道账号内所有连接广播回复。客户端必须订阅会话,再把收到的事件关联到对应请求。

明确会话范围

业务后端保存已认证用户、业务 chat_id 与服务端 context_key 的映射。不同业务会话使用不同 chat_id;不要把旧 Invoke 的 session_id 直接填入新请求,也不要自行拼接 context_key

旧 Bridge 默认 chat_typep2p,新接口默认值为 group。迁移时明确填写所需类型,避免因省略字段而改变会话归属。

注意

原客户端使用 p2p 或 /session 时

本页的完整示例使用 groupp2p 不会在提交结果中返回可直接用于订阅的 context_key;本页不覆盖保留 p2p 的迁移。切换正式流量前,请与服务提供方确认会话标识的获取、切换和恢复方式并完成验证;不能只补上 chat_type: "p2p",也不能忽略原有会话行为直接切换为 group

订阅、查询历史和执行工具前,由业务后端检查用户权限。同一渠道账号的 Token 不等于最终用户权限;分开会话也不会自动分开智能体实例使用的记忆库或沙箱。旧历史数据需要另外安排读取或迁移,协议升级不会自动转换原有会话标识。

建立会话订阅

WebSocket 握手收到 hello 后,用 SendMessage 返回的 context_key 填写 session_id

language-json
{
  "t": "sub",
  "d": {
    "session_id": "account-1_ticket-session-42",
    "since_seq": 0,
    "mode": "full"
  }
}

检查订阅结果中的 subd.d.ok,成功示例如下:

language-json
{"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.seqd.server_seq

同一连接可以订阅多个会话,不要重复发送未变化的订阅。需要审批或用户工具时使用 mode: "full"。不再关注会话时发送:

language-json
{"t":"unsub","d":{"session_id":"account-1_ticket-session-42"}}

解析回复和运行状态

用外层 sid 关联会话、mid 关联提交时的 msg_id。不要按事件到达顺序猜测它属于哪次请求,也不要继续解析旧 SSE chunk 或旧 type 帧。

回复文本示例:

language-json
{
  "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 对应的主智能体运行成功结束:

language-json
{
  "t": "ev",
  "sid": "account-1_ticket-session-42",
  "mid": "request-42",
  "seq": 184715,
  "d": {
    "kind": "lifecycle",
    "lifecycle": { "phase": "success" }
  }
}

将旧 done 判断改为主智能体对应请求的 successerrorcancelled 终态。has_pending_interrupt 表示等待审批或工具结果,不是完成;外层 sub 非空时属于子代理,不能用子代理终态结束主智能体请求。

需要展示定时任务或子代理新会话时,处理 opened 通知,用其中的 d.session_id 发送 sub。通知本身不重放,也不替代订阅;业务服务应保存已经关注的会话标识。完整事件字段见 API 参考

回传审批和工具结果

旧中断响应改为 HTTP RespondInterrupt。从 ev.d.kind = pendingpending_phase = created 的事件取得待处理项,例如:

language-json
{
  "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 时由应用提供审批,使用 APPROVEREJECTrespond 时由你的应用执行工具,成功返回 RESPOND,失败返回 ERROR。不要把用户工具的执行请求只当作审批,也不要在事件重放时重复执行业务写入。

使用与 SendMessage 相同的鉴权和 JSON 请求头,向 POST /api/bridge/v1/RespondInterrupt 提交:

language-json
{
  "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 使用产生中断的帧 midcontext_key 使用帧 sidinterrupt_id 使用 pending.idchat_id 仍是原业务会话标识。response_data 是字符串,结构化结果先序列化为 JSON;失败只返回安全的错误信息,不泄露凭证。

检查 HTTP 状态和 success_response。收到 pending_phase = resolved 后移除对应待处理项,继续等待运行终态。保存中断 ID 和业务幂等记录,避免重连或重试造成重复操作。

处理错误和断线恢复

消息提交成功、订阅成功和智能体运行成功是三个不同结果。分别处理失败,并让断线恢复只补齐状态,不重新执行业务。

区分请求、订阅和运行失败

按以下位置判断错误,不再只监听一个旧 error 帧:

失败阶段判断方式客户端处理
握手或 HTTP 请求实际 HTTP 状态码401 检查凭证,停止盲目重试;503 退避重试
HTTP 业务请求success_response = falsedata.reason即使 HTTP 为 200 也按失败处理;按 reason 分支,不依赖错误文案
会话订阅subd.d.ok = false检查该帧的 reasonmessage,不要误以为已开始接收事件
WebSocket 连接err.d.reason连接数超限时减少连接;backpressure 时检查接收速度,再携带游标重连
智能体运行lifecycle.phase = errorcancelled结束对应请求的等待,展示失败或取消,而不是成功

同一会话的上行请求串行发送。重试消息提交时复用原 msg_id;服务端的短期去重不能替代业务系统的幂等控制。其他错误值见 API 错误参考

断线后按游标续传

保持客户端库对 WebSocket 协议层 ping 的正常响应。若使用应用层心跳,将旧 {"type":"PING"} 改为 {"t":"PING"},接收 {"t":"PONG"}

断线后退避重连,重新鉴权并订阅;每个会话的 since_seq 填最后已处理并持久保存的外层 seq。不要因为断线就重新提交原消息或再次执行工具。

游标失效时重建状态

收到 reset 表示旧游标不能继续使用,例如超出保留范围或大于服务端现有事件位置。不能继续用旧的较大游标过滤新事件,也不能把它当成任务完成。

  1. 清除该会话的失效游标,以 reset.d.cursor ?? 0 作为恢复起点;省略 cursor 时使用 0

  2. 暂存随后收到的事件,同时查询消息和待处理中断。两个请求都使用 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"}
  3. 根据查询结果重建历史和待处理状态,按消息、工具调用和中断标识合并暂存事件;不要把历史全文和同一消息的回放文本重复拼接。继续处理并保存外层 seq,保留已执行业务工具的去重记录。

  4. 查询失败时保留本地记录并提示恢复失败,不假装任务已完成。

收到中断时要保存 pending.id 与原请求 mid 的对应关系。ListPendingInterrupts 不返回原 mid;如果无法从本地记录或回放事件恢复它,暂停该中断的自动应答,不要新建 msg_id 或用 tool_call_id 代替。

ListMessages 按新到旧返回,limit 默认 20、上限 200。旧 Invoke 的任务状态查询和重置请求不能照搬,正常运行状态改由生命周期事件维护,这两个查询接口只用于历史展示或状态补偿。

服务端发出 reset 后会在当前订阅继续回放,不需要每次都重新发送 sub。若连接或订阅也需要恢复,使用重置后最后已处理的外层 seq;尚未处理新事件时使用上述恢复起点。不要因 cursor 缺失而省略 since_seq,否则只会接收新事件。

验证并切换业务流量

在测试会话中确认以下结果,再让正式业务流量使用新客户端:

  1. 握手后收到 hello,HTTP 提交成功,订阅返回 subd.d.ok = true
  2. 回复对应正确的 sidmid,只有主智能体终态结束当前请求的等待。
  3. 两个业务会话的回复不混在一起,继续同一会话时保留预期上下文。
  4. 审批、拒绝、工具成功和工具失败都能通过 RespondInterrupt 完成处理,智能体随后继续运行。
  5. 断线恢复不导致重复提交、重复工具写入;实时回复和历史回放的文本一致。
  6. 收到 reset 后,即使没有 cursor,也能补齐状态并继续接收事件;中断无法关联原 mid 时暂停自动应答,失败或取消不会显示为成功。

确认通过后切换业务流量,停止旧客户端;从 Direct Invoke 迁移的应用再收回旧凭证。已有用户工具应用可结合工单助手实战验证完整业务闭环。