你有没有想过,为什么 ChatGPT 能帮你查天气、写代码、操作文件?它不是凭空做到的——背后靠的就是 Function Calling。今天从原理到实战,用一个完整项目让你彻底搞懂 Function Calling。


一、Function Calling 是什么?

一句话解释:Function Calling 让大模型能够"调用你定义的函数",从而获取实时数据、操作外部系统。

┌──────────────────────────────────────────────────────────┐
│  没有 Function Calling:                                   │
│  用户:今天北京天气怎么样?                                  │
│  AI:抱歉,我的知识截止到2025年,无法查询实时天气。           │
│                                                           │
│  有 Function Calling:                                     │
│  用户:今天北京天气怎么样?                                  │
│  AI:我来帮你查一下 → 调用 get_weather("北京") → 返回数据   │
│  AI:今天北京晴,最高温28°C,最低温18°C,建议带件外套。      │
└──────────────────────────────────────────────────────────┘

工作流程

用户提问
  │
  ▼
大模型分析 → 需要"查天气"? → 返回函数调用请求(不直接执行!)
  │             {function: "get_weather", args: {city: "北京"}}
  ▼
你的代码接收 → 解析函数名和参数 → 实际调用函数 → 得到真实结果
  │             {temp: 28, weather: "晴"}
  ▼
把结果返回给大模型 → 大模型用自然语言总结 → 回复用户
  │             "今天北京晴,最高温28°C..."
  ▼
用户收到回答

关键点:大模型不直接执行函数,它只告诉你"我想调用这个函数,参数是这些"。真正执行的是你的代码。这保证了安全性。


二、与 MCP 的关系

┌────────────────────────────────────────────────────────┐
│  Function Calling vs MCP                               │
├──────────────┬─────────────────────────────────────────┤
│              │                                         │
│ Function     │ 底层协议,AI模型直接支持                    │
│ Calling      │ 每次对话都要声明可用的函数                   │
│              │ 适合简单场景(几个函数)                    │
│              │                                         │
├──────────────┼─────────────────────────────────────────┤
│              │                                         │
│ MCP          │ 在 Function Calling 之上的标准化框架       │
│ (Model       │ 统一工具定义格式                           │
│ Context      │ 支持 Server/Client 架构                   │
│ Protocol)    │ 一次开发,所有AI平台可用                    │
│              │ 适合复杂场景(几十个工具)                   │
│              │                                         │
└──────────────┴─────────────────────────────────────────┘

关系:MCP 底层就是基于 Function Calling 的扩展和标准化。
先搞懂 Function Calling,再学 MCP 就很容易理解。

三、DeepSeek vs Claude Function Calling 对比

对比项DeepSeekClaude
API 格式兼容 OpenAI 格式Anthropic 格式
函数定义tools 参数tools 参数
并行调用支持支持
多轮对话
中文能力⭐⭐⭐⭐⭐⭐⭐⭐⭐
价格¥1/百万token¥24/百万token
适合场景国内项目、成本敏感代码质量要求高

本教程用 DeepSeek(兼容 OpenAI 格式,上手最快)。


四、实战项目:AI智能助手(查天气+查快递+查日程)

4.1 系统架构

┌──────────────────────────────────────────────────────┐
│                    用户输入                             │
│           "帮我查一下北京天气,顺便看看我的日程"          │
└─────────────────────┬────────────────────────────────┘
                      ▼
