欢迎光临
我们一直在努力

Function Calling 详解:让大模型从“只会说“到“能干活“

Function Calling 详解:让大模型从"只会说"到"能干活"

前言:大模型的"手"在哪里?

使用 GPT、文心一言等大模型时,常会遇到一类典型局限:

问它"今天北京天气怎么样",它回答得头头是道——但那是编的,它根本没查过天气。问它"帮我查一下订单 12345 的状态",它礼貌地告诉你"我无法访问你的订单系统"。

这不是模型不够聪明。大语言模型(LLM)本质上是一个文本生成器,它的知识来自训练数据的快照,没有权限访问互联网、数据库、或者任何外部系统。它能写出一篇完美的库存分析报告,却没法帮你扣减一条库存记录。

Function Calling(函数调用)就是来解决这个问题的——它给大模型装上了"手"。

2023 年 6 月,OpenAI 率先在 GPT-4 和 GPT-3.5-turbo 上推出了 Function Calling 功能。随后百度、阿里、Anthropic 等厂商纷纷跟进。到 2026 年的今天,Function Calling 已经成为构建 AI Agent(智能体)的基石技术,也是连接"大模型的语义理解能力"和"业务系统的执行能力"之间最核心的桥梁。

这篇文章会从原理到实战,把 Function Calling 讲透。代码示例覆盖 OpenAI 和百度千帆两个主流平台,直接能跑。

术语约定:下文统一采用 OpenAI 兼容协议的命名——用 tools 定义工具、用 tool_calls 承载模型返回的调用请求;“Function Calling” 与"工具调用"等价使用。


一、先纠正一个常见误区

很多人一听 Function Calling,第一反应是"大模型帮我调函数"——这个理解只对了一半。

LLM 本身并不执行任何代码。 它运行在云端的 GPU 集群上,没有你本地环境的权限,不能直接调你的数据库、不能发 HTTP 请求、不能操作文件系统。

