一、用一句更直白的话定义 Function Calling

在大语言模型(比如 GPT、Claude 等)里,Function Calling(函数调用)就是:让模型在回答问题时,不是直接给你“结果”,而是先给你一份“调用你写好的函数的说明书(函数名 + 参数)”,由你的程序去真正执行这个函数,再把结果喂回模型,最终输出自然语言答案。

你可以把它理解成:

用户 →(自然语言)→ 模型 →(结构化函数调用)→ 你的代码 / 外部系统 →(真实结果)→ 模型 →(自然语言总结)→ 用户

二、为什么需要 Function Calling?

大模型本身有几类“天生短板”:

不会真正“动手”

它能“说”,但不能直接:

发邮件、拉群

查数据库

下单、订票

操作企业内部系统

没有实时数据

训练完成后,参数里的知识是“冻结”的:

不知道最新汇率、股价、天气

不知道今天的新闻

不知道你公司刚刚新增的订单

不擅长精确计算 / 严格规则

复杂税务、工程计算容易出错

对“必须是结构化输出”的场景不稳(如严格 JSON)

Function Calling 的目的,就是:给模型配备一套“可靠工具箱”,让它在需要时主动调用这些工具(函数),用真实世界的数据和能力来弥补自身的不足。

三、完整工作流程

用一个具体例子来拆解:

用户说:「帮我查一下上海今天的天气,顺便建议我穿什么。」

步骤 1:你把“工具说明书”给模型

你在请求里告诉模型:

有一个函数叫 get_weather

它的作用:查实时天气

它接收哪些参数、类型是什么(用 JSON Schema 定义)

示例(概念性):

{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "获取指定城市的实时天气",
    "parameters": {
      "type": "object",
      "properties": {
        "location": {
          "type": "string",
          "description": "城市名称,例如 \"Shanghai\" 或 \"上海\""
        },
        "unit": {
          "type": "string",
          "enum": ["celsius", "fahrenheit"],
          "description": "温度单位"
        }
      },
      "required": ["location", "unit"],
      "additionalProperties": false
    }
  }
}

你把这些函数定义放到 tools(或类似字段)里一起发给模型。

步骤 2:用户发自然语言请求

例如:

用户:“帮我查一下上海今天的天气,顺便建议我穿什么。”

步骤 3:模型决定要不要用工具、用哪个工具

模型读完用户问题,结合你提供的函数列表,判断:

这个问题需要实时天气

自己脑子里没有今天的实时数据

你恰好提供了 get_weather 这个函数

于是它决定:调用 get_weather。

步骤 4:模型生成“函数调用请求”(结构化 JSON)

而不是直接用中文回答,模型给你一段“指令”,大致是:

{

  "name": "get_weather",

  "arguments": {

    "location": "Shanghai",

    "unit": "celsius"

  }

}

这段内容不是给用户的,而是给你的后端程序看的——告诉你应该执行哪个函数、传什么参数。

步骤 5:你的程序真正去执行函数

对应代码大致这样(伪代码):

def get_weather(location, unit):

    # 这里调用真实的天气 API 或内部服务

    # 返回结构化结果   

 return {

        "location": location,

        "temperature": 18,

        "unit": unit,

        "description": "小雨"

    }

# 从模型的输出里取出函数名和参数

call = {

  "name": "get_weather",

  "arguments": {"location": "Shanghai", "unit": "celsius"}

}

result = get_weather(**call["arguments"])

得到的 result 是真实数据,例如:

{

  "location": "Shanghai",

  "temperature": 18,

  "unit": "celsius",

  "description": "小雨"

}

步骤 6:你把执行结果再喂回给模型

你会再发起一次请求,用类似这样的内容(概念):

把这次工具调用的“结果”作为一条特殊的“tool 消息”

同时把原始用户问题也带上

模型收到后,看到:

用户问了什么

它之前调用了哪个函数

这个函数返回了什么结果

步骤 7:模型基于工具结果生成自然语言回答

模型会用自然语言组织最终答案,比如:

“上海今天小雨,气温大约 18℃,建议你穿一件带帽的外套,出门记得带伞。”

到这一步,用户才看到最终回答。

小结:

**第一次请求:主要是“我要解决什么问题 + 这些是你可以用的工具”

中间过程:模型只返回“函数调用指令”,不给用户看

你执行完函数后:再把结果回给模型

最后一次回复:才是面向用户的人类可读答案

四、函数定义里的 JSON Schema 是干嘛的?

你在告诉模型“有一个函数可以用”的时候,要把参数结构讲清楚,否则模型不知道怎么“组装参数”。

4.1 常见字段

type: 必须是 "function"(表示这是一个函数工具)

