1.引言
大模型的发展越来越进化,尤其是DeepSeek发布以来,可谓是AI界的拼多多,给产业界带来极致性价比,让大模型成本进一步普惠化。大模型的发展,从生产力工具的维度去看主要有两个方面:
- 创造增量需求
- 现有成本替代
你看总结起来,其实就是一句话:降本增效提质。但事实上,当下不管是大模型,还是Agent从技术,从需求上来看,发展都是不及业界预期的,在应用形态上还主要在于知识型工具层面,还远没有达到生产力工具的层面。
这也是大模型本身固有的一些局限,比如:
- 没有记忆能力,上下文窗口限制
- 知识更新不及时,新旧知识分不清
- 针对专业领域,不保障给出可靠的答案
- 难以和外部已有系统灵活交互
这里我们聚焦第四点:不能和外部已有系统灵活交互。这就要了命了,计算机世界发展那么多年,已经积累了那么多成套成体系的生产力工具系统,不可能因为大模型而从0到1重构一遍,成本太高了!
必须要有某种结合的方式。一方面发挥大模型的推理,理解,生成等智能化能力;一方面要能利用好现有系统能力。要解决这个问题,open ai在23年提出了function calling,给大模型找到了一条通往外部世界的渠道。
你看,就大模型而言,open ai代表了事实上的标准,从最初提出大模型技术:rest api +客户端库的形态,再到function calling。
那么,今天我试图通过一篇文章,让你搞懂function calling的本质。
2.案例
2.1.场景描述
我想要开发一个天气智能助手,输入某个城市名称,能够知道该城市当前气温等相关天气状况。要实现这个智能助手,需要借助两个平台:
- 心知天气:www.seniverse.com/。一个气象数据服务平台
- deepseek:www.deepseek.com/。大模型服务平台
关于这两个平台,你需要去注册,拿到相应的密钥key,才能访问相关api服务。具体操作,我就不展开了,都比较简单。心知天气平台,登录以后,找到控制台,添加产品,拿到私钥,像这样:
deepseek注册登录,需要充值,创建api key即可:
2.2.环境准备
在开发中,需要使用到相关第三方库,通过requests库访问外部api,通过openai 库访问大模型服务,需要安装这两个库:
pip install requests openai
你也可以像我这样,通过requirements.txt文件,管理依赖库:
然后通过以下命令安装
pip install -r requirements.txt
2.3.示例代码
2.3.1.创建访问天气服务工具
心知天气平台,提供了丰富气象api服务,文档地址:seniverse.yuque.com/hyper_data/…
这里,我通过天气实况api完成我们的智能助手工具
通过requests库,封装api工具:
import requests
def get_weather(loc):
"""查询实时天气函数"""
url = "https://api.seniverse.com/v3/weather/now.json"
params = {
"key": "", # 填写你的私钥
"location": "广州", # 你要查询的地区可以用代号,拼音或者汉字,文档在官方下载,这里举例广州
"language": "zh-Hans", # 中文简体
"unit": "c", # 获取气温
}
response = requests.get(url, params=params)
temperature = response.json()
return temperature["results"][0]["now"]
# 执行入口
if __name__ == "__main__":
print(get_weather("广州"))
2.3.2.访问大模型服务
好了,天气服务工具已经有了,来封装访问大模型服务
# 大模型部分内容
from openai import OpenAI
import json
client = OpenAI(
api_key="你的deepseek 密钥",
base_url="https://api.deepseek.com"
)
messages = [
{"role": "user", "content": "请帮我查询广州地区今日天气情况"}
]
#只输入问题,不提供函数输入测试
response = client.chat.completions.create(
model="deepseek-chat",
messages=messages
)
print(response.choices[0].message.content)
不使用工具,直接问大模型实时性问题,你看它是无能为力的,deepseek还算可以,给我们提出了寻找答案的建议和方案。
2.3.3.整合工具和大模型
要在大模型服务中整合function calling,需要对工具进行一些元数据描述,这很重要!大模型在推理过程中,在理解问题的基础上,来决定是否要调用工具
# 为大模型声明描述函数,是一个字典dict
get_weather_function = {
"name": "get_weather",
"description": "查询即时天气函数,根据输入的城市名称,查询对应城市的实时天气",
"parameters": {
"type": "object",
"properties": {
"loc": {
"type": "string",
"description": "城市名称",
},
},
"required": ["loc"],
},
}
tools = [
{
"type": "function",
"function": get_weather_function,
}
]
available_functions = {
"get_weather": get_weather,
}
调用大模型服务,使用工具:
# 提供函数输入测试
response = client.chat.completions.create(
model="deepseek-chat",
messages=messages,
tools=tools
)
response_message = response.choices[0].message
print(response_message)
print(response_message.tool_calls[0].function)
# 调用函数
function_call = response_message.tool_calls[0].function
# 获取函数名称
function_name = function_call.name
# 获得对应函数对象
function_to_call = available_functions[function_name]
# 获得执行函数所需参数
function_args = json.loads(function_call.arguments)
# 执行函数
function_response = function_to_call(**function_args)
# 输出函数调用结果
print(function_response)
# 综合消息输入大模型,做进一步理解处理
# 大模型函数选择消息
messages.append(response_message.model_dump())
# 追加函数调用结果消息
messages.append({
"role": "tool",
"content": json.dumps(function_response), # 将回复的字典转化为json字符串
"tool_call_id": response_message.tool_calls[0].id # 将函数执行结果作为tool_message添加到messages中, 并关联返回执行函数内容的id
})
# 终结调用大模型
second_response = client.chat.completions.create(
model="deepseek-chat",
messages=messages)
print(second_response.choices[0].message.content)
2.3.4.完整代码
import requests
def get_weather(loc):
"""查询实时天气函数"""
url = "https://api.seniverse.com/v3/weather/now.json"
params = {
"key": "", # 填写你的私钥
"location": "广州", # 你要查询的地区可以用代号,拼音或者汉字,文档在官方下载,这里举例广州
"language": "zh-Hans", # 中文简体
"unit": "c", # 获取气温
}
response = requests.get(url, params=params)
temperature = response.json()
return temperature["results"][0]["now"]
# 执行入口
if __name__ == "__main__":
# print(get_weather("广州"))
# 大模型部分内容
from openai import OpenAI
import json
client = OpenAI(
api_key="你的deepseek 密钥",
base_url="https://api.deepseek.com"
)
messages = [
{"role": "user", "content": "请帮我查询广州地区今日天气情况"}
]
# 为大模型声明描述函数,是一个字典dict
get_weather_function = {
"name": "get_weather",
"description": "查询即时天气函数,根据输入的城市名称,查询对应城市的实时天气",
"parameters": {
"type": "object",
"properties": {
"loc": {
"type": "string",
"description": "城市名称",
},
},
"required": ["loc"],
},
}
tools = [
{
"type": "function",
"function": get_weather_function,
}
]
available_functions = {
"get_weather": get_weather,
}
# 提供函数输入测试
response = client.chat.completions.create(
model="deepseek-chat",
messages=messages,
tools=tools
)
response_message = response.choices[0].message
print(response_message)
print(response_message.tool_calls[0].function)
# 调用函数
function_call = response_message.tool_calls[0].function
# 获取函数名称
function_name = function_call.name
# 获得对应函数对象
function_to_call = available_functions[function_name]
# 获得执行函数所需参数
function_args = json.loads(function_call.arguments)
# 执行函数
function_response = function_to_call(**function_args)
# 输出函数调用结果
print(function_response)
# 综合消息输入大模型,做进一步理解处理
# 大模型函数选择消息
messages.append(response_message.model_dump())
# 追加函数调用结果消息
messages.append({
"role": "tool",
"content": json.dumps(function_response), # 将回复的字典转化为json字符串
"tool_call_id": response_message.tool_calls[0].id # 将函数执行结果作为tool_message添加到messages中, 并关联返回执行函数内容的id
})
# 终结调用大模型
second_response = client.chat.completions.create(
model="deepseek-chat",
messages=messages)
print(second_response.choices[0].message.content)
2.3.5.运行结果
你看整个执行过程:
- 当我们需要大模型回答关于实时天气的问题:请帮我查询广州地区今日天气情况
- 大模型在理解问题的基础上,发现需要使用外部工具:get_weather
- 通过外部工具调用,获取天气数据,将工具执行结果,与原问题一并综合给到大模型
- 大模型给出最终结果
重要要关注的事情,大模型并不会调用工具,它只会帮助我们选择工具,工具调用要由外部程序控制,大模型返回选择工具信息:
在需要调用工具的场景中,大模型返回信息中有一个tool_calls信息,它是一个list,说明可以选择多个工具。