1. 从“聊天”到“干活”:Function Calling 的本质跃迁

如果你在过去一年里深度使用过 ChatGPT、Claude 或者国内的大模型,一定有过这样的体验:你跟它聊得天花乱坠,它也能对答如流,但一旦你想让它帮你做点“实事”,比如查一下明天的天气、给你的购物车算个总价、或者把一段对话内容整理成表格发到你的邮箱,它多半会礼貌地告诉你:“作为一个AI模型,我无法直接执行这个操作。” 这种感觉就像你雇了一个知识渊博的顾问,他什么都懂,但就是不会动手,所有事情都得你听完他的建议后,自己再去操作一遍。

Function Calling 的出现,彻底改变了这个局面。它不是一个具体的函数,而是一种标准化的“协议”或“能力”。简单来说,它让大模型从一个“健谈的顾问”变成了一个“能听懂指令并指挥工具干活的管家”。模型本身依然不直接操作外部世界(比如它不能真的去访问天气网站),但它学会了识别你的自然语言指令中隐含的“意图”,并将这个意图精准地“翻译”成对某个具体工具(我们称之为“函数”或“API”)的调用请求。然后,由你的程序去执行这个工具,并把结果返回给模型,模型再组织成自然语言回复你。这一来一回,就完成了从“说”到“做”的闭环。

为什么这件事如此重要?因为它解决了大模型落地应用的核心瓶颈—— 连接现实世界 。没有 Function Calling,大模型是信息孤岛,它的价值仅限于文本生成。有了 Function Calling,大模型就成了一个万能的中枢大脑,可以调度无数的外部工具和服务(数据库、计算引擎、邮件系统、硬件设备等),真正融入业务流程和用户体验中。我们常说的 AI Agent(智能体) ,其核心能力之一就是通过 Function Calling 来使用工具。所以,掌握 Function Calling,是构建实用化 AI 应用、迈向 AI Agent 开发的必经之路。

2. 核心原理拆解:大模型如何学会“发号施令”

要理解 Function Calling,我们需要暂时跳出代码,看看它背后大模型与开发者之间是如何“默契配合”的。这个过程并非魔法,而是一套设计精巧的交互协议。

2.1 交互流程的三步舞曲

一次完整的 Function Calling 交互,通常包含三个核心步骤,我把它比作一场精心编排的“三步舞曲”:

第一步:开发者“亮出工具箱”(定义函数) 在向大模型发送用户问题之前,我们(开发者)需要先告诉模型:“嗨,我这里有这些工具你可以用。” 这个“告诉”的方式,就是以 JSON Schema 的形式,描述一个或多个函数的名称、功能说明、以及所需的参数及其类型。例如,我们定义一个 get_current_weather 的函数,描述是“获取指定城市的当前天气”,它有一个参数叫 location ,类型是字符串。这一步的关键在于描述要清晰、准确,因为模型完全依赖这个描述来理解工具的用途。

第二步:大模型“理解并开单”(解析意图并返回调用请求) 当用户提出一个问题,比如“北京天气怎么样?”时,我们将用户问题和我们定义好的函数列表一起发送给大模型。大模型会进行如下思考:

  1. 用户的问题是否需要调用我已知的工具来解决?(意图识别)
  2. 如果需要,哪个工具最匹配?(函数选择)
  3. 调用这个工具,需要哪些具体信息?(参数提取)

如果模型判断需要调用函数,它 不会直接执行函数 ,而是会返回一个结构化的 JSON 对象。这个对象里包含了它“想”调用的函数名称,以及它从用户问题中提取并填充好的参数。例如: {"name": "get_current_weather", "arguments": {"location": "北京"}} 。注意,此时函数并没有被真正执行,模型只是返回了一个“调用指令单”。