┌──────────────────────────────────────────────────────┐
│                  DeepSeek API                         │
│         分析用户意图 → 决定调用哪些函数                    │
│         返回: [                                       │
│           {function: "get_weather", args: {city:"北京"}│
│           {function: "get_schedule", args: {date:"今天"}}│
│         ]                                            │
└─────────────────────┬────────────────────────────────┘
                      ▼
┌──────────────────────────────────────────────────────┐
│               你的 Python 代码                          │
│         1. 解析函数调用请求                               │
│         2. 调用对应的本地函数                              │
│         3. 收集所有函数的结果                             │
└──────┬───────────┬───────────┬────────────────────────┘
       ▼           ▼           ▼
  ┌─────────┐ ┌─────────┐ ┌──────────┐
  │get_weather│ │get_schedule│ │get_express│
  │(模拟API) │ │(模拟数据)  │ │(模拟数据) │
  └────┬─────┘ └────┬──────┘ └────┬──────┘
       ▼           ▼              ▼
       └───────────┴──────────────┘
                   ▼
┌──────────────────────────────────────────────────────┐
│               结果返回 DeepSeek                         │
│         "北京今天晴,最高28°C。你今天下午2点有一个..."      │
└──────────────────────────────────────────────────────┘

五、完整代码实现

5.1 函数定义

# functions.py
import json
from datetime import datetime

# ====== 函数定义(告诉AI有哪些函数可用) ======
FUNCTIONS = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询指定城市的天气信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,如:北京、上海、广州"
                    }
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_schedule",
            "description": "查询指定日期的日程安排",
            "parameters": {
                "type": "object",
                "properties": {
                    "date": {
                        "type": "string",
                        "description": "日期,如:今天、明天、2026-05-01"
                    }
                },
                "required": ["date"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_express",
            "description": "查询快递物流信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "tracking_number": {
                        "type": "string",
                        "description": "快递单号"
                    }
                },
                "required": ["tracking_number"]
            }
        }
    }
]


# ====== 函数实现(模拟) ======
def get_weather(city: str) -> dict:
    """查询天气(模拟数据)"""
    weather_data = {
        "北京": {"temp": 28, "weather": "晴", "humidity": 45},
        "上海": {"temp": 25, "weather": "多云", "humidity": 65},
        "广州": {"temp": 32, "weather": "雷阵雨", "humidity": 80},
        "深圳": {"temp": 31, "weather": "阵雨", "humidity": 75},
    }
    data = weather_data.get(city, {"temp": 22, "weather": "未知", "humidity": 50})
    return {
        "city": city,
        "temperature": f"{data['temp']}°C",
        "weather": data["weather"],
        "humidity": f"{data['humidity']}%"
    }


def get_schedule(date: str) -> dict:
    """查询日程(模拟数据)"""
    today = datetime.now().strftime("%Y-%m-%d")
    schedules = {
        "今天": [
            {"time": "09:00", "event": "晨会", "duration": "30min"},
            {"time": "14:00", "event": "技术评审", "duration": "1h"},
            {"time": "16:30", "event": "1v1 周报", "duration": "30min"},
        ],
        "明天": [
            {"time": "10:00", "event": "产品需求评审", "duration": "2h"},
        ],
    }
    events = schedules.get(date, [{"time": "无日程", "event": "空闲", "duration": ""}])
    return {"date": date, "schedules": events}


def get_express(tracking_number: str) -> dict:
    """查询快递(模拟数据)"""
    return {
        "tracking_number": tracking_number,
        "status": "运输中",
        "location": "北京转运中心",
        "update_time": "2026-04-28 15:30",
        "estimated_delivery": "2026-04-30"
    }


# 函数名 → 实际函数的映射
FUNCTION_MAP = {
    "get_weather": get_weather,
    "get_schedule": get_schedule,
    "get_express": get_express,
}

5.2 核心调用逻辑

# main.py
import json
import httpx
from functions import FUNCTIONS, FUNCTION_MAP

API_KEY = "sk-your-api-key"
BASE_URL = "https://api.deepseek.com"
MODEL = "deepseek-chat"


def call_deepseek(messages, tools=None):
    """调用 DeepSeek API"""
    payload = {
        "model": MODEL,
        "messages": messages,
        "temperature": 0.3,  # 低温度,让函数调用更稳定
    }
    if tools:
        payload["tools"] = tools

    response = httpx.post(
        f"{BASE_URL}/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        json=payload,
        timeout=30.0,
    )
    return response.json()


