做产品 PMaker
空格的键盘
模型的「调用」其实是一次请求 —— 真正执行的是你的代码 你写好工具清单 名字 + 参数 + 说明 模型说「我要调用」 输出一段结构化 JSON 你的代码执行它 调 API · 查库 · 发邮件 结果塞回上下文 模型据此继续想 模型真正会输出的,长这样 {"tool": "query_order", "params": {"order_id": "A1024"}} 注意:它只是「说了这句话」,没有任何代码真的去查了单 关键:工具是「你的」 —— 你决定有哪些、谁能调、做什么、要不要批准

模型不执行任何东西。它只输出「我想调用某某工具、用这些参数」,真正的动作由你的代码完成。

工具调用

「它自己会调接口查数据」,这是对 Agent 最常见的误解。模型一个接口也调不了。它唯一能做的,是在回复里写出一段结构化的「请求」——剩下的全是你的代码。

你会遇到的现象
  • 以为给模型连上数据库,它就能自己查了
  • 模型调工具时参数给错,你不知道该怪谁
  • 不知道「工具列表」对模型意味着什么,随便塞了几十个进去

它其实不会调用

模型的一切行为都是「根据上下文,输出下一个 Token」。所谓工具调用,只是它在输出里写了一段特定格式的文字——通常是一段 JSON,写着「我要调用 query_order,参数是订单号 A1024」。

这句话写完之后,模型的工作就结束了。没有任何代码因为这句话而执行。真正把订单查出来、把邮件发出去、把库改掉的,是你写的执行代码——它看到模型输出的这段 JSON,解析它,调用对应的函数,再把结果作为一段「工具返回」放回上下文。

这就是「你把手借给它」的意思:模型负责决定要干什么,你负责真的去干,并把它看到的结果还给它。它永远只活在文字世界里。

一次调用长什么样

完整的流程是这样的:

一、你把工具清单发给模型。每个工具给三样东西:名字、参数结构(各参数的类型和含义)、一段说明(这个工具是干什么的、什么时候该用它)。说明写得好不好,直接决定模型会不会用对。

二、模型在回答里输出调用请求。格式由接口规范决定(OpenAI 风格、Anthropic 风格各有各的格式),但本质都一样:一段结构化文本。

三、你的代码执行。解析请求 → 校验参数 → 调用函数 → 捕获错误。

四、结果返回。把执行结果(成功的数据、或错误信息)拼成一段文字,连同原来的对话一起,再次发给模型。模型看到结果,继续思考下一步。

注意第四步:错误信息也一定要返回给模型。「查询失败:订单不存在」这句话要让它看到,它才知道换个参数重试,还是告诉用户查不到。很多 Agent 卡死,就是执行失败的结果没有回流。

紫色是模型做的,灰色是你的代码做的 你的代码 ① 发出工具清单 名字 · 参数 · 说明 模型 ② 写出调用请求 一段 JSON,仅此而已 你的代码 ③ 解析并执行 校验 · 调函数 · 捕错 你的代码 ④ 结果回流 写进上下文 模型看到结果,进入下一轮 两处最容易搞错 模型只出现在 ② 它一个接口也调不了,只会写出一段文字 ④ 最常被漏掉:错误也要回流 失败结果没写回上下文,它就会原样重试到卡死
四个格子里有三个是你的代码。「模型自己会调接口」这句话,从第一步到最后一步都不成立——它写请求,你干活,你再把它该看到的还给它。

怎么设计工具清单

工具清单是给模型看的一份「说明书」,它的质量决定模型会不会正确伸手。三条经验:

一、少而清楚,胜过多而全。工具越多,模型越容易选错,占的上下文也越多。只放这次任务可能用到的。几十个工具塞进去,效果往往不如三个写清楚的。

二、说明里写「什么时候用、什么时候别用」。「query_order:按订单号查询订单详情。仅当用户提供了订单号时使用;订单号通常形如 A 开头加数字。」——这样的说明比「查询订单」有用得多。

三、参数名和类型要严谨。模型会照抄你的参数名。给它 order_id 它就传 order_id,给它 id 它就传 id。含糊的命名会把错误一路带到执行层。

还有一点容易被忽略:工具本身的稳定性。你的函数改了签名,但模型还按旧的参数格式输出——请求就会失败。工具变更要有版本意识,或者干脆在代码里做参数容错。

三个坑

一、以为工具是白送的。每个工具都要你写执行逻辑、处理错误、考虑并发和超时。工具越多,你的工程债越多。

二、忘了它是「你的」工具。模型会调用你列出来的任何工具——包括有副作用的。该分级授权、加人工断点的地方,一定要加。见提示注入:外部数据可能诱骗模型去调不该调的工具。

三、把工具结果当权威。工具返回的数据也会出错、会过期、会来自不可信来源。模型拿到工具结果后同样可能基于错误数据一本正经地编。链路越长,越要在关键动作后做核验。