跳到主要内容

课程说明:

  体验课时间有限,若想深度学习大模型技术,欢迎大家报名由我主讲的《2025大模型Agent智能体开发实战》

a4f3bdfb511aab72c83ee7d1fbefeff

《2025大模型Agent智能体开发实战》 为【100+小时】体系大课,总共20大模块精讲精析,零基础直达大模型企业级应用!

简略介绍图

《2025大模型Agent智能体开发实战》2025 年新春班上新&新年双重特惠,立减2000,九天老师直播间专属优惠券1000,仅需2999即可入学,【仅限前10名】详细信息扫码添加助教,回复“大模型”,即可领取课程大纲&查看课程详情👇

c6847a817fd7efd0cddcb1bcac217c3 image-20250107200452887 image-20250107200502389

《2025大模型Agent智能体开发实战》体验课

Ch 2.DeepSeek v3 Function calling实现方法

import os
from IPython.display import display, Code, Markdown
import requests
import json
ds_api_key = 'YOUR_DS_API_KEY'
# 实例化客户端
client = OpenAI(api_key=ds_api_key,
base_url="https://api.deepseek.com")
# 调用 deepseekv3 模型
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "user", "content": "你好,好久不见!"}
]
)
# 输出生成的响应内容
print(response.choices[0].message.content)
  • Function calling功能简介

  在这一基本背景下,经过数月的研发和优化,OpenAI在0613的更新中为目前最先进的Chat类模型增加了Function calling功能,该功能的本质是让大语言模型调用外部函数的能力,即Chat模型可以不再仅仅根据自身的数据库知识进行回答,而是可以额外挂载一个函数库,然后根据用户提问进行函数库检索,根据实际需求调用外部函数并获取函数运行结果,再基于函数运行结果进行回答。其基本过程如下:

7055021e99cf56e7128c4348f864c5e

而这个外部挂载的函数库,可以是简单的自定义函数,也可以是一个封装了外部工具API的功能型函数(例如一个可以调用谷歌搜索的函数、或者一个可以获取天气信息的函数)。而在OpenAI的精妙设计下,Function calling功能的实现过程也并不复杂,在编写问答函数时,我们只需要在ChatCompletions.create函数中进行参数设置、并提前定义好外部函数库即可,而在Chat模型执行Function calling时,模型会根据用户提问的语义自动检索并挑选合适的函数进行使用,整个过程并不需要人工手动干预指定使用某个函数,大预言模型能够充分发挥自身的语义理解优势,在函数库中自动挑选合适函数进行运行,并给出问题的答案。

  毫无疑问,有了外部函数库的功能加持,Chat模型的处理和解决问题的能力也必将再上一个台阶。同时,相比于此前必须借助LangChain的agent模块才能实现LLM和外部工具API的协同调用,现在Chat模型内部集成的Function calling功能实现过程更简单、开发门流程更加清晰、开发槛更低,而如此种种,也必将促进新一轮的以大语言模型为核心的AI应用的爆发。

  • 基于GPT模型多角色对话系统下的Function calling
ed42b65fef098e4428a4a71978e2175
  • 外部工具API

  首先需要简单说明的是,到底什么是外部工具API。API的全称是应用程序接口,例如目前我们在Python环境中使用的ChatCompletion.create函数,实际上就是OpenAI公司提供的GPT系列模型应用程序的一个接口,通过这个API(create函数或者openai库),我们能够非常顺利的调用GPT模型,并根据输入信息实时输出模型结果。而其他的很多应用程序,也都有相应的API。例如谷歌搜索,也可以通过调用API的形式来实现搜索功能,再比如接下来我们要介绍的OpenWeather API,就是一个可以进行实时天气查询的API。

  • OpenWeather与OpenWeather API

  OpenWeather是一家提供全球范围内的气象数据服务的公司,该公司的服务包括实时天气信息、天气预报、历史天气数据以及各种气象相关的报告等,并且OpenWeather开放了一定使用限度内完全免费的API,即我们可以在代码环境中通过调用OpenWeather API来进行实时天气查询、天气预报等功能,这意味着开发者可以将OpenWeather的天气预报功能加入到他们自己的应用或网站中。

相比OpenAI,OpenWeather更加Open。

  • 极简AI应用开发示例

  当然,一个开放的天气查询API,也意味着我们可以将其封装为一个本地的函数,即通过调用OpenWeather API来获取实时天气信息,然后再用Function calling功能调用该函数,如此一来,就相当于给Chat模型增加了实时获取天气信息的能力,而后我们即可通过Chat模型实时查询天气或根据天气情况询问穿衣建议等,从功能上来说,接入实时天气信息的Chat模型其实也就是一个非常简单的天气查询AI助手。这是一个相对简单的将其他工具API接入Chat模型的示例,接下来我们介绍具体实现方法。

