大模型开发实战篇3:函数调用FunctionCall

本文较长,建议点赞收藏。更多AI大模型应用开发学习视频及资料,在智泊AI

函数调用功能可以增强模型推理效果或进行其他外部操作,包括信息检索、数据库操作、知识图谱搜索与推理、操作系统、触发外部操作等工具调用场景。

经过测试发现以下情况:

1、市面上的大模型几乎都支持函数调用,但其稳定性不是很好,有时候无法命中函数。

2、OpenAI的函数调用在使用中会发现对中文的理解不够精准,DeepSeek对函数的支持也不够稳定。

3、各大模型对英文的支持度都比较好,比中文的理解更精准。

4、函数调用功能,测试下来发现智谱大模型最优秀,不管是中文还是英文都能命中函数。

上图为OpenAI的函数调用的时序图,其中get_weather函数为本地定义的一个函数,需要与大模型至少进行2轮对话,才可以真正调用函数回答问题。

本教程将介绍如何使用 智谱的ChatGLM 的函数调用功能,实现对模型与外部函数库的连接。

tools 是内容生成 API 中的可选参数,用于向模型提供函数定义。通过此参数,模型能够生成符合用户所提供规范的函数参数。请注意,API 实际上不会执行任何函数调用,仅返回调用函数所需要的参数。开发者可以利用模型输出的参数在应用中执行函数调用。

本教程包括以下3个部分:

  1. 如何使用 Chat Completion 接口向模型描述外部函数。
  2. 如何与模型交互,触发模型对函数的调用。
  3. 如何使用模型生成的结果调用外部函数。

如何描述外部函数

假设我们要创建一个具备查询航班功能的聊天机器人。我们定义如下两个外部函数供模型选择调用:

  1. 查询两地之间某日航班号函数:get_flight_number(departure: str, destination: str, date: str)
  2. 查询某航班某日票价函数:get_ticket_price(flight_number: str, date: str)

描述函数功能

为了向模型描述外部函数库,需要向 tools 字段传入可以调用的函数列表。参数如下表:

参数名称 类型 是否必填 参数说明
type String 设置为function
function Object
name String 函数名称
description String 用于描述函数功能。模型会根据这段描述决定函数调用方式。
parameters Object parameters字段需要传入一个 Json Schema 对象,以准确地定义函数所接受的参数。

示例:(注 代码都是使用Python编写)

ini 复制代码
tools = [
{
"type": "function",
"function": {
"name": "get_flight_number",
"description": "根据始发地、目的地和日期,查询对应日期的航班号",
"parameters": {
......
},
}
},
{
"type": "function",
"function": {
"name": "get_ticket_price",
"description": "查询某航班在某日的票价",
"parameters": {
......
},
}
},
]

此处先省略parameters参数,我们将在下一节介绍如何描述函数所需参数。

编写函数参数列表的 JSON 描述

为了准确定义函数的参数列表,在编写参数列表的 JSON Schema 时建议最少包含以下字段:

  • description :说明函数方法的用途。
  • type :定义 JSON 数据的数据类型约束。
  • properties:一个Object,其中的每个属性代表要定义的 JSON 数据中的一个键。
  • required:指定哪些属性在数据中必须被包含。
  • enum:如果一个属性是枚举类型,则此字段应当设置为枚举值的数组。

则完整的tools字段设置为:

ini 复制代码
tools = [
{
"type": "function",
"function": {
"name": "get_flight_number",
"description": "根据始发地、目的地和日期,查询对应日期的航班号",
"parameters": {
"type": "object",
"properties": {
"departure": {
"description": "出发地",
"type": "string"
},
"destination": {
"description": "目的地",
"type": "string"
},
"date": {
"description": "日期",
"type": "string",
}
},
"required": [ "departure", "destination", "date" ]
},
}
},
{
"type": "function",
"function": {
"name": "get_ticket_price",
"description": "查询某航班在某日的票价",
"parameters": {
"type": "object",
"properties": {
"flight_number": {
"description": "航班号",
"type": "string"
},
"date": {
"description": "日期",
"type": "string",
}
},
"required": [ "flight_number", "date"]
},
}
},
]

函数调用选择

在 tools 参数中,如果填写了 functions 参数,则默认情况下模型将决定何时适合使用其中一个函数。 如果要控制模型如何选择函数调用,需要设置 tool_choice 参数。参数默认值为auto,此时模型根据上下文信息自行选择是否返回函数调用。还可以通过将 tool_choice 参数设置为 "none" 来强制 API 不返回任何函数的调用。目前函数调用仅支持 auto 模式。

