引言
LLM的基本使用方式是 text in -> text out
, 输出是 text
。 但是很多时候,我们希望它能以固定的格式输出,以便解析成结构化的数据,传递给后续的处理逻辑。
在 OpenAI 发布 Function calling
之前,我们可能会议文本输入的方式,在Prompt中要求LLM格式化输出,或者通过LangChain框架提供的 Parsers
相关的抽象。现在,OpenAI 提供了 Function calling
用于将LLM的输出格式化成 Function calling
所需要的参数。
Function calling
介绍
简单的说, Function calling
就是基于(自定义)函数调用所需要的参数,输出可识别的格式化的输出。
在 API 调用中,我们可以描述一个函数(函数名、参数等),并使模型智能选择输出一个包含调用一个或多个函数的参数的 JSON 对象。 Chat Completions API
不会直接调用函数;它只会生成 JSON。我们可以在代码中使用该JSON对象调用我们描述的函数。
举个例子:
我们希望构建一个邮件小助手,可以根据用户的输入发送邮件。用户的输入类似”请向xxx@163.com发送邮件,内容是:请明天上午9:00到学校礼堂参加会议“。 发送邮件需要调用外部API,通常会在代码里封装这样的参数 send_email(to: string, body: string)
可以看到,send_email(to: string, body: string)
需要两个参数:收件人 to
和邮件内容 body
。 这两个参数需要用户的输入中提取。
我们描述好send_email(to: string, body: string)
并传递给 Chat Completions API
,它的返回内容会包含以下内容:
function=Function(arguments='{"to": "xxx@163.com", "body": "请明天上午9:00到学校礼堂参加会议"}', name='send_email')
可以看到OpenAI从用户的文本输入中提取出了调用send_email
所需要的参数收件人 to
和邮件内容 body
,并以JSON的形式赋值给了 arguments
字段。
Function calling
使用流程
我们以查询指定城市指定日期天气为例,介绍 Function calling
的使用流程,
天气查询使用高德的API。
定义函数
代码语言:javascript复制def get_weather(city, date):
base_url = "<https://restapi.amap.com/v3/weather/weatherInfo>"
api_key = os.getenv("AMAP_API_KEY") # 高德API的API-KEY从环境变量获取
params = {
'key': api_key,
'city': city,
'extensions': 'base', # 可根据需求选择 'base' 或 'all'
'output': 'json',
'date': date # 指定日期,格式如:2023-01-01
}
try:
response = requests.get(base_url, params=params)
data = response.json()
logger.info(data)
if data['status'] == '1':
weather_info = data['lives'][0] # 如果选择了 'base' 扩展,使用 'lives';选择 'all' 扩展,使用 'forecasts'
# logger.debug(f"城市: {weather_info['city']}")
# logger.debug(f"日期: {date}")
# logger.debug(f"天气: {weather_info['weather']}")
# logger.debug(f"温度: {weather_info['temperature']} ℃")
# logger.debug(f"风力: {weather_info['windpower']} 级")
return json.dumps(weather_info)
else:
print("查询天气信息失败,错误码:" data['infocode'])
except Exception as e:
print("查询天气信息时发生错误:" str(e))
为 OpenAI 的API描述函数
Function calling
支持以数组形式传入多个函数描述。
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市,指定日期的天气情况",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "要查询的城市名称,如:北京",
},
# "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
"date": {
"type": "string",
"description": "要查询的日期,示例:2023-11-23",
}
},
"required": ["city", "date"],
},
},
}
]
completions API 提取JSON格式的参数
代码语言:javascript复制client = OpenAI()
## 我们需要将用户的输入一并传递给OpenAI,以便从中提取函数调用所需要的参数。
messages = [
{
"role": "user",
"content": "深圳11月1号天气怎么样"
}
]
### 第一次调用 Chat Completions API, 提取参数
response = client.chat.completions.create(
model="gpt-3.5-turbo-1106",
messages=messages,
tools=tools,
tool_choice="auto", # auto is default, but we'll be explicit
)
response_message = response.choices[0].message
tool_calls = response_message.tool_calls # 包含了函数调用所需要的信息
response里的tool_calls,包含了我们描述的函数相关信息,以及所需要的参数。
调用自定义函数
从前面的response中提取参数,并调用 get_weather(city, date):
if tool_calls:
tool_call = tool_calls[0]
function_args = json.loads(tool_call.function.arguments)
function_response = get_weather(
city=function_args.get("city"),
date=function_args.get("date"),
)
return function_response
输出结果:
代码语言:javascript复制**(llm) ➜ function_calling python 01_function_calling.py
{"province": "\u5e7f\u4e1c", "city": "\u6df1\u5733\u5e02", "adcode": "440300", "weather": "\u6674", "temperature": "23", "winddirection": "\u4e1c\u5317", "windpower": "\u22643", "humidity": "67", "reporttime": "2023-11-22 10:31:09", "temperature_float": "23.0", "humidity_float": "67.0"}
(llm) ➜ function_calling**