Agent 工具调用基础:让 Agent 真正动手做事 · 第 1 / 6 节

第 1 课:从「只会说」到「能动手」:Agent 为什么需要工具

学习目标:

  • 说出「工具」在 Agent 里指的到底是什么,和普通的一段回复文字有什么区别
  • 认出一个工具定义至少要包含哪三样东西
  • 判断一个具体任务到底需不需要给 Agent 配工具

前置要求:会写基本 prompt、用过 Claude Code 之类的工具 | 下一课 第 2 课 >>

同一句话,问了两次

你在一个几十个文件的项目里,跟 Agent 说了这句话:

"把项目里所有 console.log 找出来。"

第一次,这个 Agent 只是一个接了 API 的聊天框,没有接任何工具。 它的回复是这样的:

我没办法直接访问你的文件系统,不过你可以在终端里运行下面的命令来查找:

grep -rn "console.log" src/

这会递归搜索 src/ 目录下所有文件里包含 console.log 的行,并打印出文件名和行号。如果你的项目用了 TypeScript,记得把 .ts/.tsx 也包含进搜索范围。

写得没错,甚至挺贴心。但你要的不是一份怎么搜的说明,你要的是搜索结果。这一轮对话结束时,你的项目里到底哪几个文件有 console.log,你一个字都不知道,还得自己去开终端敲那行命令。

第二次,同一句话,丢给一个接了搜索工具的 Agent。 它的回复是这样的:

我在项目里搜了一遍,找到 7 处 console.log

  • src/api/client.ts:42
  • src/api/client.ts:88
  • src/utils/logger.ts:12
  • src/components/Cart.tsx:156
  • src/components/Checkout.tsx:23
  • src/hooks/useAuth.ts:67
  • scripts/seed-db.js:5

其中 src/utils/logger.ts:12 那一处看起来是有意保留的日志封装,其余 6 处像是调试时留下的。要我帮你清掉吗?

两次问的是一模一样的问题。模型也没换、prompt 也没改。差别就一件事:第二次,这个 Agent 手里多了一样东西——工具(tool)。第一次它只能凭训练数据里见过的知识,猜一个"大概该怎么做"的命令讲给你听;第二次它真的跑了一遍搜索,看到了你项目此刻的真实内容,然后才开口。

这一课要讲清楚的就是:工具到底是什么、它凭什么能让 Agent 从"讲讲思路"变成"动手查了一遍"、以及什么任务其实根本不需要它。

工具是宿主给模型的一份菜单

先纠正一个直觉:搜到那 7 个文件的,不是模型本身。模型没有文件系统,没法自己打开一个目录、跑一遍正则匹配。真正跑那次搜索的,是运行这个 Agent 的宿主程序——可能是 Claude Code,也可能是你自己写的、调用 Claude API 的一个几十行的脚本。

**工具,就是宿主程序告诉模型"我这边可以帮你做这些事"的一份清单。**每一项写清楚三件事:这个能力叫什么名字、什么时候该用它、用的时候要传哪些参数。1

拿上面那次搜索举例,宿主程序在请求里塞给模型的工具清单,大概长这样:

这三个字段各管各的事:name 是模型选它时要写的标识符;description 是一段说明文字,告诉模型这个工具做什么、什么场合该用、行为上有什么特点;input_schema 是一份 JSON Schema,规定调用这个工具时要传哪些参数、每个参数是什么类型。1

模型没见过你的文件系统,但它见过这份清单。它读到 description 里写着"在项目源码目录中搜索……返回文件路径和行号",又看到你说"把项目里所有 console.log 找出来",两边一对,判断出该点这个工具,该传 pattern 等于 console\.log。这一步是模型做的——选工具、填参数,属于语言模型最擅长的事:读懂意图、匹配到合适的选项。

但选完之后呢?谁去真的翻文件?

模型只提议,动手的是宿主

这里有一条分界线,是这一课里最重要的一句话:模型从不亲自执行任何操作。它只是把"我想调用哪个工具、传什么参数"整理成一段结构化数据,交还给宿主程序;真正打开文件、跑命令、发请求的,是宿主程序自己的代码。2

具体到上面那次搜索,完整的过程是这样的:

  1. 模型看到你的问题和 search_files 这份工具清单,决定要调用它,参数是 {"pattern": "console\\.log"}。它把这个决定包成一段数据,作为这一轮回复的内容——注意,回复里没有搜索结果,因为模型手上根本没有搜索结果,它只是提了个请求。
  2. 宿主程序(Claude Code、或者你写的那个脚本)收到这段数据,看到"这是一次工具调用",于是它自己去执行——真的在磁盘上跑一遍搜索,拿到那 7 条匹配。
  3. 宿主程序把搜索结果重新塞回对话历史,再问模型一次:"这是刚才那次调用的结果,你接着说。"
  4. 模型这时候才第一次看到真实的搜索结果,据此写出你看到的那段回复。

OpenAI 的文档把这个过程直接称为"应用程序和模型之间的多步骤对话":模型调用函数时,执行和取回结果的责任在你的应用程序这一边,不在模型这一边。3 Anthropic 这边说得更直白:模型什么都不执行,它发出一个结构化请求,你的代码(或者 Anthropic 自己的服务器)去跑这个操作,结果再流回对话里。2

