OpenAPI函数调用教程:让AI自动调用REST API
技术背景
OpenAPI规范(OAS)是描述RESTful API的机器可读标准,广泛用于API文档和客户端生成。将OAS与函数调用结合,可让AI模型理解API结构并自动生成调用参数,从而无需手动编写接口调用逻辑。这解决了传统AI应用中API集成繁琐、难以扩展的问题,适用于需要动态调用多个服务的场景,如智能助手、自动化工作流。主要限制包括模型可能生成无效参数、需要处理API错误、以及规范复杂度影响解析准确性。
核心原理
核心原理是将OpenAPI规范中的每个操作(路径+方法)映射为函数定义,包含名称(operationId)、描述和参数schema。模型根据用户指令和函数定义,通过工具调用(tool_calls)返回一个JSON对象,其中包含函数名和参数。开发者解析该JSON并执行实际API调用,然后将结果作为工具消息返回给模型,以便模型生成最终回复。链式调用通过循环处理多个工具调用实现,直到模型不再请求调用或达到最大次数。
架构分析
系统分为三个主要组件:1) OpenAPI规范解析器:使用jsonref解析$ref引用,提取操作信息生成函数列表;2) 模型交互层:通过Chat Completions API发送消息和函数定义,接收模型响应;3) 执行与反馈循环:解析工具调用,执行API,将结果追加到消息历史,继续对话。设计决策包括使用operationId作为函数名以保证唯一性,将请求体和参数分别封装为parameters对象,以及设置MAX_CALLS防止无限循环。
完整实战教程
使用OpenAPI规范进行函数调用
OpenAPI规范(以前称为Swagger规范)是一种用于描述HTTP API的机器可读格式。通过将OpenAPI规范与函数调用结合,你可以让AI模型根据用户请求自动调用外部API。
为什么使用OpenAPI规范?
- 标准化:OpenAPI规范提供了一种标准化的方式来描述API,使得模型能够理解API的结构和参数。
- 自动化:模型可以自动生成函数调用,无需手动编写代码。
- 灵活性:你可以轻松地添加或修改API,而无需更改模型逻辑。
设置步骤
1. 定义OpenAPI规范
首先,你需要创建一个OpenAPI规范文件(通常为YAML或JSON格式),描述你的API。例如:
openapi: 3.0.0
info:
title: 天气API
version: 1.0.0
paths:
/weather:
get:
summary: 获取天气信息
parameters:
- name: city
in: query
required: true
schema:
type: string
responses:
'200':
description: 成功
2. 将规范传递给模型
在调用模型时,将OpenAPI规范作为上下文的一部分传递给模型。这样,模型就能理解可用的函数及其参数。
3. 解析模型输出
模型会生成一个函数调用请求,你需要解析该请求并执行相应的API调用。
代码示例
以下是一个使用Python和OpenAI API的示例:
import openai
import json
# 定义OpenAPI规范
openapi_spec = """
openapi: 3.0.0
info:
title: 天气API
version: 1.0.0
paths:
/weather:
get:
summary: 获取天气信息
parameters:
- name: city
in: query
required: true
schema:
type: string
responses:
'200':
description: 成功
"""
# 调用模型
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[
{"role": "system", "content": "你是一个助手,可以调用API获取天气信息。"},
{"role": "user", "content": "北京今天天气怎么样?"},
],
functions=[{
"name": "get_weather",
"description": "获取天气信息",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
},
}],
function_call="auto",
)
# 解析模型输出
if response.choices[0].message.get("function_call"):
function_call = response.choices[0].message.function_call
arguments = json.loads(function_call.arguments)
city = arguments["city"]
# 执行API调用
# ...
最佳实践
- 明确描述:在OpenAPI规范中,为每个操作提供清晰的描述,以便模型理解。
- 参数验证:在模型生成函数调用后,验证参数是否符合规范。
- 错误处理:处理API调用失败的情况,并向用户提供有用的反馈。
结论
使用OpenAPI规范进行函数调用,可以显著提升AI应用的交互能力。通过标准化API描述,模型能够自动调用外部服务,实现更丰富的功能。
互联网的很大一部分由 RESTful API 驱动。让 GPT 能够调用它们将开启无限可能。本笔记本演示了如何利用 GPT 智能调用 API。它利用了 OpenAPI 规范和链式函数调用。
OpenAPI 规范(OAS) 是一种通用标准,用于以机器可读和可解释的格式描述 RESTful API 的详细信息。它使人类和计算机都能理解服务的能力,并且可以用来向 GPT 展示如何调用 API。
本笔记本分为两个主要部分:
- 如何将示例 OpenAPI 规范转换为聊天补全 API 的函数定义列表。
- 如何使用聊天补全 API 根据用户指令智能调用这些函数。
我们建议在继续之前熟悉函数调用。
!pip install -q jsonref # for resolving $ref's in the OpenAPI spec
!pip install -q openai
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 规范转换为函数定义
我们这里使用的示例 OpenAPI 规范是由 gpt-4 创建的。我们将把这个示例规范转换为一组函数定义,供聊天补全 API 使用。模型根据提供的用户指令,生成一个包含调用这些函数所需参数的 JSON 对象。
在继续之前,让我们检查一下这个生成的规范。OpenAPI 规范包含有关 API 端点、它们支持的操作、接受的参数、可以处理的请求以及返回的响应的详细信息。该规范以 JSON 格式定义。
规范中的端点包括以下操作:
- 列出所有事件
- 创建新事件
- 按 ID 检索事件
- 按 ID 删除事件
- 按 ID 更新事件名称
规范中的每个操作都有一个 operationId,当我们解析规范为函数规范时,将使用它作为函数名称。规范还包括定义每个操作参数的数据类型和结构的模式。
您可以在这里看到模式:
with open('./data/example_events_openapi.json', 'r') as f:
openapi_spec = jsonref.loads(f.read()) # it's important to load with jsonref, as explained below
display(openapi_spec)
现在我们对 OpenAPI 规范有了很好的理解,可以继续将其解析为函数规范。
我们可以编写一个简单的 openapi_to_functions 函数来生成定义列表,其中每个函数表示为一个包含以下键的字典:
name:这对应于 OpenAPI 规范中定义的 API 端点的操作标识符。description:这是函数的简要描述或摘要,概述函数的功能。parameters:这是一个定义函数预期输入参数的架构。它提供有关每个参数类型、是否必需或可选以及其他相关详细信息。
对于架构中定义的每个端点,我们需要执行以下操作:
- 解析 JSON 引用:在 OpenAPI 规范中,通常使用 JSON 引用(也称为 $ref)来避免重复。这些引用指向在多个地方使用的定义。例如,如果多个 API 端点返回相同的对象结构,则该结构可以定义一次,然后在需要的地方引用。我们需要解析这些引用,并将其替换为它们指向的内容。
- 提取函数名称:我们将简单地使用 operationId 作为函数名称。或者,我们可以使用端点路径和操作作为函数名称。
- 提取描述和参数:我们将遍历
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()
如何让GPT调用这些函数
现在我们有了这些函数定义,就可以利用 GPT 根据用户输入智能地调用它们。
需要注意的是,聊天补全 API 不会执行函数;相反,它会生成 JSON,你可以用这些 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)
结论
我们已经演示了如何将 OpenAPI 规范转换为函数规范,这些规范可以提供给 GPT,使其能够智能地调用这些函数,并展示了如何将这些函数链接在一起以执行复杂的操作。
该系统的可能扩展包括:处理需要条件逻辑或循环的更复杂的用户指令,与真实 API 集成以执行实际操作,以及改进错误处理和验证,以确保指令可行且函数调用成功。
常见问题
常见错误:1) 未解析$ref导致schema不完整,应使用jsonref加载和替换引用;2) 函数名重复,需确保operationId唯一;3) 模型返回无效JSON参数,需增加验证和错误处理;4) 未处理工具调用结果,导致模型无法继续;5) 达到最大调用次数,需优化指令或增加循环上限。排查方法:打印模型响应和工具调用,检查参数是否符合schema,使用日志记录API调用。
企业应用扩展
生产环境优化:1) 使用异步API调用提高并发性能;2) 实现参数验证和错误重试机制;3) 缓存API响应以减少延迟;4) 监控模型调用和API调用成本;5) 支持更复杂的OpenAPI特性,如认证、分页、文件上传;6) 集成真实API并处理错误响应;7) 使用更强大的模型(如gpt-4)提高函数调用准确性。
总结
本文介绍如何将OpenAPI规范转换为函数定义,使GPT模型能够根据用户指令自动调用外部API。通过解析OpenAPI规范中的operationId、描述和参数,生成符合Chat Completions API要求的函数列表。模型通过工具调用机制返回结构化参数,开发者据此执行实际API调用。文章提供了完整的Python实现,包括JSON引用解析、函数定义生成、链式调用处理,并讨论了最佳实践、常见错误及生产环境优化方案。
- --
内容来源与验证
- 内容等级:PREMIUM_A
- 质量等级:A
- 难度:中级
- 验证等级:V1
- 原文作者:shyamal-anadkat, simonpfish
- 原文日期:2023-10-15
- 来源许可:MIT
- 来源版本:
7957120831067aa96a71e5ad95cab3b4ec01f043 - 查看原文
站内还有更多完整实战教程
加入会员,解锁全部教程、完整代码与持续更新内容。
加入会员