知识目录 / 大模型协议

Function Calling协议与调用过程深度剖析

全面解析大模型工具调用(Function Calling)的核心协议、消息结构、完整调用流程及最佳实践,助你构建强大的AI Agent。

Function Calling(函数调用)是大模型从“对话者”升级为“行动者”的关键能力。它允许模型在需要时,结构化地请求调用外部工具(函数、API),并将结果整合到对话中,从而极大扩展了模型的能力边界。本文将深入解析其协议细节与完整调用流程,并对比不同协议格式中的实现差异。

一、Function Calling/Tools 是什么?

要知道,大模型很聪明,但它有几个天生的限制:

  1. 无法访问实时信息:它不知道今天的天气、最新的新闻
  2. 无法执行操作:它不能帮你发邮件、不能帮你操作文件
  3. 知识有截止日期:它不知道今天发生了什么
  4. 无法访问私有数据:它不知道你的数据库、你的文件系统

工具就是大模型的"手"和"脚"。 有了工具,智能体就能:查询数据库、调用外部 API、操作文件系统、发送消息——做任何你想让它做的事情。

工具让大模型从"只能聊天"变成"能做实事"。

Function Calling 不是让模型直接执行代码,而是让模型生成一个结构化的请求,表明“我需要调用名为 X 的函数,并传入参数 Y 和 Z”。真正的函数执行由你的应用程序(客户端)完成。

核心价值:

  • 连接真实世界:获取实时数据(天气、股价)、操作外部系统(发送邮件、创建日历事件)。
  • 精确性与可控性:模型输出结构化的 JSON 参数,比自然语言描述更精确、不易出错。
  • 安全性:应用层可以审核、过滤、限制模型请求调用的函数和参数,再执行。

你可以通过阅读快速补全基础概念的信息差 来快速了解他在整个大模型交互中的定位。

Function Calling 的工作原理

整个过程可以分为四个步骤:

第一步:工具描述 你需要告诉大模型有哪些工具可用。每个工具需要提供:

  • 工具名称(name)
  • 工具描述(description)
  • 参数定义(parameters)

第二步:用户提问 用户向智能体提出问题,比如"帮我整理桌面"。

第三步:大模型决策 大模型分析用户的问题,决定是否需要调用工具。如果需要,它会返回:

  • 工具名称(比如 list_files
  • 工具参数(比如 path: "/Users/xxx/Desktop"

第四步:执行并返回 你的代码执行这个工具,把结果返回给大模型。大模型拿到结果后,继续思考并给出最终回答。

整个流程就像这样:

text
用户: "帮我整理桌面"
    ↓
大模型思考: "我需要先扫描桌面上的文件"
    ↓
大模型返回: {
    "tool": "list_files",
    "parameters": {
        "path": "/Users/xxx/Desktop"
    }
}
    ↓
你的代码执行: list_files("/Users/xxx/Desktop")
    ↓
你的代码返回: "桌面上有 15 个文件:..."
    ↓
大模型继续思考: "我需要把这些文件分类..."

二、协议核心:消息结构的变化

Function Calling 引入了新的消息角色和字段,使对话流变得更加丰富。不同协议格式在实现上有显著差异。

1. 工具定义(Tools)

在发起对话前,你需要向模型描述可用的工具。这通过请求中的 tools 参数完成。

工具定义示例(OpenAI 格式):

json
{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_current_weather",
        "description": "获取指定位置的当前天气",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "城市名称,例如:北京"
            },
            "unit": {
              "type": "string",
              "enum": ["celsius", "fahrenheit"],
              "description": "温度单位"
            }
          },
          "required": ["location"]
        }
      }
    }
  ]
}

工具定义示例(Anthropic 格式):

json
{
  "tools": [
    {
      "name": "get_current_weather",
      "description": "获取指定位置的当前天气",
      "input_schema": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "城市名称,例如:北京"
          },
          "unit": {
            "type": "string",
            "enum": ["celsius", "fahrenheit"],
            "description": "温度单位"
          }
        },
        "required": ["location"]
      }
    }
  ]
}

2. 模型响应中的工具调用请求

当模型判断需要调用工具时,其回复中会包含工具调用请求。不同格式的表示方式差异很大。

OpenAI Chat Completions / Responses 格式: 模型回复的 assistant 消息中包含 tool_calls 字段。

json
{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    {
      "id": "call_abc123",
      "type": "function",
      "function": {
        "name": "get_current_weather",
        "arguments": "{\"location\": \"北京\", \"unit\": \"celsius\"}"
      }
    }
  ]
}

Anthropic Messages 格式: 模型回复的 assistant 消息中包含 tool_use 内容块。

json
{
  "role": "assistant",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_abc123",
      "name": "get_current_weather",
      "input": {"location": "北京", "unit": "celsius"}
    }
  ]
}

关键区别:

  • 结构:OpenAI 使用 tool_calls 数组,Anthropic 使用 content 数组中的 tool_use 块。
  • 参数:OpenAI 的 arguments 是 JSON 字符串,Anthropic 的 input 是 JSON 对象。
  • ID:两者都使用唯一 ID 关联调用和结果。

3. 工具执行结果返回

你的应用程序执行函数后,需要将结果返回给模型。不同格式使用不同的消息角色。

OpenAI Chat Completions / Responses 格式: 使用 tool 角色消息。

json
{
  "role": "tool",
  "tool_call_id": "call_abc123",
  "name": "get_current_weather",
  "content": "{\"temperature\": 22, \"unit\": \"celsius\", \"description\": \"晴朗\"}"
}

Anthropic Messages 格式: 使用 user 角色消息,包含 tool_result 内容块。