def execute_function(name: str, arguments: dict) -> str:
    """执行函数并返回结果"""
    func = FUNCTION_MAP.get(name)
    if not func:
        return json.dumps({"error": f"未知函数: {name}"})

    try:
        result = func(**arguments)
        return json.dumps(result, ensure_ascii=False)
    except Exception as e:
        return json.dumps({"error": str(e)})


def chat(user_input: str) -> str:
    """完整对话流程(支持多轮 Function Calling)"""
    messages = [
        {
            "role": "system",
            "content": "你是一个智能助手,可以帮用户查天气、查日程、查快递。"
                       "根据用户需求调用相应函数获取信息。"
        },
        {"role": "user", "content": user_input},
    ]

    # 可能需要多轮函数调用(AI可能一次想调用多个函数)
    max_rounds = 5  # 防止无限循环
    for round in range(max_rounds):
        response = call_deepseek(messages, tools=FUNCTIONS)
        choice = response["choices"][0]
        message = choice["message"]

        # 情况1:AI 直接回复文本(不需要调用函数)
        if not message.get("tool_calls"):
            return message["content"]

        # 情况2:AI 要求调用函数
        # 把AI的函数调用请求加入消息历史
        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 = execute_function(func_name, func_args)

            # 把函数结果加入消息历史
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call["id"],
                "content": result,
            })

    return "抱歉,处理超时,请重试。"


# ====== 测试 ======
if __name__ == "__main__":
    print("=" * 50)
    print("AI智能助手(Function Calling Demo)")
    print("=" * 50)

    test_cases = [
        "帮我查一下北京今天天气怎么样",
        "我今天的日程是什么?",
        "帮我查一下快递单号 SF1234567890 到哪了",
        "查一下上海天气和我明天的日程",
    ]

    for question in test_cases:
        print(f"\n👤 用户: {question}")
        answer = chat(question)
        print(f"🤖 助手: {answer}")
        print("-" * 50)

5.3 运行效果

==================================================
AI智能助手(Function Calling Demo)
==================================================

👤 用户: 帮我查一下北京今天天气怎么样
  🔧 调用函数: get_weather({'city': '北京'})
🤖 助手: 北京今天的天气情况:
  - 🌡️ 温度:28°C
  - ☀️ 天气:晴
  - 💧 湿度:45%
  天气不错,适合出行!

--------------------------------------------------

👤 用户: 查一下上海天气和我明天的日程
  🔧 调用函数: get_weather({'city': '上海'})
  🔧 调用函数: get_schedule({'date': '明天'})
🤖 助手: 帮您查到了:
  上海明天:多云,25°C,湿度65%
  明天的日程:
  - 10:00 产品需求评审(2小时)
  建议带件外套,温度适中。

六、踩坑记录

踩坑1:JSON Schema 定义错误

// ❌ 错误:description 写成了中文逗号
"description": "城市名称,如:北京、上海,广州"

// ❌ 错误:type 大小写不对
"type": "String"

// ✅ 正确
"description": "城市名称,如:北京、上海、广州",
"type": "string"

踩坑2:函数调用结果必须是字符串

DeepSeek 要求 tool role 的 content 必须是字符串。如果你返回的是 dict,先 json.dumps()

踩坑3:temperature 不要设太高

Function Calling 建议设 temperature=0.1~0.3。太高会导致 AI 胡乱调用函数或参数格式错误。

踩坑4:死循环防护

AI 可能无限循环调用函数,必须设最大轮次限制(建议 5 轮)。


七、扩展方向

  1. 接入真实API:高德天气API、顺丰快递API、飞书日程API
  2. 动态注册函数:通过配置文件或数据库管理函数
  3. 接入MCP:用MCP Server替代本地函数,实现标准化
  4. 多模态:结合图片识别函数(如OCR)

🎉 Function Calling 是 AI 应用的"万能接口"——理解了它,你就掌握了让 AI 连接真实世界的能力。从这3个函数开始,你可以无限扩展。

如果觉得有帮助,点赞 + 收藏支持一下!有问题欢迎评论区讨论 💬

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