4、工具调用(Tool Use):让模型学会“动手”——Function Calling的原理与实战
咱们之前聊了提示工程、RAG,模型看起来挺聪明了。但有个问题一直卡着——模型只能“动嘴”,不能“动手”。
你让它查个天气,它说“我无法实时获取数据”。你让它发个邮件,它说“我没有权限”。这就像你雇了个顶级分析师,但他只能坐在那空想,连个计算器都不让碰。
Function Calling,就是给模型配上了“手”。
4.1 为什么模型需要“动手”?
说白了,大模型本质上是一个“文本生成器”。它根据你输入的上下文,预测下一个最合理的词。它没有感官,不能访问数据库,不能调用API,更不能操作你的系统。
我刚开始做Agent系统时,犯过一个低级错误。我让模型“帮我查一下上个月的销售数据”,结果它给我编了一份。数据看起来有模有样,月份、数字、增长率全对,但全是幻觉。为什么?因为它根本查不到真实数据,它只是在“猜”一个合理的回答。
这就是问题所在。模型需要一种机制,让它能主动说:“我需要调用一个外部工具来获取这个信息。” 然后等工具返回结果,它再基于真实数据继续推理。
4.2 Function Calling 的工作原理
咱们拆开来看,其实不复杂。整个过程分三步:
- 注册工具: 你告诉模型,有哪些工具可以用。每个工具叫什么名字、有什么参数、用来干什么。
- 模型决策: 模型分析用户的问题,判断是否需要调用某个工具。如果需要,它会返回一个结构化的“调用请求”。
- 执行与反馈: 你拿到这个请求,去执行真正的API调用,然后把结果塞回给模型,让它继续生成回答。
嗯,这里要注意一个关键点:模型本身不执行任何代码。它只是“提议”调用哪个工具、传什么参数。真正干活的是你的应用程序。
4.3 实战:用代码让模型调用工具
光说不练假把式。咱们直接上代码。我用的是 OpenAI 的 API,其他模型(Claude、通义千问)原理类似,只是参数名略有不同。
假设我们要做一个“天气助手”。模型需要能调用一个查天气的函数。
第一步:定义工具描述
你得给模型一份“工具说明书”。用 JSON Schema 格式描述清楚。
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的实时天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,例如:北京、上海、广州"
},
"date": {
"type": "string",
"description": "日期,格式为YYYY-MM-DD,默认为今天"
}
},
"required": ["city"]
}
}
}
]
第二步:发起对话,让模型决策
import openai
messages = [
{"role": "user", "content": "北京今天天气怎么样?"}
]
response = openai.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto" # 让模型自动决定是否调用
)
# 检查模型是否想调用工具
assistant_message = response.choices[0].message
if assistant_message.tool_calls:
# 模型决定调用工具
print("模型想调用工具:", assistant_message.tool_calls[0].function.name)
print("参数:", assistant_message.tool_calls[0].function.arguments)
else:
print("模型直接回答:", assistant_message.content)
你运行一下就会发现,模型返回的不是一段文字,而是一个结构化的 JSON。里面包含了要调用的函数名和参数。
第三步:执行工具,返回结果
这一步得你来写。模型不干活,你得替它干。
import json
# 假设你有一个真实的天气API
def get_weather(city, date=None):
# 这里替换成真实的API调用
return f"{city}今天天气:晴,温度25°C,适合出行。"
# 解析模型返回的工具调用
tool_call = assistant_message.tool_calls[0]
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
# 执行函数
if function_name == "get_weather":
result = get_weather(**arguments)
# 把结果塞回给模型
messages.append(assistant_message) # 加入模型的调用请求
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
})
# 让模型基于工具结果生成最终回答
final_response = openai.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools
)
print(final_response.choices[0].message.content)
4.4 多工具协同与冲突处理
实际项目中,你不会只有一个工具。你可能同时有查天气的、查日历的、发邮件的、操作数据库的。模型需要从多个工具中选一个最合适的。
我遇到过最头疼的情况是:用户说“帮我安排明天下午3点的会议,并通知参会人员”。这需要两个工具协作——先查日历看有没有空档,再发邮件通知。模型能不能连续调用两个工具?
答案是:可以。模型可以在一次回复中返回多个 tool_calls。你按顺序执行,把结果依次返回即可。
| 场景 | 推荐策略 | 注意事项 |
|---|---|---|
| 单工具调用 | 直接执行,返回结果 | 确保参数完整 |
| 多工具并行 | 同时执行多个独立工具 | 工具之间不能有依赖关系 |
| 多工具串行 | 先执行A,结果作为B的输入 | 需要维护中间状态 |
| 工具调用失败 | 返回错误信息,让模型重试或换方案 | 不要替模型做决定 |
4.5 从原理到思维:模型不再是“答案机”
学完 Function Calling,我希望你转变一个认知:模型不再是那个“你问它答”的答案机器了。
它变成了一个“调度员”。它分析问题,拆解任务,决定用什么工具,然后等你把结果拿回来,它再整合成最终答案。
你想想看,这像什么?像不像一个项目经理?项目经理不需要自己写代码、自己查数据,但他知道什么时候该找谁、用什么资源。模型也是一样。
我个人习惯把 Function Calling 看作是 Agent 系统的“手脚”。没有它,模型再聪明也只是个空想家。有了它,模型才能真正参与到业务流程中。
嗯,这一节就到这。代码不多,但背后的思维转变才是重点。下次你设计 Agent 时,先问问自己:这个任务需要模型调用什么工具?