第三步:程序“执行并反馈”(执行函数并返回结果) 我们的应用程序收到这个 JSON 指令后,就会在本地(或远程)找到对应的 get_current_weather 函数,传入参数“北京”,真正执行它。执行后,我们会得到一个结构化的结果,比如 {"temperature": 22, "unit": "celsius", "description": "晴朗"} 。然后,我们将这个结果作为新的信息,再次发送给大模型,并请它根据这个结果来生成面向用户的最终回答。模型此时会说:“北京目前天气晴朗,气温22摄氏度。”

2.2 关键设计:结构化输出与“零样本”学习

Function Calling 的实现,依赖于大模型两项核心能力的结合:

  1. 强大的结构化信息抽取能力 :大模型,特别是 GPT-4 这个级别的模型,在理解自然语言并从中精准提取结构化信息(如时间、地点、人名、事件)方面表现卓越。Function Calling 本质上就是将这种能力标准化、定向化。我们通过函数描述,引导模型去关注和提取我们关心的那几类参数。

  2. 基于描述的“零样本”函数理解 :我们不需要给模型提供成千上万个调用示例来训练它。我们只需要用自然语言清晰地描述函数是干什么的、需要什么参数,模型就能在“零样本”(即没有见过该函数具体调用例子)的情况下,正确理解并使用它。这得益于大模型在预训练阶段获得的、对世界知识的通用理解能力。

注意 :这里有一个非常重要的认知纠正。Function Calling 并不是在模型内部“运行”了你的代码。模型对函数一无所知,它只是根据你的描述,做了一个“模式匹配”和“信息填充”的工作。真正的执行权始终牢牢掌握在你的应用程序手中。这既是出于安全考虑(防止模型执行恶意代码),也使得架构更加清晰(业务逻辑与模型逻辑分离)。

3. 主流平台实战:OpenAI、Anthropic 与国产模型的实现

理论讲清楚了,我们来看看具体怎么干。不同的大模型平台对 Function Calling 的实现细节略有不同,但核心思想一致。我将以最主流的 OpenAI(GPT)和快速崛起的 Anthropic(Claude)为例,并简要对比国产主流模型的现状。

3.1 OpenAI / Azure OpenAI API 实现详解

OpenAI 是 Function Calling 的提出者和标准定义者,其 API 最为成熟。在最新的 Chat Completions API 中,通过 tools 参数来定义函数。

第一步:定义工具(函数)列表 你需要准备一个 tools 数组,其中每个元素描述一个函数。 type 固定为 "function" ,核心在 function 对象内的描述。

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "获取指定城市的当前天气信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市名称,例如:北京、San Francisco"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "温度单位,默认为摄氏度(celsius)"
                    }
                },
                "required": ["location"]
            }
        }
    }
]

第二步:发起对话并处理模型响应 将用户消息和 tools 一起发送给 API。关键是要设置 tool_choice 参数。如果设为 "auto" ,模型将自行决定是否调用函数;如果设为 {"type": "function", "function": {"name": "xxx"}} ,则可以强制要求模型调用特定函数。

import openai
from openai import OpenAI

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

response = client.chat.completions.create(
    model="gpt-3.5-turbo", # 或 gpt-4-turbo
    messages=[{"role": "user", "content": "上海今天热吗?"}],
    tools=tools,
    tool_choice="auto", # 让模型自主决定
)

message = response.choices[0].message

第三步:判断并执行函数调用 检查响应中是否包含 tool_calls 。如果有,则遍历每个调用请求,执行对应的本地函数,并将结果收集起来。

if message.tool_calls:
    # 准备一个列表来存放所有工具调用的结果
    tool_messages = []
    for tool_call in message.tool_calls:
        function_name = tool_call.function.name
        function_args = json.loads(tool_call.function.arguments)

        # 根据 function_name 找到并执行对应的本地函数
        if function_name == "get_current_weather":
            location = function_args.get("location")
            unit = function_args.get("unit", "celsius")
            # 调用你的真实天气API或函数
            weather_result = get_real_weather(location, unit)
            # 将结果格式化为模型期望的格式
            tool_messages.append({
                "role": "tool",
                "content": json.dumps(weather_result), # 结果必须是字符串
                "tool_call_id": tool_call.id # 必须关联对应的调用ID
            })

    # 第四步:将工具执行结果送回给模型,让它生成最终回答
    second_response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "user", "content": "上海今天热吗?"},
            message, # 包含原始 tool_calls 的助理消息
            *tool_messages # 展开所有工具执行结果
        ]
    )
    final_answer = second_response.choices[0].message.content
    print(final_answer) # 输出:“上海今天天气多云,气温28摄氏度,体感较热。”
