AI 只会聊天不够:Tool Use 如何让 Agent 真正「动手」
Tool Use(工具调用)— Agent 在 Loop 的 Act 阶段调用外部能力(读文件、执行命令、查 API 等),把「说」变成「做」,并把真实结果送回 Observe。
概述
前三篇我们分别聊了 Agent Loop、Harness 和 Context Engineering:Loop 决定怎么一步步做事,Harness 决定能不能做、做完怎么办,Context 决定每一轮 Plan 站在什么信息起点上。
但有一个问题还没展开:Agent 的「行动」究竟是怎么发生的?
纯文本模型只能生成文字。没有 Tool Use,它无法读取你的代码库、无法运行测试、无法确认改动是否生效——只能凭训练数据「猜」。Tool Use 正是打破这一限制的关键机制:让 LLM 在推理过程中主动选择并调用外部工具,拿到真实反馈,再进入下一轮 Loop。
可以把 Tool Use 理解为 Agent 的「双手」:Harness 提供调度与权限,Context 提供决策依据,Tool Use 则是连接数字世界与真实环境的桥梁。
没有 Tool Use 时,Agent 缺什么
上一篇提到的「引用而非内联」——保留文件路径、需要时再读取——也依赖 Tool Use:Agent 必须能调用 read_file 一类工具,才能把引用变成 fresh 的 Observe 输入。
Tool Use 在 Loop 中的位置
Tool Use 几乎完全对应 Agent Loop 的 Act 阶段,并由 Harness 的 工具层 负责落地执行:

一次典型的 Tool Use 往返:
LLM 输出工具调用请求 — 指定工具名与参数(非自然语言「帮我读一下那个文件」) Harness 策略校验 — 该工具是否在白名单?参数是否越权? 工具层执行 — 调用真实实现,捕获输出或错误 结果结构化回传 — 作为 Observe 注入下一轮 Context LLM 继续推理 — 基于真实结果 Plan 下一步,或结束任务
Harness 在这里的角色至关重要:LLM 只负责「想调用什么」,能不能调、怎么调、调坏了怎么办,都由 Harness 兜底。
Function Calling 的基本流程
Function Calling(函数调用)是 Tool Use 最常见的实现方式——模型不直接执行代码,而是输出一段结构化请求,由运行时替它执行。
用户:修复 login.ts 的空指针问题Loop 1 — LLM 输出: tool: search_code args: { "query": "login", "path": "src/auth" }Harness 执行 → 返回: { "matches": ["src/auth/login.ts", "src/auth/session.ts"] }Loop 2 — LLM 输出: tool: read_file args: { "path": "src/auth/login.ts", "start_line": 40, "end_line": 60 }Harness 执行 → 返回文件片段Loop 3 — LLM 输出: tool: edit_file args: { "path": "...", "patch": "..." }Harness 执行 → 返回 { "success": true }Loop 4 — LLM 输出: tool: run_command args: { "command": "npm test -- login" }Harness 执行 → 返回测试报告注意几个特征:
模型与执行分离:LLM 生成调用意图,Harness 负责执行——模型本身没有 shell 权限 多轮交替:一次任务往往连续多次 Tool Use,而非一次调用搞定 错误也是有效 Observe: file not found、测试失败、权限拒绝,都应原样回传,供 Reflect 调整策略
工具描述(Schema)的设计
LLM 怎么知道有哪些工具、每个工具干什么?靠 Tool Schema(工具描述)——通常在任务启动时注入 Context,格式类似 JSON Schema:
{"name":"read_file","description":"读取指定路径的文件内容,支持行号范围","parameters":{"type":"object","properties":{"path":{"type":"string","description":"相对于项目根目录的文件路径"},"start_line":{"type":"integer","description":"起始行号,可选"},"end_line":{"type":"integer","description":"结束行号,可选"}},"required":["path"]}}Schema 设计直接影响 Tool Use 的可靠性:
| name | read_file、run_tests | do_stuff、helper |
| description | ||
| parameters | ||
| 粒度 | ||
| 数量 |
description 是最被低估的字段。 模型选错工具,多半不是因为「笨」,而是因为两个工具的描述重叠、或缺少「何时不用我」的说明。
编程场景常见工具族:
感知类: read_file、list_dir、search_code、git_diff变更类: edit_file、write_file、apply_patch验证类: run_command、run_tests、http_request协作类: ask_user(请求人工确认或补充信息)
变更类与验证类应成对出现——Harness 策略层可以鼓励「改完必测」,避免 Agent 只写不验。
工具选择的可靠性问题
Tool Use 落地后,最常见的痛点不是「工具能不能跑」,而是 Agent 会不会用、用对不对。
1. 选错工具
表现:该搜索代码却去读 README;该跑测试却手动 curl。原因:工具描述重叠;可用工具过多;Plan 阶段上下文不足。应对:精简工具集;在 description 中写清使用场景;Context 里注入当前步骤需要的工具提示。
2. 参数幻觉
表现:文件路径不存在、命令拼错、把相对路径写成绝对路径。原因:模型未先 Observe 目录结构就猜测路径。应对:优先 list_dir / search_code;Harness 对路径做存在性校验,失败信息明确回传。
3. 重复调用
表现:同一文件读五遍、同一失败命令反复执行。原因:Observe 结果未进入有效 Context;Reflect 缺少重复检测。应对:Harness 记录调用历史;重复时强制换策略(参见 [[02-给 AI 套上「缰绳」:Harness 如何让编程 Agent 不失控]])。
4. 跳过工具
表现:模型直接在回复里贴「修改后的代码」,不调用 edit_file。原因:Chat 习惯未打破;未强制要求通过工具完成变更。应对:Harness 策略层规定「代码变更必须经工具落地」;纯文本输出仅用于解释,不视为 Act 完成。
5. 工具链断裂
表现:改了代码却不跑测试,或测试失败就宣布完成。原因:缺少验证步骤约束;工具集没有配套的验证工具。应对:任务模板或 Skill 中定义标准工具链:read → edit → test → confirm。
一次任务的 Tool Use 轨迹
延续前几篇的登录修复示例,Tool Use 视角下的轨迹如下:
Loop 1 search_code("login") → 定位候选文件Loop 2 read_file(login.ts:40-60) → 发现空值风险Loop 3 edit_file(patch) → 添加空值检查Loop 4 write_file(test) → 补充单元测试Loop 5 run_command(npm test) → 全部通过 → 任务结束对比纯 Chat:Chat 可能在 Loop 0 就输出「你应该在第 47 行加 if (user)」——但无法确认第 47 行现在是什么、改完测试是否通过。Tool Use 让每一步都有 可 Observe 的证据。
常见反模式
1. 工具箱堆砌
注册大量工具「以防万一」,包括 Agent 当前任务根本用不到的。
后果:选择困难、误调用增多、Schema 占用 Context Token。应对:按任务类型动态加载工具子集;Harness 支持工具分组与按需启用。
2. Schema 即文档
把复杂业务逻辑全塞进 description,参数列表冗长。
后果:模型抓不住重点,关键约束被淹没。应对:description 保持简洁;复杂流程放到 Skill 或 Rule 静态上下文中。
3. 无结构化返回
工具返回一大段非结构化日志,原样塞进 Context。
后果:Context 洪水,关键错误信息被稀释(参见 [[03-上下文才是隐藏 Boss:AI 编程效率,多半取决于你喂了什么]])。应对:工具层对输出做截断与结构化;只回传与决策相关的字段。
4. 信任模型自检
不跑测试,仅凭模型说「应该没问题了」就结束 Loop。
后果:隐蔽 bug 流入代码库。应对:Harness 终止条件要求验证类 Tool Use 成功,而非 LLM 的自我声明。
5. 裸工具无 Harness
工具直接暴露给 LLM,无权限控制、无超时、无审计。
后果:误删文件、命令注入、不可追溯。应对:所有 Tool Use 必须经 Harness 工具层与策略层。
实践要点
工具要少而精:完成当前任务所需的最小集合,比「全能工具箱」更可靠 description 写清楚边界:不只说「能做什么」,还要说「什么时候不该用我」 参数可校验:Harness 在执行前校验类型、路径、权限,失败信息要 actionable 返回结构化的 Observe:截断、摘要、标注成功/失败,方便下一轮 Plan 变更必验证:编辑类工具之后,策略层引导或强制调用验证类工具 记录调用轨迹:工具名、参数、返回值、耗时——调试 Agent 选错工具的第一手证据 与 Context 配合:工具结果进入 Context 前做裁剪;Stale 的文件内容在 re-read 后替换旧 Observe
Tool Use 解决的是 Agent 「能不能动手」的问题。下一篇我们将看 MCP(Model Context Protocol)——当工具不再局限于内置的几个,而是需要连接数据库、浏览器、Slack、Figma 等外部系统时,如何用标准协议统一接入。
本文是 AI Agent 系列的第四篇,前序文章:
[01] Agent Loop:AI 如何像人一样一步步思考 [02] Harness:给 AI 套上「缰绳」 [03] Context Engineering:上下文才是隐藏 Boss
夜雨聆风