3. 提示词与接口交互

Function Calling 这个词,听起来挺唬人的。其实说白了,就是让大模型能调用你写好的函数。

我刚开始接触这个概念时,也觉得它很神秘。后来发现,它本质上就是一个「翻译」过程——把自然语言翻译成结构化的函数调用指令。嗯,今天我们就把它彻底讲透。

3.1 Function Calling 的核心原理

传统的 LLM 对话,模型只能输出文本。你问它「帮我查一下北京的天气」,它只能回复「好的,我帮你查一下」,然后就没有然后了。它没法真的去调 API。

Function Calling 解决了这个问题。它的工作流程是这样的:

  1. 你预先定义好一批工具函数,告诉模型这些函数叫什么、有什么参数
  2. 用户提问后,模型判断是否需要调用某个函数
  3. 如果需要,模型返回一个结构化的函数调用请求(不是真的执行)
  4. 你拿到这个请求,自己去执行函数,把结果返回给模型
  5. 模型基于函数结果,生成最终的自然语言回复

关键点:模型不执行代码,它只负责「决定该调用哪个函数、传什么参数」。执行权始终在你手里。

我在项目中遇到过不少新手,以为 Function Calling 是模型直接跑 Python 脚本。不是的。模型输出的是 JSON,你拿到 JSON 后自己去调函数。这个设计很安全,也很灵活。

Function Calling 完整流程 用户 大模型 函数调用请求 (JSON 格式) 执行 预定义的函数 Schema get_weather(city: string, date: string) → 返回天气数据 send_email(to: string, subject: string, body: string) → 返回发送结果 calculate(expression: string) → 返回计算结果 自然语言回复

3.2 定义工具函数 Schema

要让模型知道你的函数长什么样,你得给它一份「说明书」。这份说明书就是 Function Schema。

不同的模型厂商,Schema 格式略有差异。但核心结构是相通的。我以 的格式为例,这是目前最主流的写法:

{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "获取指定城市在指定日期的天气信息",
    "parameters": {
      "type": "object",
      "properties": {
        "city": {
          "type": "string",
          "description": "城市名称,如北京、上海"
        },
        "date": {
          "type": "string",
          "description": "日期,格式为 YYYY-MM-DD"
        }
      },
      "required": ["city", "date"]
    }
  }
}

你看,每个字段都有明确的含义:

字段 说明 我的建议
name 函数名,模型靠它来匹配 用下划线命名法,别用驼峰
description 函数描述,越详细越好 写清楚什么场景下调用
parameters 参数定义,遵循 JSON Schema 每个参数都要写 description
required 必填参数列表 能设默认值的就别放 required

小技巧:description 字段非常关键。模型靠它来判断「这个函数适不适合当前场景」。我习惯在 description 里写清楚边界条件,比如「仅支持中国城市,国外城市返回错误」。这样能减少模型乱调用的概率。

3.3 多工具并行调用

早期的 Function Calling 一次只能调一个函数。但现在主流模型都支持 并行调用——一次回复里包含多个函数调用请求。

举个例子。用户说:「帮我查一下北京和上海的天气,然后给张三发邮件汇报结果。」

模型可能会返回这样的结构:

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_1",
          "type": "function",
          "function": {
            "name": "get_weather",
            "arguments": "{\"city\": \"北京\", \"date\": \"2024-01-15\"}"
          }
        },
        {
          "id": "call_2",
          "type": "function",
          "function": {
            "name": "get_weather",
            "arguments": "{\"city\": \"上海\", \"date\": \"2024-01-15\"}"
          }
        },
        {
          "id": "call_3",
          "type": "function",
          "function": {
            "name": "send_email",
            "arguments": "{\"to\": \"zhangsan@example.com\", \"subject\": \"天气汇报\", \"body\": \"详见附件\"}"
          }
        }
      ]
    }
  }]
}

三个调用之间没有依赖关系,你可以并行执行它们。这大大提升了效率。

注意:并行调用虽然快,但要注意 API 限流。我曾经在一个项目里,模型一次返回了 20 个并行调用请求,直接把第三方天气 API 打爆了。建议你在代码里加个并发数控制,比如最多同时执行 5 个。

3.4 实际项目中的避坑指南

做了几个项目后,我总结了一些血泪教训:

  • 参数校验不能省:模型生成的参数不一定合法。比如日期格式可能不对,城市名可能拼错。我习惯在函数入口做一层校验。
  • 超时处理要到位:有些函数执行时间很长(比如调用第三方 API),模型可能等不及。建议设置超时时间,超时后返回一个友好的错误信息。
  • 错误信息要结构化:函数执行失败时,返回的错误信息最好也带个 code 和 message,方便模型理解。比如 {"error": {"code": "RATE_LIMIT", "message": "请求过于频繁"}}。
  • 上下文管理:每次函数调用的结果都会塞回对话里。如果函数返回的数据很大(比如几万行表格),对话上下文会迅速膨胀。我一般会做摘要,只把关键信息返回给模型。

3.5 一个完整的调用示例

最后,给你看一个完整的 Python 实现片段。这是我常用的模式:

import json
from  import 

client = (="your-key")

# 1. 定义工具
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", "date"]
            }
        }
    }
]

# 2. 调用模型
response = client.chat.completions.create(
    model="",
    messages=[{"role": "user", "content": "北京今天天气怎么样?"}],
    tools=tools,
    tool_choice="auto"
)

# 3. 处理函数调用
message = response.choices[0].message
if message.tool_calls:
    for tool_call in message.tool_calls:
        func_name = tool_call.function.name
        args = json.loads(tool_call.function.arguments)
        # 执行你的函数
        result = your_function(func_name, args)
        # 把结果返回给模型
        messages.append({
            "tool_call_id": tool_call.id,
            "role": "tool",
            "content": json.dumps(result)
        })
    # 再次调用模型生成最终回复
    final_response = client.chat.completions.create(
        model="",
        messages=messages
    )
    print(final_response.choices[0].message.content)

这个模式很通用。你只需要替换 your_function 的实现,就能接入任意工具。

核心要点回顾:

  • Function Calling 是模型输出结构化调用请求,不是模型执行代码
  • Schema 定义要详细,description 是关键
  • 并行调用能提升效率,但要注意限流
  • 参数校验、超时处理、错误结构化,一个都不能少