else:
    # 如果模型没有调用工具,直接输出其回复
    print(message.content)

实操心得:

  • 描述(description)是灵魂 :函数的 description 和参数的 description 至关重要。要用清晰、无歧义的自然语言撰写。例如,“获取天气”就不如“获取指定城市的实时温度、天气状况和湿度”来得精确。
  • 处理多个工具调用 :模型在一次回复中可能同时调用多个工具( parallel function calling )。你的代码需要能处理一个 message 中包含多个 tool_call 的情况,并确保将每个结果通过正确的 tool_call_id 关联回去。
  • 温度(temperature)参数 :对于需要稳定输出结构化数据的 Function Calling 场景,建议将 temperature 设置为 0 或接近 0 的值,以减少输出的随机性,确保参数提取的准确性。

3.2 Anthropic Claude Messages API 实现

Anthropic 的 Claude 3 系列模型也支持类似功能,但术语和 API 格式有所不同。它称之为 Tool Use 。整体流程相似,但细节有差异。

第一步:定义工具(Tools) 在 Claude API 中,工具定义在 tools 参数下,结构略有不同。

tools = [{
    "name": "get_current_weather",
    "description": "获取指定城市的当前天气信息",
    "input_schema": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "城市名称"
            },
            "unit": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"],
                "default": "celsius"
            }
        },
        "required": ["location"]
    }
}]

第二步:发起对话并处理 Block 响应 Claude 的响应消息 ( Message ) 包含一个 content 数组,其中可能包含 TextBlock ToolUseBlock

import anthropic

client = anthropic.Anthropic(api_key="your-api-key")

response = client.messages.create(
    model="claude-3-sonnet-20240229",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "对比一下北京和杭州的天气。"}]
)

# 检查响应内容
for block in response.content:
    if block.type == ‘text‘:
        print(f“Text: {block.text}“)
    elif block.type == ‘tool_use‘:
        print(f“Tool to use: {block.name}“)
        print(f“Tool input: {block.input}“)
        # 这里 block.id 相当于 OpenAI 的 tool_call_id

第三步:执行工具并提交结果 你需要收集所有 ToolUseBlock ,执行对应函数,然后将结果以 ToolResultBlock 的形式,作为新的用户消息提交给 Claude 进行后续处理。

tool_results = []
for block in response.content:
    if block.type == ‘tool_use‘:
        if block.name == “get_current_weather“:
            weather = get_real_weather(block.input[“location“])
            tool_results.append({
                “type“: “tool_result“,
                “tool_use_id“: block.id, # 关联 ID
                “content“: json.dumps(weather)
            })

# 将工具结果作为后续对话输入
if tool_results:
    follow_up_response = client.messages.create(
        model=“claude-3-sonnet-20240229“,
        max_tokens=1024,
        messages=[
            {“role“: “user“, “content“: “对比一下北京和杭州的天气。“},
            response, # 包含 ToolUseBlock 的助理消息
            {“role“: “user“, “content“: tool_results} # 提交工具结果
        ]
    )
    print(follow_up_response.content[0].text)

与 OpenAI 的主要差异点:

  1. 术语 :OpenAI 叫 tools / function calling , Claude 叫 tools / tool use
  2. 响应结构 :OpenAI 的调用信息在 message.tool_calls 里;Claude 的则在 content 数组中以 ToolUseBlock 形式出现。
  3. 结果提交 :OpenAI 要求将结果以 role: tool 的消息格式插入历史;Claude 要求将结果作为新的 user 消息中的 ToolResultBlock 提交。
  4. 并行调用 :两者都支持,但处理代码的编写方式因上述结构差异而不同。

