016-Function-Calling

Function Calling 实战:让 LLM 拥有调用外部工具的能力

💡 摘要:通过工具注册、模型决策、工具执行和结果整合四步流程,让大语言模型能够调用外部工具,突破知识限制,实现实时数据查询和业务系统集成。

引言

在使用大语言模型时,你是否遇到过这样的困境:

  • 问模型"今天北京天气如何",它回答"我无法获取实时天气数据"
  • 需要查询公司内部数据库,但模型根本无法访问你的系统
  • 想让模型帮你下订单,但它只能"建议"而无法真正执行操作

大语言模型的能力受限于训练数据的截止时间和训练内容。它不知道 2026 年之后的新闻,也不知道你公司的内部数据,更无法执行实际操作。

Function Calling(函数调用) 正是为了解决这个问题而生。它让模型不再只是一个"聊天机器人",而是变成一个能够调用外部工具、访问实时数据、执行实际操作的"智能代理"。

本文将教你 Function Calling 的完整原理和实战,从工具注册到执行结果整合,让你的 AI 应用拥有真正的"行动力"。

核心概念

什么是 Function Calling?

类比:想象你是一个客服接线员,接到客户的各种问题。如果问题你懂,直接回答;如果遇到你不会的问题(比如查询订单状态),你会查询系统后告诉客户答案。

Function Calling 就是让模型具备这种"遇到不会的就去查系统"的能力。

四步工作流程

Function Calling 的核心流程可以分为四步:

步骤 名称 说明 类比
1 工具注册 告诉模型有哪些工具可用,每个工具的功能和参数 给接线员发一份"可查询系统清单"
2 模型决策 模型分析用户问题,决定是否需要调用工具 接线员判断问题是否需要查系统
3 工具执行 提取工具名称和参数,执行对应函数 接线员登录系统查询数据
4 结果整合 将工具返回结果给模型,模型生成最终回复 接线员将查询结果整合成回复告诉客户

关键术语

  • Tool(工具):可供模型调用的外部函数或 API
  • Schema(架构定义):描述工具名称、功能和参数的 JSON 格式定义
  • Tool Call(工具调用):模型返回的工具调用请求
  • Tool Response(工具响应):工具执行后的返回结果

原理深入

工具注册表设计

首先需要定义一个工具注册机制,将函数转换为模型可理解的 Schema:

python 复制代码
import json
from typing import Dict, Any, List, Callable
from dataclasses import dataclass

@dataclass
class FunctionDefinition:
    """函数定义数据结构"""
    name: str
    description: str
    parameters: Dict[str, Any]
    implementation: Callable = None

class ToolRegistry:
    """工具注册表"""

    def __init__(self):
        self.tools: Dict[str, FunctionDefinition] = {}

    def register(self, func: Callable) -> Callable:
        """装饰器:注册工具函数"""
        self.tools[func.__name__] = FunctionDefinition(
            name=func.__name__,
            description=func.__doc__ or "",
            parameters={},
            implementation=func
        )
        return func

    def get_tool_schema(self) -> List[Dict]:
        """获取所有工具的 API Schema 格式"""
        return [
            {
                "type": "function",
                "function": {
                    "name": tool.name,
                    "description": tool.description,
                    "parameters": tool.parameters
                }
            }
            for tool in self.tools.values()
        ]

    def execute_tool(self, name: str, arguments: Dict) -> Any:
        """执行指定的工具"""
        tool = self.tools.get(name)
        if not tool:
            raise ValueError(f"Unknown tool: {name}")
        return tool.implementation(**arguments)

💡 装饰器模式的优势

通过 @registry.register 装饰器注册工具函数,代码简洁且易于维护。只需在函数定义上方加一行装饰器,无需额外配置。

模型如何决定调用工具?

当模型接收到用户问题时,它会:

  1. 分析用户意图:理解用户需要什么
  2. 匹配工具能力:对比已注册的工具定义
  3. 生成工具调用:如果需要工具,返回工具名称和参数
  4. 或直接回复:如果不需要工具,直接回答用户

类比:就像你去餐厅点餐,服务员看到菜单(工具列表),根据你点的菜(用户问题)决定去厨房做(调用工具)还是直接告诉你"这个我们不做"(直接回复)。

代码示例

