Function-Calling与工具使用

本节目标:理解如何让大模型调用外部工具(函数),从"只能说"变成"能做事"。这是 Agent 和 MCP 的基础。


一、为什么大模型需要工具?

1.1 大模型的天然局限

ini 复制代码
大模型擅长的:          大模型不擅长的:
✓ 理解语言              ✗ 精确计算(1234 × 5678 = ?)
✓ 生成文本              ✗ 获取实时信息(今天的天气)
✓ 逻辑推理              ✗ 操作外部系统(发邮件、查数据库)
✓ 代码理解              ✗ 访问最新数据(最新股价)

解决方案:给大模型一些"工具",让它在需要时自己调用!

1.2 一个生活化的比喻

arduino 复制代码
大模型 = 一个很聪明的助手

没有工具的助手:
  你:"帮我查一下明天北京的天气"
  助手:"我记得北京这个季节一般是...(可能说错)"

有工具的助手:
  你:"帮我查一下明天北京的天气"
  助手:(打开天气App查了一下)"明天北京晴,15-25度"

工具让助手从"猜测"变成"查证"!

二、Function Calling 工作原理

2.1 整体流程

graph TD A["第1步:你告诉模型有哪些工具可用
┌──────────────────────────┐
工具清单:
1. get_weather(城市)
2. search_web(关键词)
3. send_email(收件人,内容)
└──────────────────────────┘"] B["第2步:用户提问
'明天北京天气怎么样?'"] C["第3步:模型决定调用哪个工具(模型不执行,只返回调用意图)
→ {'function': 'get_weather', 'args': {'city': '北京'}}"] D["第4步:你的程序执行这个函数
→ get_weather('北京') → '晴,15-25度'"] E["第5步:把执行结果告诉模型
→ 模型收到 '晴,15-25度'"] F["第6步:模型组织语言回答用户
→ '明天北京天气晴朗,气温15到25度,适合出行~'"] A --> B B --> C C --> D D --> E E --> F classDef actor fill:#e3f2fd,stroke:#1565c0,stroke-width:2px; classDef step fill

关键理解:模型只负责"决定调用什么",不负责"真正执行"。执行是你的程序做的。

sequenceDiagram participant P as 你的程序 participant M as 大模型 P->>M: "①定义工具" P->>M: "②用户提问" M-->>P: "③返回调用意图" Note over P: ④执行函数(本地执行)" P->>M: "⑤返回执行结果" M-->>P: "⑥自然语言回答"

三、实战:OpenAI Function Calling

3.1 定义工具

python 复制代码
# 第1步:定义可用的工具(用 JSON Schema 描述)
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"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "进行数学计算",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "数学表达式,如 '2 + 3 * 4'"
                    }
                },
                "required": ["expression"]
            }
        }
    }
]

3.2 完整调用流程

python 复制代码
from openai import OpenAI
import json

client = OpenAI()

# 实际的工具函数
def get_weather(city: str, unit: str = "celsius") -> str:
    """模拟天气API(实际项目中会调用真实API)"""
    weather_data = {
        "北京": {"temp": 22, "condition": "晴"},
        "上海": {"temp": 25, "condition": "多云"},
    }
    data = weather_data.get(city, {"temp": 20, "condition": "未知"})
    return json.dumps({"city": city, "temperature": data["temp"],
                       "condition": data["condition"]}, ensure_ascii=False)

def calculate(expression: str) -> str:
    """安全的数学计算"""
    try:
        result = eval(expression)  # 注意:生产环境应使用更安全的方式
        return json.dumps({"expression": expression, "result": result})
    except Exception as e:
        return json.dumps({"error": str(e)})

# 工具函数映射
tool_functions = {
    "get_weather": get_weather,
    "calculate": calculate,
}

# 完整的对话循环
def chat_with_tools(user_message: str):
    messages = [{"role": "user", "content": user_message}]

    # 第1次调用:模型决定是否使用工具
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=messages,
        tools=tools,
    )

    message = response.choices[0].message

    # 检查模型是否要调用工具
    if message.tool_calls:
        messages.append(message)  # 把模型的回复加入对话

        # 执行每个工具调用
        for tool_call in message.tool_calls:
            func_name = tool_call.function.name
            func_args = json.loads(tool_call.function.arguments)

            print(f"  调用工具:{func_name}({func_args})")

            # 执行函数
            result = tool_functions[func_name](**func_args)

            # 把结果告诉模型
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result,
            })

        # 第2次调用:模型根据工具结果生成最终回答
        final_response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
        )
        return final_response.choices[0].message.content
    else:
        # 不需要工具,直接返回
        return message.content

# 测试
print(chat_with_tools("北京今天天气怎么样?"))
# 调用工具:get_weather({'city': '北京'})
# 输出:北京今天天气晴朗,气温22度,适合外出活动!

print(chat_with_tools("帮我算一下 1234 × 5678"))
# 调用工具:calculate({'expression': '1234 * 5678'})
# 输出:1234 × 5678 = 7,006,652

