9.3.4 工具调用策略

上一节你已经知道怎样把工具安全地接到模型后面。 这一节要继续往前走一步:
工具不是接上就行,关键是怎么用。
真正拉开系统质量差距的,往往不是“有没有工具”,而是“什么时候调、先调哪个、失败后怎么办”。
学习目标
- 理解工具调用策略为什么是 Agent 成败关键之一
- 分清“不调用 / 单次调用 / 多步调用 / 回退策略”几种常见模式
- 学会设计基本的路由、重试和验证逻辑
- 看懂一个更完整的工具策略示例
为什么“有工具”不等于“会用工具”?
一个常见误解
很多人做 Agent 的第一步是:
- 接上搜索工具
- 接上计算器
- 接上数据库
然后就觉得系统会变强。
但现实里经常出现:
- 不该调工具时乱调
- 该调工具时反而不调
- 调错工具
- 一件事连着调 5 次还停不下来
所以真正的问题不是:
系统有没有工具
而是:
系统有没有“使用工具的策略”。
一个生活类比
你家厨房有刀、锅、烤箱、微波炉,不代表你就会做菜。 关键在于:
- 什么时候切
- 什么时候煮
- 什么时候烤
- 哪一步出错了怎么补救
Agent 的工具调用策略也是一样。
先把几种常见策略分清楚
不调用工具
适合:
- 常识性解释
- 简单改写
- 文风转换
例如:
“把这段话改得更正式一点”
这种任务通常不需要外部工具。
单次调用
适合:
- 查天气
- 算数学式
- 查一条知识库记录
这是最简单也最稳定的调用方式。
多步调用
适合:
- 先查订单,再查退款规则,再给结论
- 先搜资料,再总结,再生成输出
这时策略不再只是“调用哪个工具”,而是“下一步还要不要继续调”。
回退与兜底
如果:
- 主工具失败
- 结果不可信
- 参数校验不过
系统就要决定:
- 重试
- 换工具
- 让用户补充信息
- 直接承认无法完成
这也是工具策略的重要部分。
工具调用前要先判断什么?
这件事真的需要工具吗?
并不是所有问题都值得走工具链。 每次调用工具都会增加:
- 延迟
- 成本
- 失败路径
所以第一步常常是:
先判断需不需要调用工具。
如果需要工具,该选哪个?
例如用户问:
“我这个订单还能退款吗?”
可能需要:
- 查订单状态
- 查退款政策
所以工具选择不总是“单选题”,有时是“有顺序的组合题”。
参数是否足够?
有些问题即使知道该调哪个工具,也可能参数还不够。
例如:
“帮我查天气”
缺城市名。 这时最合理的策略不是乱猜,而是:
先向用户追问。
工具调用后还要判断什么?
结果是否可信?
工具返回了,不代表就可以直接用。
比如:
- 接口超时后返回空值
- 搜索结果相关性不高
- 数据库查不到记录
是否需要继续下一步?
有些任务一次调用拿不到最终答案。
例如:
- 先查知识库
- 再做计算
- 再汇总成用户能读懂的话
所以工具策略本质上经常是:
调用 -> 观察 -> 再决定下一步
一个最小但有教学意义的策略示例
下面这个例子会区分三种情况:
- 不调工具
- 调单个工具
- 参数不足时先追问
def route_query(query):
if "总结" in query or "改写" in query:
return {"action": "no_tool", "reason": "纯文本任务"}
if "天气" in query:
if "北京" in query:
return {"action": "tool", "tool": "weather", "arguments": {"city": "北京"}}
return {"action": "ask_user", "question": "你想查哪个城市的天气?"}
if "计算" in query:
expression = query.replace("计算", "").strip()
return {"action": "tool", "tool": "calculator", "arguments": {"expression": expression}}
return {"action": "fallback", "reason": "当前没有合适策略"}
queries = [
"把这段话总结一下",
"北京天气怎么样",
"帮我查天气",
"计算 12 * 7"
]
for q in queries:
print(q, "->", route_query(q))
预期输出:
把这段话总结一下 -> {'action': 'no_tool', 'reason': '纯文本任务'}
北京天气怎么样 -> {'action': 'tool', 'tool': 'weather', 'arguments': {'city': '北京'}}
帮我查天气 -> {'action': 'ask_user', 'question': '你想查哪个城市的天气?'}
计算 12 * 7 -> {'action': 'tool', 'tool': 'calculator', 'arguments': {'expression': '12 * 7'}}
这个例子虽然简单,但已经体现出“策略”这个层次了:
- 不是所有输入都交给工具
- 不是一缺参数就硬猜
- 不知道怎么办时有 fallback
一个更完整的策略闭环
定义几个工具
import ast
import operator
OPS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
}
def safe_calculate(expression):
def visit(node):
if isinstance(node, ast.Expression):
return visit(node.body)
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.BinOp) and type(node.op) in OPS:
return OPS[type(node.op)](visit(node.left), visit(node.right))
if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
return -visit(node.operand)
raise ValueError("unsupported_expression")
return visit(ast.parse(expression, mode="eval"))
def get_weather(city):
return {"city": city, "temperature": 22, "condition": "sunny"}
def calculate(expression):
return {"result": safe_calculate(expression)}
调度 + 校验 + 执行
def execute_strategy(query):
decision = route_query(query)
if decision["action"] == "no_tool":
return {"type": "answer", "content": "这类任务更适合直接由模型生成文本结果。"}
if decision["action"] == "ask_user":
return {"type": "question", "content": decision["question"]}
if decision["action"] == "tool":
if decision["tool"] == "weather":
result = get_weather(**decision["arguments"])
return {"type": "tool_result", "content": result}
if decision["tool"] == "calculator":
result = calculate(**decision["arguments"])
return {"type": "tool_result", "content": result}
return {"type": "fallback", "content": "当前无法稳定处理这个请求。"}
for q in ["北京天气怎么样", "帮我查天气", "计算 9 + 8"]:
print(q, "->", execute_strategy(q))
预期输出:
北京天气怎么样 -> {'type': 'tool_result', 'content': {'city': '北京', 'temperature': 22, 'condition': 'sunny'}}
帮我查天气 -> {'type': 'question', 'content': '你想查哪个城市的天气?'}
计算 9 + 8 -> {'type': 'tool_result', 'content': {'result': 17}}

