工具调用与 Function Calling 实战

2024-11-18 编程开发

一、什么是 Function Calling

Function Calling(函数调用)是 LLM 的一项关键能力——模型在生成回复时,可以输出一个结构化的函数调用请求,而不是直接生成文本。这使得 Agent 能够与外部系统交互。

用户:"帮我查一下北京的天气"
LLM 输出:search_weather(city="北京")
工具执行:{"temperature": 22, "condition": "晴"}
LLM 最终输出:"北京今天 22°C,天气晴朗"

1.1 为什么需要工具调用

场景 LLM 做不到的 工具解决的
实时数据 训练数据有截止日期 联网搜索
精确计算 大数运算不准确 计算器工具
操作外部系统 无法直接操作 API 调用
私有数据 未在训练集中 数据库查询

二、Function Calling 的工作原理

2.1 完整流程

① 定义工具 Schema(JSON Schema 格式)
② 将 Schema 随 System Prompt 一起发给 LLM
③ LLM 推理 → 决定调用哪个工具 → 返回结构化参数
④ 解析参数 → 执行工具函数 → 获取结果
⑤ 将结果返回给 LLM → LLM 继续推理
⑥ 重复 ③~⑤ 直到任务完成

2.2 OpenAI 格式的工具定义

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的实时天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,如 北京、上海"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "温度单位"
                    }
                },
                "required": ["city"]
            }
        }
    }
]

2.3 执行工具调用


三、常见工具类型及实现

3.1 网络搜索

import requests
from duckduckgo_search import DDGS

def search_web(query: str, max_results: int = 5) -> List[Dict]:
    """搜索互联网获取最新信息"""
    with DDGS() as ddgs:
        results = []
        for r in ddgs.text(query, max_results=max_results):
            results.append({
                "title": r["title"],
                "url": r["href"],
                "snippet": r["body"]
            })
        return results

3.2 代码执行

import subprocess
import tempfile

def execute_python(code: str) -> str:
    """在沙箱中执行 Python 代码"""
    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".py", delete=False
    ) as f:
        f.write(code)
        f.flush()

        result = subprocess.run(
            ["python3", f.name],
            capture_output=True,
            text=True,
            timeout=30
        )

        if result.returncode == 0:
            return result.stdout
        else:
            return f"错误: {result.stderr}"

3.3 文件操作

import os

def read_file(path: str) -> str:
    """读取文件内容"""
    if not os.path.exists(path):
        return f"文件不存在: {path}"
    if os.path.getsize(path) > 1024 * 1024:
        return "文件超过 1MB 限制"
    with open(path, "r", encoding="utf-8") as f:
        return f.read()

def write_file(path: str, content: str) -> str:
    """写入文件"""
    os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
    with open(path, "w", encoding="utf-8") as f:
        f.write(content)
    return f"已写入 {len(content)} 字符到 {path}"

四、工具设计最佳实践

4.1 工具定义原则

  1. 单一职责:每个工具只做一件事
  2. 清晰的命名和描述:LLM 靠描述理解工具用途
  3. 参数校验:对输入参数做类型和范围校验
  4. 友好的错误返回:错误信息要能被 LLM 理解

4.2 工具注册模式


五、错误处理与重试策略

5.1 常见错误类型

错误类型 原因 处理方式
参数错误 LLM 生成的参数不合法 返回清晰错误信息让 LLM 修正
超时 工具执行时间过长 设置超时并返回友好提示
限流 API 调用频率超限 指数退避重试
依赖失败 下游服务故障 返回降级结果

5.2 带重试的工具执行

import time
from functools import wraps

def with_retry(max_retries: int = 3, delay: float = 1.0):
    """工具执行重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last_error = None
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_error = str(e)
                    if attempt < max_retries - 1:
                        time.sleep(delay * (2 ** attempt))
            return {"error": f"重试 {max_retries} 次后失败: {last_error}"}
        return wrapper
    return decorator

@with_retry(max_retries=3)
def call_external_api(url: str) -> Dict:
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    return response.json()

六、总结

Function Calling 是 Agent 与外部世界交互的核心能力。掌握工具定义、调用流程和错误处理,是构建可靠 Agent 的基础。

关键要点:

  1. 工具 Schema 要清晰准确,描述要详细
  2. 完善的错误处理让 Agent 能自我修正
  3. 单一职责原则让 LLM 更容易选择正确工具
  4. 限流和重试保障系统稳定性

下一篇将深入讲解 Agent 的规划与推理机制。