news 2026/9/8 8:45:18

JSON-RPC 2.0 成 AI Agent 通信底座:协议原理与实战避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JSON-RPC 2.0 成 AI Agent 通信底座:协议原理与实战避坑指南

做 Agent 开发这段时间,我越来越觉得一个现象很有意思:原本躺在 JSON-RPC 规范文档里吃灰二十年的老协议,居然因为 AI Agent 的爆发被重新翻了出来,而且成了很多 Agent Harness、MCP Server、工具调用链路里默认的通信底座。查了下社区趋势,光是围绕 JSON-RPC 2.0 和 Agent Harness 的讨论,今年以来热度明显上升。这篇文章我就以“漫话”的方式,把这一轮 protocol 复兴的前因后果、协议细节、实战编码和踩坑经验一次性讲清楚。

我会先从协议本身聊起,再结合 Agent Harness 里真实的调度场景,给出可以直接抄的代码示例和配置方案,最后聊聊我在生产环境里遇到过的典型问题。如果你正准备做 agent 工具注册、MCP server、或者任何“让大模型调用外部能力”的中间层,这篇内容应该能帮你省掉不少摸索时间。

1. 先搞明白:JSON-RPC 2.0 到底是个什么协议

1.1 一句人话概括它的定位

JSON-RPC 2.0 是一种基于 JSON 的远程过程调用协议。什么意思?就是它定义了一套“我该怎么把一个函数调用请求发给对方,对方该怎么把执行结果返回给我”的标准格式。想象你给远方的朋友寄快递,你不能只把东西塞进箱子就完事,总得有个面单,上面写着寄件人、收件人、物品名称、备注信息。JSON-RPC 2.0 就是这个面单的规范:怎么填写方法名、怎么填写参数、怎么区分这次调用是第几次、出错时怎么描述错误。

这套规范在 2010 年正式定稿,由 JSON-RPC 工作组维护,目前主流版本是 2.0。注意,它不依赖具体传输层,HTTP、WebSocket、TCP、Unix Socket 都可以承载,这种“传输无关”的特性,正是它后来被 AI Agent 生态选中的关键原因之一。

1.2 协议核心规则,其实也就五条

JSON-RPC 2.0 的规范不长,核心规则可以浓缩成五条,掌握这五条基本上就能读懂 90% 的 Agent 工具调用报文:

  • 请求必须是一个 JSON 对象,包含jsonrpc: "2.0"methodparamsid四个字段,其中jsonrpcmethod必填,params可以缺省,id用于配对请求和响应。
  • 响应分两种:成功响应返回result字段,失败响应返回error字段,两者互斥,id必须和请求一致。
  • 参数支持两种形式:按位置传入的数组params: [10, 20],和按名称传入的对象params: {"base": 10, "height": 20}
  • 通知(Notification)是一种特殊的请求,它没有id字段,表示“你执行就好,不用回复我”。
  • 支持批量调用,客户端可以把多个请求放进一个 JSON 数组,一次性发给服务端,服务端返回一个响应的数组。

这五条里面,id的配对逻辑是最容易被忽略的。在实际 Agent 调度中,多个工具调用往往是并发的,比如 Agent 同时调用了两个工具请求,返回时两个响应可能乱序到达,客户端就是靠id来区分“这个响应到底对应哪个请求”。我见过不少同学第一次写工具调用时,把id写死成 1,并发一上来,响应全部错乱。

1.3 为什么它火了二十年,直到今天才被“捧红”

说句公道话,JSON-RPC 2.0 在 2010 年定稿后,长时间处于“规范经典但应用不温不火”的状态。原因也不难理解,REST 风格借着 HTTP 的东风占领了互联网 API 的主流,大家习惯用 GET/POST 加资源路径来描述业务,前端后端联调也简单。JSON-RPC 2.0 这种 RPC 风格反而显得有点“老派”。

