第3章 工具调用协议设计:HTTP / JSON-RPC / WebSocket、请求-响应模式、异常协议
协议是 Agent 与工具之间通信的语法契约。本章聚焦三种主流协议——HTTP REST、JSON-RPC、WebSocket——在设计工具调用时的核心差异、适用场景、请求-响应模式以及异常协议规范。
3.1 协议选型:性能、语义与适用场景
选择哪种协议取决于工具的使用模式:同步查询、异步任务、流式推送还是高频双向通信。下表从关键维度做了横向对比。
| 维度 | HTTP (REST) | JSON-RPC | WebSocket |
|---|---|---|---|
| 通信模式 | 请求-响应(同步) | 请求-响应(同步/异步) | 全双工(持久连接) |
| 消息格式 | 多样(JSON/XML/表单) | JSON 严格结构 | 文本/二进制帧 |
| 状态管理 | 无状态(stateless) | 无状态 / 可携带上下文 | 有状态(连接保持) |
| 传输开销 | 较高(头部冗余) | 中(可复用连接) | 低(长连接) |
| 典型延迟 | 几十~几百 ms | 几十 ms(keep-alive) | 毫秒级 |
| 适用场景 | CRUD / 知识查询 | 远程过程调用 / 工具编排 | 流式输出 / 实时监控 |
| 工具调用示例 | GET /tool/weather?city=北京 | {"method":"getWeather","params":["北京"]} | 订阅工具实时事件 |
选型建议: 80% 的工具调用场景用 HTTP REST 即可满足;当工具需要传递结构化参数并返回统一错误格式时,优先选择 JSON-RPC;需要流式推送或低延迟双向通信(如聊天 Agent 连续调用多个工具)时,使用 WebSocket。
3.2 HTTP 协议:端点设计与请求-响应模式
3.2.1 端点(Endpoint)设计原则
- 使用名词复数表示资源:
/tools、/tools/{id}/invoke - 动作通过 HTTP 方法表达:
GET查询、POST调用、DELETE撤销 - 查询参数用于过滤,请求体用于传递调用输入
标准工具调用端点示例:
POST /api/v1/tools/weather/invoke
Content-Type: application/json
{
"city": "北京",
"date": "2025-06-10",
"unit": "celsius"
}
响应:
HTTP/1.1 200 OK
Content-Type: application/json
{
"success": true,
"data": {
"temperature": 28,
"condition": "晴",
"humidity": 45
},
"request_id": "req-7a3f2c"
}
3.2.2 请求-响应生命周期
- Agent 构造请求体(含工具名、参数、元数据)
- 通过 HTTP POST 发送到工具端点
- 工具服务端校验参数 → 执行业务逻辑 → 构造响应
- Agent 接收响应并提取
data或error - 若超时或状态码非 2xx,触发重试或降级策略
3.3 JSON-RPC 协议:结构化调用契约
JSON-RPC 是轻量级远程过程调用协议,天然适合 Agent 工具调用。每个请求包含固定的 jsonrpc、method、params、id 字段,响应严格对称。
3.3.1 请求与响应结构
// 请求
{
"jsonrpc": "2.0",
"method": "getWeather",
"params": {
"city": "北京",
"date": "2025-06-10"
},
"id": 1
}
// 成功响应
{
"jsonrpc": "2.0",
"result": {
"temperature": 28,
"condition": "晴"
},
"id": 1
}
// 错误响应
{
"jsonrpc": "2.0",
"error": {
"code": -32000,
"message": "服务暂时不可用",
"data": {
"retry_after": 5
}
},
"id": 1
}
关键约束:
id 必须唯一且由请求方生成,用于匹配请求与响应;服务端不得修改 id。批量请求时可使用数组包裹多个请求对象。
3.3.2 方法命名规范
| 命名模式 | 示例 | 说明 |
|---|---|---|
动词+名词 | getWeather | 查询类工具 |
名词+动词 | orderCreate | 操作类工具 |
领域.动词+名词 | payment.refund | 带命名空间的工具 |
3.4 WebSocket 协议:流式与双向调用
当 Agent 需要连续调用多个工具或监听工具推送事件时,WebSocket 的长连接特性可以显著降低延迟。
3.4.1 连接建立与消息帧
// 客户端 → 服务端:调用请求
{
"type": "invoke",
"tool": "weather_stream",
"params": { "city": "北京", "interval": 60 },
"request_id": "ws-req-001"
}
// 服务端 → 客户端:流式数据块
{
"type": "data",
"request_id": "ws-req-001",
"payload": { "temperature": 28, "timestamp": "2025-06-10T10:00:00Z" }
}
// 服务端 → 客户端:完成通知
{
"type": "complete",
"request_id": "ws-req-001",
"status": "success"
}
// 服务端 → 客户端:错误通知
{
"type": "error",
"request_id": "ws-req-001",
"code": -32001,
"message": "数据源断开",
"retryable": true
}
3.4.2 心跳保持与重连策略
- 客户端每 30 秒发送
{"type":"ping"},服务端回复{"type":"pong"} - 若连续 3 次未收到 pong,触发重连
- 重连时使用指数退避:1s、2s、4s、8s …… 最大 60s
3.5 异常协议:统一错误码与异常传播
无论使用哪种传输协议,工具调用的异常信息必须结构化、可枚举、可处理。一套统一的异常协议能让 Agent 快速决策是重试、降级还是终止。
3.5.1 标准错误码体系
| 错误码范围 | 类别 | 示例 | 是否可重试 |
|---|---|---|---|
| -32700 ~ -32000 | JSON-RPC 标准错误 | -32600(无效请求)、-32601(方法不存在) | 否 |
| -32000 ~ -31900 | 工具通用错误 | -32000(服务内部错误)、-32001(超时) | 部分 |
| 1000 ~ 1999 | 业务校验错误 | 1001(参数缺失)、1002(参数类型错误) | 否 |
| 2000 ~ 2999 | 资源状态错误 | 2001(余额不足)、2002(频率限制) | 是(等待后重试) |
| 3000 ~ 3999 | 基础设施错误 | 3001(下游超时)、3002(连接池耗尽) | 是 |
异常响应的最小字段集:
{
"error": {
"code": 2001,
"message": "余额不足,当前可用额度 0.5 元",
"data": {
"required_amount": 10.0,
"current_balance": 0.5,
"suggestion": "请充值后重试"
},
"retryable": false,
"retry_after": null
}
}
3.5.2 异常传播链路
当工具调用链中某个环节失败时,异常信息应沿着调用链向上传播,并携带原始错误上下文,避免在中间层被吞没。
3.6 协议适配器架构
为支持多协议统一管理,建议在工具网关层引入 协议适配器,将不同协议的请求统一转换为内部标准 Invocation 对象。
3.7 调试与部署检查清单
| 检查项 | HTTP | JSON-RPC | WebSocket |
|---|---|---|---|
| 端点/路径是否正确 | 校验路由和 method | 校验 method 是否存在 | 校验连接 URL 和协议版本 |
| 参数格式 | JSON body 或 query | 严格 JSON 结构 | JSON 文本帧 |
| 超时设置 | 客户端 10s~30s | 客户端 10s~30s | 心跳 30s,数据超时 60s |
| 错误码映射 | HTTP 状态码 + body | error.code + message | type=error + code |
| 重试策略 | 5xx 或超时重试 | -32000 ~ -31999 重试 | 断线重连指数退避 |
| 日志记录 | 记录 request_id + 耗时 | 记录 id + method + 耗时 | 记录 request_id + 连接 ID |
3.8 常见错误与规避
- 协议混用但适配器缺失:某工具同时暴露 HTTP 和 WebSocket 端点,但内部未统一调用模型,导致逻辑分支爆炸。解决方法是在网关层做协议适配。
- JSON-RPC id 冲突:多个请求使用相同 id,导致响应匹配错乱。保证每次调用生成唯一 id(UUID 或递增整数)。
- WebSocket 未处理心跳:连接被网络设备静默断开,Agent 仍认为连接可用。必须实现 ping/pong + 超时重连。
- 异常被中间件吞没:工具链中某环节捕获异常后返回 null,上游误判为成功。始终传递 error 对象,不允许空响应代表成功。
规范红线: 所有工具调用响应(含异常)必须包含
request_id 或 id,否则 Agent 无法关联请求与结果。这条规则应作为 Code Review 的强制检查项。
3.9 性能与稳定性优化
| 优化方向 | 措施 | 预期效果 |
|---|---|---|
| 连接复用 | HTTP Keep-Alive / WebSocket 长连接 | 减少握手开销 30%~50% |
| 批量调用 | JSON-RPC 批量请求数组 | 一次往返调用多个工具 |
| 响应压缩 | HTTP 启用 gzip / WebSocket 启用 permessage-deflate | 传输体积减少 60%~80% |
| 熔断降级 | 连续错误数 > N 时熔断 30s | 防止雪崩 |
| 请求去重 | 基于 idempotency_key 去重 | 避免重复扣费或重复操作 |
本章核心交付物:
① 协议选型决策矩阵(HTTP / JSON-RPC / WebSocket)
② 统一异常协议错误码表(5 段编码体系)
③ 协议适配器架构图(含异常传播链路)
④ 调试检查清单(15 项覆盖三种协议)
① 协议选型决策矩阵(HTTP / JSON-RPC / WebSocket)
② 统一异常协议错误码表(5 段编码体系)
③ 协议适配器架构图(含异常传播链路)
④ 调试检查清单(15 项覆盖三种协议)