前言
在AI开发的路上,我们经常会遇到这样的问题:如何让AI模型不仅仅是回答问题,还能够执行具体的操作?比如查询股票价格、获取天气信息、操作数据库等。这就是今天要讲的核心概念——Function Call(函数调用) 。
什么是Function Call?
Function Call是大语言模型(LLM)的一个重要功能,它允许AI模型在对话过程中主动调用外部函数或API来获取实时数据或执行特定操作。简单来说,就是让AI不仅能"说话",还能"做事"。
传统对话 vs Function Call
传统对话模式:
- 用户:青岛啤酒的股价是多少?
- AI:抱歉,我无法获取实时股价信息...
Function Call模式:
- 用户:青岛啤酒的股价是多少?
- AI:(调用股价查询函数)青岛啤酒当前收盘价为67.92元
凡事先看官方文档:Function Calling | DeepSeek API Docs
官方文档里有示例代码的模板,也有些代码解释。
接下来,我就用官网的模板进行演示
实战案例:股票价格查询系统
让我们通过一个完整的代码示例来理解Function Call的工作原理。
1. 环境准备
首先,如果你没有安装过,我们需要安装必要的依赖包:
pip3 install openai
2. 核心代码解析
import json
from openai import OpenAI
# 初始化客户端
client = OpenAI(
api_key=os.getenv('DEEPSEEK_API_KEY'),
base_url="https://api.deepseek.com/v1",
)
3. 定义Function Call工具
这是Function Call的核心部分——工具定义:
tools = [
{
"type": "function",
"function": {
"name": "get_closing_price",
"description": "获取指定股票的收盘价",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "股票名称",
},
},
"required": ["name"],
}
}
}
]
关键字段解释:
type: 工具类型,这里是"function"name: 函数名称,AI会根据这个名称调用对应函数description: 函数描述,帮助AI理解何时使用这个函数parameters: 参数定义,遵循JSON Schema格式required: 必需参数列表
4. 实现具体函数
这里并没有真的去某某股票网站获取api,我只是写了一个简单的函数,帮助理解这个工具函数的作用,具体的api获取不是本篇文章的重点。
def get_closing_price(name):
if name == '青岛啤酒':
return '67.92'
elif name == '贵州茅台':
return '1488.21'
else:
return '未找到该股票'
这个函数就是模拟了股票价格查询的逻辑。在实际应用中,你可以接入真实的股票API。
5. 发送消息并处理Function Call
def send_message(messages):
response = client.chat.completions.create(
model='deepseek-reasoner',
messages=messages,
tools=tools,
tool_choice='auto' # 自动选择是否使用工具
)
return response
Function Call的完整执行流程
让我们跟踪一次完整的Function Call过程:
第一步:用户提问
messages = [{"role": "user", "content": "青岛啤酒的收盘价是多少?"}]
response = send_message(messages)
第二步:AI决定使用工具
AI分析用户问题,发现需要调用get_closing_price函数:
# AI的响应会包含tool_calls
print("AI回复:", response.choices[0].message.content)
print("工具调用:", response.choices[0].message.tool_calls)
第三步:执行函数调用
if response.choices[0].message.tool_calls != None:
tool_call = response.choices[0].message.tool_calls[0]
if tool_call.function.name == "get_closing_price":
# 解析参数
arguments_dict = json.loads(tool_call.function.arguments)
# 调用实际函数
price = get_closing_price(arguments_dict['name'])
# 将结果添加到消息历史
messages.append({
"role": "tool",
"content": price,
"tool_call_id": tool_call.id
})
第四步:生成最终回复
# 再次调用AI,让它基于函数结果生成回复
response = send_message(messages)
print("最终回复:", response.choices[0].message.content)
消息历史的演变
在整个过程中,消息历史会经历以下变化:
# 初始状态
[
{'role': 'user', 'content': '青岛啤酒的收盘价是多少?'}
]
# AI决定调用工具后
[
{'role': 'user', 'content': '青岛啤酒的收盘价是多少?'},
{'role': 'assistant', 'content': '我需要调用函数来获取青岛啤酒的收盘价。', 'tool_calls': [...]}
]
# 函数执行结果添加后
[
{'role': 'user', 'content': '青岛啤酒的收盘价是多少?'},
{'role': 'assistant', 'content': '我需要调用函数来获取青岛啤酒的收盘价。', 'tool_calls': [...]},
{'role': 'tool', 'content': '67.92', 'tool_call_id': 'call_xxx'}
]
完整代码
这三行代码是为了git代码时,保护我的密钥写的,你可以忽略不看,直接替换自己的密钥即可
import os
from dotenv import load_dotenv # 首先:pip3 install dotenv
load_dotenv('.env.local')
import json
from openai import OpenAI #首次:pip3 install openai
import os
from dotenv import load_dotenv # 首先:pip3 install dotenv
load_dotenv('.env.local')
client = OpenAI(
api_key=os.getenv('DEEPSEEK_API_KEY'),
base_url="https://api.deepseek.com/v1",
)
def send_message(messages):
response = client.chat.completions.create(
model='deepseek-reasoner',
messages=messages,
tools=tools,
tool_choice='auto'
)
return response
# 打造一个函数调用的工具
tools = [
{
"type": "function",
"function": {
"name": "get_closing_price",
"description": "获取指定股票的收盘价",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "股票名称",
},
},
"required": ["name"],
}
}
}
]
# 定义函数
def get_closing_price(name):
if name == '青岛啤酒':
return '67.92'
elif name == '贵州茅台':
return '1488.21'
else:
return '未找到该股票'
if __name__ == '__main__':
messages = [{"role": "user", "content": "青岛啤酒的收盘价是多少?"}]
response = send_message(messages)
message = response.choices[0].message
messages.append({
"role": message.role,
"content": message.content,
"tool_calls": message.tool_calls
})
print("回复:")
print(response.choices[0].message.content)
print("工具选择:")
print(response.choices[0].message.tool_calls)
# LLM 已经确定了它要用的函数
if response.choices[0].message.tool_calls != None:
tool_call = response.choices[0].message.tool_calls[0]
if tool_call.function.name == "get_closing_price":
arguments_dict = json.loads(tool_call.function.arguments) # {"name": "青岛啤酒"}
price = get_closing_price(arguments_dict['name'])
messages.append({
"role": "tool",
"content": price,
"tool_call_id": tool_call.id
})
print("messages:", messages)
response = send_message(messages)
print("回复:")
print(response.choices[0].message.content)
代码执行结果: 可以看到在messages里面看到完整的消息链
Function Call的应用场景
- 数据查询:股票价格、天气信息、新闻资讯
- 系统操作:文件操作、数据库查询、API调用
- 计算任务:复杂计算、数据分析、图表生成
- 外部集成:第三方服务调用、支付处理、消息发送
最佳实践
1. 函数设计原则
- 函数功能单一,职责明确
- 参数验证和错误处理
- 返回格式统一
2. 工具描述优化
{
"name": "get_stock_price",
"description": "获取指定股票的实时价格信息,支持A股主要股票查询",
"parameters": {
"type": "object",
"properties": {
"stock_name": {
"type": "string",
"description": "股票名称,如'青岛啤酒'、'贵州茅台'"
},
"price_type": {
"type": "string",
"enum": ["current", "closing", "opening"],
"description": "价格类型:current-当前价格,closing-收盘价,opening-开盘价"
}
},
"required": ["stock_name"]
}
}
3. 错误处理
def get_closing_price(name):
try:
# 实际的股票API调用
price = fetch_stock_price(name)
return f"股票{name}的收盘价为{price}元"
except Exception as e:
return f"获取{name}股价失败:{str(e)}"
进阶技巧
1. 多函数协作
tools = [
{"type": "function", "function": {"name": "get_stock_price", ...}},
{"type": "function", "function": {"name": "analyze_stock_trend", ...}},
{"type": "function", "function": {"name": "get_market_news", ...}}
]
2. 条件性工具选择
# 根据用户意图选择不同的tool_choice策略
if "紧急" in user_message:
tool_choice = {"type": "function", "function": {"name": "emergency_handler"}}
else:
tool_choice = "auto"
总结
Function Call是LLM这颗大脑进化出“手和脚”的第一步,它让AI从"信息处理器"升级为"行动执行者"。这项技术已经进化多轮,演化为如今爆火的MCP,但是,对于,初学者而言,我们需要知道、理解、掌握来龙去脉,这对我们进一步了解AIGC的知识有很大帮助。
让我们一起在AI的学习道路上乘风破浪!
欢迎点赞👍收藏⭐关注🔔~