这段代码真正教的是:
工具调用策略不是一行
if,而是“判断 + 分流 + 执行 + 兜底”的链路设计。
真实系统里最常见的几种策略模式
Router 模式
先判断问题属于哪个工具或哪个子系统。
适合:
- 工具很多
- 任务边界明确
Verify 模式
工具调用后,不马上信结果,而是再做检查。
适合:
- 外部数据不稳定
- 工具失败率较高
Retry / Fallback 模式
先重试,再降级,再兜底。
适合:
- 外部 API 波动
- 线上服务不稳定
Plan-then-tool 模式
先规划,再决定工具顺序。
适合:
- 多步任务
- 多工具依赖
什么时候该“少调工具”?
这其实也是很重要的策略能力。
少调工具的典型场景
- 纯总结
- 纯改写
- 风格转换
- 已有上下文足够
为什么少调有时更好?
因为每增加一次工具调用,就增加一次:
- 时延
- 失败可能
- 状态管理成本
所以一个成熟系统不是“能调就调”,而是:
该省的时候就省。
初学者最常踩的坑
把工具调用策略理解成“路由规则”
路由只是其中一部分。 真正的策略还包括:
- 是否调用
- 是否追问
- 是否继续下一步
- 是否回退
调用失败后没有下一步
没有重试、没有 fallback、没有补问,这种系统线上会很脆。
每次都默认模型自己决定一切
实际工程里,很多策略应该由程序框架明确约束,而不是完全放给模型自由发挥。
小结
这一节最重要的不是知道“可以调哪些工具”,而是理解:
工具调用策略决定了 Agent 会不会在正确的时机、以正确的顺序、用正确的方式调用工具。
这往往比“多接几个工具”更影响系统质量。
练习
- 给本节示例再加一个
search_docs(keyword)工具,并扩展路由逻辑。 - 增加一个“如果工具执行报错,则 fallback 到人工确认”的分支。
- 想一想:如果用户问“帮我查天气并计算穿衣指数”,策略层应该怎样拆分这件事?
- 用自己的话解释:为什么说工具调用策略是 Agent 质量的分水岭之一?