但事情到 2024、2025 年出现了转机。AI Agent 兴起后,系统里出现了一个很尴尬的需求:大模型不是直接调数据库、调 API 的,它要通过 Agent Harness 这类调度框架去调用外部工具。这里面的每一次工具调用,都天然是一个“远程过程调用”——你要调用一个函数,这个函数不在本地,在执行环境或远端服务里,可能是另一个进程、另一台机器。REST 风格在这一场景下显得很笨重,你需要自己定义一堆“任务创建接口”“任务查询接口”“结果回调接口”,而 JSON-RPC 2.0 直接用一个请求就能说清楚“我要调这个函数,参数是这些”。

再加上 Anthropic 的 MCP(Model Context Protocol)协议明确选择了 JSON-RPC 2.0 作为消息格式基础,等于给这份老协议做了一次行业级背书。MCP 现在几乎是 Agent 工具接入的事实标准,它的初始化、工具列表、工具调用、资源读取,底层全是 JSON-RPC 2.0。于是大量开发者开始补课:原来这份躺在仓库里的老协议,才是 Agent 生态的真正“交通规则”。

2. AI Agent 把这份老协议从仓库里翻了出来

2.1 Agent Harness 的通信困境,恰好是老协议的主场

先解释一下 Agent Harness 是什么。它不是一个具体的软件,而是一个抽象概念:负责把大模型、工具、记忆、执行环境这些组件编排起来,形成一个能自主完成任务的闭环系统。你可以把它理解成 Agent 的“驾驶室”或“控制台”。LLM 是发动机,工具是方向盘和刹车,Harness 就是把这些部件连接起来的线路和操作系统。

Harness 里面最核心的循环是这样:Agent 接收用户指令,LLM 生成下一步决策,Harness 解析这个决策,调用对应工具,把工具结果返回给 LLM,LLM 再基于新信息继续决策。这个循环里,Harness 和工具执行端之间的通信频率非常高,而且每次通信都带着“调用哪个方法、传什么参数、给我什么结果”的语义。如果用 REST,你得设计一堆资源,比如POST /tool-callsGET /tool-calls/{id},还要处理轮询和回调,链路复杂不说,延迟还高。JSON-RPC 2.0 天然就是为这种“我要调用你的一个函数”设计的,请求-响应一一对应,语义干净利落。

我在实际项目里见过一个很典型的报错热词:error: agent harness runtime "codex" is unavailable because its plugin registration ...。说白了就是 Harness 尝试通过某个插件注册通道去连接名为 codex 的运行时,结果插件注册失败。这类问题往往就出在 Harness 与插件、运行时之间的通信配置上,而这一层通信现在越来越多地跑在 JSON-RPC 2.0 之上。理解协议本身,对排查这类问题非常有帮助。

2.2 对比 REST、WebSocket 自定义协议,它赢在哪

拿 REST 来比。REST 把动作都映射到资源上,工具调用这种“函数式”操作塞进 REST 里,怎么想怎么别扭。比如“调用fetch_page并传入url参数”,REST 可能要写成POST /api/fetch-page,参数放 body,状态靠 HTTP 状态码表达,还得专门设计错误结构。JSON-RPC 2.0 则直截了当,method: "fetch_page"params: {"url": "..."},整个报文的意图一眼就能看懂。

再拿 WebSocket 自定义协议来比。很多团队在 Agent 场景下选择 WebSocket,因为需要长连接和双向通信,这没错。但不少团队直接在 WebSocket 上发明自己的消息格式,今天{type: "call", function: "xxx"},明天改成{action: "invoke", name: "xxx"},几个月后接手的人一头雾水。JSON-RPC 2.0 提供了一个标准化的消息格式,你把它跑在 WebSocket 上,格式稳定、文档齐全、有现成 SDK,等于在灵活性之上加了一层可靠约束。说白了,JSON-RPC 2.0 不是来取代 WebSocket 的,它和 WebSocket 是搭档,WebSocket 负责管道,JSON-RPC 2.0 负责管道里流动的报文格式。