Function Calling 的实际工作方式是这样的:

  • 你把一组"工具"的描述(函数名、功能说明、参数结构)传给模型
  • 模型分析用户的意图,判断需不需要调工具
  • 如果需要,模型返回一段结构化 JSON,告诉你"我想调这个函数,参数是这些"
  • 你的代码接收这个 JSON,执行真正的函数
  • 把执行结果回传给模型
  • 模型基于真实数据,生成最终的自然语言回复
  • 用一句话概括:模型负责决策,你的程序负责执行。 这种"意图在模型、执行在代码"的边界划分,是 AI 系统安全性的核心保障。

    用户:"帮我查一下北京今天的天气"

    LLM 分析语义,发现自己没有实时天气数据

    LLM 返回:建议调用 get_weather,参数 {"city": "北京"}

    ★ 你的代码实际执行 get_weather() 函数

    将执行结果回传给 LLM

    LLM 基于真实数据生成最终回答


    二、完整工作流程

    下面先把整个流程拆开看,再提炼几个绕不开的约束。

    2.1 时序拆解

    用「查天气」为例,一次标准函数调用的来回是这样的(ASCII 图,任意平台都能正常显示):

    用户 应用程序 大模型(LLM) 外部函数
    │ "北京天气?" │ │ │
    │─────────────>│ 消息 + tools │ │
    │ │───────────────>│ │
    │ │ │ 分析意图 │
    │ │ │ 决定调工具 │
    │ │<── tool_calls ─│ (函数名+参数) │
    │ │ 执行函数 │ │
    │ │────────────────────────────────>│
    │ │<───── "晴天 25°C" ──────────────│
    │ │ 结果作为 tool 消息回传 │
    │ │───────────────>│ │
    │ │ │ 生成最终回复 │
    │<─ "北京晴天25°C"─│<──────────│ │

    2.2 核心关键约束

    无论用哪个平台,下面六步都成立:

    • 第一步:每次调用都要把 tools 定义带上传给模型,告诉它有哪些工具可用
    • 第二步:模型自己判断要不要调、调哪个、传什么参数——你没法强制它
    • 第三步:模型返回的不是最终答案,而是一个"函数调用请求"
    • 第四步:你的代码解析这个请求,执行真正的函数
    • 第五步:把执行结果以 tool 角色的消息追加到对话历史里,再次调用模型
    • 第六步:模型拿到真实数据后,生成用户能看懂的最终回复

    整个过程是一个多轮对话循环,函数调用的结果会作为上下文喂回给模型。


    三、实战:OpenAI 平台

    3.1 环境准备

    pip install openai

    3.2 完整示例:天气查询助手

    下面以「天气查询」为例完整跑一遍:先定义工具 tools,再实现真实函数,最后发起对话、处理模型返回的调用请求。

    from openai import OpenAI
    import json

    client = OpenAI(api_key="your-api-key")

    # ———- 第一步:定义工具 ———-
    tools = [
    {
    "type": "function",
    "function": {
    "name": "get_weather",
    "description": "获取指定城市的当前天气信息",
    "parameters": {
    "type": "object",
    "properties": {
    "city": {
    "type": "string",
    "description": "城市名称,例如:北京、上海"
    },
    "unit": {
    "type": "string",
    "enum": ["celsius", "fahrenheit"],
    "description": "温度单位,默认摄氏度"
    }
    },
    "required": ["city"]
    }
    }
    }
    ]

    # ———- 第二步:实现真实函数 ———-
    def get_weather(city: str, unit: str = "celsius") > str:
    """实际项目中这里应该调用真实的天气 API"""
    weather_data = {
    "北京": {"celsius": "25°C 晴", "fahrenheit": "77°F 晴"},
    "上海": {"celsius": "22°C 多云", "fahrenheit": "72°F 多云"},
    "深圳": {"celsius": "30°C 雨", "fahrenheit": "86°F 雨"},
    }
    return weather_data.get(city, {}).get(unit, "暂无该城市天气数据")

    # ———- 第三步:发起对话 ———-
    messages = [
    {"role": "user", "content": "北京和深圳今天天气怎么样?"}
    ]

    # 第一次调用:模型决定是否需要调用函数
    response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=tools,
    tool_choice="auto" # auto 表示模型自己决定是否调用
    )

    message = response.choices[0].message
    messages.append(message)

    # ———- 第四步:处理函数调用 ———-
    if message.tool_calls:
    for tool_call in message.tool_calls:
    func_name = tool_call.function.name
    func_args = json.loads(tool_call.function.arguments)

    print(f"模型请求调用: {func_name}({func_args})")

    # 执行真实函数
    if func_name == "get_weather":
    result = get_weather(**func_args)
    else:
    result = "未知函数"

    # 把结果作为 tool 消息回传
    messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,
    "content": result
    })

    # ———- 第五步:第二次调用,模型生成最终回复 ———-
    final_response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=tools
    )

    print(final_response.choices[0].message.content)
    else:
    # 模型直接回答了,不需要调用函数
    print(message.content)

    运行结果:

    模型请求调用: get_weather({'city': '北京', 'unit': 'celsius'})
    模型请求调用: get_weather({'city': '深圳', 'unit': 'celsius'})
    北京今天晴天,气温25°C;深圳今天有雨,气温30°C。出门记得带伞!

    注意看:用户一次问了两个城市的天气,模型并行返回了两个 tool_call。你的代码需要遍历处理每一个调用,然后把所有结果一起回传给模型。


    四、实战:百度千帆平台

    百度千帆在 2026 年已提供 OpenAI 兼容的 /v2 端点,函数调用的入参、返回格式与 OpenAI 完全一致——你甚至不需要装 qianfan SDK,只要用 openai 库、把 base_url 指向千帆即可。目前该端点支持 ERNIE 系列、DeepSeek 系列、Qwen 系列等多个模型。

    注:千帆旧版 qianfan.ChatCompletion + function_call 方案属于历史遗留接口,新业务请统一使用下面的 OpenAI 兼容 /v2 接口,避免被老教程误导。

    4.1 环境准备

    pip install openai

    千帆的 API Key 在百度智能云千帆控制台获取,配置到环境变量 QIANFAN_API_KEY 即可(不再需要旧的 AK/SK 配对)。

    4.2 完整示例:数据库文件查询

    与第三节结构完全一致——只是把 client 换成千帆的 OpenAI 兼容端点,模型换成千帆支持的其中之一。

    import os
    import json
    from openai import OpenAI

    # 千帆提供 OpenAI 兼容的 /v2 端点,只需换 base_url 和 api_key
    client = OpenAI(
    api_key=os.environ["QIANFAN_API_KEY"],
    base_url="https://qianfan.baidubce.com/v2"
    )

    # ———- 定义真实函数 ———-
    def get_file_num(language: str) > str:
    """获取数据库中指定语言的代码文件数量"""
    language_map = {
    "python": 35,
    "java": 10,
    "javascript": 25,
    "c/c++": 35,
    "go": 32,
    }
    return str(language_map.get(language.lower(), 0))

    # ———- 定义工具描述(与 OpenAI 格式完全一致)———-
    tools = [
    {
    "type": "function",
    "function": {
    "name": "get_file_num",
    "description": "获取内部数据库中以某一编程语言编写的文件数量",
    "parameters": {
    "type": "object",
    "properties": {
    "language": {
    "type": "string",
    "description": "代码所运用的编程语言,例如:python、c/c++、go、java"
    }
    },
    "required": ["language"]
    }
    }
    }
    ]

    # ———- 发起对话 ———-
    messages = [
    {"role": "user", "content": "请帮我查询一下数据库中用 Python 撰写的代码文件数量"}
    ]

    # 第一次调用:模型决定是否调用工具(与 OpenAI 完全一致)
    response = client.chat.completions.create(
    model="deepseek-v3", # 千帆支持的模型之一,可按需替换为 ERNIE / Qwen 等
    messages=messages,
    tools=tools,
    tool_choice="auto"
    )

    message = response.choices[0].message
    messages.append(message)

    # ———- 处理函数调用 ———-
    if message.tool_calls:
    for tool_call in message.tool_calls:
    func_name = tool_call.function.name
    func_args = json.loads(tool_call.function.arguments)

    print(f"模型请求调用: {func_name}({func_args})")

    if func_name == "get_file_num":
    result = get_file_num(**func_args)
    else:
    result = "未知函数"

    # 把结果作为 tool 消息回传(与 OpenAI 完全一致)
    messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,
    "content": str(result)
    })

    # 第二次调用,模型生成最终回复
    final_response = client.chat.completions.create(
    model="deepseek-v3",
    messages=messages,
    tools=tools
    )
    print(final_response.choices[0].message.content)
    else:
    print(message.content)

    运行结果:

    模型请求调用: get_file_num({'language': 'Python'})
    数据库中使用 Python 编写的代码文件共有 35 个。

    可以看到,换成千帆之后代码几乎和第三节的 OpenAI 示例一模一样——因为千帆的 /v2 端点直接兼容 OpenAI 的 tools / tool_calls 协议。千帆早期 SDK 曾用 function_call 字段(旧版格式),现在官方已统一到 tool_calls 标准。 核心逻辑始终不变:模型只产出调用请求,真实执行在你的代码里。


    五、tool_choice 参数详解

    tool_choice 控制模型调用工具的行为,四个选项:

    取值行为适用场景
    "auto" 模型自己决定调不调 默认值,大多数场景用这个
    "none" 禁止调用任何工具 想让模型纯对话回答时用
    "required" 必须调用至少一个工具 强制走工具流程时用
    {"type": "function", "function": {"name": "xxx"}} 强制调用指定函数 已知必须调某个函数时用

    # 强制模型必须调用 get_weather 函数
    response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=tools,
    tool_choice={"type": "function", "function": {"name": "get_weather"}}
    )

    实际开发中,"auto" 基本够用。只有在特定业务流程里需要强制走工具时,才用 "required" 或指定函数。


    六、进阶:多工具并行调用

    实际项目中,用户一句话可能需要调多个函数。比如"帮我查一下北京天气,再查一下我的订单状态"——模型应该并行返回两个 tool_call。下面给出完整可运行示例:

    tools = [
    {
    "type": "function",
    "function": {
    "name": "get_weather",
    "description": "获取城市天气",
    "parameters": {
    "type": "object",
    "properties": {
    "city": {"type": "string", "description": "城市名"}
    },
    "required": ["city"]
    }
    }
    },
    {
    "type": "function",
    "function": {
    "name": "get_order_status",
    "description": "查询订单状态",
    "parameters": {
    "type": "object",
    "properties": {
    "order_id": {"type": "string", "description": "订单编号"}
    },
    "required": ["order_id"]
    }
    }
    }
    ]

    messages = [
    {"role": "user", "content": "帮我查一下北京天气,还有订单 12345 的状态"}
    ]

    response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=tools,
    tool_choice="auto"
    )

    # 模型会返回两个 tool_call,遍历处理
    message = response.choices[0].message
    for tool_call in message.tool_calls:
    func_name = tool_call.function.name
    func_args = json.loads(tool_call.function.arguments)

    # 根据函数名分发到对应的处理逻辑
    if func_name == "get_weather":
    result = get_weather(**func_args)
    elif func_name == "get_order_status":
    result = get_order_status(**func_args)

    messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,
    "content": str(result)
    })

    # 把所有结果一起回传,模型生成最终回复
    final_response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=tools
    )

    处理多工具调用的关键是:遍历所有 tool_call,分别执行,然后把所有结果一起回传给模型。不要只处理第一个就停。


    七、踩坑与最佳实践

    落地多个 Function Calling 业务后,整理出高频踩坑点与标准化实践:

    1. description 写清楚,模型才能选对

    工具的 description 字段不是写给人看的注释,是写给模型看的"使用说明书"。描述越清晰,模型选对工具的概率越高。

    # 差:太模糊
    "description": "查询天气"

    # 好:说清楚什么场景该用
    "description": "获取指定城市的当前天气信息。当用户询问某地天气、温度、是否下雨时使用此工具。"

    2. 参数描述也要具体

    # 差
    "city": {"type": "string", "description": "城市"}

    # 好
    "city": {"type": "string", "description": "城市名称,如'北京'、'上海',不要用缩写"}

    模型是根据这些描述来提取参数的,描述含糊会导致参数提取出错。

    3. 函数执行结果要转成字符串

    tool 角色消息的 content 必须是字符串。如果你的函数返回的是字典或数字,记得 json.dumps() 或 str() 转一下:

    result = get_weather(city="北京") # 返回 dict
    messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,
    "content": json.dumps(result, ensure_ascii=False) # 转 JSON 字符串
    })

    4. 处理模型不调函数的情况

    模型有可能判断不需要调函数,直接回复了。你的代码要处理这种情况:

    if message.tool_calls:
    # 有函数调用
    ...
    else:
    # 没有函数调用,直接用模型的回复
    print(message.content)

    5. Token 消耗会增大

    把 tools 定义传给模型会增加 prompt token 消耗。百度千帆的文档提到,内置 prompt 模板拼接会导致约 300 token 的膨胀。工具定义越多,消耗越大。别一股脑塞几十个工具进去,按场景分组、按需加载。

    6. 安全边界:别把危险函数暴露给模型

    Function Calling 的安全性靠的是"模型只做决策不执行"这个边界。但如果你把删库、转账等高危函数也定义成工具,模型一旦判断失误,你的代码就会真的执行。面向生产环境,建议至少落实以下四条:

    • 入参校验与白名单:函数执行前对模型传入的参数做类型、范围、白名单过滤,防止 SQL 注入、路径遍历(../../etc/passwd)等恶意参数直接打到后端。
    • 高危操作禁止自动执行:写/改/删数据、资金转账、权限变更这类接口,不要封装成"模型可自动调用"的工具,必须保留人工二次确认(或独立的审批流)才能落地。
    • 全链路调用日志落库:记录用户 prompt、模型产生的 tool_calls 参数、函数返回结果,便于出问题时的审计与溯源。
    • 限制单轮最大调用次数:给 Agent 循环设上限(如单轮最多 10 次工具调用),防止模型陷入无限循环把接口流量和 token 费用打爆。

    7. 生产健壮性:异常处理、循环上限、并发控制

    示例里的代码为了聚焦主流程,省略了几处生产环境绕不开的健壮性处理,上线前务必补上:

    • 捕获函数调用异常:网络抖动、接口超时、参数缺失都会导致执行失败,要用 try/except 包住工具执行,失败时把错误信息回传给模型,而不是直接让程序崩溃。
    • 给 Agent 循环设最大轮次:多步推理场景如果不限制循环次数,模型可能反复调用工具,产生高额 token 费用甚至死循环。务必用计数器或超时掐断。
    • 并行调用做好限流:模型一次可能返回多个 tool_call,真实业务可以异步并发执行来加速,但要加并发上限和熔断,避免瞬间打爆下游接口。

    八、从 Function Calling 到 Agent

    理解了 Function Calling,你就理解了 AI Agent 的核心。

    Agent 不是什么神秘的东西,它本质上就是一个循环(ASCII 图,任意平台都能正常显示):

    ┌──────────────────────────────────────┐
    │ ↓
    用户输入 → LLM 思考 → 需要调工具? ─否─→ 输出最终回复 → 返回用户
    ↑ │是
    │ ↓
    │ 返回 tool_calls
    │ ↓
    │ 执行函数
    │ ↓
    └────── 结果回传 LLM ←──┘

    • LLM 是大脑,负责理解意图、规划步骤、选择工具
    • Function Calling 是手脚,负责执行具体操作
    • 循环(Agentic Loop)让模型可以多步推理、连续调用工具,直到完成任务

    LangChain、AutoGPT、各种 Agent 框架,底层都是在这个循环上做封装。理解了 Function Calling 的原始 API,再用这些框架时就知道它们帮你省了什么、在哪里可能出问题。


    总结

    维度要点
    核心本质 模型输出结构化 JSON,程序执行真实函数
    关键边界 模型只决策不执行,安全性靠代码层保障
    标准流程 定义工具 → 用户输入 → 模型判断 → 返回调用请求 → 执行函数 → 结果回传 → 生成回复
    主流平台 OpenAI / 百度千帆(均遵循 OpenAI tool_calls 标准)、Anthropic(tool_use)
    进阶能力 多工具并行、tool_choice 控制、多轮循环
    最佳实践 description 写清楚、参数描述具体、结果转字符串、处理无调用情况、控制 token、守安全边界、补生产健壮性

    Function Calling 不复杂,但它重要。它是大模型从"聊天工具"走向"生产力工具"的关键一步。掌握了它,你就能让 AI 真正替你干活——查数据、调接口、发通知、做决策。

    写这篇的时候我尽量把原理和代码都讲到位了,有不清楚的地方欢迎评论区交流。

    参考文档

    • OpenAI Function Calling 文档:https://developers.openai.com/docs/function-calling
    • 百度千帆 v2 函数调用文档:https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Alm6k3z0t

    本文代码基于 2026 年 8 月的 API 版本,实际使用时请以官方最新文档为准。 传 → 生成回复 | | 主流平台 | OpenAI / 百度千帆(均遵循 OpenAI tool_calls 标准)、Anthropic(tool_use) | | 进阶能力 | 多工具并行、tool_choice 控制、多轮循环 | | 最佳实践 | description 写清楚、参数描述具体、结果转字符串、处理无调用情况、控制 token、守安全边界、补生产健壮性 |

    Function Calling 不复杂,但它重要。它是大模型从"聊天工具"走向"生产力工具"的关键一步。掌握了它,你就能让 AI 真正替你干活——查数据、调接口、发通知、做决策。

    写这篇的时候我尽量把原理和代码都讲到位了,有不清楚的地方欢迎评论区交流。

    参考文档

    • OpenAI Function Calling 文档:https://developers.openai.com/docs/function-calling
    • 百度千帆 v2 函数调用文档:https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Alm6k3z0t

    本文代码基于 2026 年 8 月的 API 版本,实际使用时请以官方最新文档为准。

    赞(0)
    未经允许不得转载:171主机测评 » Function Calling 详解:让大模型从“只会说“到“能干活“
    分享到: 更多 (0)

    评论 抢沙发

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