🔥 DeepSeek 工具调用全解析:让大模型学会"查天气",Function Calling 实战指南

3 阅读8分钟

🔥 DeepSeek 工具调用全解析:让大模型学会"查天气",Function Calling 实战指南

前言:大模型不知道今天的天气?股价?新闻?本文带你彻底掌握 Function Calling 技术,让 DeepSeek 学会调用外部 API,实现实时数据查询!结合完整代码,从原理到实战,一篇搞定!


📌 一、核心概念速览

🎯 为什么需要工具调用?

┌─────────────────────────────────────────────────────────────┐
│              大模型的知识边界                               │
├─────────────────────────────────────────────────────────────┤
│  ✅ 擅长:逻辑推理、文本生成、代码编写                      │
│  ❌ 不擅长:实时数据、私有数据、外部系统                    │
│                                                             │
│  📊 训练数据截止时间:20247 月                          │
│  📊 新闻、股价、天气等实时信息:❌ 不知道                    │
│  📊 解决方案:Function Calling + 外部 API                   │
└─────────────────────────────────────────────────────────────┘

🔧 技术栈全景

组件说明作用
DeepSeek深度求索大模型提供 reasoning 推理能力
ModelScope阿里云魔搭平台开源模型下载、微调、部署
Jupyter (.ipynb)交互式笔记本实验算法、调试代码
OpenAI SDKAPI 接口标准绝大多数大模型的统一调用方式
Function Calling工具调用机制让模型学会使用外部工具

📌 二、模块化编程:分离关注点

💡 核心原则

┌─────────────────────────────────────────────────────────────┐
│                  模块化编程最佳实践                          │
├─────────────────────────────────────────────────────────────┤
│  ✅ 一个模块一个文件,干一件事                              │
│  ✅ 提高代码可维护性和可拓展性                              │
│  ✅ 便于测试和复用                                          │
│  ✅ OpenAI SDK 是事实标准,兼容大多数大模型                 │
└─────────────────────────────────────────────────────────────┘

📁 推荐项目结构

project/
├── main.py              # 主程序入口
├── config.py            # 配置管理(API Key 等)
├── tools/               # 工具模块目录
│   ├── __init__.py
│   ├── weather.py       # 天气查询工具
│   ├── stock.py         # 股票查询工具
│   └── news.py          # 新闻查询工具
├── utils/               # 工具函数
│   └── logger.py
└── requirements.txt     # 依赖管理

🔑 核心导入

from openai import OpenAI  # ✅ 事实标准 SDK
import requests
import json

📌 三、chat.completions 多轮会话详解

📋 消息角色(role)

角色说明使用场景
system系统指令设置模型身份和约定,只在最初设置一次
user用户输入用户的提问和指令
assistant助手回复模型的回答
tool工具返回工具调用的结果(新增)

🔄 多轮对话流程

┌──────────────────────────────────────────────────────────────────┐
│                      多轮对话消息流                              │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  [system]  你是一个天气助手...                                   │
│      ↓                                                           │
│  [user]    北京天气怎么样?                                      │
│      ↓                                                           │
│  [assistant]  我需要调用天气查询工具... (tool_calls)            │
│      ↓                                                           │
│  [tool]     北京天气:晴,温度:25°C                             │
│      ↓                                                           │
│  [assistant]  北京今天天气晴朗,温度 25°C...                     │
│      ↓                                                           │
│  [user]    那上海呢?                                            │
│      ↓                                                           │
│  [assistant]  上海今天天气...                                    │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

📌 四、Function Calling 完整实现流程

🎯 六步走流程

┌─────────────────────────────────────────────────────────────────┐
│              Function Calling 六步流程                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  第 1 步:定义工具函数(Python 实现)                            │
│      ↓                                                          │
│  第 2 步:定义工具描述(JSON Schema)                           │
│      ↓                                                          │
│  第 3 步:发送用户消息(携带 tools 参数)                       │
│      ↓                                                          │
│  第 4 步:模型返回 tool_calls(决定调用哪个工具)               │
│      ↓                                                          │
│  第 5 步:执行工具函数,获取结果                                │
│      ↓                                                          │
│  第 6 步:将结果返回模型,获取最终回复                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

📌 五、完整代码实战

第 1 步:定义天气查询工具函数

import requests
from typing import Optional

