Agent-Function Calling

Function Calling诞生

大语言模型本身局限性:

  • 比如GPT4的知识库截至2024年4月,无法获取最新信息数据。
  • 例如无法回答“今天的天气怎么样”这种简单问题,极大限制了其实际应用价值。
  • 尽管大模型具备强大的涌现能力,但固有知识无法涌现,只能给出文字建议,无法直接解决问题。

让GPT模型拥有调用外部接口的能力-Function Calling

核心

赋予大语言模型调用外部API的能力

本质

让大语言模型调用外部函数的能力,即Chat Completions模型可以不再仅仅根据自身的数据库知识进行回答,
而是可以额外挂载一个函数库,然后根据用户提问去函数库检索,
按照实际需求调用外部函数并获取函数运行结果,
再基于函数运行结果进行回答。

实现过程

构建函数

创建函数库

  • 输入数据格式
  • JSON数据格式(OpenAi官方推荐交互模式)

当数据很大的时候,大模型也有并不能非常准确的将字符串对象识别为DataFrame对象类型的时候。更为通用的方法是借助JSON格式进行跨函数和跨编程环境的通信。

  • 编写函数规范
在使用Function Calling(函数调用)与Chat Completion模型进行交互时,当选定了关键的输入数据格式后,函数编写的规范也同等重要,至少应备:
明确的函数名: 选择一个清晰、描述性强的函数名。
参数顺序和命名: 参数应有逻辑顺序,并使用描述性强的名称。
详细的函数描述:要对函数功能和设置的参数变量有明确的说明

注意:函数库对象必须是一个字典,一个键值对代表一个函数,其中Key是代表函数名称的字符串,而value表示对应的函数。所以上述过程可以简单的理解为:所谓的外部函数库,就是用一个大的字典来存储某应用场景中的所需要的所有函数定义。
我们以一个能计算数据集中所有人年龄总和的例子给大家介绍下这个流程:
Step 1:准备数据
Step 2:设定需求
Step 3:编写计算年龄总和的函数
Step 4:功能测试
Step 5:定义函数库

使用tools参数传递外部函数信息

将这个函数相关的信息传递给Chat Completions模型

  • 构建外部函数的JSON Schema
JSON Schema是一个用于描述JSON数据格式和结构的元数据标准。它用于验证、注释以及操控 JSON文档。JSON Schema本身是用JSON格式表示的,提供了一种灵活的方式来校验数据的结构,包括对象属性的类型、数组长度、数字和字符串的值范围等等。

加载Function calling功能

当定义了外部函数仓库、功能函数及功能函数对应的JSON Schema对象描述后,准备工作基本就做完了。
接下来就可以进行与大模型的数据交互,具体来说就是在对话参数的基础上补充两个额外参数。

完整流程代码

import pandas as pd
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI()
import json
from io import StringIO

# 加载样例数据
df_complex = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie'],
'Age': [55, 30, 35],
'Salary': [50000.0, 100000.5, 150000.75],
'IsMarried': [True, False, True]
})
print(df_complex)

# 将样例数据中的DataFrame转换为JSON格式(按split方向)
df_complex_json = df_complex.to_json(orient='split')


# 构建messages对话
messages = [
{"role": "system","content": "你是一位优秀的数据分析师, 现在有这样一个数据集input_json:%s,数据集以JSON形式呈现" % df_complex_json},
{"role": "user", "content": "请在数据集input_json上执行计算所有人年龄总和函数"}
]


# 编写函数功能
def calculate_total_age_from_split_json(input_json):
"""
从给定的JSON格式字符串(按'split'方向排列)中解析出DataFrame,计算所有人的年龄总和,并以JSON格式返回结果。
参数:
input_json (str): 包含个体数据的JSON格式字符串。
返回:
str: 所有人的年龄总和,以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)})

# 测试函数功能 使用函数计算年龄总和,并以JSON格式输出
result = calculate_total_age_from_split_json(df_complex_json)
print("The JSON output is:", result)


response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
# 加载两个函数调用的参数
tools = [{ # 用 JSON 描述函数。可以定义多个。由大模型决定调用谁。也可能都不调用
"type": "function",
"function": {
"name": "calculate_total_age_from_split_json",
"description": "计算年龄总和的函数,从给定的JSON格式字符串(按'split'方向排列)中解析出DataFrame,计算所有人的年龄总和,并以JSON格式返回结果。",
"parameters": {
"type": "object",
"properties": {
"input_json": {
"type": "string",
"description": "执行计算年龄总和的数据集",
},
},
"required": ["input_json"],
}
}
}], # 编写JSON Schema描述
tool_choice="auto"
)
print('message: ',messages)
print('model_response_message: ',response.choices[0].message)


# 保存交互过程中的函数名称
function_name = response.choices[0].message.tool_calls[0].function.name
print('function_name: ', function_name)

# 加载交互过程中的参数
function_args = json.loads(response.choices[0].message.tool_calls[0].function.arguments)
print('function_args:',function_args)

# 保存交互过程中的函数ID
tool_calls_id = response.choices[0].message.tool_calls[0].id
print('tool_calls_id: ',tool_calls_id)


# 定义函数库
function_repository = {
"calculate_total_age_from_split_json": calculate_total_age_from_split_json,
}

# 得到外部函数的响应结果
local_fuction_call = function_repository[function_name]
print('local_fuction_call: ',local_fuction_call)
function_response = local_fuction_call(**function_args)
print('function_response:',function_response)


# 拼接第一次模型返回结果messages
messages.append(response.choices[0].message)
print('function_messages1: ',messages)


# 拼接第二次模型返回结果messages
messages.append({"role": "tool",
"name": function_name,
"tool_call_id": tool_calls_id,
"content": function_response})

print('function_messages2: ',messages)


# 再次向ChatCompletion模型提问
final_response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
)

print('answer: ',final_response.choices[0].message.content)

支持Function Calling国产大模型

Function Calling 会成为所有大模型的标配,支持它的只会越来越多。不支持的大模型,某种程度上是不大可用的。

目前国产主流大模型中,支持Function Calling的有:

  • 阿里千问
  • 智谱清言ChatGLM
  • Deepseek…