print(chat_with_tools("你好啊"))
# (不调用工具,直接回答)
# 输出:你好!有什么我可以帮你的吗?

四、Anthropic Tool Use(Claude)

Claude 的工具使用方式略有不同,但核心思想一样:

python 复制代码
import anthropic
import json

client = anthropic.Anthropic()

# 定义工具
tools = [
    {
        "name": "get_weather",
        "description": "获取指定城市的天气信息",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "城市名称"
                }
            },
            "required": ["city"]
        }
    }
]

# 第1次调用
response = client.messages.create(
    model="claude-sonnet-4-6-20250415",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "北京天气怎么样?"}]
)

# 检查是否需要调用工具
if response.stop_reason == "tool_use":
    # 找到工具调用块
    tool_use = next(b for b in response.content if b.type == "tool_use")
    print(f"调用工具:{tool_use.name}({tool_use.input})")

    # 执行工具并返回结果
    tool_result = get_weather(**tool_use.input)

    # 第2次调用:带上工具结果
    final_response = client.messages.create(
        model="claude-sonnet-4-6-20250415",
        max_tokens=1024,
        tools=tools,
        messages=[
            {"role": "user", "content": "北京天气怎么样?"},
            {"role": "assistant", "content": response.content},
            {
                "role": "user",
                "content": [{
                    "type": "tool_result",
                    "tool_use_id": tool_use.id,
                    "content": tool_result
                }]
            }
        ]
    )
    print(final_response.content[0].text)

五、多工具并行调用

模型可以一次调用多个工具,你的程序并行执行后一起返回:

ini 复制代码
用户:"帮我查一下北京和上海的天气,顺便算一下 100 * 200"

模型返回(一次性3个调用):
  1. get_weather(city="北京")
  2. get_weather(city="上海")
  3. calculate(expression="100 * 200")

你的程序并行执行这3个函数 → 把3个结果一起告诉模型 → 模型综合回答
python 复制代码
import asyncio

async def execute_tools_parallel(tool_calls):
    """并行执行多个工具调用"""
    tasks = []
    for call in tool_calls:
        func = tool_functions[call.function.name]
        args = json.loads(call.function.arguments)
        # 创建异步任务
        tasks.append(asyncio.to_thread(func, **args))

    # 并行执行所有任务
    results = await asyncio.gather(*tasks)
    return results

六、工具设计最佳实践

6.1 好的工具描述

python 复制代码
# ❌ 差的工具定义
{
    "name": "query",
    "description": "查询数据",
    "parameters": {
        "properties": {
            "q": {"type": "string"}
        }
    }
}
# 问题:名称模糊、描述太简单、参数不清楚

# ✅ 好的工具定义
{
    "name": "search_products",
    "description": "在商品数据库中搜索商品。"
                   "支持按商品名称、类别、价格范围搜索。"
                   "返回匹配的商品列表,包含名称、价格、库存信息。",
    "parameters": {
        "properties": {
            "keyword": {
                "type": "string",
                "description": "搜索关键词,如'笔记本电脑'、'耳机'"
            },
            "category": {
                "type": "string",
                "enum": ["电子产品", "图书", "服装", "食品"],
                "description": "商品类别筛选"
            },
            "max_price": {
                "type": "number",
                "description": "最高价格(元),如 1000"
            },
            "limit": {
                "type": "integer",
                "description": "返回结果数量上限,默认10",
                "default": 10
            }
        },
        "required": ["keyword"]
    }
}

6.2 工具设计原则

arduino 复制代码
┌────────────────────────────────────────────────────────────┐
│                  工具设计七原则                              │
│                                                            │
│  1. 命名清晰:用动词+名词,如 search_products, send_email      │
│  2. 描述详细:说清楚做什么、需要什么参数、返回什么                 │
│  3. 参数精确:用 enum 限制可选值,用 description 解释           │
│  4. 职责单一:一个工具只做一件事                                │
│  5. 错误友好:返回有意义的错误信息                              │
│  6. 安全第一:敏感操作需确认,限制危险参数                       │
│  7. 结果简洁:只返回必要的信息,不要返回大段无关数据               │
└────────────────────────────────────────────────────────────┘

七、错误处理与安全

7.1 工具调用可能出错

python 复制代码
def safe_tool_execution(func_name: str, func_args: dict) -> str:
    """安全地执行工具函数"""
    try:
        # 检查函数是否存在
        if func_name not in tool_functions:
            return json.dumps({"error": f"未知工具:{func_name}"})

        # 执行函数(设置超时)
        import signal

        def timeout_handler(signum, frame):
            raise TimeoutError("工具执行超时")

        signal.signal(signal.SIGALRM, timeout_handler)
        signal.alarm(30)  # 30秒超时

        result = tool_functions[func_name](**func_args)

        signal.alarm(0)  # 取消超时
        return result

    except TimeoutError:
        return json.dumps({"error": "工具执行超时,请稍后重试"})
    except Exception as e:
        return json.dumps({"error": f"工具执行失败:{str(e)}"})