def get_weather(location: str) -> str:
    """
    获取指定城市的实时天气
    
    Args:
        location: 城市名称,如 "抚州"、"北京"
    
    Returns:
        天气信息字符串
    """
    # ✅ 心知天气 API
    API_KEY = "你的 API 密钥"  # 替换为真实密钥
    url = "https://api.seniverse.com/v3/weather/now.json"
    
    params = {
        "key": API_KEY,
        "location": location,
        "language": "zh-Hans"
    }
    
    try:
        # 发送 HTTP 请求
        resp = requests.get(url, params=params, timeout=10)
        resp.raise_for_status()  # 检查 HTTP 状态
        data = resp.json()
        
        # 解析响应数据
        if "results" in data and len(data["results"]) > 0:
            now = data["results"][0]["now"]
            return (
                f"{location}天气:{now.get('text')},"
                f"温度:{now.get('temperature')}°C,"
                f"湿度:{now.get('humidity')}%"
            )
        else:
            return "未查询到天气数据"
            
    except requests.exceptions.Timeout:
        return "异常:请求超时"
    except requests.exceptions.RequestException as e:
        return f"异常:网络错误 - {e}"
    except Exception as e:
        return f"异常:{e}"

# 测试函数
if __name__ == "__main__":
    result = get_weather("抚州")
    print(result)

第 2 步:定义工具描述(JSON Schema)

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市名称,如北京、上海、抚州"
                    }
                },
                "required": ["location"]  # 必填参数
            }
        }
    }
]

第 3 步:初始化 DeepSeek 客户端

from openai import OpenAI

client = OpenAI(
    api_key="sk-your-api-key",  # 替换为你的 DeepSeek API Key
    base_url="https://api.deepseek.com"
)

第 4 步:发送消息并处理工具调用

import json

# 初始化对话历史(包含 system 角色)
messages = [
    {
        "role": "system",
        "content": "你是一个智能助手,可以查询天气信息。当用户询问天气时,请调用天气查询工具。"
    },
    {
        "role": "user",
        "content": "北京天气怎么样?"
    }
]

# 第一次调用:模型决定是否使用工具
response = client.chat.completions.create(
    model="deepseek-reasoner",  # 或 deepseek-chat
    messages=messages,
    tools=tools,
    tool_choice="auto",  # auto: 自动决定 | none: 不用工具 | {"type":"function"...}: 强制用
    temperature=0.3
)

response_message = response.choices[0].message
print("=== 首次响应 ===")
print(response_message)

第 5 步:执行工具函数

# 将助手回复添加到消息历史
messages.append(response_message)

# 检查是否有工具调用
if response_message.tool_calls:
    print("\n=== 检测到工具调用 ===")
    
    for tool_call in response_message.tool_calls:
        function_name = tool_call.function.name
        # JSON 字符串转 JSON 对象
        function_args = json.loads(tool_call.function.arguments)
        
        print(f"调用函数:{function_name}")
        print(f"参数:{function_args}")
        
        # 根据函数名调用对应的 Python 函数
        if function_name == "get_weather":
            function_response = get_weather(function_args["location"])
        else:
            function_response = "未知工具"
        
        print(f"工具返回:{function_response}")
        
        # 将工具结果添加到消息历史
        # ✅ 注意:tool 消息格式需要 tool_call_id
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": function_response
        })

第 6 步:获取最终回复

# 第二次调用:将工具结果返回给模型,获取最终回复
print("\n=== 获取最终回复 ===")
final_response = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=messages,
    tools=tools  # 保持 tools 以便后续可能继续调用
)

print(final_response.choices[0].message.content)