顺带一提,"谁来执行"这件事本身还能再分一层。文档里把需要你的程序动手执行的工具叫"客户端工具",把 Anthropic 自己的服务器代为执行的(比如网页搜索)叫"服务器工具"。4 但无论哪种,模型这一侧的行为都没变——它还是只提议,不亲自动手,区别只在"谁替它把提议变成真实动作"。

拿你正在用的 Claude Code 当例子会更具体:它本身就是这样一个宿主程序,内置了读文件、写文件、跑终端命令、搜索代码等一组工具,模型每次决定用哪个,都是从这份固定的清单里挑,而不是凭空发明一个新能力出来。5 这份清单从哪来、每一项具体长什么样,我们第 3 课会挨个过一遍。

不是所有任务都要配工具

看完上面的过程,容易走向另一个极端:既然工具这么好用,是不是干脆什么都给 Agent 配上工具?不是的。判断的标准很简单,问自己一句话就够了:这个任务需要的信息或动作,模型自己手上有吗?

有些任务模型自己就能搞定,不需要接触外部世界:

  • 把一段话改写得更简洁
  • 总结一段会议记录
  • 把一段 Python 代码翻译成同样逻辑的 JavaScript
  • 根据你描述的需求,直接写一段新代码(还没涉及读写你项目里已有的文件)

这些任务用到的知识,模型在训练时已经见过足够多类似的例子,凭语言能力就能完成。给这类任务硬塞一个工具,模型该调用还得先判断"这次要不要调用",多一次判断就多一次可能选错的机会,纯粹是浪费。

另一些任务,模型无论多聪明都做不到,因为它缺的不是能力,是信息

  • "我项目里现在有几处 console.log"——模型的知识停在训练那一刻,它对你此刻磁盘上的文件内容一无所知
  • "刚才那条命令的输出是什么"——命令还没跑,输出根本不存在,模型不可能预先知道
  • "这个接口现在返回的数据长什么样"——那是这一刻服务器给出的响应,跟模型训练时见过的示例没有任何关系

这一类任务,不管你把 prompt 写得多细致、多有诱导性,模型都变不出真实答案,因为它手上压根没有这份数据。唯一的办法是给它一条通道,让宿主程序替它去拿——这就是工具存在的理由。

这五类工具具体是什么样子——读、写、执行命令、搜索、调用外部服务——我们放到第 3 课挨个拆开讲;这一课你只需要记住"要不要配工具"这道判断题怎么问。

小结

  • 工具(tool)是宿主程序暴露给模型的一份可调用能力清单,每一项至少写清楚 namedescriptioninput_schema 三样东西,模型据此判断该不该调用、传什么参数。1
  • **模型只提议,从不亲自执行。**它把"调用哪个工具、传什么参数"打包成结构化数据交还给宿主程序,真正打开文件、跑命令、发请求的是宿主程序自己的代码。32
  • 结果要经过一次来回:宿主执行完之后把结果重新塞回对话,模型看到真实结果,才写出最终回复——这一步的完整往返,下一课细讲。
  • 判断要不要给一个任务配工具,问一句话就够:**这个任务需要的信息或动作,模型自己手上有吗?**没有,才需要工具;有,配了反而是浪费。
  • 同一句提问,有没有工具,结果可能天差地别——没有工具时 Agent 只能靠训练时见过的通用知识给你讲讲思路,有了工具它才能真正接触到你项目此刻的样子。

>> 第 2 课:一次工具调用的完整往返

Footnotes

  1. Define tools — Claude API — https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools 2 3

  2. How tool use works — Claude API — https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works 2 3

  3. Function calling — OpenAI API Guides — https://developers.openai.com/api/docs/guides/function-calling 2

  4. Tool use with Claude — Overview — https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview

  5. Tools reference — Claude Code Docs — https://code.claude.com/docs/en/tools-reference

练习

01

下面六个任务,判断每一个是否需要给 Agent 配工具才能真正完成,并说明理由(提示:问自己"模型手上有没有这份信息")。

Level 1:给六个任务分类
  1. "帮我把这段英文邮件翻译成中文,语气客气一点"
  2. "帮我看看昨晚 3 点那次部署的日志里有没有报错"
  3. "帮我写一个校验邮箱格式的正则表达式"
  4. "帮我看看这个仓库里 package.json 里依赖的 React 版本是多少"
  5. "帮我把这份需求文档拆成 5 条验收标准"
  6. "帮我调用一下天气接口,看看北京明天会不会下雨"
完成标准 · 本地勾选
02

任务是:"让 Agent 能查到某个 npm 包在 npmjs.com 上最新发布的版本号。" 参照本课里 search_files 那份工具定义的写法,给这个能力写一份工具定义草稿,至少包含 name、description、input_schema 三个字段。不要求写出合法可运行的 JSON Schema,把三个字段的内容想清楚、大致的格式对就行。

Level 2:给一个任务草拟工具定义
完成标准 · 本地勾选

我的笔记

记下想法、痛点、没懂的地方。只写进这门课的附录,正课文件不动。