大部分互联网都是由 RESTful API 提供支持的。本文演示如何使用 GPT 智能调用 API。它利用 OpenAPI 规范和链式函数调用。
本文分为两个主要部分:
建议先熟悉函数调用➡️(如何使用大模型调用函数)。
安装依赖
# for resolving $ref's in the OpenAPI spec
pip install -q jsonref
pip install -q openai
client 初始化
import os
import json
import jsonref
from openai import OpenAI
import requests
from pprint import pp
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "<your OpenAI API key if not set as env var>"))
OpenAPI 规范包括有关 API 、它们支持的操作、它们接受的参数、它们可以处理的请求以及它们返回的响应的详细信息。规范以 JSON 格式定义。
规范中的 endpoints 包括以下操作:
规范中的每个操作都有一个 operationId ,当我们将规范解析为函数规范时,我们将使用它作为函数名称。该规范还包括定义每个操作的参数的数据类型和结构的架构。
JSON 格式定义示例如下:
{
'openapi': '3.0.0',
'info': {
'version': '1.0.0',
'title': 'Event Management API',
'description': 'An API for managing event data'
},
'paths': {
'/events': {
'get': {
'summary': 'List all events',
'operationId': 'listEvents',
'responses': {
'200': {
'description': 'A list of events',
'content': {
'application/json': {
'schema': {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'id': {
'type': 'string'
},
'name': {
'type': 'string'
},
'date': {
'type': 'string',
'format': 'date-time'
},
'location': {
'type': 'string'
}
},
'required': ['name', 'date', 'location']
}
}
}
}
}
}
},
'post': {
'summary': 'Create a new event',
'operationId': 'createEvent',
'requestBody': {
'required': True,
'content': {
'application/json': {
'schema': {
'type': 'object',
'properties': {
'id': {
'type': 'string'
},
'name': {
'type': 'string'
},
'date': {
'type': 'string',
'format': 'date-time'
},
'location': {
'type': 'string'
}
},
'required': ['name', 'date', 'location']
}
}
}
},
'responses': {
'201': {
'description': 'The event was created',
'content': {
'application/json': {
'schema': {
'type': 'object',
'properties': {
'id': {
'type': 'string'
},
'name': {
'type': 'string'
},
'date': {
'type': 'string',
'format': 'date-time'
},
'location': {
'type': 'string'
}
},
'required': ['name', 'date', 'location']
}
}
}
}
}
}
},
'/events/{id}': {
'get': {
'summary': 'Retrieve an event by ID',
'operationId': 'getEventById',
'parameters': [{
'name': 'id',
'in': 'path',
'required': True,
'schema': {
'type': 'string'
}
}],
'responses': {
'200': {
'description': 'The event',
'content': {
'application/json': {
'schema': {
'type': 'object',
'properties': {
'id': {
'type': 'string'
},
'name': {
'type': 'string'
},
'date': {
'type': 'string',
'format': 'date-time'
},
'location': {
'type': 'string'
}
},
'required': ['name', 'date', 'location']
}
}
}
}
}
},
'delete': {
'summary': 'Delete an event by ID',
'operationId': 'deleteEvent',
'parameters': [{
'name': 'id',
'in': 'path',
'required': True,
'schema': {
'type': 'string'
}
}],
'responses': {
'204': {
'description': 'The event was deleted'
}
}
},
'patch': {
'summary': "Update an event's details by ID",
'operationId': 'updateEventDetails',
'parameters': [{
'name': 'id',
'in': 'path',
'required': True,
'schema': {
'type': 'string'
}
}],
'requestBody': {
'required': True,
'content': {
'application/json': {
'schema': {
'type': 'object',
'properties': {
'name': {
'type': 'string'
},
'date': {
'type': 'string',
'format': 'date-time'
},
'location': {
'type': 'string'
}
},
'required': ['name', 'date', 'location']
}
}
}
},
'responses': {
'200': {
'description': "The event's details were updated",
'content': {
'application/json': {
'schema': {
'type': 'object',
'properties': {
'id': {
'type': 'string'
},
'name': {
'type': 'string'
},
'date': {
'type': 'string',
'format': 'date-time'
},
'location': {
'type': 'string'
}
},
'required': ['name', 'date', 'location']
}
}
}
}
}
}
}
},
'components': {
'schemas': {
'Event': {
'type': 'object',
'properties': {
'id': {
'type': 'string'
},
'name': {
'type': 'string'
},
'date': {
'type': 'string',
'format': 'date-time'
},
'location': {
'type': 'string'
}
},
'required': ['name', 'date', 'location']
}
}
}
}
现在我们已经很好地理解了 OpenAPI 规范,我们可以继续将其解析为函数规范。
我们可以编写一个简单的 openapi_to_functions 函数来生成定义列表,其中每个函数都表示为包含以下键的字典:
name :这对应于 OpenAPI 规范中定义的 API 操作标识符。description :这是函数的简要说明或摘要,概述了函数的功能。parameters :用于定义函数的预期输入参数。它提供有关每个参数的类型、它是必需的还是可选的以及其他相关详细信息的信息。对于 JSON schema 定义的每个 API,我们需要执行以下操作:
description 、 summary 和 字段以填充函数的描述 requestBody 和 parameters 参数。具体实现如下:
def openapi_to_functions(openapi_spec):
functions = []
for path, methods in openapi_spec["paths"].items():
for method, spec_with_ref in methods.items():
# 1. Resolve JSON references.
spec = jsonref.replace_refs(spec_with_ref)
# 2. Extract a name for the functions.
function_name = spec.get("operationId")
# 3. Extract a description and parameters.
desc = spec.get("description") or spec.get("summary", "")
schema = {"type": "object", "properties": {}}
req_body = (
spec.get("requestBody", {})
.get("content", {})
.get("application/json", {})
.get("schema")
)
if req_body:
schema["properties"]["requestBody"] = req_body
params = spec.get("parameters", [])
if params:
param_properties = {
param["name"]: param["schema"]
for param in params
if "schema" in param
}
schema["properties"]["parameters"] = {
"type": "object",
"properties": param_properties,
}
functions.append(
{"type": "function", "function": {"name": function_name, "description": desc, "parameters": schema}}
)
return functions
functions = openapi_to_functions(openapi_spec)
for function in functions:
pp(function)
print()
输出结果
{
'type': 'function',
'function': {
'name': 'listEvents',
'description': 'List all events',
'parameters': {
'type': 'object',
'properties': {}
}
}
}
{
'type': 'function',
'function': {
'name': 'createEvent',
'description': 'Create a new event',
'parameters': {
'type': 'object',
'properties': {
'requestBody': {
'type': 'object',
'properties': {
'id': {
'type': 'string'
},
'name': {
'type': 'string'
},
'date': {
'type': 'string',
'format': 'date-time'
},
'location': {
'type': 'string'
}
},
'required': ['name',
'date',
'location'
]
}
}
}
}
}
{
'type': 'function',
'function': {
'name': 'getEventById',
'description': 'Retrieve an event by ID',
'parameters': {
'type': 'object',
'properties': {
'parameters': {
'type': 'object',
'properties': {
'id': {
'type': 'string'
}
}
}
}
}
}
}
{
'type': 'function',
'function': {
'name': 'deleteEvent',
'description': 'Delete an event by ID',
'parameters': {
'type': 'object',
'properties': {
'parameters': {
'type': 'object',
'properties': {
'id': {
'type': 'string'
}
}
}
}
}
}
}
{
'type': 'function',
'function': {
'name': 'updateEventDetails',
'description': "Update an event's details by ID",
'parameters': {
'type': 'object',
'properties': {
'requestBody': {
'type': 'object',
'properties': {
'name': {
'type': 'string'
},
'date': {
'type': 'string',
'format': 'date-time'
},
'location': {
'type': 'string'
}
},
'required': ['name',
'date',
'location'
]
},
'parameters': {
'type': 'object',
'properties': {
'id': {
'type': 'string'
}
}
}
}
}
}
}
现在有了这些函数定义,可以利用 GPT 根据用户输入智能地调用它们。
请务必注意,Chat Completions API 不执行该函数;相反,它会生成可用于在自己的代码中调用函数的 JSON。
有关函数调用的更多信息,请参阅函数调用指南 ➡️(如何使用大模型调用函数)。
SYSTEM_MESSAGE = """
You are a helpful assistant.
Respond to the following prompt by using function_call and then summarize actions.
Ask for clarification if a user request is ambiguous.
"""
# Maximum number of function calls allowed to prevent infinite or lengthy loops
MAX_CALLS = 5
def get_openai_response(functions, messages):
return client.chat.completions.create(
model="gpt-3.5-turbo-16k",
tools=functions,
tool_choice="auto", # "auto" means the model can pick between generating a message or calling a function.
temperature=0,
messages=messages,
)
def process_user_instruction(functions, instruction):
num_calls = 0
messages = [
{"content": SYSTEM_MESSAGE, "role": "system"},
{"content": instruction, "role": "user"},
]
while num_calls < MAX_CALLS:
response = get_openai_response(functions, messages)
message = response.choices[0].message
print(message)
try:
print(f"\n>> Function call #: {num_calls + 1}\n")
pp(message.tool_calls)
messages.append(message)
# For the sake of this example, we'll simply add a message to simulate success.
# Normally, you'd want to call the function here, and append the results to messages.
messages.append(
{
"role": "tool",
"content": "success",
"tool_call_id": message.tool_calls[0].id,
}
)
num_calls += 1
except:
print("\n>> Message:\n")
print(message.content)
break
if num_calls >= MAX_CALLS:
print(f"Reached max chained function calls: {MAX_CALLS}")
USER_INSTRUCTION = """
Instruction: Get all the events.
Then create a new event named AGI Party.
Then delete event with id 2456.
"""
process_user_instruction(functions, USER_INSTRUCTION)
ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_jmlvEyMRMvOtB80adX9RbqIV', function=Function(arguments='{}', name='listEvents'), type='function')])
>> Function call #: 1
[ChatCompletionMessageToolCall(id='call_jmlvEyMRMvOtB80adX9RbqIV', function=Function(arguments='{}', name='listEvents'), type='function')]
ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_OOPOY7IHMq3T7Ib71JozlUQJ', function=Function(arguments='{\n "requestBody": {\n "id": "1234",\n "name": "AGI Party",\n "date": "2022-12-31",\n "location": "New York"\n }\n}', name='createEvent'), type='function')])
>> Function call #: 2
[ChatCompletionMessageToolCall(id='call_OOPOY7IHMq3T7Ib71JozlUQJ', function=Function(arguments='{\n "requestBody": {\n "id": "1234",\n "name": "AGI Party",\n "date": "2022-12-31",\n "location": "New York"\n }\n}', name='createEvent'), type='function')]
ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_Kxluu3fJSOsZNNCn3JIlWAAM', function=Function(arguments='{\n "parameters": {\n "id": "2456"\n }\n}', name='deleteEvent'), type='function')])
>> Function call #: 3
[ChatCompletionMessageToolCall(id='call_Kxluu3fJSOsZNNCn3JIlWAAM', function=Function(arguments='{\n "parameters": {\n "id": "2456"\n }\n}', name='deleteEvent'), type='function')]
ChatCompletionMessage(content='Here are the actions I performed:\n\n1. Retrieved all the events.\n2. Created a new event named "AGI Party" with the ID "1234", scheduled for December 31, 2022, in New York.\n3. Deleted the event with the ID "2456".', role='assistant', function_call=None, tool_calls=None)
>> Function call #: 4
None
>> Message:
Here are the actions I performed:
1. Retrieved all the events.
2. Created a new event named "AGI Party" with the ID "1234", scheduled for December 31, 2022, in New York.
3. Deleted the event with the ID "2456".
我们演示了如何将 OpenAPI 规范转换为函数规范,这些规范可以提供给 GPT 以便它智能地调用它们,并展示了如何将它们链接在一起以执行复杂的操作。
该系统的可能扩展可能包括处理需要条件逻辑或循环的更复杂的用户指令,与实际 API 集成以执行实际操作,以及改进错误处理和验证以确保指令可行且函数调用成功。
参考资料:
Function-calling with an OpenAPI specification | OpenAI Cookbook
https://cookbook.openai.com/examples/function_calling_with_an_openapi_spec
原文作者:HelloTech技术派;如有侵权,请联系管理员删除。