018-Tool-Calling-Deep-Practice
Tool Calling 深度实践:四种模式与并行调用的完整指南
💡摘要:深入掌握 Tool Calling 的
tool_choice四种模式(auto/any/具体工具名/disabled)、.bind_tools()绑定语法和parallel_tool_call并行调用机制,让你的 Agent 更高效、更可控地调用外部工具。
引言
在上一篇 Function Calling 的文章中,我们学习了如何让模型调用外部工具。但实际使用中,你会发现还有更多控制需求:
- 有时候希望模型自己判断是否调用工具
- 有时候需要强制模型必须调用某个特定工具
- 有时候模型需要同时调用多个工具(比如同时查两个城市的天气)
- 有时候只想纯聊天,完全禁用工具调用
如何精细控制模型的工具调用行为?答案就是tool_choice参数和parallel_tool_call机制。
本文将深入讲解 Tool Calling 的四种模式、并行调用机制和最佳实践,让你对模型的工具调用拥有完全的控制力。
核心概念
tool_choice 是什么?
tool_choice是一个控制模型是否调用工具、调用哪些工具的参数。你可以把它理解为“工具调用开关+方向”。
类比:就像汽车的驾驶模式:
- Auto(自动模式):车辆自己判断用运动模式还是经济模式
- Any(强制调用):必须使用至少一个档位
- 具体工具名(手动指定):强制用三档
- Disabled(关闭):纯手动驾驶,不用任何辅助
四种模式对比
| 模式 | 值 | 行为 | 适用场景 |
|---|---|---|---|
| Auto | "auto" | 模型自主决定是否调用工具 | 通用场景,最常用 |
| Any | "any" | 模型必须调用至少一个工具 | 调试、确保工具被使用 |
| 具体工具名 | {"type": "function", "function": {"name": "xxx"}} | 模型必须调用指定工具 | 强制使用特定功能 |
| Disabled | "none" | 模型不调用任何工具 | 纯对话,禁用工具 |
原理深入
.bind_tools() 绑定语法
在使用tool_choice之前,需要先用.bind_tools()将工具绑定到模型上:
fromlangchain_openaiimportChatOpenAIfromlangchain.toolsimporttool# 定义工具@tooldefget_weather(location:str,unit:str="celsius")->str:"""查询指定城市的天气信息"""returnf"{location}天气:晴,22°C"@tooldefget_time(location:str)->str:"""获取指定位置的当前时间"""fromdatetimeimportdatetimereturnf"{location}时间:{datetime.now().strftime('%H:%M')}"# 初始化模型llm=ChatOpenAI(model="gpt-4o-mini",temperature=0)# 绑定工具llm_with_tools=llm.bind_tools([get_weather,get_time])💡bind_tools 的作用
.bind_tools()将工具的定义(名称、描述、参数)注入到模型中,使模型知道有哪些工具可用。但它不控制模型何时调用这些工具。
tool_choice 四种模式详解
1. Auto 模式(最常用)
让模型自主判断是否需要调用工具:
llm_with_tools=llm.bind_tools([get_weather,get_time],tool_choice="auto")# 模型会根据问题自主决定msg=llm_with_tools.invoke([{"role":"user","content":"北京今天天气怎么样?"}])# → 模型会调用 get_weather 工具msg=llm_with_tools.invoke([{"role":"user","content":"你好!"}])# → 模型直接回答,不调用工具2. Any 模式(强制工具调用)
强制模型必须调用至少一个工具,不允许直接回答:
llm_forced=llm.bind_tools([get_weather,get_time],tool_choice="any")# 即使用户说"你好",模型也必须调用工具msg=llm_forced.invoke([{"role":"user","content":"你好!"}])# → 模型会强制调用一个工具(可能是 get_weather 或 get_time)💡什么时候用 Any?
调试阶段非常有用。如果你想确认工具注册是否正确、模型能否识别工具,用 Any 模式可以确保模型一定会调用。
3. 具体工具名(强制指定工具)
强制模型调用某个特定工具:
注意:当模型处于思考模式(thinking mode)时,强制指定
tool_choice为具体函数或required会触发 API 400 错误,提示参数不支持,此时需要关闭模型的思考能力。
llm=ChatOpenAI(model="gpt-4o-mini",temperature=0,extra_body={"thinking":{"type":"disabled"}})llm_weather=llm.bind_tools([get_weather,get_time],tool_choice={"type":"function","function":{"name":"get_weather"}})# 模型必须调用 get_weathermsg=llm_weather.invoke([{"role":"user","content":"北京现在几点?"}])# → 即使问题问的是时间,模型也会调用 get_weather4. Disabled 模式(禁用工具)
完全禁用工具调用,即使绑定了工具也不会使用:
llm_plain=llm.bind_tools([get_weather,get_time],tool_choice="none")# 模型不会调用任何工具msg=llm_plain.invoke([{"role":"user","content":"北京今天天气怎么样?"}])# → 模型直接回答,因为它没有实时天气数据💡为什么绑定了工具又禁用?
有时候你在同一个 Agent 中需要根据场景切换。比如:闲聊时禁用工具,正式问答时启用。通过切换tool_choice即可实现。
代码示例
parallel_tool_call 并行调用
当用户的问题需要多个独立工具时,可以并行调用:
defexecute_tool(tool_name,arguments):iftool_name=="get_weather":returnget_weather.invoke(arguments)eliftool_name=="get_time":returnget_time.invoke(arguments)else:returnf"Unknown tool:{tool_name}"# 启用并行调用llm_parallel=llm.bind_tools([get_weather,get_time],parallel_tool_calls=True)msg=llm_parallel.invoke([{"role":"user","content":"查询北京的天气和上海的时间"}])# 模型会返回两个 Tool Call 请求# 1. get_weather(location="beijing")# 2. get_time(location="shanghai")# 你可以并发执行这两个工具调用tool_calls=msg.tool_calls results=[]fortcintool_calls:tool_name=tc["name"]id=tc["id"]arguments=tc["args"]result=execute_tool(tool_name,arguments)results.append(result)print(results)并行调用的前提条件:
- 工具之间没有依赖关系
- 工具调用的参数可以独立确定
- 模型判断需要同时使用多个工具
实战应用
场景 1:智能客服系统
在客服系统中,根据用户意图动态控制工具调用:
# 默认 Auto 模式llm_customer_service=llm.bind_tools([search_database,get_order_status,get_weather],tool_choice="auto")# 用户问天气,模型不调用数据库工具msg=llm_customer_service.invoke([{"role":"user","content":"今天天气怎么样?"}])# → 调用 get_weather# 用户查询订单msg=llm_customer_service.invoke([{"role":"user","content":"我的订单 #12345 状态如何?"}])# → 调用 get_order_status场景 2:调试工具注册
在开发阶段,用 Any 模式确认工具是否正确注册:
# 调试模式:强制调用llm_debug=llm.bind_tools([get_weather,get_time],tool_choice="any")msg=llm_debug.invoke([{"role":"user","content":"测试工具"}])# 检查是否返回了 Tool Callif'tool_calls'inmsg:print("✅ 工具注册成功!模型能识别并调用工具。")else:print("❌ 工具可能未正确注册。")场景 3:多城市天气查询
利用并行调用一次性查询多个城市:
llm_parallel=llm.bind_tools([get_weather,get_time],parallel_tool_calls=True)msg=llm_parallel.invoke([{"role":"user","content":"同时查询北京、上海、深圳的天气"}])# 模型可能返回三个 get_weather 调用tool_calls=msg.tool_callsprint(f"需要调用{len(tool_calls)}个工具")# 并发执行所有工具调用fortcintool_calls:tool_name=tc["name"]args=tc["args"]result=execute_tool(tool_name,args)print(f"{args.get('location')}:{result}")执行过程:
需要调用 3 个工具 北京: 北京 天气:晴,22°C 上海: 上海 天气:多云,26°C 深圳: 深圳 天气:小雨,29°C场景 4:纯对话模式
当用户只是闲聊时,禁用工具调用节省成本:
# 检测到闲聊意图时llm_chat=llm.bind_tools([get_weather,get_time],tool_choice="none"# 禁用工具)msg=llm_chat.invoke([{"role":"user","content":"你叫什么名字?"}])# → 直接回答,不会调用任何工具常见问题和解决方案
问题 1:模型调用了错误的工具
原因:工具描述相似,模型无法区分。
解决方案:确保每个工具的描述独一无二,使用.bind_tools()时可以自定义工具名称:
@tool("web_search")# 自定义名称defsearch(query:str)->str:"""Search the web for current information."""returnf"Web results for:{query}"@tool("database_search")# 另一个名称defsearch_db(query:str)->str:"""Search the internal customer database for records."""returnf"DB results for:{query}"问题 2:并行调用时工具执行顺序混乱
原因:工具之间有依赖关系,不应该并行调用。
解决方案:对有依赖的工具关闭并行调用,或拆分为多个步骤:
# 无依赖的工具:并行调用llm_parallel=llm.bind_tools([get_weather,get_time],parallel_tool_calls=True)# 有依赖的工具:串行调用(关闭并行)llm_serial=llm.bind_tools([search_database,analyze_data],parallel_tool_calls=False)问题 3:tool_choice=“any” 时模型调用了不相关的工具
原因:Any 模式强制调用,但模型可能选择不合适的工具。
解决方案:如果只需要特定工具,使用具体工具名模式:
注意:当模型处于思考模式(thinking mode)时,强制指定tool_choice为具体函数或required会触发 API 400 错误,提示参数不支持,此时需要关闭模型的思考能力。
llm_specific=llm.bind_tools([get_weather,get_time],tool_choice={"type":"function","function":{"name":"get_weather"}})最佳实践
- Auto 模式最常用:生产环境默认使用 Auto,让模型自行判断
- Any 模式用于调试:开发阶段确认工具注册是否正确
- 具体工具名用于强制场景:当业务逻辑要求必须使用某个工具时
- Disabled 模式节省成本:闲聊或不需要工具的场景关闭工具调用
- 并行调用提升效率:多个独立工具同时调用,减少总等待时间
- 工具描述要独特:每个工具的描述应独一无二,避免模型混淆
- 使用自定义工具名称:用
@tool("custom_name")覆盖函数名,使工具名更清晰
总结
Tool Calling 的核心要点:
- 四种模式:Auto(自主)、Any(强制)、具体工具名(指定)、Disabled(禁用)
- bind_tools():将工具绑定到模型,注入工具定义
- parallel_tool_call:并行调用独立工具,提升执行效率
- 场景适配:根据用户需求动态切换 tool_choice 模式
- 调试技巧:用 Any 模式快速验证工具注册状态
掌握了 Tool Calling 的四种模式和并行调用,你就能对模型的工具调用行为拥有完全的控制力,构建更高效、更可控的 AI 应用。
