function-calling · ZH · 2026-09-27

函数 / 工具调用

函数调用(Function Calling / Tool Use)让模型在对话中输出结构化的调用请求,由你的应用执行后再把结果回传。本文解释它是什么、什么时候值得用、请求里 tools 字段的基本结构,以及用聚合 API 时需要注意的兼容性差异。

函数调用到底在做什么

模型本身不会执行代码、不会查询你的数据库,也不能真的调用外部接口。它能做的是:在合适的时机输出一段结构化的调用意图,比如「需要调用 get_weather,参数是 city=上海」。

真正执行的是你的应用。执行完之后,你把结果作为一条新消息塞回对话,模型再基于结果继续生成自然语言回答。

所以函数调用不是一个让模型「变聪明」的开关,而是把模型的语言理解能力接到你已有的确定性代码上的一个接口。

一次典型的往返

  1. 你在请求里带上可用的工具定义(tools)。
  2. 用户提问,模型判断需要调用工具,返回一个工具调用请求(含工具名和参数)。
  3. 你的代码解析参数、执行真实逻辑、拿到返回值。
  4. 你把返回值作为工具结果消息追加进对话历史,再发一次请求。
  5. 模型读取结果,生成面向用户的最终回答。

注意第 2 步模型可能一次返回多个调用,也可能什么都不调直接回答——这两者都是正常结果,不是错误。

请求里长什么样

概念上,tools 是一个数组,每个元素描述一个可用函数:名称、用途说明、参数的 JSON Schema。大致形状:

{
  "model": "your-model",
  "messages": [{"role": "user", "content": "上海现在天气怎么样?"}],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "查询指定城市的当前天气",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {"type": "string", "description": "城市名"}
          },
          "required": ["city"]
        }
      }
    }
  ]
}

模型的回复里会出现工具调用结构,而不是(或除了)普通文本。你解析它,执行,然后把结果按该提供方约定的格式回传。

description 和参数说明是给模型看的提示词,不是给人看的注释。写得含糊,模型就容易选错工具或填错参数。

什么时候值得用

适合的场景通常有这些共同点:

  • 需要实时或私有数据:天气、库存、订单状态、内部知识库。这些模型训练数据里没有,或者已经过期。
  • 需要精确计算或确定性逻辑:金额计算、日期差、正则匹配、单位换算。让代码算比让模型「心算」可靠。
  • 需要触发副作用:下单、发邮件、创建工单。这类操作建议加人工确认或权限校验,别让模型直接决定。
  • 需要收敛输出格式:把「抽取结构化字段」做成一个工具,比在提示词里反复要求 JSON 更稳定。

不太值得用的情况:纯改写、翻译、总结这类文本任务;或者只有一个固定流程、根本不需要模型判断分支的场合——直接写代码更简单。

用聚合 API 时的几个注意点

一个 key 调多家模型时,函数调用的细节会有差异:

  • Schema 深度:有的提供方对嵌套对象、数组、枚举支持更完整,有的对深层嵌套或某些关键字支持有限。先用简单扁平参数跑通,再逐步加复杂度。
  • 并行调用:是否支持一次返回多个工具调用、并行执行的语义,各家不同。
  • 结果回传格式:工具结果消息的字段名和结构在不同提供方之间不完全一致。切换模型时这部分最容易出问题。
  • 强制调用:有的提供方支持指定「必须调用某个工具」或「禁止调用」,有的没有等价选项。
  • 流式:流式返回时工具调用的参数是分片到达的,需要自己拼接完整后再解析 JSON,不能边收边解析。

实际做法是:把工具定义和解析逻辑抽成一层薄薄的适配,针对不同模型族做小分支,而不是在业务代码里到处写 if。

常见坑

  • 参数是字符串不是对象:有些模型会把 JSON 参数作为字符串返回,需要显式解析,解析失败要有兜底。
  • 编造参数:模型可能填一个工具定义里不存在的字段,或者类型不对。校验要在执行前做。
  • 死循环:工具返回错误后模型反复重试同一个调用。给循环次数设上限。
  • 把工具结果当用户输入:工具返回的内容同样可能包含诱导性文本(比如抓取的网页),不要无脑当指令执行。
  • 工具太多:一次塞几十个工具会明显降低选择准确率。按场景分组,或者先做一次粗筛。

小结

函数调用的本质是把模型的判断力和你代码的可靠性拼起来:模型负责「选哪个、填什么」,你负责「真的执行」。设计时先想清楚哪些能力必须由确定性代码完成,再把这些能力包装成描述清晰的工具,剩下的交给模型。