1.OpenWeather注册及API key获取方法

  为了能够调用OpenWeather服务,和OpenAI的API使用过程类似,我们首先需要先注册OpenWeather账号,并获取OpenWeather API Key。这里需要注意的是,对于大多数在线服务的API来说,都需要通过API key来进行身份验证,尽管OpenWeather相对更加Open,有非常多的免费使用的次数,但身份验证仍然是必要的防止API被滥用的有效手段。OpenWeather API key获取流程如下:

  • Step 1.登录OpenWeather官网并点击Sign—>create account完成注册。该网站无需魔法即可直接登录,可以使用国内邮箱或者QQ邮箱均可进行注册,官网地址为:https://openweathermap.org/
e9a6286c483ccf21ab96af1972a918b 577e19b46f4c124b50a0455ccc132e9
  • Step 2.获取API-key:注册完成后,即可在API keys页面查看当前账户的API key:
f44ba05a810500a61516caa28a4fcb4 e497a7ebb4f8d50ff3b6e362e4b569a

一般来说完成注册后,就会有一个已经激活的API-key。和OpenAI一样,OpenWeather的API key也创建多个。

  • Step 3.将其设置为环境变量:和OpenAI API key类似,为了方便后续调用,我们也可以直接将OpenWeather API key设置为环境变量,变量名为OPENWEATHER_API_KEY。具体设置环境变量的方法参考Ch.1中OpenAI APkey设置环境变量流程,此处不再赘述。
aa0f15de151921727c20918fccc8de0

设置完了环境变量之后,接下来即可按照如下方式创建OpenWeather API key变量:

open_weather_key = os.getenv("OPENWEATHER_API_KEY")

2.2 利用OpenWeather API获取实时天气信息

  接下来我们通过一个简单的示例,来介绍如何通过OpenWeather API获取实时天气信息:

import requests

# Step 1.构建请求
url = "https://api.openweathermap.org/data/2.5/weather"

# Step 2.设置查询参数
params = {
"q": "Beijing", # 查询北京实时天气
"appid": open_weather_key, # 输入API key
"units": "metric", # 使用摄氏度而不是华氏度
"lang":"zh_cn" # 输出语言为简体中文
}

# Step 3.发送GET请求
response = requests.get(url, params=params)

# Step 4.解析响应
data = response.json()

这里需要注意的是,城市名必须输入英文名,否则无法正确识别。接下来查看返回结果。首先我们先查看response结果:

response
type(response)

在未解析之前,我们只能查看到基本请求结果状态,这里的200代表成功相应,即本次发送请求获得了对应的响应,且响应内容包含在response中。考虑到默认情况下返回结果是JSON格式,因此后续代码使用了response.json()对其进行解析。解析内容如下:

data
def get_weather(loc):
"""
查询即时天气函数
:param loc: 必要参数,字符串类型,用于表示查询天气的具体城市名称,\
注意,中国的城市需要用对应城市的英文名称代替,例如如果需要查询北京市天气,则loc参数需要输入'Beijing';
:return:OpenWeather API查询即时天气的结果,具体URL请求地址为:https://api.openweathermap.org/data/2.5/weather\
返回结果对象类型为解析之后的JSON格式对象,并用字符串形式进行表示,其中包含了全部重要的天气信息
"""
# Step 1.构建请求
url = "https://api.openweathermap.org/data/2.5/weather"

# Step 2.设置查询参数
params = {
"q": loc,
"appid": open_weather_key, # 输入API key
"units": "metric", # 使用摄氏度而不是华氏度
"lang":"zh_cn" # 输出语言为简体中文
}

# Step 3.发送GET请求
response = requests.get(url, params=params)

# Step 4.解析响应
data = response.json()
return json.dumps(data)

