【Agent 从零到一】S02:Tool Calling —— 极简代码打通 Agent 工具调用核心能力

发布时间:2026/6/3 1:32:43

【Agent 从零到一】S02:Tool Calling —— 极简代码打通 Agent 工具调用核心能力 在上一篇《不到 30 行代码写一个完整的 Agent》中我们拆解了 Agent 最核心的本质一个 while 循环 一个停止条件。这个极简的循环是所有 Agent 的基石无论多么复杂的智能体最终都跑在这个无限循环之上。但 s01 的 Agent 有一个致命的缺陷它只有一个 bash 工具。读文件要拼 cat写文件要拼 echo改文件要拼 sed。模型明明想的是 “读这个文件”却要先翻译成 shell 命令多了一层不必要的转换。今天我们来看 s02——整个系列中最具启发性的一章。它用一行代码的改动解决了 Agent 工具扩展的核心问题并且提出了一个可以泛化到所有 Agent 系统的设计范式。一、s01 的 “原罪”单一工具的局限性s01 的 Agent 虽然能用但本质上是一个 “会写 shell 脚本的模型”。所有能力都建立在 bash 之上这带来了三个无法回避的问题翻译开销与错误率模型需要将自然语言意图翻译成 shell 语法这不仅浪费 token还极易出错。比如处理包含特殊字符的文件名、多行内容时sed 和 echo 经常会出现意想不到的行为。完全失控的安全面bash 没有任何沙箱限制。一句rm -rf /就能摧毁整个系统Agent 在执行复杂任务时很容易误生成危险命令。能力边界模糊模型不知道自己能做什么、不能做什么。它可能会尝试调用不存在的命令或者用错误的参数执行命令导致大量无效的工具调用。关键问题来了我们需要为每个新能力都修改 Agent 的核心循环吗s02 给出了一个响亮的答案不需要。二、s02 的核心突破工具分发机制s02 对 s01 的代码改动只有一行。s01 的硬编码工具调用# s01: 硬编码只能调用bashforblockinresponse.content:ifblock.typetool_use:outputrun_bash(block.input[command])# 这里写死了run_bashresults.append({type:tool_result,tool_use_id:block.id,content:output,})s02 的查表式工具调用# s02: 用字典查表分发TOOL_HANDLERS{bash:run_bash,read_file:run_read,write_file:run_write,edit_file:run_edit,glob:run_glob,}forblockinresponse.content:ifblock.typetool_use:handlerTOOL_HANDLERS[block.name]# 一行改动查表outputhandler(**block.input)# 通用调用results.append(...)就是这一行改动彻底改变了 Agent 的扩展性。现在给 Agent 加一个新工具只需要做两件事在TOOLS数组中添加工具的 JSON Schema告诉模型 “我能做什么”在TOOL_HANDLERS字典中添加一个映射告诉系统 “怎么做”核心循环一行都不用改。这就是 s02 最伟大的洞察Agent 的核心循环应该与具体工具实现完全解耦。循环只负责消息流转和停止判断工具能力通过可插拔的方式注入。三、泛化通用工具系统的设计原则s02 的设计绝不是 Claude Code 特有的技巧而是一个可以应用到所有 Agent 系统的通用架构范式。我们可以从中提炼出四个核心设计原则1. 核心循环与工具实现的完全解耦这是整个架构的基石。核心循环只关心三件事把消息发给 LLM检查是否需要停止处理工具调用并返回结果它不应该知道任何具体工具的存在也不应该包含任何工具相关的业务逻辑。反模式在循环里写满if tool_name read_file: ... elif tool_name write_file: ...的条件判断。每加一个工具就要改循环代码很快就会变成无法维护的面条代码。2. 声明式定义与命令式实现的分离声明式定义Schema告诉模型工具的名称、描述、参数格式。这是模型和工具之间的接口契约。命令式实现Handler工具的具体执行逻辑。这是系统和真实世界之间的连接。这种分离带来了巨大的灵活性你可以修改工具的实现而不需要告诉模型你可以给同一个工具提供不同的实现比如本地文件系统 vs 云存储你可以轻松地为不同的模型生成不同格式的 Schema3. 统一的分发层作为横切关注点的入口所有工具调用都经过同一个分发点这让我们可以在不修改任何工具代码的情况下添加全局的横切关注点权限检查这个工具允许被调用吗s03 的核心内容日志与审计记录所有工具调用的参数和结果监控与指标统计工具调用的成功率、耗时重试与熔断处理工具调用失败的情况输入验证统一校验工具参数的合法性Claude Code 的工具调用实际上经过了一个完整的 5 步验证管线Schema验证 → 工具级输入验证 → PreToolUse钩子 → 权限检查 → 执行所有这些逻辑都在分发层完成工具本身只需要关心自己的业务逻辑。4. 多工具调用的编排能力现代 LLM 几乎都支持一次返回多个工具调用。比如模型可能会说“先读 a.py 和 b.py然后列出所有 .py 文件”。s02 的教学版采用了最简单的顺序执行策略按照模型返回的顺序逐个执行。而 Claude Code 的生产级实现则更加智能用isConcurrencySafe(input)判断每个工具调用是否可以并发执行将工具调用按连续块分区并发安全的块内并行执行非并发安全的块串行执行保证操作顺序输入: [read A, read B, glob *.py, bash rm x, read C] 输出: batch1(并发): [read A, read B, glob *.py] batch2(串行): [bash rm x] batch3(并发): [read C]这种策略在保证正确性的前提下最大化了工具调用的并发度显著提升了 Agent 的执行速度。四、关键实现细节解析1. 路径沙箱安全的第一道防线s02 引入了safe_path()函数这是所有文件操作工具的基础defsafe_path(p:str)-Path:path(WORKDIR/p).resolve()ifnotpath.is_relative_to(WORKDIR):raiseValueError(fPath escapes workspace:{p})returnpath这个简单的函数确保了 Agent 只能操作指定工作区内的文件防止了路径遍历攻击。2. 工具粒度的选择s02 没有设计一个万能的 “文件操作” 工具而是拆成了read_file、write_file、edit_file、glob四个独立的工具。这是一个非常重要的设计决策细粒度工具模型更容易理解和正确使用参数更简单错误率更低粗粒度工具减少工具数量但模型需要更复杂的参数来表达意图Claude Code 的经验是优先选择细粒度的专用工具。模型使用edit_file(old_text, new_text)的准确率远高于使用一个通用的modify_file(content)工具。3. 工具结果的大小限制Claude Code 的每个工具都有一个maxResultSizeChars字段。当工具返回的结果超过这个阈值时结果会被写入临时文件模型只会看到一个预览和文件路径。这是一个非常关键的细节它防止了大文件内容撑爆 LLM 的上下文窗口。特别需要注意的是read_file工具的这个值应该设为Infinity否则会出现 “读文件→结果落盘→再读落盘文件→再落盘” 的无限循环。五、进阶思考工具系统的设计权衡专用工具 vs 通用 bash一个永恒的争论是我们应该提供尽可能多的专用工具还是保留一个万能的 bash 工具答案是两者都需要。专用工具安全、可靠、模型使用准确率高应该覆盖 90% 的常见场景bash 工具灵活、强大可以处理专用工具覆盖不到的边缘情况Claude Code 的做法是保留 bash 工具但对其进行严格的权限控制。危险命令需要用户批准并且默认禁止执行交互式命令。工具的幂等性设计理想情况下所有工具都应该是幂等的执行多次和执行一次的效果相同。比如 s02 的edit_file工具只替换一次匹配的文本text.replace(old_text,new_text,1)# 注意最后的1这就是为了防止因为网络重试或者模型重复调用导致文件被错误地修改多次。流式工具执行Claude Code 还有一个非常酷的特性流式工具执行。它不需要等模型把整个响应输出完只要工具调用的参数完整了就可以立即开始执行工具。这意味着当模型还在输出 “我来分析一下这个文件的内容…” 的时候read_file工具可能已经跑完了。这种 “边生成边执行” 的模式可以显著减少用户的等待时间。六、总结与预告s02 用一行代码的改动为我们展示了 Agent 架构的极简之美。它的核心贡献不是增加了几个文件工具而是提出了“循环不变工具可插拔”的设计范式。s02 的公式可扩展的Agent 不变的核心循环 可插拔的工具分发字典这个公式的威力在于它的无限扩展性。无论你想给 Agent 加什么能力 —— 调用 API、访问数据库、控制硬件、发送邮件 —— 你都只需要写一个处理函数加一个 Schema 定义在字典里加一行映射核心循环永远不变。现在我们的 Agent 有了 5 个安全的专用工具可以可靠地操作文件系统。但还有一个问题没有解决我们怎么知道 Agent 要做的操作是安全的它会不会删除重要文件会不会修改生产环境的配置这就是 s03 要解决的问题权限控制。我们将在工具执行之前加一道门让 Agent 在执行危险操作之前必须获得用户的批准。附录s01 → s02 变更速查表组件s01s02工具数量1 (仅 bash)5 (read, write, edit, glob)工具执行硬编码run_bash()TOOL_HANDLERS字典查表分发安全机制无safe_path()路径沙箱核心循环while True stop_reason完全不变扩展方式修改循环代码加 handler 加 Schema

相关新闻