3.3 国产大模型的适配与现状

目前,国内主流大模型平台(如百度文心、阿里通义、智谱GLM、月之暗面Kimi等)也都在快速跟进 Function Calling 能力。通常有以下几种实现方式:

  1. 兼容 OpenAI 格式 :这是最友好的方式。许多国产模型 API 在设计上力求与 OpenAI API 兼容,这意味着你为 GPT 编写的 tools 定义和调用代码,只需更换 API Endpoint 和 API Key,就能在一定程度上直接运行。例如,部分平台提供的“Chat Completions 兼容接口”。
  2. 自有格式 :部分平台提供了自己定义的函数调用格式,通常也会提供详细的 SDK 和文档。其核心流程(定义、调用、执行、回调)万变不离其宗,学习成本在于熟悉其特定的 JSON 结构字段名。
  3. 插件/工具平台 :一些平台通过更高层次的“插件市场”或“工具平台”来提供类似能力,开发者以配置化方式上传工具,模型通过平台调度。这种方式对开发者更简单,但灵活性可能不如直接调用 API。

给开发者的建议 :在开始一个涉及 Function Calling 的项目前,先调研目标模型平台的官方文档,查看其“工具调用”或“函数调用”相关章节。优先选择提供 OpenAI 兼容接口的平台,可以最大程度复用代码和降低迁移成本。同时,要关注不同模型在意图理解准确率、复杂参数提取能力上的差异,必要时需要调整函数描述或加入少量示例(few-shot)进行引导。

4. 高级模式与架构设计:构建健壮的 AI 应用

掌握了基础调用,我们就可以探讨更复杂的应用模式了。单个函数调用解决单一问题,而现实世界的任务往往是多步骤、有条件分支的。这就需要更高级的设计模式。

4.1 多轮对话与状态管理

Function Calling 天然支持多轮对话。例如,用户问:“帮我订一张明天从北京飞上海的机票。”模型可能先调用 search_flights 函数,返回一堆航班选项。用户接着说:“要下午出发的。”这时,你需要将整个对话历史(包括之前的函数调用和结果)连同新的用户指令一起发送给模型。模型会理解上下文,它可能不会再次调用搜索,而是从之前的搜索结果中筛选,或者调用一个新的 filter_flights 函数。

关键点:状态管理。 你的应用程序需要维护完整的对话历史记录。一个典型的对话状态( ConversationState )应该包括:

  • 用户和助理的文本消息列表。
  • 历史上所有发生过的工具调用( tool_calls )及其结果( tool_messages )。
  • 任何自定义的会话上下文(如用户ID、会话偏好等)。

每次与模型交互时,都将这个完整的状态作为 messages 发送过去。模型会根据整个上下文来决定下一步是直接回复,还是调用新工具。

4.2 并行与串行调用策略

  • 并行调用 :当用户的一个请求涉及多个独立任务时,模型可以一次性返回多个 tool_calls 。例如,“查一下北京天气和上海股市大盘”。你的后端应该并行执行 get_weather get_stock_index 这两个函数,以降低总延迟。所有结果返回后,再一次性提交给模型进行总结。
  • 串行调用 :很多任务具有依赖性,必须串行。例如,“查一下特斯拉的股价,如果超过200美元就发邮件提醒我”。这需要先调用 get_stock_price(“TSLA”) ,得到结果后,在下一轮对话中,模型根据结果判断条件成立,再调用 send_email 函数。这需要你的程序能驱动多轮交互循环。

架构设计模式:控制流引擎 对于复杂的串行或带条件分支的任务,一个常见的架构是引入一个轻量级的“控制流引擎”或“工作流引擎”。这个引擎负责:

  1. 维护与模型的对话循环。
  2. 解析模型的响应,判断是文本回复还是工具调用。
  3. 执行工具调用,管理其输入输出。
  4. 根据工具执行结果和预定义的业务逻辑(或让模型决策),决定下一步是继续询问模型还是执行其他操作。
  5. 这种模式是构建复杂 AI Agent 的雏形。