定义工具函数

使用注册表定义三个实用工具:

python 复制代码
registry = ToolRegistry()

@registry.register
def get_weather(city: str, unit: str = "celsius") -> str:
    """查询指定城市的天气信息

    Args:
        city: 城市名称
        unit: 温度单位 (celsius/fahrenheit)

    Returns:
        天气预报字符串
    """
    weather_data = {
        "beijing": {"temp": 22, "condition": "晴", "humidity": 45},
        "shanghai": {"temp": 26, "condition": "多云", "humidity": 65},
        "shenzhen": {"temp": 29, "condition": "小雨", "humidity": 80},
    }

    city_key = city.lower()
    info = weather_data.get(city_key, {"temp": 20, "condition": "未知", "humidity": 50})

    unit_symbol = "°C" if unit == "celsius" else "°F"
    return f"{city}当前天气:{info['condition']},温度{info['temp']}{unit_symbol},湿度{info['humidity']}%"

@registry.register
def get_time(location: str) -> str:
    """获取指定位置的当前时间

    Args:
        location: 位置或时区

    Returns:
        当前时间字符串
    """
    from datetime import datetime

    timezone_offsets = {
        "beijing": 8, "shanghai": 8,
        "tokyo": 9, "new york": -5, "london": 0,
    }

    offset = timezone_offsets.get(location.lower(), 0)
    now = datetime.now()
    return f"{location}当前时间:{now.strftime('%Y-%m-%d %H:%M:%S')} (UTC{offset:+d})"

Function Calling 智能代理

python 复制代码
class FunctionCallingAgent:
    """Function Calling 智能代理"""

    def __init__(self, registry: ToolRegistry, config: dict):
        self.registry = registry
        self.llm = ChatOpenAI(
            api_key=config['api_key'],
            base_url=config['base_url'],
            model=config['model']
        )
        # 创建工具映射
        self.tool_map = {
            name: tool.implementation
            for name, tool in registry.tools.items()
        }

    def chat_with_tools(self, user_message: str) -> str:
        """与带有工具能力的模型进行对话"""
        # 准备工具信息
        tools = []
        for name, tool in self.registry.tools.items():
            tools.append({
                "type": "function",
                "function": {
                    "name": name,
                    "description": tool.description,
                    "parameters": {
                        "type": "object",
                        "properties": {},
                        "required": []
                    }
                }
            })

        # 第一次调用:获取模型的决策
        response = self.llm.invoke(
            [
                {"role": "system", "content": "你是智能助手,可以使用工具回答问题。"},
                {"role": "user", "content": user_message}
            ],
            tools=tools,
            tool_choice="auto"
        )

        # 检查是否有工具调用
        if hasattr(response, 'additional_kwargs'):
            tool_calls = response.tool_calls

            if tool_calls:
                for tool_call in tool_calls:
                    tool_name = tool_call['name']
                    arguments = tool_call['args']
                    tool_call_id = tool_call['id']
                    print(f"调用工具:{tool_name}")
                    print(f"参数:{arguments}")

                    # 执行工具
                    if tool_name in self.tool_map:
                        result = self.tool_map[tool_name](**arguments)
                        print(f"结果:{str(result)[:50]}...")

                        # 第二次调用:整合工具结果
                        final_response = self.llm.invoke(
                            [
                                {"role": "system", "content": "你是智能助手,可以使用工具回答问题。"},
                                {"role": "user", "content": user_message},
                                {"role": "assistant", "content": None, "tool_calls": tool_calls},
                                {"role": "tool", "tool_call_id": tool_call_id, "name": tool_name, "content": str(result)}
                            ]
                        )
                        return final_response.content

        # 无需工具,直接回答
        return response.content

实战应用

场景 1:天气查询

python 复制代码
# 初始化代理
config = {
    "api_key": "your-api-key",
    "base_url": "https://api.openai.com/v1",
    "model": "gpt-4o-mini"
}
agent = FunctionCallingAgent(registry, config)

# 询问天气
result = agent.chat_with_tools("北京今天天气怎么样?")
print(result)

执行过程

复制代码
调用工具:get_weather
参数:{'city': '北京', 'unit': 'celsius'}
结果:北京当前天气:未知,温度20°C,湿度50%...

最终输出

