• 欢迎关注我的微信公众号“ Falost ” 右边扫描关注 --->>

Function Calling 与 Tool Use 实战:教大模型用工具(查天气、做计算、调 API)

AI / 大模型 神棍 70℃ 0评论

之前写 RAG 系列文章的时候,我发现一个问题:纯靠向量检索加 LLM 生成的问答系统,有一个绕不过去的天花板——模型不会”做事”。你问它”今天北京的天气怎么样”,它要么说”我的知识截止到 XX 年,没法查实时信息”,要么编造一个天气数据。这不是模型的错,大语言模型本质上是文本生成器,不是操作系统,它没法直接调 API、查数据库、算数学。

那怎么让模型”动手干活”呢?OpenAI 在 2023 年 6 月推出了 Function Calling(函数调用)功能——后来改名为 Tool Use——核心思路很简单:模型输出结构化的 JSON 请求,告诉你要调用哪个函数、传什么参数,你负责执行,再把结果喂回给模型。模型并不自己执行代码,它只是一个”聪明的中介”,负责调度和整合。

这篇文章我从头梳理 Function Calling 的实现流程,用可运行的代码示例演示怎么让模型查天气、做计算、调 API,最后总结一些我在实践中踩过的坑。

一、Function Calling 到底是什么

一句话说清楚:你给模型定义一批工具(函数),模型根据用户的问题决定要不要调用、调用哪个、传什么参数,然后你的代码执行这个函数,把结果返回给模型,模型根据结果生成最终回答。

整个流程是这样的:

  1. 用户提问 → 模型收到消息 + 工具列表
  2. 模型判断是否需要调用工具 → 如果需要,返回 tool_calls(包含函数名 + JSON 参数)
  3. 你的代码执行对应的函数,拿到结果
  4. 把结果作为 tool 消息返回给模型
  5. 模型根据结果生成自然语言回答

你可以把它理解为”思维链 + 行动”。模型先想:这个问题需要外部信息吗?需要→调用工具→得到结果→再回答。不需要→直接回答。

二、最简实践:让模型查天气

先说准备工作:你需要一个 OpenAI API Key(OpenAI 的 Function Calling 最成熟,生态最好)。如果不想花钱,Ollama 从 0.2.0 版本开始也支持 Tool Calling,我用 llama3.1 试过能用,但稳定性不如 GPT。

先定义工具:

import json
from openai import OpenAI

client = OpenAI()

# 1. 定义工具
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市名,比如 北京、上海、Tokyo"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "温度单位"
                    }
                },
                "required": ["location"]
            }
        }
    }
]


# 2. 工具的实现函数(模拟调用天气 API)
def get_weather(location: str, unit: str = "celsius") -> str:
    # 实际项目里这里调真实的天气 API
    data = {
        "北京": {"celsius": 28, "condition": "晴"},
        "上海": {"celsius": 32, "condition": "多云"},
        "Tokyo": {"celsius": 26, "condition": "小雨"},
    }
    info = data.get(location, {"celsius": 22, "condition": "未知"})
    return json.dumps({
        "location": location,
        "temperature": info["celsius"] if unit == "celsius" else round(info["celsius"] * 9/5 + 32),
        "unit": unit,
        "condition": info["condition"]
    })

然后是核心的调用循环:

# 3. 消息和工具映射
tool_map = {"get_weather": get_weather}

messages = [{"role": "user", "content": "今天北京天气怎么样?"}]

# 第一轮调用
response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=tools,
    tool_choice="auto"
)

message = response.choices[0].message

# 如果模型要求调用工具
if message.tool_calls:
    messages.append(message)
    for tool_call in message.tool_calls:
        func_name = tool_call.function.name
        args = json.loads(tool_call.function.arguments)
        result = tool_map[func_name](**args)
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": result
        })

    # 第二轮调用:把工具结果给模型生成回答
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=messages
    )
    print(response.choices[0].message.content)

看到最关键的几个细节没有?

  • tool_choice=”auto”:让模型自己判断要不要调工具。你也可以改成 “none”(禁止)或 “required”(强制调用)。
  • tool_call.id:每次工具调用都有唯一 ID,返回结果时必须带上这个 ID,模型才能把结果配对上。
  • role=”tool”:工具结果的角色是 tool,不是 user 也不是 assistant。

三、多工具路由:一个模型、多个能力

实际项目中不会只有一个工具。我常用的是三个工具的配置:查天气 + 做计算 + 查文档。

tools = [
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "执行数学计算",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "数学表达式,支持 +-*/ 和括号"
                    }
                },
                "required": ["expression"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
                },
                "required": ["location"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_knowledge",
            "description": "搜索内部知识库",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string", "description": "搜索关键词"}
                },
                "required": ["query"]
            }
        }
    }
]

模型会自己判断用户的问题对应哪个工具。你试试问”2的10次方加28乘以3等于多少”,GPT-4o 会自动调 calculate 工具;问”北京天气怎么样再加上海的”,它可能会并发调两次 get_weather(同一次响应里返回多个 tool_calls)。

这里有一个值得注意的点:模型可能一次返回多个 tool_calls,你的代码必须支持并发或串行处理。我的做法是顺序执行(对于依赖关系明确的场景更安全),但如果工具之间没有依赖,你也可以用 asyncio 并发执行。

四、踩坑记录

写了几套 Function Calling 代码之后,我遇到几个比较经典的坑,列出来供参考:

问题 现象 解决方法
参数描述太模糊 模型不调用工具,或传错参数 description 要写得具体,告诉模型什么时候用、参数怎么填
工具太多 模型困惑,频繁选错工具 控制单个请求的 tools ≤ 10 个,太多就分层路由
不设 token 上限 工具返回太长,超出上下文 工具结果控制在几百 token,必要时截断或摘要
忘记处理错误 工具抛异常后模型乱回答 try/except 后返回 {“error”: “失败原因”},让模型自己应对
没有超时控制 工具调用卡死,整个流程挂住 每个工具调用设 timeout,超时返回 “工具超时”

五、总结

Function Calling 是让大模型从”只会说”变成”会动手”的关键能力。核心就三件事:定义工具、调度执行、结果回传。代码量不大,但细节很多——参数描述写好了模型自己就能选对工具,写不好它就一脸懵。

如果你用的是 Ollama 本地模型,从 llama3.1 和 qwen2.5 开始也支持 tool calling,API 格式和 OpenAI 兼容,代码基本可以直接迁移。

下期预告:Function Calling 进阶:并行工具调用、流式输出与多层 Agent 架构
从单工具到多 Agent 协作,拆解复杂任务调度模式。

参考资料

转载请注明:Falost的小窝 » Function Calling 与 Tool Use 实战:教大模型用工具(查天气、做计算、调 API)

如果你觉得这篇文章不错或者对你有帮助,想请我喝一杯咖啡,可以打赏
喜欢 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址