DeepSeek Function Calling 实战:让大模型学会「调用工具」
DeepSeek Function Calling 实战:让大模型学会「调用工具」
2026 年 5 月,DeepSeek V4 系列模型正式上线。除了更强的对话能力,它还有一个被很多开发者忽略的杀手锏——Function Calling。本文将带你从零上手,用 Python 写出一个真正"能干活的"智能应用。
一、从一个尴尬的对话说起
不知道你有没有遇到过这种情况:
你: 今天杭州天气怎么样?
模型: 抱歉,我的知识截止于2024年,无法获取实时天气信息。
单纯的对话模型就像一个"博学的笔友"——知识丰富,但和现实世界是断开的。Function Calling 就是在这之间架起的一座桥:模型不需要自己知道天气,只要它知道该去调用哪个函数获取天气就够了。
用一句话概括:Function Calling 让模型从「回答问题」升级为「调用工具完成任务」。
二、核心流程:四步走
整个 Function Calling 的工作流程其实非常简单,就四步:
第1步:你告诉模型有哪些工具可用(函数名 + 参数说明)
第2步:模型根据用户问题,决定要不要调用工具、调用哪个、传什么参数
第3步:你的代码执行这个工具函数,拿到真实数据
第4步:把结果塞回给模型,模型生成最终回复
记住一个关键点:模型只负责"决定调用什么",不负责"执行"。真正干活的是你写的 Python 函数。
三、环境准备
3.1 安装依赖
pip install openai
是的,就一个包。DeepSeek API 完全兼容 OpenAI SDK,直接复用即可。
3.2 获取 API Key
打开 platform.deepseek.com,注册后在「API Keys」页面创建一个 Key。新用户注册有免费额度,足够你把本文的例子全部跑一遍。
记得把 Key 存到环境变量:
export DEEPSEEK_API_KEY="sk-xxxxxxxxxxxxxxxx"
四、第一个例子:查询天气
先看完整代码,再逐段拆解。
import json
from openai import OpenAI
# 初始化客户端
client = OpenAI(
api_key="你的API Key", # 建议用环境变量
base_url="https://api.deepseek.com",
)
# 定义工具:一个查询天气的函数
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的实时天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,例如 杭州、北京"
}
},
"required": ["city"]
}
}
}
]
def get_weather(city):
"""实际获取天气的函数(这里用模拟数据演示)"""
weather_data = {
"杭州": {"temp": 26, "desc": "多云转晴", "humidity": "65%"},
"北京": {"temp": 22, "desc": "晴", "humidity": "30%"},
"上海": {"temp": 24, "desc": "小雨", "humidity": "80%"},
}
return weather_data.get(city, {"temp": "未知", "desc": "暂无数据"})
def run_conversation(user_query):
"""一次完整的 Function Calling 对话"""
messages = [{"role": "user", "content": user_query}]
# 第1次请求:模型决定要不要调用工具
response = client.chat.completions.create(
model="deepseek-v4-flash", # 用最新的 V4 Flash,性价比最高
messages=messages,
tools=tools,
)
msg = response.choices[0].message
# 如果模型决定调用工具
if msg.tool_calls:
tool_call = msg.tool_calls[0]
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
print(f" 模型决定调用: {func_name}({func_args})")
# 第2步:执行工具函数
if func_name == "get_weather":
result = get_weather(func_args["city"])
else:
result = "未知工具"
# 第3步:把结果喂回模型
messages.append(msg)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})
# 第2次请求:模型根据工具结果生成回复
final_response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
)
return final_response.choices[0].message.content
# 不需要调用工具,直接返回
return msg.content
# 测试
if __name__ == "__main__":
queries = [
"今天杭州天气怎么样?",
"北京适合出门吗?",
"帮我写一首关于春天的诗", # 这个问题不需要工具
]
for q in queries:
print(f"\n用户: {q}")
print(f"回复: {run_conversation(q)}")
运行结果:
用户: 今天杭州天气怎么样?
模型决定调用: get_weather({'city': '杭州'})
回复: 杭州今天多云转晴,气温26℃,湿度65%。总体来说天气不错,适合外出活动。
用户: 北京适合出门吗?
模型决定调用: get_weather({'city': '北京'})
回复: 北京今天晴天,气温22℃,湿度30%。天气非常好,很适合出门!
用户: 帮我写一首关于春天的诗
回复: (直接输出一首诗,不调用任何工具)
注意第三个问题——模型很聪明地判断出"写诗"不需要天气查询,直接回答。这就是 Function Calling 的优雅之处:模型自己决定什么时候需要调工具。
五、进阶:多工具协同
一个应用通常不止一个工具。假设我们要做一个"生活助手",支持三个能力:
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询城市天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称"}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "get_news",
"description": "获取最新新闻,按关键词搜索",
"parameters": {
"type": "object",
"properties": {
"keyword": {"type": "string", "description": "搜索关键词"},
"count": {"type": "integer", "description": "返回条数,默认5"}
},
"required": ["keyword"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "执行数学计算",
"parameters": {
"type": "object",
"properties": {
"expression": {"type": "string", "description": "数学表达式,例如 '2+3*4'"}
},
"required": ["expression"]
}
}
}
]
模型会自动根据用户意图选择合适的工具——甚至一次调用多个。比如用户问"杭州天气怎么样?顺便帮我算一下 15% 的 380 是多少",模型会先后调用 get_weather 和 calculate。
执行多工具调用的代码需要处理 tool_calls 是个列表的情况:
# 处理多个工具调用
if msg.tool_calls:
messages.append(msg)
for tool_call in msg.tool_calls:
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
# 路由到对应函数
if func_name == "get_weather":
result = get_weather(func_args["city"])
elif func_name == "get_news":
result = get_news(func_args["keyword"], func_args.get("count", 5))
elif func_name == "calculate":
result = safe_calculate(func_args["expression"])
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})
# 所有工具结果返回后,再请求一次模型
final = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
)
return final.choices[0].message.content
六、实战:对接真实 API
上面用的是模拟数据,实际项目中你得接真实接口。以天气为例,高德地图提供了免费天气 API,每天有配额,适合个人项目。
去 高德开放平台 注册,创建应用获取 Key,然后这样写:
import requests
def get_weather_realtime(city):
"""通过高德天气 API 获取真实天气"""
api_key = "你的高德Key"
# 先根据城市名获取 adcode
geo_url = f"https://restapi.amap.com/v3/config/district?key={api_key}&keywords={city}"
geo_data = requests.get(geo_url).json()
adcode = geo_data["districts"][0]["adcode"]
# 再查天气
weather_url = f"https://restapi.amap.com/v3/weather/weatherInfo?key={api_key}&city={adcode}"
weather_data = requests.get(weather_url).json()
live = weather_data["lives"][0]
return {
"temperature": f"{live['temperature']}℃",
"weather": live["weather"],
"wind": live["winddirection"],
"city": live["city"]
}
把上面的 get_weather 替换成这个真实版本,你的助手就能查真正的实时天气了。
小提示:大多数免费 API 都有调用频率限制。如果你的应用需要稳定运行,可以考虑把服务部署到云服务器上,配合定时任务缓存数据。腾讯云的轻量应用服务器对新用户有挺大的折扣(点此了解),跑个 Python 小应用完全够用。
七、strict 模式:杜绝格式错误
Function Calling 在实际开发中最头疼的问题就是模型返回的参数偶尔不符合预期。比如你要求 count 是整数,它偶尔给你个字符串 "5"。
DeepSeek V4 提供了一个 strict 模式来解决这个问题。开启后,模型严格按你的 JSON Schema 输出,绝对不会出现类型错误。
开启方式很简单,把 base_url 换成 beta 端点,并在工具定义中加 "strict": true:
client = OpenAI(
api_key="你的Key",
base_url="https://api.deepseek.com/beta", # 注意:beta 端点
)
tools = [
{
"type": "function",
"function": {
"name": "search_product",
"strict": True, # 开启严格模式
"description": "搜索商品",
"parameters": {
"type": "object",
"properties": {
"keyword": {"type": "string"},
"min_price": {"type": "number", "minimum": 0},
"max_price": {"type": "number"},
"category": {
"type": "string",
"enum": ["电子产品", "服饰", "食品", "图书"]
}
},
"required": ["keyword", "min_price", "max_price", "category"],
"additionalProperties": False
}
}
}
]
strict 模式的硬性要求(不满足会直接报错):
- 每个 object 的所有属性必须列在
required中 - 必须设置
"additionalProperties": false - 不支持
minLength/maxLength(string 长度用pattern正则控制)
如果你的工具参数比较复杂且对格式要求高,strict 模式是必选项。
八、踩坑记录
在实际开发中遇到的几个问题,分享出来帮你省时间:
坑1:忘了把 assistant 消息追加回 messages
很多新手只追加了 tool 结果,忘了把包含 tool_calls 的那条 assistant 消息加回去。模型会懵:我怎么不知道我调用过工具?
# 正确做法
messages.append(msg) # ← 这行不能少
messages.append({"role": "tool", ...})
坑2:用旧模型名
deepseek-chat 将在 2026 年 7 月 24 日下线。如果你还在用老名字,赶紧换成 deepseek-v4-flash 或 deepseek-v4-pro。
| 新名字 | 定位 | 适合场景 |
|---|---|---|
deepseek-v4-flash |
快速、便宜 | 对话、Function Calling、批量任务 |
deepseek-v4-pro |
推理强、更贵 | 复杂逻辑、代码生成、数学推理 |
日常开发用 v4-flash 足够,响应快,而且 Function Calling 能力完全一样。
坑3:工具描述写得太随便
模型的"阅读理解"能力直接取决于你的描述写得好不好。对比一下:
# 差:太模糊
{"description": "查天气"}
# 好:场景明确
{"description": "查询指定城市的实时天气信息,包括温度、天气状况和湿度。需要传入城市名称。"}
多花两分钟写清楚描述,比事后 debug 半天省事得多。
九、总结
Function Calling 的价值不在于"让模型变聪明",而在于打破模型和现实世界之间的墙。有了它,你可以让模型:
- 查询数据库(而不是只靠训练数据猜)
- 调用第三方 API(天气、新闻、物流)
- 操作文件系统(读、写、搜索)
- 触发业务流程(发邮件、下单、审批)
DeepSeek V4 的 Function Calling 兼容 OpenAI 格式,迁移成本几乎为零。配合严格的 JSON Schema 校验和极低的调用成本,是目前做应用开发非常实惠的选择。
完整代码
以上所有示例的整合版,拿去就能跑:
"""
DeepSeek V4 Function Calling 完整示例
依赖: pip install openai
"""
import json
from openai import OpenAI
client = OpenAI(
api_key="sk-你的Key",
base_url="https://api.deepseek.com",
)
TOOLS = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的实时天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "执行数学计算",
"parameters": {
"type": "object",
"properties": {
"expression": {"type": "string", "description": "数学表达式"}
},
"required": ["expression"]
}
}
}
]
# 模拟数据
WEATHER_DB = {
"杭州": {"temp": 26, "desc": "多云转晴"},
"北京": {"temp": 22, "desc": "晴"},
"上海": {"temp": 24, "desc": "小雨"},
"深圳": {"temp": 30, "desc": "雷阵雨"},
}
def get_weather(city):
return WEATHER_DB.get(city, {"temp": "未知", "desc": "暂无数据"})
def calculate(expr):
try:
return {"result": eval(expr, {"__builtins__": {}}, {})}
except Exception as e:
return {"error": str(e)}
TOOL_MAP = {"get_weather": get_weather, "calculate": calculate}
def chat(user_input):
messages = [{"role": "user", "content": user_input}]
resp = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
tools=TOOLS,
)
msg = resp.choices[0].message
if not msg.tool_calls:
return msg.content
messages.append(msg)
for tc in msg.tool_calls:
fn = TOOL_MAP[tc.function.name]
args = json.loads(tc.function.arguments)
result = fn(**args)
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps(result, ensure_ascii=False)
})
final = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
)
return final.choices[0].message.content
if __name__ == "__main__":
while True:
q = input("\n你: ")
if q.lower() in ("quit", "exit", "q"):
break
print(f"助手: {chat(q)}")
本文涉及的 API 价格以 2026 年 5 月官网信息为准。示例代码仅用于学习交流。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)