2.3 与 MCP 的关系,以及其他协议热词的一笔带过

近期热词里经常看到 MCP 协议,或者再往广了说,SPI、MIPI、CAN、MODBUS、MQTT、USB 等等都出现了。这类协议热词的集中出现,其实反映出两件事:一是硬件和嵌入式场景对协议的需求长期存在,二是 AI Agent 生态又把“协议”这个词推到了大众眼前。这里要厘清一个概念:SPI、IIC 这类是硬件总线协议,MQTT 是物联网消息协议,MODBUS 是工业控制协议,它们和 JSON-RPC 2.0 不是同一个层次的东西,解决的问题也不同。

MCP 不一样,它和 JSON-RPC 2.0 的关系是“建筑”和“建材”的关系。MCP 定义了 Agent 应用如何发现工具、如何调用工具、如何获取资源等一整套交互规则,但它的消息载体就是 JSON-RPC 2.0。你可以打开任意一个 MCP 的调试日志,看到的所有报文都是 JSON-RPC 2.0 格式。因此,你要学 MCP,本质上先要精通 JSON-RPC 2.0。这也是我强烈建议每个做 Agent 开发的工程师,花一个小时认真读一遍 JSON-RPC 2.0 规范原文的原因。

3. 从零手写一轮 JSON-RPC 2.0 调用,就这么简单

3.1 一个最简 Agent 工具调用请求

纸上谈兵没有意义,我们直接看一个真实场景。假设你的 Agent 需要调用一个网页抓取工具fetch_page,参数是目标 URL,预期的返回值是网页正文内容。在 Agent Harness 和工具服务之间,走 HTTP 传输的 JSON-RPC 2.0 报文长这样:

{ "jsonrpc": "2.0", "id": 1, "method": "fetch_page", "params": { "url": "https://example.com/article/123" } }

服务端处理成功后,返回:

{ "jsonrpc": "2.0", "id": 1, "result": { "title": "Example Article", "content": "......", "status_code": 200 } }

看到没有,整个交互就像一次本地函数调用,方法名、参数、返回值,一目了然。这里的id: 1不是什么魔法数字,它是这次请求的流水号。Agent 同时发出多个工具调用时,每个请求的id都不同,响应里的id用来告诉客户端这是哪次调用的结果。

如果你只是想快速测试,用 curl 直接 POST 一个 JSON 请求就行。比如工具服务监听在localhost:9000/jsonrpc,可以这样做:

curl -X POST http://localhost:9000/jsonrpc \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "fetch_page", "params": { "url": "https://example.com" } }'

注意两个细节:一是 HTTP 方法用什么不重要,POST 是惯例,但也有团队用 GET 或 PUT,JSON-RPC 2.0 规范本身不管这个,实际选型时跟随团队的既有规范即可;二是Content-Type必须是application/json,很多联调问题就出在application/json; charset=utf-8application/json的差异上,后文专门讲。

3.2 通知、批量调用、错误处理,三条容易被忽略的硬规则

讲完最简场景,接着讲三个不太起眼但影响深远的规则。

第一条是通知。通知就是没有id的请求,比如:

{ "jsonrpc": "2.0", "method": "log_event", "params": { "event": "agent_started" } }

服务端收到通知后必须执行对应操作,但不返回任何响应。这个特性在 Agent 场景里最适合做日志上报、状态同步、进度通知。你不需要等待结果,也犯不着为了一次日志上报专门设计一个回调接口。但要记住,通知不能用于那些你必须知道执行结果的调用。如果你把一个工具调用写成通知,Agent 将永远等不到工具的返回结果,整个决策循环就卡死了。

第二条是批量调用。客户端可以把多个请求放进一个数组,一次性发给服务端:

[ {"jsonrpc": "2.0", "id": 1, "method": "fetch_page", "params": {"url": "https://example.com/a"}}, {"jsonrpc": "2.0", "id": 2, "method": "search_web", "params": {"q": "JSON-RPC agent"}}, {"jsonrpc": "2.0", "method": "log_event", "params": {"event": "batch_started"}} ]