Function Call 流程实践

本节将以上文定义的具备查询航班功能的聊天机器人为例,介绍如何与模型对话完成函数调用。

初始化函数定义和client:

ini 复制代码
from zhipuai import ZhipuAI
client = ZhipuAI(api_key="")
messages = []
tools = [
{
"type": "function",
"function": {
"name": "get_flight_number",
"description": "根据始发地、目的地和日期,查询对应日期的航班号",
"parameters": {
"type": "object",
"properties": {
"departure": {
"description": "出发地",
"type": "string"
},
"destination": {
"description": "目的地",
"type": "string"
},
"date": {
"description": "日期",
"type": "string",
}
},
"required": [ "departure", "destination", "date" ]
},
}
},
{
"type": "function",
"function": {
"name": "get_ticket_price",
"description": "查询某航班在某日的票价",
"parameters": {
"type": "object",
"properties": {
"flight_number": {
"description": "航班号",
"type": "string"
},
"date": {
"description": "日期",
"type": "string",
}
},
"required": [ "flight_number", "date"]
},
}
},
]
messages = []

我们想查询2024年1月20日从北京前往上海的航班。我们向模型提供这个信息:

ini 复制代码
messages = []
messages.append({"role": "user", "content": "帮我查询从2024年1月20日,从北京出发前往上海的航班"})
response = client.chat.completions.create(
model="glm-4",  # 填写需要调用的模型名称
messages=messages,
tools=tools,
)
print(response.choices[0].message)
messages.append(response.choices[0].message.model_dump())

此时模型成功触发对get_flight_number函数的调用,参数为:date="2024-01-20",departure="北京",destination="上海"

rust 复制代码
content=None role='assistant' tool_calls=[CompletionMessageToolCall(id='call_8252663420321749719', function=Function(arguments='{"date":"2024-01-20","departure":"北京","destination":"上海"}', name='get_flight_number'), type='function')]

现在,清空消息历史。我们尝试提供信息,触发模型对get_ticket_price函数的调用。

ini 复制代码
messages = []
messages.append({"role": "system", "content": "不要假设或猜测传入函数的参数值。如果用户的描述不明确,请要求用户提供必要信息"})
messages.append({"role": "user", "content": "帮我查询2024年1月20日1234航班的票价"})
response = client.chat.completions.create(
model="glm-4",  # 填写需要调用的模型名称
messages=messages,
tools=tools,
)
print(response.choices[0].message)
messages.append(response.choices[0].message.model_dump())

此时模型成功触发对get_ticket_price函数的调用,参数为:date="2024-01-20",flight_number="1234"

rust 复制代码
content=None role='assistant' tool_calls=[CompletionMessageToolCall(id='call_8252648611274312180', function=Function(arguments='{"date":"2024-01-20","flight_number":"1234"}', name='get_ticket_price'), type='function')]

我们也可以强制模型使用特定函数,比如,我们通过设置tool_choice为{"type": "function", "function": {"name": "get_ticket_price"}}以强制模型生成调用get_ticket_price的参数。

ini 复制代码
messages = []
messages.append({"role": "system", "content": "不要假设或猜测传入函数的参数值。如果用户的描述不明确,请要求用户提供必要信息"})
messages.append({"role": "user", "content": "帮我查询1234航班的票价"})
response = client.chat.completions.create(
model="glm-4",  # 填写需要调用的模型名称
messages=messages,
tools=tools,
tool_choice={"type": "function", "function": {"name": "get_ticket_price"}},
)
print(response.choices[0].message)
messages.append(response.choices[0].message.model_dump())

此时模型被强制触发对get_ticket_price函数的调用,参数为:date="2022-01-01",flight_number="1234"。注意到此时模型假设了一个date。

rust 复制代码
content=None role='assistant' tool_calls=[CompletionMessageToolCall(id='call_8252663214163297577', function=Function(arguments='{"date":"2022-01-01","flight_number":"1234"}', name='get_ticket_price'), type='function')]

我们也可以强制模型不调用函数。需要设置tool_choice为none。

使用模型生成的参数调用函数

将所需的函数实现

