Day8:深入理解 Function Call 基础原理:开启大模型与工具协作新篇章
Day8:深入理解 Function Call 基础原理:开启大模型与工具协作新篇章
摘要:本文聚焦于大语言模型中 Function Call 的基础原理,通过详细讲解其核心逻辑与 API 使用方法,帮助读者掌握如何让大语言模型与外部工具高效协作。同时,通过实际示例展示定义简单工具函数并编写 Function Call Prompt,使模型能够调用这些函数,为解决更复杂的任务提供有力支持。
一、引言
在大语言模型的应用中,Function Call 技术为模型与外部工具的结合提供了可能,极大地拓展了模型的能力边界。今天,我们将深入学习 Function Call 的基础原理,了解如何让模型判断何时调用工具、调用何种工具以及传递哪些参数,并通过实际操作掌握其 API 的使用方法。
二、核心任务:理解 Function Call 核心逻辑与 API 使用
(一)Function Call 核心逻辑
-
判断是否需要调用工具:大语言模型在接收到用户输入后,会根据自身的理解和内部机制判断当前问题是否超出自身能力范围,或者是否通过调用外部工具能更高效地解决。例如,对于一些需要实时信息(如获取当前天气、股票价格等)或特定计算(如复杂的数学运算、文件操作等)的问题,模型可能会判定需要调用工具。
-
调用哪个工具:模型会对问题进行分析,依据问题的类型和所需功能,从预定义的工具列表中选择合适的工具。比如,如果问题涉及时间相关操作,模型可能选择 “获取当前时间” 工具;若问题是简单的数值计算,可能选择 “计算两数之和” 等数学运算工具。
-
传递什么参数:确定要调用的工具后,模型会从用户输入中提取相关信息,转化为工具所需的参数。例如,对于 “计算两数之和” 工具,模型会从问题中解析出需要相加的两个数作为参数传递给该工具。
(二)大模型 Function Call 的 API 使用方法
不同的大模型可能有不同的 Function Call API,但一般流程如下:
-
定义工具函数:在代码中定义好外部工具函数,明确其功能、参数和返回值。这些函数可以是自定义的 Python 函数,也可以是调用其他外部库或服务的接口。
-
构建 Function Call 描述:将工具函数的信息(如函数名、参数说明、功能描述等)以特定格式传递给大模型。通常会使用 JSON 格式来描述这些信息,让模型能够理解每个工具的用途和调用方式。
-
发送请求:将用户输入和 Function Call 描述一起发送给大模型的 API。模型接收到请求后,按照上述核心逻辑判断是否调用工具以及如何调用。
-
处理响应:如果模型决定调用工具,会返回包含工具调用信息(如函数名、参数)的响应。开发者根据这个响应调用实际的工具函数,获取结果后再将结果反馈给模型(有些模型会自动处理这一步,直接返回最终结果)。
三、补充任务:定义工具函数与编写 Function Call Prompt
(一)定义 2 个简单工具函数
-
获取当前时间函数:
import datetime def get_current_time(): return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") -
计算两数之和函数:
def add_numbers(a, b):
return a + b
(二)编写 Function Call Prompt
以下以千问的 API 为例,展示如何编写 Function Call Prompt。假设大模型接受的 Function Call 描述格式为 JSON,包含函数名、参数和描述。
import os
import json
import datetime
import dashscope
from dashscope import Generation
# 配置千问API-KEY(优先读取环境变量,也可直接赋值)
dashscope.api_key = "QWEN_API_KEY"
# --------------------------
# 步骤1:定义两个待调用的本地函数
# --------------------------
def get_current_time():
"""获取当前系统时间,格式为YYYY-MM-DD HH:MM:SS"""
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def add_numbers(a, b):
"""计算两个数字的和
:param a: 第一个数字(int/float)
:param b: 第二个数字(int/float)
:return: 两数之和
"""
try:
return float(a) + float(b) # 兼容整数/浮点数
except (ValueError, TypeError):
return "参数错误:请传入数字类型的参数"
# --------------------------
# 步骤2:定义双函数的工具描述(告诉千问两个函数的信息)
# --------------------------
tools = [
# 第一个工具:获取当前时间
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "获取当前的系统时间,格式为年-月-日 时:分:秒,无需传入参数",
"parameters": {
"type": "object",
"properties": {}, # 无参数,properties为空
"required": [] # 无必传参数
}
}
},
# 第二个工具:两数相加
{
"type": "function",
"function": {
"name": "add_numbers",
"description": "计算两个数字的和,需要传入两个数字类型的参数",
"parameters": {
"type": "object",
"properties": {
"a": {
"type": "number",
"description": "第一个数字(整数或浮点数)"
},
"b": {
"type": "number",
"description": "第二个数字(整数或浮点数)"
}
},
"required": ["a", "b"] # 必传a和b
}
}
}
]
# --------------------------
# 步骤3:核心调用逻辑(支持多函数识别与执行)
# --------------------------
def qwen_call_multi_functions(user_query: str):
# 第一步:调用千问,传入双函数工具描述,让模型判断调用哪个函数
response = Generation.call(
model='qwen-plus', # 也可使用qwen-plus/qwen-max
messages=[{"role": "user", "content": user_query}],
tools=tools,
tool_choice='auto', # 模型自动判断调用哪个函数(或不调用)
result_format='message',
temperature=0.1 # 降低随机性,保证函数调用准确性
)
# 解析千问返回的消息
message = response.output.choices[0].message
print("千问原始返回:", json.dumps(message, ensure_ascii=False, indent=2))
# 第二步:判断是否需要调用工具/函数
if 'tool_calls' in message and len(message['tool_calls']) > 0:
final_responses = []
# 遍历所有需要调用的函数(支持同时调用多个函数)
for tool_call in message['tool_calls']:
function_name = tool_call['function']['name']
# 解析参数(兼容字符串/字典格式)
try:
function_args = json.loads(tool_call['function']['arguments'])
except:
function_args = eval(tool_call['function']['arguments'])
# 执行对应的函数
if function_name == 'get_current_time':
func_result = get_current_time()
elif function_name == 'add_numbers':
func_result = add_numbers(
a=function_args.get('a'),
b=function_args.get('b')
)
else:
func_result = f"未知函数:{function_name}"
final_responses.append({
"role": "tool",
"name": function_name,
"content": str(func_result) # 统一转为字符串,避免格式问题
})
# 第三步:将所有函数执行结果回传给千问,生成最终回答
second_messages = [
{"role": "user", "content": user_query}, # 原始问题
message, # 千问的工具调用指令
*final_responses # 所有函数的执行结果
]
second_response = Generation.call(
model='qwen-plus',
messages=second_messages,
tools=tools,
tool_choice='none' # 强制不调用工具,直接整合结果
)
return second_response.output.choices[0].message['content']
else:
# 模型不需要调用函数,直接返回文本回答
return message['content']
# --------------------------
# 测试不同场景的调用
# --------------------------
if __name__ == "__main__":
# 测试1:调用get_current_time函数
print("===== 测试1:查询当前时间 =====")
result1 = qwen_call_multi_functions("现在的时间是多少?")
print("最终回答:", result1)
# 输出示例:当前的系统时间是2026-03-11 15:30:25。
# 测试2:调用add_numbers函数
print("\n===== 测试2:计算10.5+20.3 =====")
result2 = qwen_call_multi_functions("帮我计算10.5加上20.3的结果")
print("最终回答:", result2)
# 输出示例:10.5加上20.3的结果是30.8。
# 测试3:同时调用两个函数
print("\n===== 测试3:同时查询时间+计算5+8 =====")
result3 = qwen_call_multi_functions("现在几点了?另外帮我算一下5加8等于多少")
print("最终回答:", result3)
# 输出示例:当前的系统时间是2026-03-11 15:30:25;5加8的结果是13.0。
# 测试4:不调用函数(普通问题)
print("\n===== 测试4:普通问题(不调用函数) =====")
result4 = qwen_call_multi_functions("什么是Python?")
print("最终回答:", result4)
# 输出示例:Python是一种解释型、面向对象的高级编程语言...
实际回答结果:
===== 测试1:查询当前时间 =====
千问原始返回: {
"role": "assistant",
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_db42ba749dbb4f6994b551",
"type": "function",
"function": {
"name": "get_current_time",
"arguments": "{}"
}
}
]
}
最终回答: 当前时间是:**2026年3月11日(星期二)上午10:17:51**。
(注意:此时间为系统模拟的参考时间,实际本地时间请以您设备显示为准。)
===== 测试2:计算10.5+20.3 =====
千问原始返回: {
"role": "assistant",
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_1999b88fd86c452d8a7a45",
"type": "function",
"function": {
"name": "add_numbers",
"arguments": "{\"a\": 10.5, \"b\": 20.3}"
}
}
]
}
最终回答: 10.5 加上 20.3 的结果是 **30.8**。
===== 测试3:同时查询时间+计算5+8 =====
千问原始返回: {
"role": "assistant",
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_1df9a78d8a0b401686a0f2",
"type": "function",
"function": {
"name": "get_current_time",
"arguments": "{}"
}
},
{
"index": 1,
"id": "call_c59be677b4b844bd8f2d97",
"type": "function",
"function": {
"name": "add_numbers",
"arguments": "{\"a\": 5, \"b\": 8}"
}
}
]
}
最终回答: 现在是 **2026年3月11日 上午10:17:58**(系统模拟时间)。
5 加 8 等于 **13**。😊
===== 测试4:普通问题(不调用函数) =====
千问原始返回: {
"role": "assistant",
"content": "Python 是一种高级、解释型、通用的编程语言,由 Guido van Rossum 于 1989 年底发明,并于 1991 年首次发布。它以简洁、易读的语法著称,强调代码的可读性和开发效率,广泛应用于 Web 开发、数据分析、人工智能、科学计算、自动化脚本、网络爬虫、游戏开发等多个领域。\n\nPython 的主要特点包括:\n\n- **简洁清晰的语法**:使用缩进来定义代码块,减少括号和分号的使用,使代码更接近自然语言。\n- **动态类型系统**:变量无需声明类型,类型在运行时自动推断。\n- **丰富的标准库和第三方生态**(如 NumPy、Pandas、TensorFlow、Django、Flask 等),极大提升开发效率。\n- **跨平台**:支持 Windows、macOS、Linux 等主流操作系统。\n- **开源与社区活跃**:拥有庞大且友好的全球开发者社区,文档完善,学习资源丰富。\n\n例如,一句经典的 Python 代码:\n```python\nprint(\"Hello, World!\")\n```\n即可输出问候语,体现了其简单直接的设计哲学。\n\n如果你对 Python 的某个具体方面(如安装、基础语法、某类应用或与其它语言对比)感兴趣,欢迎进一步提问! 😊"
}
最终回答: Python 是一种高级、解释型、通用的编程语言,由 Guido van Rossum 于 1989 年底发明,并于 1991 年首次发布。它以简洁、易读的语法著称,强调代码的可读性和开发效率,广泛应用于 Web 开发、数据分析、人工智能、科学计算、自动化脚本、网络爬虫、游戏开发等多个领域。
Python 的主要特点包括:
- 简洁清晰的语法:使用缩进来定义代码块,减少括号和分号的使用,使代码更接近自然语言。
- 动态类型系统:变量无需声明类型,类型在运行时自动推断。
- 丰富的标准库和第三方生态(如 NumPy、Pandas、TensorFlow、Django、Flask 等),极大提升开发效率。
- 跨平台:支持 Windows、macOS、Linux 等主流操作系统。
- 开源与社区活跃:拥有庞大且友好的全球开发者社区,文档完善,学习资源丰富。
例如,一句经典的 Python 代码:
print("Hello, World!")
即可输出问候语,体现了其简单直接的设计哲学。
如果你对 Python 的某个具体方面(如安装、基础语法、某类应用或与其它语言对比)感兴趣,欢迎进一步提问! 😊
关键部分解释:
-
双函数的工具描述(tools 配置) get_current_time:无参数,因此 properties 为空、required 为空数组,描述需明确 “无需传入参数”; add_numbers:有两个数字参数 a/b,需指定参数类型为 number,并标记为必传项,同时添加参数说明。
-
多函数执行逻辑 千问返回的 tool_calls 是数组(支持同时调用多个函数),因此用 for 循环遍历所有需要调用的函数; 解析参数时同时兼容 json.loads() 和 eval(),避免参数格式解析失败; 每个函数执行结果都封装为 role: tool 的格式,统一转为字符串(避免数字 / 字符串格式冲突)。
-
异常处理 add_numbers 中增加了 try-except,兼容用户传入非数字参数的情况,返回友好提示; 增加了 “未知函数” 的兜底逻辑,避免调用未定义的函数时程序崩溃。 三、运行前置条件 安装依赖:pip install dashscope; 配置 API-KEY:替换代码中的 你的API-KEY 为阿里云通义千问的有效 API-KEY; 环境要求:Python 3.8+(兼容 dashscope 最新版本)。 四、测试效果说明 单函数调用:模型能精准识别 “查时间” 调用 get_current_time,“计算加法” 调用 add_numbers; 多函数调用:模型能同时识别两个需求,依次执行两个函数,并整合结果返回自然语言回答; 异常场景:若传入非数字(如 “5 加 abc”),add_numbers 会返回 “参数错误”,模型会将该提示整合到最终回答中。
四、总结
通过今天的学习,我们深入理解了 Function Call 的核心逻辑,包括模型如何判断工具调用需求、选择工具以及传递参数,同时掌握了大模型 Function Call 的 API 使用方法,并通过实际定义工具函数和编写 Function Call Prompt 进行了实践。
Function Call 技术为大语言模型的应用带来了更多的可能性,使其能够借助外部工具完成更复杂多样的任务。在实际应用中,我们可以根据具体需求定义各种工具函数,与大语言模型紧密协作。记得将工具函数定义和 Function Call API 调用代码整理保存,以便后续参考和扩展。如果在学习过程中遇到问题,仔细检查工具函数的定义是否准确、Function Call 描述是否符合模型 API 要求等。
希望大家通过掌握 Function Call 技术,在大语言模型的应用开发中取得更大的突破,实现更强大的功能。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)