🔥 DeepSeek 工具调用全解析:让大模型学会"查天气",Function Calling 实战指南
前言:大模型不知道今天的天气?股价?新闻?本文带你彻底掌握 Function Calling 技术,让 DeepSeek 学会调用外部 API,实现实时数据查询!结合完整代码,从原理到实战,一篇搞定!
📌 一、核心概念速览
🎯 为什么需要工具调用?
┌─────────────────────────────────────────────────────────────┐
│ 大模型的知识边界 │
├─────────────────────────────────────────────────────────────┤
│ ✅ 擅长:逻辑推理、文本生成、代码编写 │
│ ❌ 不擅长:实时数据、私有数据、外部系统 │
│ │
│ 📊 训练数据截止时间:2024 年 7 月 │
│ 📊 新闻、股价、天气等实时信息:❌ 不知道 │
│ 📊 解决方案:Function Calling + 外部 API │
└─────────────────────────────────────────────────────────────┘
🔧 技术栈全景
| 组件 | 说明 | 作用 |
|---|---|---|
| DeepSeek | 深度求索大模型 | 提供 reasoning 推理能力 |
| ModelScope | 阿里云魔搭平台 | 开源模型下载、微调、部署 |
| Jupyter (.ipynb) | 交互式笔记本 | 实验算法、调试代码 |
| OpenAI SDK | API 接口标准 | 绝大多数大模型的统一调用方式 |
| 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 | 内容 | 特殊字段 |
|---|---|---|---|
| 1 | system | 系统指令 | 无 |
| 2 | user | 用户问题 | 无 |
| 3 | assistant | 决定调用工具 | tool_calls |
| 4 | tool | 工具返回结果 | tool_call_id |
| 5 | assistant | 最终回复 | 无 |
⚠️ 常见错误及修复
# ❌ 错误 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 |
| 最佳实践 | 模块化、错误处理、多轮对话历史维护 |
| 扩展方向 | 多工具支持、并行调用、工具链编排 |
💡 学习建议:
- 先跑通单工具示例
- 理解 message 历史维护机制
- 尝试添加第二个工具
- 用 Jupyter Notebook 逐行调试
- 参考 OpenAI 官方文档
👍 觉得有用请点赞收藏,有问题欢迎评论区交流!
📢 资源:
- DeepSeek 官网:deepseek.com
- ModelScope:modelscope.cn
- 心知天气 API:www.seniverse.com
本文参考 DeepSeek 官方文档及 OpenAI Function Calling 规范 同步发布于稀土掘金,转载请注明出处