前言
前面学习了 什么是AI Agent,更早也在 Langhcain-Tool 中学习了 Langchain 中怎么使用工具,其中也谈到了Function Calling技术。但是我们当时并没有细究 Langchain 的@tool或者tool()到底是怎么让 LLM 识别并精准调用的。这次就来研究研究这个。
定义
Function Calling(函数调用) :这是模型在推理过程中生成的一个结构化输出。当模型决定要使用某个工具时,它不会直接执行代码,而是返回一个 JSON 对象,其中包含了要调用的工具名称(name)和具体的参数(arguments)。这是模型“思考”的结果。
-
核心:赋予大语言模型调用外部 API的能力,也成为 AI Agent 的核心技能。
-
流程:
- 额外挂载某些具备特定功能的函数库(api)。
- LLM 收到用户提问,判断是否需要外挂”帮助“。如果需要,返回这个结构化输出
- 调用外部函数并获取函数运行结果后,大模型再基于函数结果进行回答。
借用 openAI官网 的描述:
实现
交互格式
Function Calling 是 openAI 在 2023 年 6 月率先提出的,所以 Function 和 LLM 的数据交互格式最开始也是 openAI 定义的。
# 定义工具
tools = [{
"type": "function", # 固定写法
"function": {
"name": "calculate_total_age_from_split_json", #告诉大模型函数的名字
"description": "计算年龄总和的函数,会从给定的JSON格式字符串中解析出DataFrame,计算所有人的年龄总和,并以JSON格式返回结果。",
"parameters": {
"type": "object",# 固定写法
"properties": {
"input_json": {
"type": "string",
"description": "包含待计算年龄总和的数据集",
},
},
"required": ["input_json"],
}
}
}]
函数编写
这里就是普通的函数编写。因为是原生的 LLM,不借助 Langchain等框架,所以不需要 @tool 这样的装饰器。我们只需要按照上面要求的交互格式 和 LLM 交互即可。
# 编写函数功能
def calculate_total_age_from_split_json(input_json):
"""
从给定的JSON格式字符串(按'split'方向排列)中解析出DataFrame,计算所有人的年龄总和,并以JSON格式返回结果。
参数:
input_json (str): 包含个体数据的JSON格式字符串。
返回:
str: 所有人的年龄总和,以JSON格式返回。
"""
print("函数calculate_total_age_from_split_json被调用")
# 将JSON字符串转换为DataFrame
df = pd.read_json(StringIO(input_json), orient='split')
# 计算所有人的年龄总和
total_age = df['Age'].sum()
# 将结果转换为字符串形式,然后使用json.dumps()转换为JSON格式
return json.dumps({"total_age": str(total_age)})
Calling
response = client.chat.completions.create(
model=ALI_TONGYI_PLUS_MODEL,
messages=messages,
tools=tools, # 编写JSON Schema描述
tool_choice="auto"
)
response = client.chat.completions.create(
model=ALI_TONGYI_PLUS_MODEL,
messages=messages,
tools=tools, # 编写JSON Schema描述
tool_choice="auto"
)
思考
前面我们在 Langchain 中使用工具获取答案时,LLM 的返回结构是这样的:
那么,我们现在知道 tool 调用的本质需要依赖 Function Calling。我们不管是使用 @tool 还是 tool() 都并没有定义 openAI 要求的这种固定的交互格式。那么为什么 LLM 能调用到这些 function 呢?
其实,LangChain会自动将工具定义(args_schema和description)转换成底层模型(openAI)所要求的 Function Calling 交互格式。这就是 Agent 框架的好处,他会为我们封装好很多底层的功能,供我们更方便快捷地调用。