背景
OpenAI 在模型 gpt-4-0613 和 gpt-3.5-turbo-0613 开始支持 function calling,开发者可以通过描述函数,让模型输出被选择的函数以及包含参数的JSON对象,这样就可以将 GPT 与外部工具和 API 连接起来,扩展 GPT 的能力。 可以使用的场景包括:
- 通过调用外部工具创建回答问题的聊天机器人: 例如将提问”今天杭州天气如何“转换成函数 get_current_weather(location)
- 将自然语言转换成 API 调用或者数据库查询: 例如将提问”获取本月客户列表“转换成函数 get_customers(start_date, end_date)
- 从文本中提取结构化数据: 例如将任务”给张三买一斤荔枝,手机号是13400000000,地址是火炬动力港“ 转换成函数 create_order(orders)
如何使用
使用 HTTP API
- 在请求模型时添加 functions 描述
请求:
curl https://api.openai.com/v1/chat/completions -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
"model": "gpt-3.5-turbo-0613",
"messages": [
{"role": "user", "content": "What is the weather like in Boston?"}
],
"functions": [
{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
]
}'
- functions: 模型可以选择的函数列表,生成包含参数的 JSON 对象。
- functions.name: 函数名称
- functions.description: 函数描述
- functions.parameters: 参数,被描述为 JSON Schema object.
响应:
{
"id": "chatcmpl-123",
...
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"function_call": {
"name": "get_current_weather",
"arguments": "{ \"location\": \"Boston, MA\"}"
}
},
"finish_reason": "function_call"
}]
}
- function_call: 包含选中的函数名称和可以使用的参数对象
- finish_reason: 包括 stop 和 function_call,如果是 stop 则说明已经返回全部的内容,如果是 function_call 则说明返回的是函数调用
- 执行模型返回的函数,该函数由本地实现
function get_current_weather({location}){
return { "temperature": 22, "unit": "celsius", "description": "Sunny" }
}
get_current_weather({ "location": "Boston, MA"})
- 将响应再次发送给模型,让其进行总结
请求:
curl https://api.openai.com/v1/chat/completions -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
"model": "gpt-3.5-turbo-0613",
"messages": [
{"role": "user", "content": "What is the weather like in Boston?"},
{"role": "assistant", "content": null, "function_call": {"name": "get_current_weather", "arguments": "{ \"location\": \"Boston, MA\"}"}},
{"role": "function", "name": "get_current_weather", "content": "{\"temperature\": "22", \"unit\": \"celsius\", \"description\": \"Sunny\"}"}
],
"functions": [
{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
]
}'
将第一次 GPT 的返回和本地执行函数后的结果拼接之后都发送给模型,详见参数 messages
响应:
{
"id": "chatcmpl-123",
...
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "The weather in Boston is currently sunny with a temperature of 22 degrees Celsius.",
},
"finish_reason": "stop"
}]
}
最后获得回复:
The weather in Boston is currently sunny with a temperature of 22 degrees Celsius.
使用 openai
import os
import openai
GPT_MODEL = "gpt-3.5-turbo-0613"
function_descriptions = [
{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
]
user_query = "What is the weather like in Boston?"
# 把问题与函数定义交给 function call,自动选择合适的函数
response = openai.ChatCompletion.create(
model=GPT_MODEL,
messages=[{"role": "user", "content": user_query}],
functions=function_descriptions,
function_call="auto",
)
response_message = response["choices"][0]["message"]
# 调用 function call 选择的函数,得到返回值
import json
import inspect
def get_function_parameter_names(function):
if function is not None and inspect.isfunction(function):
parameter_names = inspect.signature(function).parameters.keys()
return list(parameter_names)
else:
return None
# Locate the function and make the call
def cal_function(_function_name, _arguments):
the_function = globals().get(_function_name)
parameter_names = get_function_parameter_names(the_function)
parameter_values = []
for parameter_name in parameter_names:
parameter_values.append(_arguments[parameter_name])
return the_function(*parameter_values)
def get_current_weather(location):
return json.dumps({ "temperature": 22, "unit": "celsius", "description": "Sunny", "location": location })
function_name = response_message["function_call"]["name"]
arguments = json.loads(response_message["function_call"]["arguments"])
returned_value = cal_function(function_name, arguments)
# 将响应再次发送给模型,让其进行总结
second_response = openai.ChatCompletion.create(
model=GPT_MODEL,
messages=[
{"role": "user", "content": user_query},
response_message,
{
"role": "function",
"name": "get_current_weather",
"content": returned_value,
},
],
)
使用 LangChain Agent
from langchain.chat_models import ChatOpenAI
from langchain.tools import tool
# 在 function description 中描述参数结构,LLM 可能会自动识别,但最好还是配置 args_schema
@tool("get_current_weather")
def get_current_weather(location):
"""Get the current weather in a given location"""
return json.dumps({ "temperature": 22, "unit": "celsius", "description": "Sunny", "location": location })
# 工具是给 agent 自动分析使用
tools = [get_current_weather]
question_format_str = "What is the weather like in Boston?"
from langchain.agents import initialize_agent
from langchain.agents import AgentType
llm = ChatOpenAI(model_name=GPT_MODEL, temperature=0)
agent = initialize_agent(tools, llm, agent=AgentType.OPENAI_MULTI_FUNCTIONS, verbose=True)
agent_response = agent.run(question_format_str)
print(agent_response)