4、工具调用(Tool Use):让模型学会“动手”——Function Calling的原理与实战

咱们之前聊了提示工程、RAG,模型看起来挺聪明了。但有个问题一直卡着——模型只能“动嘴”,不能“动手”。

你让它查个天气,它说“我无法实时获取数据”。你让它发个邮件,它说“我没有权限”。这就像你雇了个顶级分析师,但他只能坐在那空想,连个计算器都不让碰。

Function Calling,就是给模型配上了“手”。

4.1 为什么模型需要“动手”?

说白了,大模型本质上是一个“文本生成器”。它根据你输入的上下文,预测下一个最合理的词。它没有感官,不能访问数据库,不能调用API,更不能操作你的系统。

我刚开始做Agent系统时,犯过一个低级错误。我让模型“帮我查一下上个月的销售数据”,结果它给我编了一份。数据看起来有模有样,月份、数字、增长率全对,但全是幻觉。为什么?因为它根本查不到真实数据,它只是在“猜”一个合理的回答。

这就是问题所在。模型需要一种机制,让它能主动说:“我需要调用一个外部工具来获取这个信息。” 然后等工具返回结果,它再基于真实数据继续推理。

核心认知: Function Calling 不是让模型变得更聪明,而是让模型学会“求助”。它知道自己能力的边界,知道什么时候该调用工具,什么时候该直接回答。

4.2 Function Calling 的工作原理

咱们拆开来看,其实不复杂。整个过程分三步:

  1. 注册工具: 你告诉模型,有哪些工具可以用。每个工具叫什么名字、有什么参数、用来干什么。
  2. 模型决策: 模型分析用户的问题,判断是否需要调用某个工具。如果需要,它会返回一个结构化的“调用请求”。
  3. 执行与反馈: 你拿到这个请求,去执行真正的API调用,然后把结果塞回给模型,让它继续生成回答。

嗯,这里要注意一个关键点:模型本身不执行任何代码。它只是“提议”调用哪个工具、传什么参数。真正干活的是你的应用程序。

用户提问 大模型 分析意图 决策是否调用工具 需要 工具? 直接生成回答 返回工具调用请求 应用执行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"]
            }
        }
    }
]
我的经验: description 字段非常重要。模型就是靠这个描述来判断什么时候该调用这个工具。描述写得越清晰,模型决策越准确。我曾经把 description 写得太模糊,结果模型动不动就调用天气工具,连“今天心情怎么样”这种问题都去查天气。

第二步:发起对话,让模型决策

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)
避坑指南: 我曾经犯过一个错——忘记把 assistant_message 追加到 messages 里。结果模型第二次调用时完全不知道之前发生了什么,上下文断了。记住:每次交互都要保留完整的消息历史,包括模型的工具调用请求。

4.4 多工具协同与冲突处理

实际项目中,你不会只有一个工具。你可能同时有查天气的、查日历的、发邮件的、操作数据库的。模型需要从多个工具中选一个最合适的。

我遇到过最头疼的情况是:用户说“帮我安排明天下午3点的会议,并通知参会人员”。这需要两个工具协作——先查日历看有没有空档,再发邮件通知。模型能不能连续调用两个工具?

答案是:可以。模型可以在一次回复中返回多个 tool_calls。你按顺序执行,把结果依次返回即可。

场景 推荐策略 注意事项
单工具调用 直接执行,返回结果 确保参数完整
多工具并行 同时执行多个独立工具 工具之间不能有依赖关系
多工具串行 先执行A,结果作为B的输入 需要维护中间状态
工具调用失败 返回错误信息,让模型重试或换方案 不要替模型做决定

4.5 从原理到思维:模型不再是“答案机”

学完 Function Calling,我希望你转变一个认知:模型不再是那个“你问它答”的答案机器了。

它变成了一个“调度员”。它分析问题,拆解任务,决定用什么工具,然后等你把结果拿回来,它再整合成最终答案。

你想想看,这像什么?像不像一个项目经理?项目经理不需要自己写代码、自己查数据,但他知道什么时候该找谁、用什么资源。模型也是一样。

我个人习惯把 Function Calling 看作是 Agent 系统的“手脚”。没有它,模型再聪明也只是个空想家。有了它,模型才能真正参与到业务流程中。

思维转型关键: 别再想着“让模型记住所有知识”。而是思考“如何让模型知道去哪里获取知识”。工具调用,就是模型与真实世界之间的桥梁。

嗯,这一节就到这。代码不多,但背后的思维转变才是重点。下次你设计 Agent 时,先问问自己:这个任务需要模型调用什么工具?