服务端会返回一个数组,数组里只包含有id的请求的响应。批量调用适合一次需要调用多个互不依赖的 Agent 工具的场景,可以减少网络往返。不过这里有个坑,后面讲排查时细说。

第三条是错误处理。当服务端执行失败时,返回的对象里不能有result,必须有error

{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32602, "message": "Invalid params", "data": { "detail": "url must be a valid HTTP URL" } } }

error对象包含codemessagedata三个字段,其中codemessage必填,data可选。这里最关键的认知是:编码错误码时,不要自己发明一套风格迥异的错误体系,而应该从 JSON-RPC 2.0 的保留错误码出发,扩展业务错误码。

3.3 在 Agent Harness 里注册一个工具,原来要经历这些步骤

明白了协议报文,我们再回到 Harness 场景,看一个工具从注册到被调用的完整链路。以我常用的 Python 栈为例,假设我用 Flask 搭建了一个极简的 JSON-RPC 2.0 工具服务:

from flask import Flask, request, jsonify app = Flask(__name__) TOOLS = { "fetch_page": { "description": "Fetch a web page and return its text content", "params": {"url": {"type": "string", "required": True}} } } def handle_request(payload): if not isinstance(payload, dict) or payload.get("jsonrpc") != "2.0": return {"jsonrpc": "2.0", "id": None, "error": {"code": -32600, "message": "Invalid Request"}} method = payload.get("method") params = payload.get("params", {}) req_id = payload.get("id") if method not in TOOLS: return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32601, "message": f"Method not found: {method}"}} if method == "fetch_page": url = params.get("url") if not url: return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32602, "message": "Invalid params: url is required"}} try: result = {"title": "Example", "content": "page content here"} return {"jsonrpc": "2.0", "id": req_id, "result": result} except Exception as e: return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32000, "message": str(e)}} @app.route("/jsonrpc", methods=["POST"]) def jsonrpc_endpoint(): payload = request.get_json() if isinstance(payload, list): responses = [handle_request(item) for item in payload] responses = [r for r in responses if r.get("id") is not None] return jsonify(responses) return jsonify(handle_request(payload)) if __name__ == "__main__": app.run(port=9000)

在这个服务端实现里,有几个地方值得注意。一是入口统一处理了批量请求,然后过滤掉通知(idNone)的响应,因为通知不需要返回。二是错误码的选取,-32600表示无效请求,-32601表示方法不存在,-32602表示参数无效,-32000起是服务端错误,这些都是规范里的保留范围。三是handle_requestjsonrpc版本做了校验,防止老版本客户端接入。

服务端就绪后,Agent Harness 侧的调用逻辑反而简单,因为它的职责就是“构造请求、解析响应、把结果反馈给 LLM”。用一个 Python 客户端示例:

import requests def call_tool(method, params, request_id): resp = requests.post( "http://localhost:9000/jsonrpc", json={"jsonrpc": "2.0", "id": request_id, "method": method, "params": params}, timeout=10, ) payload = resp.json() if "error" in payload: raise RuntimeError(f"Tool call failed: {payload['error']['message']}") return payload["result"] # Agent 决策循环里的一次调用 page = call_tool("fetch_page", {"url": "https://example.com"}, request_id=101)

到这里,一个最简的 Agent 工具调用闭环就跑通了。但实践里远远没有这么简单,下面这部分才是真正的经验所在。

4. 实操中踩过的坑:从“好像能通”到“稳如老狗”

4.1 Content-Type 与 HTTP 状态码,协议之外的隐形杀手

先说一个最基础也最容易翻车的点:HTTP 层的状态码和 Content-Type。JSON-RPC 2.0 规范没有规定 HTTP 状态码怎么用,所以有些服务端实现不管执行成功还是失败,一律返回 HTTP 200,错误信息只放在 body 的error字段里。另一些实现则会在业务错误时返回 HTTP 500 甚至 400。这两种风格在 Agent 生态里都存在,MCP SDK 普遍倾向于“HTTP 200 + 协议错误码”的方式。