python 复制代码
def get_flight_number(date:str , departure:str , destination:str):
flight_number = {
"北京":{
"上海" : "1234",
"广州" : "8321",
},
"上海":{
"北京" : "1233",
"广州" : "8123",
}
}
return { "flight_number":flight_number[departure][destination] }
def get_ticket_price(date:str , flight_number:str):
return {"ticket_price": "1000"}

定义处理 Function call 的函数:

ini 复制代码
def parse_function_call(model_response,messages):
# 处理函数调用结果,根据模型返回参数,调用对应的函数。
# 调用函数返回结果后构造tool message,再次调用模型,将函数结果输入模型
# 模型会将函数调用结果以自然语言格式返回给用户。
if model_response.choices[0].message.tool_calls:
tool_call = model_response.choices[0].message.tool_calls[0]
args = tool_call.function.arguments
function_result = {}
if tool_call.function.name == "get_flight_number":
function_result = get_flight_number(**json.loads(args))
if tool_call.function.name == "get_ticket_price":
function_result = get_ticket_price(**json.loads(args))
messages.append({
"role": "tool",
"content": f"{json.dumps(function_result)}",
"tool_call_id":tool_call.id
})
response = client.chat.completions.create(
model="glm-4",  # 填写需要调用的模型名称
messages=messages,
tools=tools,
)
print(response.choices[0].message)
messages.append(response.choices[0].message.model_dump())

查询北京到广州的航班:

ini 复制代码
import json
# 清空对话
messages = []
messages.append({"role": "system", "content": "不要假设或猜测传入函数的参数值。如果用户的描述不明确,请要求用户提供必要信息"})
messages.append({"role": "user", "content": "帮我查询1月23日,北京到广州的航班"})
response = client.chat.completions.create(
model="glm-4",  # 填写需要调用的模型名称
messages=messages,
tools=tools,
)
print(response.choices[0].message)
messages.append(response.choices[0].message.model_dump())
parse_function_call(response,messages)

返回

rust 复制代码
content=None role='assistant' tool_calls=[CompletionMessageToolCall(id='call_8282666790542042140', function=Function(arguments='{"date":"2023-01-23","departure":"北京","destination":"广州"}', name='get_flight_number'), type='function')]

content='根据您的要求,我已经查询到了1月23日从北京到广州的航班号,航班号为8321。' role='assistant' tool_calls=None

查询1234航班票价:

ini 复制代码
messages.append({"role": "user", "content": "这趟航班的价格是多少?"})
response = client.chat.completions.create(
model="glm-4",  # 填写需要调用的模型名称
messages=messages,
tools=tools,
)
print(response.choices[0].message)
messages.append(response.choices[0].message.model_dump())
parse_function_call(response,messages)

返回

rust 复制代码
content=None role='assistant' tool_calls=[CompletionMessageToolCall(id='call_8282666893621289712', function=Function(arguments='{"date":"2023-01-23","flight_number":"8321"}', name='get_ticket_price'), type='function')]
content='这趟航班的票价为1000元。' role='assistant' tool_calls=None

学习资源推荐

如果你想更深入地学习大模型,以下是一些非常有价值的学习资源,这些资源将帮助你从不同角度学习大模型,提升你的实践能力。

本文较长,建议点赞收藏。更多AI大模型应用开发学习视频及资料,在智泊AI

相关推荐
菠菠萝宝2 小时前
【Java手搓OpenManus】-5- 工具系统设计
java·开发语言·人工智能·openai·agent·manus
Jing_Rainbow2 小时前
【 Weapp-3 /Lesson20(2025-11-04)】路虎卫士小程序开发详解:从架构到细节的深度解析🚙📱
微信·微信小程序·程序员
申阳2 小时前
Day 18:01. 基于 SpringBoot4 开发后台管理系统-快速了解一下 SpringBoot4 新特性
前端·后端·程序员
Cleaner3 小时前
大模型的手和脚:从提示工程到 MCP
人工智能·llm·mcp
带刺的坐椅4 小时前
Solon AI 开发学习7 - chat - 四种消息类型及提示语增强
java·ai·llm·solon
小霖家的混江龙5 小时前
不再害怕数学,给开发者的 AI 向量 (Vector) 入门课,看完秒懂!
人工智能·llm
16324015415 小时前
从 GPT-2 到 gpt-oss[1]:架构进展分析
llm
16324015415 小时前
回顾-llama[1]-->“ 一句话概括论文核心+技术亮点总结”
llm