7.2 安全考量

复制代码
┌────────────────────────────────────────────────────────────┐
│                   安全检查清单                               │
│                                                            │
│  ✓ 永远不要让模型直接执行任意代码                               │
│  ✓ 对危险操作(删除、支付)添加人工确认步骤                       │
│  ✓ 限制工具的访问范围(只读 vs 读写)                           │
│  ✓ 对输入参数进行验证和清洗                                    │
│  ✓ 设置执行超时和重试限制                                      │
│  ✓ 记录所有工具调用日志                                        │
│  ✓ 防止 Prompt 注入导致的恶意工具调用                           │
└────────────────────────────────────────────────────────────┘

八、从 Function Calling 到 MCP

css 复制代码
Function Calling 的局限:

  每个 AI 应用都要自己实现工具 → 重复造轮子
  ┌─────────┐  ┌─────────┐  ┌─────────┐
  │ App A   │  │ App B   │  │ App C   │
  │ ┌─────┐ │  │ ┌─────┐ │  │ ┌─────┐ │
  │ │天气  │ │  │ │天气  │ │ │ │天气  │ │  ← 每个App都要写一遍
  │ │数据库│ │  │ │数据库 │ │ │ │邮件  │ │
  │ │邮件  │ │  │ │搜索  │ │ │ │搜索  │ │
  │ └─────┘ │  │ └─────┘ │  │ └─────┘ │
  └─────────┘  └─────────┘  └─────────┘

MCP 的解决方案:标准化!

  ┌─────────┐  ┌─────────┐  ┌─────────┐
  │ App A   │  │ App B   │  │ App C   │
  └────┬────┘  └────┬────┘  └────┬────┘
       │            │            │
       └────────────┼────────────┘
                    │
              MCP 标准协议
                    │
       ┌────────────┼────────────┐
       │            │            │
  ┌────┴────┐ ┌────┴────┐ ┌────┴────┐
  │ 天气     │ │ 数据库   │ │ 邮件    │  ← 写一次,到处用
  │ Server  │ │ Server  │ │ Server  │
  └─────────┘ └─────────┘ └─────────┘

九、本篇小结

javascript 复制代码
┌─────────────────────────────────────────────────────┐
│                  本篇知识地图                         │
│                                                     │
│  Function Calling = 让大模型能调用外部工具              │
│                                                     │
│  核心流程:                                           │
│  定义工具 → 用户提问 → 模型选择工具 →                    │
│  程序执行 → 返回结果 → 模型回答                         │
│                                                     │
│  关键点:                                            │
│  ├── 模型只负责"决定调用什么"                          │
│  ├── 你的程序负责"真正执行"                            │
│  ├── 支持多工具并行调用                                │
│  └── 需要注意安全性                                   │
│                                                     │
│  工具设计原则:                                       │
│  命名清晰 + 描述详细 + 职责单一 + 安全第一               │
│                                                     │
│  演进方向:                                           │
│  Function Calling → MCP(标准化协议)                 │
└─────────────────────────────────────────────────────┘

十、扩展学习资源

必读

推荐

  • Gorilla LLM ------ 专门优化工具调用的研究项目
  • ToolBench ------ 工具使用评测基准

动手实践

  • 实现一个能查天气 + 算数学的对话机器人
  • 给现有项目的 API 包装成 Function Calling 工具
  • 尝试让模型在一次对话中连续调用多个工具完成复杂任务

下一篇章预告 :将讲解 MCP(Model Context Protocol)------一个标准化的协议,让工具像 USB 一样"即插即用"。


觉得有用的话,点个关注吧!大模型方面你想看什么?留言区说,我来写。

声明:本博客内容素材来源于网络,文章由AI技术辅助生成。如有侵权或不当引用,请联系作者进行下架或删除处理。

相关推荐
司南-70491 小时前
Dense结构下的 大模型系统架构研究
服务器·人工智能·后端
GISer_Jing1 小时前
AI全栈转型_TS后端学习路线
前端·人工智能·后端·学习
漫游的渔夫1 小时前
前端开发者做 Agent:别只会执行,用 4 类失败策略让 AI 知道怎么停
前端·人工智能·typescript
娃娃略1 小时前
Frame
人工智能·深度学习·机器学习
Python私教1 小时前
Pure-Admin-Thin 深度解析:完整版和精简版到底怎么选?
vue.js·人工智能·开源
星马梦缘1 小时前
强化学习实战8.3——用PPO打赢星际争霸【编写自定义环境GYM】
人工智能·强化学习·gymnasium·星际争霸·sc2·starcraft2·sb3
翔云1234561 小时前
大模型部署全流程深度解析
人工智能·ai·大模型
BU摆烂会噶1 小时前
【LangGraph】持久化实现的三大能力——人机交互
数据库·人工智能·python·langchain·人机交互
沐风老师1 小时前
开发AI机器人操作系统用什么编程语言?
人工智能·ai编程·机器人操作系统