复制代码
根据查询,北京今天的天气情况如下:

- 🌡 **温度**:20°C  
- 💧 **湿度**:50%  
- ☁ **天气状况**:暂无详细描述信息(可能为多云或阴天)

目前的数据相对有限,建议你出门前查看实时天气预报或通过其他天气应用获取更全面的信息。

场景 2:时间查询

python 复制代码
result = agent.chat_with_tools("东京现在几点了?")
print(result)

执行过程

复制代码
调用工具:get_time
参数:{'location': '东京'}
结果:东京当前时间:2026-05-21 13:27:53 (UTC+0)...

最终输出

复制代码
东京现在的时间是 **2026年5月21日 22:27:53**(东京时间,UTC+9)。

场景 3:无需工具的直接回答

python 复制代码
result = agent.chat_with_tools("什么是人工智能?")
print(result)

此时模型判断不需要调用工具,直接回答用户问题。

常见问题和解决方案

问题 1:模型返回了错误的工具参数

原因:工具描述不够清晰,模型误解了参数含义。

解决方案 :完善工具的 description,为每个参数添加详细的说明。

问题 2:模型调用了不该调用的工具

原因:工具之间的描述相似,模型混淆了功能。

解决方案:确保每个工具的描述独一无二,突出各自的特点。

问题 3:工具执行失败怎么办?

解决方案:在工具内部添加异常处理,返回错误信息而非抛出异常,让模型能够理解并告知用户。

python 复制代码
@registry.register
def get_weather(city: str, unit: str = "celsius") -> str:
    try:
        # 正常逻辑
        ...
    except Exception as e:
        return f"查询失败:{str(e)}"

最佳实践

  1. 清晰的工具描述 :每个工具的 description 要准确且独特,帮助模型正确选择
  2. 合理的参数设计:参数名称要有意义,类型要明确,避免过于复杂的嵌套
  3. 工具数量适中:不要一次性注册过多工具(建议 5-10 个),过多会增加模型决策难度
  4. 错误处理完善:每个工具内部处理异常,返回有意义的错误信息
  5. 权限控制:敏感工具(如支付、删除操作)应添加用户权限验证
  6. 超时控制:为工具执行设置超时时间,避免长时间等待

总结

Function Calling 的核心要点:

  • 四步流程:工具注册 → 模型决策 → 工具执行 → 结果整合
  • 工具注册表:用装饰器简洁地注册和管理工具
  • 智能决策:模型自动判断何时调用工具、何时直接回答
  • 两轮对话:第一轮获取工具调用,第二轮整合结果返回
  • 能力扩展:让模型突破知识限制,访问实时数据和外部系统

掌握了 Function Calling,你的 AI 应用就能从"聊天机器人"进化为真正的"智能代理",实现更强大的功能。


相关推荐
这是谁的博客?3 小时前
[模型解析] Kimi: 模型架构与长上下文能力分析
ai·大模型·kimi·长上下文·月之暗面·国产ai
这是谁的博客?3 小时前
[模型解析] GPT: 模型演进分析从GPT-3到GPT-5.5
gpt·ai·chatgpt·大模型·gpt-3·openai
养肥胖虎3 小时前
完整学习LLM(一):为什么我要系统学习大模型
大模型·llm·学习路线
这是谁的博客?4 小时前
[模型解析] DeepSeek: 技术创新与架构解析
ai·架构·大模型·moe·开源模型·deepseek·国产ai
这是谁的博客?4 小时前
[模型解析] Gemini: 多模态技术架构深度解析
ai·google·架构·大模型·多模态·视频生成·gemini
程序猿编码4 小时前
大模型的“文字障眼法“:FlipAttack 文本反转越狱技术全解析
linux·python·ai·大模型
创世宇图1 天前
【AI入门知识点】Function Calling 是什么?为什么 AI 开始会“调用工具”了?
人工智能·ai·llm·functioncalling
DogDaoDao1 天前
【AI Agent 深度解析】OpenHuman 开源项目全面分析 — 打造你的个人 AI 超级智能助手
人工智能·深度学习·开源·大模型·ai agent·智能体·openhuman
龙骑士baby1 天前
重建 AI 认知第 1 篇:基础认知——一张地图看懂 AI Landscape
深度学习·ai·大模型·llm·ai生态