json
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_abc123",
      "content": "{\"temperature\": 22, \"unit\": \"celsius\", \"description\": \"晴朗\"}"
    }
  ]
}

关键区别:

  • 消息角色:OpenAI 使用专门的 tool 角色,Anthropic 使用 user 角色。
  • 内容结构:OpenAI 使用简单的 content 字符串,Anthropic 使用 tool_result 内容块。
  • 关联方式:OpenAI 通过 tool_call_id,Anthropic 通过 tool_use_id

4. 模型的最终回复

模型收到工具结果后,会将其整合,并生成最终面向用户的自然语言回复。

最终的 assistant 消息示例(两种格式类似):

json
{
  "role": "assistant",
  "content": "北京现在的天气是晴朗,气温22摄氏度。"
}

三、完整调用流程(时序图)

下面是一个完整的 Function Calling 交互流程:

text
序列图
    participant 用户
    participant 客户端应用
    participant 大模型API
    participant 外部函数

    用户->>客户端应用: 发送问题:“北京今天天气怎么样?”
    客户端应用->>大模型API: 请求(messages + tools定义)
    大模型API-->>客户端应用: 响应(assistant消息,含工具调用请求)
    客户端应用->>外部函数: 调用 get_current_weather(location=“北京”)
    外部函数-->>客户端应用: 返回天气数据
    客户端应用->>大模型API: 发送工具执行结果
    大模型API-->>客户端应用: 最终响应(assistant消息,自然语言回复)
    客户端应用-->>用户: 显示:“北京天气晴朗,22℃。”

关键步骤解析:

  1. 工具定义先行:每次请求都可以(也应该)携带 tools 定义,告诉模型它有哪些“武器”可用。
  2. 模型决策:模型根据用户问题和工具描述,自主决定是否调用、调用哪个工具、传入什么参数。这是模型的“推理”能力体现。
  3. 客户端执行重要! 模型不执行代码,它只输出调用意图。执行权在你的应用手中。这保证了安全性和可控性。
  4. 结果回传:将执行结果以特定格式回传,模型才能知道执行结果。
  5. 整合回复:模型基于工具返回的数据,生成最终的回答。

四、并行函数调用(Parallel Function Calling)

现代模型支持一次请求中调用多个函数,这些调用可以并行执行以节省时间。

OpenAI 格式示例(并行调用):

json
{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    {
      "id": "call_abc",
      "type": "function",
      "function": {"name": "get_weather", "arguments": "{\"location\": \"北京\"}"}
    },
    {
      "id": "call_def",
      "type": "function",
      "function": {"name": "get_weather", "arguments": "{\"location\": \"上海\"}"}
    }
  ]
}

Anthropic 格式示例(并行调用):

json
{
  "role": "assistant",
  "content": [
    {"type": "tool_use", "id": "toolu_abc", "name": "get_weather", "input": {"location": "北京"}},
    {"type": "tool_use", "id": "toolu_def", "name": "get_weather", "input": {"location": "上海"}}
  ]
}

你需要为每个工具调用分别执行函数,并返回对应的结果(通过 ID 关联)。

五、最佳实践与陷阱

最佳实践:

  1. 清晰的函数描述description 和参数描述要准确,这是模型决定何时调用的主要依据。
  2. 严格的参数校验:模型生成的参数可能有误,务必校验类型、范围,防止注入攻击。
  3. 优雅的错误处理:函数执行可能失败。将错误信息作为工具结果返回,模型通常能理解并告知用户。
  4. 控制工具数量:提供的工具过多会增加模型决策难度和 token 消耗。按需提供。
  5. 管理对话历史:完整的工具调用链都需要保留在对话历史中,以保证上下文连贯。
  6. 注意格式差异:不同协议格式的工具调用实现差异很大,务必查阅官方文档。

常见陷阱:

  • 幻觉调用:模型可能“幻想”出一个不存在的工具名或参数。确保你的应用只响应 tools 定义中存在的函数。
  • 无限循环:模型可能陷入“调用-结果-再调用”的循环。设置最大调用轮次限制。
  • 参数格式错误:模型输出的参数可能格式有误。做好解析失败的处理。
  • 格式混淆:在不同协议格式间切换时,容易混淆工具调用和结果返回的格式。

六、与协议角色的关系

在 Function Calling 流程中,角色有了新的内涵:

  • system:可以定义工具使用的总体策略(如:“优先使用本地数据库,再调用远程API”)。
  • user:提出需要工具才能解决的需求。
  • assistant:扮演“调度员”角色,决定调用哪个工具,并解析结果。
  • tool / tool_result:代表工具的执行结果。它扩展了对话的参与者。

在不同协议格式中的差异:

  • OpenAI 格式:使用专门的 tool 角色消息返回结果。
  • Anthropic 格式:使用 user 角色消息中的 tool_result 内容块返回结果。
  • 角色复用:Anthropic 的设计复用了 user 角色,而 OpenAI 引入了新角色。

七、总结

Function Calling 协议通过引入工具定义、工具调用请求和工具执行结果,构建了一个完整的“模型-工具”交互循环。不同协议格式(OpenAI Chat Completions/Responses 与 Anthropic Messages)在实现细节上差异显著,但核心思想一致:让模型能够安全、可控地与外部世界交互。

掌握这一协议,意味着你不再仅仅是在使用一个聊天机器人,而是在编排一个能够思考和行动的智能系统


系列文章导航:

  1. 大模型基础协议概览:OpenAI Chat Completions、OpenAI Responses与Anthropic Messages格式解析
  2. 上下文角色深度解析:System、User、Assistant与Developer
  3. Function Calling协议与调用过程深度剖析(本文)

本文为“大模型底层通信协议深究”系列第三篇。