4.3 错误处理与用户反馈

工具执行可能失败(网络超时、API限流、参数错误等)。健全的 Function Calling 应用必须有完善的错误处理机制。

  1. 工具执行失败 :当本地函数执行抛出异常时,不要崩溃。应该将错误信息(例如“天气服务暂时不可用”)作为该工具调用的“结果”返回给模型。模型有能力理解错误,并可能生成对用户友好的解释,如“抱歉,天气查询服务出了点问题,请您稍后再试。”
  2. 模型“幻觉”调用 :有时模型可能会错误地调用一个不存在的函数,或为参数生成完全不合逻辑的值(如 location: 12345 )。你的代码需要在执行前进行验证:
    • 检查 function_name 是否在已注册的工具列表中。
    • 使用 JSON Schema 验证器(如 jsonschema 库)检查 arguments 是否符合预定义的模式。
    • 对于关键参数,进行业务逻辑验证(如城市名是否在支持列表中)。 如果验证失败,可以将验证错误信息作为结果返回给模型,让它有机会纠正。
  3. 用户澄清 :如果模型提取的参数模糊不清(例如 location: “南方” ),你的工具函数可能无法处理。一种策略是让工具返回一个特定的错误码或消息,模型收到后,会主动向用户提问以澄清:“您具体想查询南方哪个城市的天气呢?”

5. 实战:从零构建一个智能旅行助手 Agent

让我们综合运用以上知识,构建一个简单的“智能旅行助手”AI Agent。它能理解用户关于旅行的复杂请求,并通过调用多个外部工具来完成。

5.1 定义工具集

我们为助手装备三个核心工具:

  1. search_flights :查询航班。
  2. search_hotels :查询酒店。
  3. get_city_info :获取城市基本信息(如天气、景点,这里简化为一个函数)。
tools = [
    {
        “type“: “function“,
        “function“: {
            “name“: “search_flights“,
            “description“: “根据出发地、目的地、日期搜索航班信息。日期格式为 YYYY-MM-DD。“,
            “parameters“: {
                “type“: “object“,
                “properties“: {
                    “departure_city“: {“type“: “string“, “description“: “出发城市“},
                    “arrival_city“: {“type“: “string“, “description“: “到达城市“},
                    “date“: {“type“: “string“, “description“: “出发日期,YYYY-MM-DD“}
                },
                “required“: [“departure_city“, “arrival_city“, “date“]
            }
        }
    },
    {
        “type“: “function“,
        “function“: {
            “name“: “search_hotels“,
            “description“: “根据城市和入住/离店日期搜索酒店信息。日期格式为 YYYY-MM-DD。“,
            “parameters“: {
                “type“: “object“,
                “properties“: {
                    “city“: {“type“: “string“, “description“: “城市名称“},
                    “check_in“: {“type“: “string“, “description“: “入住日期“},
                    “check_out“: {“type“: “string“, “description“: “离店日期“}
                },
                “required“: [“city“, “check_in“, “check_out“]
            }
        }
    },
    {
        “type“: “function“,
        “function“: {
            “name“: “get_city_info“,
            “description“: “获取城市的基本信息,如当前天气、主要景点等。“,
            “parameters“: {
                “type“: “object“,
                “properties“: {
                    “city_name“: {“type“: “string“, “description“: “城市名称“}
                },
                “required“: [“city_name“]
            }
        }
    }
]

5.2 实现主控循环与工具执行器

我们将创建一个 TravelAssistant 类来管理整个对话状态和流程。

import json
import openai
from typing import List, Dict, Any