这意味着你在写客户端时,不能简单依赖resp.status_code判断成败,必须先拿到 body,解析 JSON,再看里面有没有error字段。我在生产环境里遇到过一个问题:服务端网关把application/json响应拦下来,误改成了text/plain,客户端一用resp.json()解析直接抛 JSONDecodeError,排查半天才发现是网关配置的问题。所以,第一件事就是确认整个链路里的 Content-Type 保持application/json不被篡改。

4.2 id 的匹配与并发,一个写死 id 引发的线上事故

前文提过写死id的问题,这里展开讲。Agent 场景下,Harness 经常会并发调用多个工具,比如同时查天气和查日历。如果客户端代码把id写死成某个常量,那么两个并发的请求都带id: 1,服务端返回两个id: 1的响应,客户端根本分不清哪个对应哪个,结果就是 Agent 拿天气数据去回答日历问题,或者直接报 mismatch 错误。

正确做法是维护一个自增或 UUID 的 id 生成器,或者至少在客户端代码里用request_id = str(uuid.uuid4())保证唯一。另外要注意,JSON-RPC 2.0 规范里id可以是字符串、数字或 null,但如果客户端和服务端对 id 的类型约定不一致,比如客户端发字符串"101",服务端返回数字101,也会导致匹配失败。我的经验是,在团队内部约定统一用字符串类型的 UUID,规避隐式类型转换的坑。

4.3 批量请求的“部分失败”陷阱

批量调用看起来很方便,但有一个隐藏很深的规则:如果批量请求中的任何一个请求不是有效的 JSON-RPC 请求,服务端应当返回一个包含单个错误响应的数组,而不是继续处理其他请求。反过来说,即使每个请求都合法,服务端也是逐项处理,某些成功、某些失败,失败项走error字段。

这个规则的工程含义是:不要盲目把大量工具调用放进一个批量请求。如果里面有一个请求构造有问题,整个批量可能被判定为无效,其他正常的请求也会被一起作废。最好把批量数量控制在合理范围内,例如 10 个以内,并且对每个请求单独做参数校验。此外,批量请求的响应顺序不保证与请求顺序一致,客户端必须根据响应里的id重新配对,不能想当然按数组下标取。

4.4 通知、超时与幂等,Agent 场景里绕不开的工程问题

通知适合日志和状态上报,但它有一个隐患:如果服务端执行失败,客户端永远不知道。这在 Agent 决策循环里可能是致命的——你上报了“工具执行完成”,但工具其实没执行成功,Agent 就会带着错误的信息继续往下走。所以,对于关键状态同步,我建议不要用通知,老老实实用带id的请求,哪怕只是返回一个{"ok": true}

超时是另一个高频坑。LLM 生成响应通常需要几秒到几十秒,但工具调用往往是毫秒级,可有些工具就是慢,比如网页抓取、大文件处理。你给 HTTP 客户端设了 5 秒超时,工具执行 8 秒,客户端直接超时报错。这里要区分是“调用失败”还是“结果延迟”。如果是对耗时敏感的工具,最好在设计时就让工具服务端先快速返回一个“任务已受理”的结果,后续 Agent 再通过另一个查询接口取结果,或者把超时时间放宽到 30 秒以上。

幂等性也值得一提。Agent 决策循环遇到网络超时时,常常会重试同一工具调用,如果工具本身不幂等,比如“下单”“发送消息”这类操作,就会造成重复执行。这不是 JSON-RPC 2.0 协议本身能解决的问题,但你在设计工具 API 时必须考虑进来,最好在参数里增加一个request_id或者让工具服务端对相同id的去重。实际中,我通常把 JSON-RPC 2.0 的id同时作为幂等键使用,服务端缓存最近处理过的 id,重复请求直接返回缓存结果。

4.5 错误码速查表,排查问题时的对照手册