📌 六、完整整合代码(可直接运行)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
DeepSeek Function Calling 完整示例
功能:让大模型学会查询天气
"""

import requests
import json
from openai import OpenAI
from typing import Dict, List

# ==================== 配置区 ====================
DEEPSEEK_API_KEY = "sk-your-api-key"      # 替换为你的 Key
SENIVERSE_API_KEY = "your-weather-key"    # 替换为心知天气 Key
DEEPSEEK_BASE_URL = "https://api.deepseek.com"

# ==================== 工具函数区 ====================
def get_weather(location: str) -> str:
    """获取指定城市的实时天气"""
    url = "https://api.seniverse.com/v3/weather/now.json"
    params = {
        "key": SENIVERSE_API_KEY,
        "location": location,
        "language": "zh-Hans"
    }
    
    try:
        resp = requests.get(url, params=params, timeout=10)
        resp.raise_for_status()
        data = resp.json()
        
        if "results" in data and len(data["results"]) > 0:
            now = data["results"][0]["now"]
            return (
                f"{location}天气:{now.get('text')},"
                f"温度:{now.get('temperature')}°C,"
                f"湿度:{now.get('humidity')}%"
            )
        return "未查询到天气数据"
    except Exception as e:
        return f"查询失败:{e}"

# ==================== 工具描述区 ====================
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市名称,如北京、上海、抚州"
                    }
                },
                "required": ["location"]
            }
        }
    }
]

# ==================== 核心逻辑区 ====================
def chat_with_tools(user_message: str) -> str:
    """
    支持工具调用的对话函数
    
    Args:
        user_message: 用户输入
    
    Returns:
        模型最终回复
    """
    # 初始化客户端
    client = OpenAI(
        api_key=DEEPSEEK_API_KEY,
        base_url=DEEPSEEK_BASE_URL
    )
    
    # 初始化消息历史
    messages = [
        {
            "role": "system",
            "content": "你是一个智能助手。当用户询问天气时,请调用 get_weather 工具查询。"
        },
        {"role": "user", "content": user_message}
    ]
    
    # 第一次调用
    response = client.chat.completions.create(
        model="deepseek-reasoner",
        messages=messages,
        tools=TOOLS,
        tool_choice="auto",
        temperature=0.3
    )
    
    response_message = response.choices[0].message
    messages.append(response_message)
    
    # 处理工具调用
    if response_message.tool_calls:
        for tool_call in response_message.tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)
            
            # 调用对应函数
            if function_name == "get_weather":
                function_response = get_weather(function_args["location"])
            else:
                function_response = "未知工具"
            
            # 添加工具结果
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": function_response
            })
        
        # 第二次调用获取最终回复
        final_response = client.chat.completions.create(
            model="deepseek-reasoner",
            messages=messages,
            tools=TOOLS
        )
        return final_response.choices[0].message.content
    else:
        return response_message.content

# ==================== 测试区 ====================
if __name__ == "__main__":
    print("🤖 DeepSeek 天气助手")
    print("=" * 50)
    
    # 测试用例
    test_questions = [
        "北京天气怎么样?",
        "上海今天热吗?",
        "你好,介绍一下你自己"  # 不需要调用工具
    ]
    
    for question in test_questions:
        print(f"\n👤 用户:{question}")
        answer = chat_with_tools(question)
        print(f"🤖 助手:{answer}")
        print("-" * 50)

📌 七、关键知识点总结

📊 消息格式对比

轮次role内容特殊字段
1system系统指令
2user用户问题
3assistant决定调用工具tool_calls
4tool工具返回结果tool_call_id
5assistant最终回复

⚠️ 常见错误及修复

# ❌ 错误 1:tool 消息缺少 tool_call_id
messages.append({
    "role": "tool",
    "content": "天气数据"  # 缺少 tool_call_id
})

# ✅ 正确
messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,  # 必须匹配
    "content": "天气数据"
})

# ❌ 错误 2:JSON 解析错误
function_args = tool_call.function.arguments  # 这是字符串
location = function_args["location"]  # 会报错

# ✅ 正确
function_args = json.loads(tool_call.function.arguments)
location = function_args["location"]

# ❌ 错误 3:URL 参数错误
url = "https://api.xxx.com",  # 多了逗号
params = {...}

# ✅ 正确
url = "https://api.xxx.com"  # 去掉逗号
params = {...}

📌 八、扩展:多工具支持

# 定义多个工具
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string", "description": "城市名称"}
                },
                "required": ["location"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_stock_price",
            "description": "获取指定股票的当前价格",
            "parameters": {
                "type": "object",
                "properties": {
                    "symbol": {"type": "string", "description": "股票代码,如 600519"}
                },
                "required": ["symbol"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_news",
            "description": "搜索最新新闻",
            "parameters": {
                "type": "object",
                "properties": {
                    "keyword": {"type": "string", "description": "搜索关键词"}
                },
                "required": ["keyword"]
            }
        }
    }
]

# 工具函数映射
FUNCTION_MAP = {
    "get_weather": get_weather,
    "get_stock_price": get_stock_price,
    "search_news": search_news
}

# 动态调用
if function_name in FUNCTION_MAP:
    function_response = FUNCTION_MAP[function_name](**function_args)

📌 九、Jupyter Notebook 实验建议

# .ipynb 文件适合:
# ✅ 逐条运行,调试 API 调用
# ✅ 实验不同参数效果
# ✅ 可视化天气数据
# ✅ 保存运行结果

# 推荐单元格结构:
# Cell 1: 导入依赖
# Cell 2: 配置 API Key
# Cell 3: 定义工具函数
# Cell 4: 定义工具描述
# Cell 5: 测试单次调用
# Cell 6: 测试多轮对话
# Cell 7: 可视化结果

📝 总结:Function Calling 核心要点

要点说明
为什么需要大模型不知道实时数据,需要外部工具
核心流程定义工具 → 描述工具 → 调用模型 → 执行工具 → 返回结果
消息角色system/user/assistant/tool 四种
关键参数tools, tool_choice, tool_call_id
最佳实践模块化、错误处理、多轮对话历史维护
扩展方向多工具支持、并行调用、工具链编排

💡 学习建议

  1. 先跑通单工具示例
  2. 理解 message 历史维护机制
  3. 尝试添加第二个工具
  4. 用 Jupyter Notebook 逐行调试
  5. 参考 OpenAI 官方文档

👍 觉得有用请点赞收藏,有问题欢迎评论区交流!

📢 资源


本文参考 DeepSeek 官方文档及 OpenAI Function Calling 规范 同步发布于稀土掘金,转载请注明出处