class TravelAssistant:
    def __init__(self, api_key: str, model: str = “gpt-3.5-turbo“):
        self.client = openai.OpenAI(api_key=api_key)
        self.model = model
        self.conversation_history: List[Dict] = [] # 存储完整对话历史

    def add_user_message(self, content: str):
        “““添加用户消息到历史。“““
        self.conversation_history.append({“role“: “user“, “content“: content})

    def _execute_tool(self, tool_name: str, arguments: Dict) -> str:
        “““模拟执行工具。在实际应用中,这里会调用真实的API。“““
        if tool_name == “search_flights“:
            # 模拟航班搜索
            return json.dumps({
                “flights“: [
                    {“airline“: “航司A“, “flight_no“: “CA1234“, “dep_time“: “08:00“, “price“: 1200},
                    {“airline“: “航司B“, “flight_no“: “MU5678“, “dep_time“: “14:00“, “price“: 900}
                ]
            })
        elif tool_name == “search_hotels“:
            # 模拟酒店搜索
            return json.dumps({
                “hotels“: [
                    {“name“: “酒店X“, “star“: 4, “price_per_night“: 500},
                    {“name“: “酒店Y“, “star“: 5, “price_per_night“: 1200}
                ]
            })
        elif tool_name == “get_city_info“:
            # 模拟城市信息
            city = arguments.get(“city_name“)
            return json.dumps({
                “weather“: “晴朗,25℃“,
                “attractions“: [“景点1“, “景点2“]
            })
        else:
            return json.dumps({“error“: f“未知工具: {tool_name}“})

    def process_query(self, user_query: str) -> str:
        “““处理用户查询的核心循环。“““
        # 1. 添加用户消息到历史
        self.add_user_message(user_query)

        # 2. 调用模型,传入完整历史和工具定义
        response = self.client.chat.completions.create(
            model=self.model,
            messages=self.conversation_history,
            tools=tools,
            tool_choice=“auto“,
            temperature=0
        )

        assistant_message = response.choices[0].message
        # 3. 将助理的回复(可能包含文本或工具调用)加入历史
        self.conversation_history.append(assistant_message.to_dict())

        final_answer = None
        # 4. 检查是否需要调用工具
        if assistant_message.tool_calls:
            tool_messages = []
            for tool_call in assistant_message.tool_calls:
                func_name = tool_call.function.name
                func_args = json.loads(tool_call.function.arguments)
                print(f“[Agent 正在执行] {func_name}({func_args})“)

                # 执行工具
                tool_result = self._execute_tool(func_name, func_args)

                # 构造工具结果消息
                tool_messages.append({
                    “role“: “tool“,
                    “content“: tool_result,
                    “tool_call_id“: tool_call.id
                })

            # 5. 将所有工具结果加入历史
            self.conversation_history.extend(tool_messages)

            # 6. 再次调用模型,让它基于工具结果生成最终回复
            second_response = self.client.chat.completions.create(
                model=self.model,
                messages=self.conversation_history,
                temperature=0
            )
            final_message = second_response.choices[0].message
            self.conversation_history.append(final_message.to_dict())
            final_answer = final_message.content
        else:
            # 没有工具调用,直接返回文本回复
            final_answer = assistant_message.content

        return final_answer

# 使用助手
assistant = TravelAssistant(api_key=“your-key“)
answer = assistant.process_query(“我想下周五从北京飞上海,住两晚,推荐一下航班和酒店,顺便说说上海现在天气怎么样。“)
print(“助手回复:“, answer)

5.3 运行示例与解析

当你运行上面的代码,输入复杂查询时,模型可能会进行以下操作:

  1. 首先,它识别出三个子任务:查航班、查酒店、查天气。
  2. 它可能一次性并行调用 search_flights (北京,上海,下周五) 和 get_city_info (上海)。因为这两个任务没有依赖关系。
  3. 在得到航班和城市信息后,它发现查询酒店需要入住和离店日期。它可以从用户“住两晚”和航班日期中推断出日期,然后调用 search_hotels
  4. 最后,它汇总所有工具返回的结构化数据,生成一段连贯、友好的自然语言回复给你:“为您找到以下航班... 上海的天气晴朗25℃,推荐景点有... 根据您的行程,推荐以下酒店...”

