发布时间: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 篇(第二季 · 工程实现)。