1.functions参数解释与定义过程

  在准备好外部函数及函数库之后,接下来非常重要的一步就是需要将外部函数的信息以某种形式传输给Chat模型。此时就需要使用到ChatCompletion.create函数的functions参数,类似于messages参数是用于向模型传输消息,functions参数专门用于向模型传递当前可以调用的外部函数信息。并且,从参数的具体形式来看,functions参数和messages参数也是非常类似的——都是包含多个字典的list。对于messages来说,每个字典都是一条信息,而对于functions参数来说,每个字典都是一个函数。在大语言模型实际进行问答时,会根据functions参数提供的信息对各函数进行检索。

  很明显,functions参数对于Chat模型的Function calling功能的实现至关重要。接下来我们详细解释functions中每个用于描述函数的字典编写方法。总的来说,每个字典都有三个参数(三组键值对),各参数(Key)名称及解释如下:

  • name:代表函数函数名称字的符串,必选参数,按照要求函数名称必须是 a-z、A-Z、0-9,或包含下划线和破折号,最大长度为 64。需要注意的是,name必须输入函数名称,而后续模型将根据函数名称在外部函数库中进行函数筛选;
  • description:用于描述函数功能的字符串,虽然是可选参数,但该参数传递的信息实际上是Chat模型对函数功能识别的核心依据。即Chat函数实际上是通过每个函数的description来判断当前函数的实际功能的,若要实现多个备选函数的智能挑选,则需要严谨详细的描述函数功能;(需要注意的是,在某些情况下,我们会通过其他函数标注本次对话特指的函数,此时模型就不会执行这个根据描述信息进行函数挑选的过程,此时是可以不设置description的。)
  • parameters:函数参数,必选参数,要求遵照JSON Schema格式进行输入,JSON Schema是一种特殊的JSON对象,专门用于验证JSON数据格式是否满足要求。

  例如,对于get_weather函数,我们需要创建如下字典来对其进行完整描述:

get_weather_function = {
'name': 'get_weather',
'description': '查询即时天气函数,根据输入的城市名称,查询对应城市的实时天气',
'parameters': {
'type': 'object',
'properties': {
'loc': {
'description': "城市名称,注意,中国的城市需要用对应城市的英文名称代替,例如如果需要查询北京市天气,则loc参数需要输入'Beijing'",
'type': 'string'
}
},
'required': ['loc']
}
}

同时还需要封装外部函数库,用于关联外部函数名称和外部函数对象

available_functions = {
"get_weather": get_weather,
}

对于GLM-4模型的Function calling功能来说,还需要将进行更高层次封装,封装为一个tools对象,同时说明当前Function性质:

tools = [
{
"type": "function",
"function":get_weather_function
}
]

2.Function calling功能实现

2.1 First response

  在进行了一系列基础准备工作之后,接下来我们尝试在Chat模型对话执行Function calling功能。这里我们创建如下messages:

# 实例化客户端
client = OpenAI(api_key=ds_api_key,
base_url="https://api.deepseek.com")
messages=[
{"role": "user", "content": "请帮我查询北京地区今日天气情况"}
]

首先我们测试如果只输入这个信息而不输入外部函数库的时候,模型能否知道如何查询天气:

response = client.chat.completions.create(
model="deepseek-chat",
messages=messages
)
response.choices[0].message.content

很明显,模型无法进行回答。

  接下来我们尝试将函数库相关信息输入给Chat模型,这里需要额外设置两个参数,其一是tools参数,用于申明外部函数库当前情况,其二则是需要设置tool_choice参数,该参数用于控制是否执行Function calling功能,该参数有两种不同的取值,默认取值为auto,表示表示模型将根据用户实际对话情况,有选择性的自动挑选合适函数进行执行,而若想让模型在本次对话中特定执行tools中的某个函数,则可以通过输入如下形式的字典:{"name": "my_function"}进行申明,此时模型不再会自动挑选模型,而是会在tools中挑选"my_function"进行执行。

  这里我们尝试让模型自动挑选函数来进行执行,参数设置方法与执行过程如下:

response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "user", "content": "请帮我查询北京地区今日天气情况"}
],
tools=tools,
)

观察此时response结果:

response
response_message = response.choices[0].message
response_message

能够发现,此时返回的message中content为空,而增了一个"tool_calls"的list,该list就包含了当前调用外部函数的全部信息:

response_message.tool_calls
response_message.tool_calls[0]

对于当前CompletionMessageToolCall对象,id为外部函数调用发起请求id,function则表示调用外部函数基本信息,而type则代表了当前当前调用外部函数类型,function代表调用自定义的外部函数。除此之外还可以选择retrieval或web_search,分别代表进行在线知识库的知识检索和进行在线搜索。

response_message.tool_calls[0].id
response_message.tool_calls[0].function

我们可以在此基础上分别提取调用外部函数名称信息和参数信息,分别保存为function_name和function_args对象:

# 完成对话需要调用的函数名称
function_name = response_message.tool_calls[0].function.name
function_name
# 具体的函数对象
fuction_to_call = available_functions[function_name]
fuction_to_call
response_message.tool_calls[0].function.arguments
# 执行该函数所需要的参数
function_args = json.loads(response_message.tool_calls[0].function.arguments)
function_args

需要注意的是,外部函数的计算过程仍然是在本地执行,即Chat模型并不会将代码读取到服务器上再进行在线计算,因此接下来我们需要根据模型返回的函数和函数参数,在本地完成函数计算,然后再将计算过程和结果保存为message并追加到messages后面,并第二次调用Chat模型分析函数的计算结果,并最终根据函数计算结果输出用户问题的答案。

2.2 Second response

  这里我们只需要借助**方法,直接将function_args对象传入fuction_to_call中,即可一次性传输全部参数,**方法的功能可以参考如下示例:

def function_to_call_test(a, b, c):
return a + b + c

function_args_test = {'a': 1, 'b': 2, 'c': 3}

result = function_to_call_test(**function_args_test)

print(result)

**方法其实是一种较为特殊、但同时也非常便捷的参数传递方法吗,该方法会将字典中的每个key对应的value传输到同名参数位中。接下来我们将function_args对象传入fuction_to_call中并完成计算:

function_response = fuction_to_call(**function_args)
function_response

能够发现,模型已经顺利完成计算。接下来我们在messages对象中追加两条消息,第一条消息是第一次模型返回的结果(即调用模型的assistant message),第二条消息则是外部函数计算结果,该条消息的role为function,且name为函数名称。这也是我们首次接触function message,和user、system、assistant message不同,function message必须要输入关键词name,且function message的内容源于外部函数执行的计算结果,并且需要手动进行输入。具体添加过程如下:

messages
response_message.model_dump()
# 追加第一次模型返回结果消息
messages.append(response_message.model_dump())
messages
function_response
# 追加function返回消息
messages.append({
"role": "tool",
"content": function_response,
"tool_call_id":response_message.tool_calls[0].id
})
messages

接下来,再次调用Chat模型来围绕messages进行回答。需要注意的是,此时我们不再需要向模型重复提问,只需要简单的将我们已经准备好的messages传入Chat模型即可:

second_response = client.chat.completions.create(
model="deepseek-chat",
messages=messages)
second_response.choices[0].message

能够发现,模型最终做出了准确回答。

second_response.choices[0].message.content
tools
get_weather
ds_api_key
def run_conv(messages,
api_key,
tools=None,
functions_list=None,
model="deepseek-chat"):
"""
能够自动执行外部函数调用的Chat对话模型
:param messages: 必要参数,输入到Chat模型的messages参数对象
:param api_key: 必要参数,调用模型的API-KEY
:param tools: 可选参数,默认为None,可以设置为包含全部外部函数的列表对象
:param model: Chat模型,可选参数,默认模型为deepseek-chat
:return:Chat模型输出结果
"""
user_messages = messages

client = OpenAI(api_key=api_key,
base_url="https://api.deepseek.com")

# 如果没有外部函数库,则执行普通的对话任务
if tools == None:
response = client.chat.completions.create(
model=model,
messages=user_messages
)
final_response = response.choices[0].message.content

# 若存在外部函数库,则需要灵活选取外部函数并进行回答
else:
# 创建外部函数库字典
available_functions = {func.__name__: func for func in functions_list}

# 创建包含用户问题的message
messages = user_messages

# first response
response = client.chat.completions.create(
model=model,
messages=user_messages,
tools=tools,
)
response_message = response.choices[0].message

# 获取函数名
function_name = response_message.tool_calls[0].function.name
# 获取函数对象
fuction_to_call = available_functions[function_name]
# 获取函数参数
function_args = json.loads(response_message.tool_calls[0].function.arguments)

# 将函数参数输入到函数中,获取函数计算结果
function_response = fuction_to_call(**function_args)

# messages中拼接first response消息
user_messages.append(response_message.model_dump())

# messages中拼接外部函数输出结果
user_messages.append(
{
"role": "tool",
"content": function_response,
"tool_call_id":response_message.tool_calls[0].id
}
)

# 第二次调用模型
second_response = client.chat.completions.create(
model=model,
messages=user_messages)

# 获取最终结果
final_response = second_response.choices[0].message.content

return final_response
messages = [{"role": "user", "content": "请问什么是机器学习?"}]
run_conv(messages=messages,
api_key = ds_api_key)
messages = [{"role": "user", "content": "请问北京今天天气如何?"}]
run_conv(messages=messages,
api_key = ds_api_key,
tools=tools,
functions_list=[get_weather])