这个简单的 Agent 已经具备了处理多步骤、有条件任务的能力。通过扩展工具集(如租车、景点门票、餐厅预订),它可以变得更强大。

6. 避坑指南与性能优化

在实际生产环境中应用 Function Calling,会遇到许多在教程中不会提及的“坑”。以下是我从多个项目中总结出的关键经验。

6.1 描述(Description)撰写的艺术

函数的 description 是模型理解工具用途的唯一途径。写得不好,模型就会用错。

  • 避免歧义 :“处理数据”是糟糕的描述。“根据用户ID从数据库查询其最近3个月的订单记录”是好的描述。
  • 明确输入输出 :在函数描述中,可以简要说明输入是什么,输出大概是什么。例如:“输入城市名,返回该城市当前温度、天气状况和湿度百分比。”
  • 参数描述要具体 location 的描述如果是“地点”,模型可能填入“我家门口”。改成“城市或机场的IATA代码(如‘北京’或‘PEK’)”,效果会好得多。
  • 利用枚举(enum)和默认值 :对于有限的选项,如单位 {“celsius“, “fahrenheit“} ,或分类 {“economy“, “business“} ,使用 enum 可以极大提高准确率。对于可选参数,设置合理的 default 值。

6.2 处理模型的不确定性

即使描述再完美,模型也可能出错。

  • 设置最大重试次数 :当模型返回的参数无法通过验证,或调用了错误的函数时,不要直接报错给用户。可以设计一个重试循环:将验证错误信息作为系统提示,重新向模型提问(例如:“上次调用失败,原因是参数XX格式错误。请根据以下正确格式重新生成调用。”),通常重试1-2次就能成功。
  • 提供少量示例(Few-shot) :对于极其复杂或容易出错的函数,可以在 messages 的系统提示或历史中,提供一两个正确调用该函数的示例对话。这能显著提升模型在复杂场景下的表现。
  • 后处理与修正 :对于模型提取的参数,在执行前进行后处理。例如,将“明天”转换为具体的日期,将“北上广”拆分为三个城市等。

6.3 成本与延迟优化

Function Calling 会增加 API 调用次数(一次用户查询可能触发多轮模型调用),进而增加成本和延迟。

  • 批量处理 :鼓励用户一次性提出完整需求,利用模型的并行调用能力,减少交互轮次。
  • 缓存工具结果 :对于相同参数的工具调用(如短时间内多次查询同一城市天气),在客户端或服务端实现缓存,避免重复调用外部 API。
  • 精简上下文 :虽然需要维护完整对话历史,但对于非常长的对话,可以考虑只保留最近N轮或总结之前的对话内容,以减少发送给模型的 token 数量,降低成本。
  • 模型选型 :对于工具调用本身(即解析意图、生成调用参数), gpt-3.5-turbo 在大多数场景下已经足够准确且成本更低。对于需要高度推理或总结复杂工具结果的最终回复,可以酌情使用 gpt-4

6.4 安全与权限考量

让模型决定调用哪个函数,存在潜在风险。

  • 最小权限原则 :只向模型暴露完成当前任务所必需的最少工具。例如,一个订餐助手不需要拥有“删除用户账户”的工具。
  • 参数校验与净化 :在执行任何工具前,必须对模型提供的参数进行严格的校验和净化,防止 SQL 注入、命令注入等攻击。永远不要相信模型的直接输入。
  • 用户确认 :对于具有重大影响或不可逆的操作(如发送邮件、支付、删除数据),即使在模型调用后,也应在真正执行前增加一层用户确认(例如,“我将为您发送这封邮件,确认吗?”)。

Function Calling 不仅仅是一个 API 特性,它代表了一种全新的、让 AI 融入现实工作流的范式。从简单的数据查询到复杂的多步骤自动化,它的边界只取决于你的工具库和想象力。现在,是时候为你的大模型装上“手和脚”,让它真正开始为你“干活”了。

更多推荐