name: 函数名(如 get_weather)

description: 用人话说明该函数用来干什么

parameters: 用 JSON Schema 定义参数

type: 一般是 "object"

properties: 参数列表及类型

required: 哪些参数是必填

additionalProperties: 是否允许多余字段

strict: 是否启用严格模式(让模型必须**遵守 schema)

4.2 参数类型和约束

在 properties 里,你可以定义:

string, number, integer, boolean, object, array 等

enum:枚举值限制,比如 ["celsius", "fahrenheit"]

description:解释这个字段的含义,模型依靠它理解怎么用

示例(更完整一点):

"parameters": {

  "type": "object",

  "properties": {

    "location": {

      "type": "string",

      "description": "城市名称,如 \"Shanghai\" 或 \"上海\""

    },

    "unit": {

      "type": "string",

      "enum": ["celsius", "fahrenheit"],

      "description": "温度单位"

    },

    "lang": {

      "type": "string",

      "enum": ["zh", "en"],

      "description": "返回内容的语言"

    }

  },

  "required": ["location", "unit"],

  "additionalProperties": false

}

好处:

模型知道该传哪些字段、用什么值,错误率明显降低

你可以对不合法的入参做校验,提升系统健壮性

五、和“普通 API 调用”有什么本质不同?

一句话概括:

传统方式:“开发者说了算”(逻辑都写死在代码里)

Function Calling:“模型说了算”(模型理解需求 → 自动选择工具)

六、常见落地场景(更细一点)

智能客服 / 智能助理**

函数:query_order_status, cancel_order, create_ticket

效果:用户直接问「帮我查一下 123456 这个订单的物流」,模型会自动:

从话里抽出订单号

调用 query_order_status

整理查询结果、解释给用户听

企业内部知识 / 数据查询

函数:query_crm, get_project_status, search_docs

模型变成“公司内部信息入口”:

「我们和 XX 客户上一次签的合同金额是多少?」

→ 调 CRM API → 查合同表 → 模型总结回答。

个人助手

函数:create_calendar_event, add_todo, send_email

用户说:「帮我下周三下午 3 点和张三开会,拉个日历事件,提醒我提前半小时准备。」

→ 模型调用日历 API → 创建日程 → 再用自然语言确认。

数据分析 / 报表生成

函数:run_sql_query, generate_chart

模型先通过 run_sql_query 拉出原始数据,再生成文字分析和可视化建议。

复杂多步骤任务

比如“帮我规划一个上海周末两日游的行程并预订酒店”:

步骤1:search_spots 搜景点

步骤2:plan_route 规划路线

步骤3:search_hotels 找酒店

步骤4:book_hotel 预订

整个链路中,可以有多次函数调用 + 中间自然语言解释。

七、对开发者来说,具体要做哪几件事?

可以理解为“三件核心工作 + 一点工程实践”。

7.1 核心三件事

设计并描述你的函数(工具)

把业务能力拆成一个个函数:get_weather、query_db、send_email …

给每个函数写清楚:

函数名(name)

功能说明(description)

参数结构(parameters JSON schema)

这一步更像写“API 文档”,但对象是**大模型。

在后端真正实现这些函数**

根据你的技术栈实现:

Python/Java/Go 等

内部服务调用、外部 API 调用、数据库、第三方平台

写一个“中间层”去调度

接收模型返回的 function call(JSON)

根据 name 跳转到对应实现

执行后把结果再打包成“tool 输出”喂回模型

处理错误(比如 API 挂了、参数不合法)

7.2 工程实践建议

对每个函数:

做参数校验(长度、格式、枚举值)

做错误兜底(请求外部 API 失败要有 fallback)

对整体系统:

记录每次函数调用的日志(方便排查问题)

控制调用频率和并发(防止接口被打爆)

监控成本(过多的函数定义会增加 Token 消耗)

八、Function Calling 的优势与局限

8.1 明显优势

大大简化“自然语言 → API 调用”这一步

以前要写一堆 NLP / 规则解析,现在交给模型自动做。

提高系统灵活性和可扩展性

新功能 = 新增/修改一个函数定义和实现。

可以和检索(RAG)、多模态等能力组合

例如:先用检索拿到文档,再调用函数做计算。

8.2 需要注意的局限

**不是万能的:

模型有时会给出不合适的参数或调用不合适的函数,因此需要:

严格的 JSON Schema

后端的参数验证和错误处理

增加了系统复杂度:

相比“纯聊天”,你要多维护一层“工具层”和“调度逻辑”。

延迟和成本:

每一次函数调用通常意味着:

一次额外的模型请求(把结果喂回模型)

一次外部 API 请求

需要在体验和准确性之间平衡

更多推荐