发布时间:2026-07-12
标签:AI Agent|LLM|Tool Calling|Function Call|工程实现
系列导航
上一篇:AI Agent 工程实践(12):为什么很多 Multi-Agent 项目最后都失败了?
下一篇:AI Agent 工程实践(14):MCP——为什么它正在成为 Agent 的 USB 接口?
本文是 [AI Agent 工程实践] 系列的第 13 篇(第二季 · 工程实现)。
Agent 最让人憋屈的时刻:它分析了一堆,然后告诉你"你应该改这个函数"——但你得自己去改。
它能认识问题,不能动手解决问题。像一个只出方案、不下工地的顾问。
让它能动起来的,只有一个东西:Tool。Tool 是 Agent 的手——没有它,再聪明的 Agent 也只是个聊天机器人。但很多人把 Tool Calling 要么想得太简单("在 prompt 里写个命令就行"),要么想得太复杂("需要一套完整的插件系统")。
这一篇讲清楚中间态:Tool Calling 到底怎么设计,从 Schema 到 Registry 到 Selection 到 Retry,让 Agent 不只是"说",而是"做"。
本文你将学到
✓ Agent 为什么必须会用工具——以及不用工具时能力的上限在哪
✓ Tool 的六层能力梯度:从 LLM 内嵌到 Shell/Database 外部系统
✓ Tool Calling 四大核心概念:Registry / Schema / Selection / Retry
✓ 一个可复用的 Tool Calling 设计模板——直接拿到项目里用
适合阅读
✓ 用 Function Call 做过 Agent、但觉得"调用不稳定"的人
✓ 在搭 Agent 的工具系统、不确定怎么组织和管理的人
✓ 被 Tool Calling "格式漂移"折磨过的开发者
问题背景
Agent 前几篇搭好了记忆(10)、工作流(11)、决策框架(12),但还缺一条腿:它怎么和外部世界交互。
没有 Tool 的 Agent,能力边界就是 LLM 的知识截止日期 + 上下文。它能写代码,但不能跑代码;能建议你查数据库,但不能自己查;能告诉你"你去搜一下",但不能自己搜。
这就是 Tool Calling 要解决的问题:让 LLM 从"说"变成"做"。但 Tool Calling 不是简单地在 prompt 里加一句"你可以调用以下函数"。它涉及四个实际的工程问题:
- Tool 怎么声明——LLM 怎么知道"有这个工具、参数是什么"
- Tool 怎么选择——有 50 个工具时,怎么选对的
- Tool 怎么调用——调用格式不稳定怎么办
- Tool 调用失败怎么办——重试?换工具?降级?
一句话:没有工具的 Agent 是顾问,有工具的 Agent 是工程师。而从顾问到工程师,差的不是一行 prompt,是一整套工具调用系统。
错误尝试
第一次:在 prompt 里手写工具调用
最早的"Tool Calling"就是一段 prompt:"当需要查数据库时,输出 SQL:SELECT * FROM ...,我会执行后把结果贴给你。"
结果:格式经常漂移——有时输出 SQL,有时直接输出解释,有时忘了加 ``。更致命的是,模型会"幻想"SQL 语法——它写了SELECT * FROM nonexistent_table`,我执行失败后它说"那你先建表"——没有约束的 Tool Calling 就是和模型玩文字游戏。
第二次:给每个工具硬编码调用逻辑
吸取教训,工具调用不和模型商量了——硬编码:if task == "db_query": run_sql()。
结果:灵活度归零。加一个新工具要改 Router 代码(第 05 篇的问题重现),换一个工具要改调用逻辑。硬编码的工具系统,和硬编码的规则系统一样脆弱——不是 Tool Calling,是 if-else 地狱。
两次尝试指向同一个结论:Tool Calling 需要的是"声明式管理 + 结构化调用",不是 prompt 里的自由文本,也不是代码里的硬编码。它需要一套独立的治理机制。
关键观察
我把"工具调用成功"和"失败"的案例做了对比,发现失败集中在四个环节:
| 失败环节 | 典型表现 | 占比 |
|---|---|---|
| Schema 不清晰 | 参数类型填错、必填字段遗漏 | ~30% |
| Selection 错误 | 有更合适的工具但没选到 | ~25% |
| 调用格式漂移 | 输出了工具描述而不是参数 JSON | ~25% |
| 失败无重试 | 一次调用失败就停了 | ~20% |
pie title Tool Calling 失败原因分布 "Schema 不清晰" : 30 "Selection 错误" : 25 "调用格式漂移" : 25 "失败无重试" : 20没有工具的 Agent 是顾问,有工具的 Agent 是工程师。
但更准确地说:有工具的 Agent 可能是工程师,也可能是一个乱用扳手的学徒——Tool Calling 需要治理,不只是"有"就行。
问题不在"要不要 Tool",而在"怎么管 Tool"——Schema 让 LLM 知道怎么用、Registry 统一管理能力清单、Selection 在多个工具里找到对的、Retry 在失败时兜底。这四个就是 Tool Calling 的治理四件套。
最终方案:Tool Calling 四件套 + 六层能力梯度
Tool 的六层能力梯度
不是所有 Tool 都在同一层级。从模型内部到外部系统,能力是逐级梯度扩展的:
越往右,Tool 离 LLM 越远,风险越大:LLM 内嵌推理零风险,Shell 命令可能删库。Tool 设计的第一原则:危险性越高的 Tool,越需要 Schema 约束和人工审批。
四件套:Registry / Schema / Selection / Retry
1. Tool Registry —— 统一能力清单
Registry 是工具注册中心——所有可用工具在这里声明。它不是代码里的字典,是可被 LLM 读取的声明文件:
# tool-registry.yaml browser_search: schema: search.yaml # 工具 Schema 引用 risk_level: low db_query: schema: query.yaml risk_level: medium retry: 3 # 最大重试次数 shell_exec: schema: shell.yaml risk_level: high requires_approval: true # 高危工具需审批Registry 的作用:让加工具不需要改代码。新工具加一个 yaml 就上线,和第 05 篇 Rule Router 的"加 heavy 文件"同一种设计哲学。
2. Tool Schema —— 契约式声明
每个 Tool 必须有 JSON Schema,定义名称、描述、参数类型、约束条件。LLM 不认识"你的代码",但一定认识 Schema:
{ "name": "db_query", "description": "执行一个只读 SQL 查询。仅支持 SELECT。", "parameters": { "type": "object", "properties": { "sql": { "type": "string", "description": "要执行的 SELECT 语句", "pattern": "^SELECT.*$" // 只允许 SELECT }, "database": { "type": "string", "enum": ["users", "orders"] // 限定可查的库 } }, "required": ["sql"] } }Schema 是 Tool Calling 的契约——LLM 按契约填参数,系统按契约校验。没有 Schema 的 Tool,等于没有接口文档的 API。
3. Tool Selection —— 在多个工具里找对的
50 个工具时,"该用哪个"不是 LLM 靠直觉判断的——需要 Selection 机制:
- 按任务类型路由(和第 05 篇 Router 同构):
task_type=db→ 只暴露 db 相关工具 - 按危险等级过滤:普通任务只露出
risk_level ≤ medium的工具 - 按上下文裁剪:当前对话里用过的工具提权,没出现过的不建议
Selection 不是让 LLM 在海量工具里"猜",而是先缩小候选集,再让 LLM 精准选择——和第 10 篇 Memory 的"先过滤再检索"同一个模式。
4. Tool Retry —— 调用失败后的兜底
Tool Calling 的失败不是"要不要重试"的问题,是"怎么重试更聪明":
| 失败类型 | 策略 |
|---|---|
| 参数格式错误 | 重试 1 次,纠正参数 |
| 工具不存在 | 不重试,找替代工具 |
| 超时 | 重试 3 次,指数退避(1s/2s/4s) |
| 高危工具失败 | 不重试,转人工审批 |
def call_with_retry(tool, args, max_retries=3): for attempt in range(max_retries): try: return tool.run(args) except SchemaError: # 参数问题 → 纠正重试 args = correct_args(args) except ToolNotFound: # 工具不存在 → 找替代 tool = find_alternative(tool) except TimeoutError: # 超时 → 退避重试 sleep(2 ** attempt) return fallback() # 全失败 → 降级Retry 不只是"再来一次",它是"根据失败原因换策略"——Schema 错就修参数,工具不存在就换工具,超时就等一等。这是一种带上下文的恢复机制。
架构图 / 流程图
Tool Calling 完整链路
关键点:LLM 不直接访问工具——它通过 Schema 描述工具、通过 Selection 筛选工具、通过 Registry 获取工具、通过 Retry 恢复工具。每一层都是"让 LLM 和工具之间保持安全距离"的防火墙。
代码或配置示例
完整工具声明(Schema + Registry)
# tools/browser_search.yaml name: browser_search description: "使用 Tavily Search API 搜索互联网" parameters: query: type: string description: "搜索关键词" required: true max_results: type: integer default: 5 risk_level: low retry: max: 3 strategy: exponential_backoff# tool-registry.yaml — 统一注册 tools: browser_search: schema: browser_search.yaml risk: low db_query: schema: db_query.yaml risk: medium retry: 3 shell_exec: schema: shell_exec.yaml risk: high requires_approval: true # 高危必审批Tool Calling 入口逻辑
def tool_call(task, registry, llm): # 1. LLM 决策:需要哪个工具、什么参数 tool_name, args = llm.decide(task, tools=registry.list_schemas()) # 2. Selection:校验合法性(任务匹配 + 安全等级) if not registry.is_allowed(tool_name, task): return fallback("Tool not allowed for this task") # 3. 从 Registry 拿到工具实例 tool = registry.get(tool_name) # 4. 执行 + Retry return call_with_retry(tool, args)代码不长,但四个概念全在里面:LLM 读 Schema 选工具 → Selection 做合法性校验 → Registry 管理能力 → Retry 兜底执行。
设计权衡
| 候选方案 | 优点 | 缺点 | 为什么不选 |
|---|---|---|---|
| Prompt 手写工具调用 | 零工程 | 格式漂移、无约束、不可靠 | Tool Calling 不是文字游戏 |
| 硬编码工具选择 | 稳定 | 不灵活、无法动态扩展 | 每加工具改代码,不可持续 |
| 声明式四件套 | 可扩展、有约束、可恢复 | 需维护 Schema | 选择理由:唯一把 Tool Calling 从"碰运气"变成"可治理"的方案 |
Tool Calling 不是越复杂越好。如果你的 Agent 只用一个工具(比如只查数据库),一个直接调用就够了,不需要 Registry。四件套的价值在工具多、风险分层、需要动态扩展时体现。
总结
✅ Agent 没有 Tool 只是顾问——能做分析,不能做事。Tool 是 Agent 的手。
✅ 六层能力梯度:LLM 内嵌 → Function Call → Python → Browser → Shell → Database,越往右离 LLM 越远、风险越大。
✅ Tool Calling 四件套:Schema(契约声明)/ Registry(统一管理)/ Selection(安全裁剪)/ Retry(分类兜底)。
✅ 核心设计原则:LLM 不直接访问工具——通过 Schema 描述、Selection 过滤、Registry 获取、Retry 恢复。
✅ 工具少就别上四件套——一个直接调用就够。工具多、风险分层时才值得。
参考资料
- OpenAI Function Calling 官方文档→ Tool Schema 的标准格式与参数约束规范
- Anthropic — Tool Use 文档→ 声明式 Tool Calling 的设计哲学与安全模型
- LangChain — Tool 抽象与 Callbacks→ Registry 与 Retry 的工程参考实现
- 第 05 篇:Rule Router→ 任务路由思想,Tool Selection 的同构设计
- 第 10 篇:Memory 架构→ "先过滤再检索"模式,Tool Calling"先 Selection 再执行"的同源设计
系列导航
上一篇:AI Agent 工程实践(12):为什么很多 Multi-Agent 项目最后都失败了?
下一篇:AI Agent 工程实践(14):MCP——为什么它正在成为 Agent 的 USB 接口?
本文是 [AI Agent 工程实践] 系列的第 13 篇(第二季 · 工程实现)。