前文提到了错误码,这里整理一份速查表,方便你在开发联调时快速定位问题。

错误码含义可能出现的位置常见原因
-32700解析错误服务端入口请求不是合法的 JSON,比如多了个逗号
-32600无效请求服务端入口请求对象不是合法 JSON-RPC 请求,jsonrpc 版本缺失
-32601方法不存在方法分发层调用了未注册的工具或方法名拼写错误
-32602参数无效参数校验层缺少必填参数、参数类型错误
-32603内部错误业务逻辑层服务端代码抛了未捕获异常
-32000 至 -32099服务端错误业务逻辑层工具执行过程中出错,如网络请求失败

我在排查线上问题时,第一眼总是先看错误码落在哪个区间。落在 -32600 到 -32603,基本是客户端报文构造问题,优先查请求体;落在 -32000 区间,是服务端执行问题,优先查日志;如果在 -32000 以下或者自定义范围,通常是业务层抛出的特定错误,需要查工具实现。

5. 深入一点:手写协议 vs 现成 SDK,Agent 框架里怎么选

5.1 主流语言的现成实现,省力但要看场景

全手写 JSON-RPC 2.0 报文并不复杂,几十行代码就够,但生产环境里我还是建议大家优先用成熟的 SDK,尤其是涉及 WebSocket、错误处理、批量请求这些细节时,踩过的坑别人已经帮你填平了。

主流语言都有对应的实现:

  • Python:json-rpcpython-json-rpc,以及 Flask/FastAPI 的扩展插件。
  • Node.js:jayson是最常用的,支持 HTTP、TCP、WebSocket、TLS 多种传输层。
  • Go:golang.org/x/exp/jsonrpc2github.com/gorilla/rpc都可用。
  • Java:json-rpc-2.0这个项目覆盖得比较完整。
  • Rust:jsonrpc-core生态比较成熟,适合高并发场景。

选 SDK 时建议关注三点:是否支持你需要的传输层、批量请求是否实现正确、错误处理是否符合规范。不要为了“少依赖一个包”去手写全部,尤其是 JSON-RPC over WebSocket 的服务端,发送顺序、心跳、断线重连这些工程细节,现成 SDK 会省掉很多麻烦。

5.2 哪些场景值得自己封装

当然,某些情况下手写反而更合适。比如你的 Agent Harness 只是一个内部模块,只需要在一个进程里做线程间方法调用,为了一个 JSON-RPC 调用引入一整个 HTTP 服务和一堆路由配置,明显过度设计。这种时候直接用 Python 的concurrent.futures或者 Node 的worker_threads就能解决,连网络层都不需要。

还有一种情况:你已经有一个基于 gRPC 或 Thrift 的内部微服务通信体系,团队也更熟悉那一套。这时候硬塞一个 JSON-RPC 2.0 进去就不太合适。协议选型要看整个技术栈的一致性,不能因为 Agent 火了就跟风。

如果你决定自己封装,我建议至少覆盖几个核心点:统一的id生成器、请求上下文的注入、错误码到业务异常的映射、超时控制、日志里带上id做链路追踪。这些基本功做到位,后续扩展会顺畅很多。

5.3 从单机到分布式:JSON-RPC 2.0 如何撑起 Agent 集群

最后聊一个更宏观的层面。当你只有一个 Agent Harness、一个工具服务时,JSON-RPC 2.0 看起来就像一个小工具。但当你的 Agent 系统扩展到多机分布式时,JSON-RPC 2.0 的价值才会真正体现。

比如,你可以把工具执行服务单独部署成一组无状态节点,前面挂负载均衡,所有节点都监听同一个 JSON-RPC 2.0 入口。因为报文本身没有“会话状态”,任何一个节点都可以处理任意请求。又比如,你可以在消息总线和 Agent 服务之间跑 JSON-RPC 2.0 over WebSocket,让 Agent 可以实时订阅工具执行进度,这就把同步调用扩展成了类似事件驱动的架构。

这里有个实践细节:分布式场景下,每个请求最好带上一个全局唯一的id,并且把id和业务 trace_id 打通。这样无论是日志检索还是排查问题,拿到一个id就能把整条调用链串起来。我个人的做法是直接用 UUID 作为id,同时在params里附带一个trace_id字段,服务端记录日志时两者都打点,后续通过任何一个 ID 都能定位。

再延伸一步,JSON-RPC 2.0 的批量调用在分布式场景下还能用来做“扇出”。比如 Agent 需要同时调用三个数据源,客户端把三个请求放进一个数组发给网关,网关并行调用三个后端服务,再把结果聚合返回。注意这里要做超时保护,不能让一个慢服务拖垮整个批量请求。更保险的做法是控制并发度和批大小,不要把一个包含 50 个请求的大数组一次性砸给网关,容易把下游打爆。

6. 写在最后的几点个人体会

我做 Agent 相关开发这么长时间,最大的感触是:很多看似高深的新概念,底层其实都藏着一些“上了年纪”的老技术。JSON-RPC 2.0 就是典型代表,它不是为 AI 而生,却在 AI 时代被重新发掘出了光芒。理解它的关键,不在于背下规范里那几个字段,而在于想明白它解决了什么问题——让一个程序像调用本地函数一样,去调用另一个程序的函数,干净、简单、可靠。

如果你正准备开发 Agent Harness、MCP Server,或者任何需要和外部工具通信的中间层,我的建议是:不要急着铺一堆框架,先用 JSON-RPC 2.0 把最核心的一次工具调用跑通,体会一下“函数调用”的语义如何在这份协议里被优雅地表达,再逐步叠加 WebSocket、批量、并发这些复杂度。跑通之后,你会发现后续学 MCP、学各种 Agent 协议,都会顺畅很多。

另外再分享一个我踩过多次的坑:无论是用现成 SDK 还是自己封装,都要在开发初期就把日志打完整,特别是请求报文和响应报文的原文。Agent 场景下的问题往往不是单点故障,而是链路里某一次工具调用的返回和预期不一致,导致 LLM 的后续决策全部跑偏。有了完整报文日志,定位问题的速度能快一倍。这份看似不起眼的习惯,在 Agent 这种“一次决策可能引发十几次调用”的系统里,性价比高得惊人。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 8:44:06

利尔达双誉加冕“物联之星”:物联网行业从拼概念走向拼落地

今天想聊聊利尔达拿下2025年“物联之星”两项大奖这件事。刚看到获奖名单刷屏的时候,我其实不意外——作为物联网行业的老面孔,利尔达这些年在新品发布和项目落地上一直没断过档。但“双誉加冕”这个词还是值得细品一下:一年评一次、覆盖全产…

作者头像 李华
网站建设 2026/9/8 8:44:02

漏水检测技术全解析:从热成像到听漏仪的专业定位方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 8:43:26

dlib 68点人脸关键点检测:从原理到性能优化的实战指南

简介:面向深度学习与计算机视觉入门者,该资源围绕dlib库的人脸关键点提取,提供了针对图片和视频两种输入形式的完整实践方案。资源包内共6个文件,压缩包大小约69.46MB,核心包含两个可直接运行的Python脚本、一个用于68…

作者头像 李华
网站建设 2026/9/8 8:42:29

s3c6410+TVP5150的Linux V4L2驱动开发实战:从框架到调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 8:42:19

多AI助手并行开发?用tmux和tabby拯救被刷屏的终端

我试过在终端里同时开五个 AI 编程助手,结果我的终端窗口活生生被刷成了瀑布流。原本指望它们各连各路、彼此互补,结果五个进程的输出全挤进同一个标准输出,画面疯狂翻滚,连光标都找不到。那一刻,我真的觉得自己不是程…

作者头像 李华
网站建设 2026/9/8 8:39:12

本地搭建RAG课程问答助手:文档解析、向量检索与大模型生成

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华