Appearance
OpenAPI 规范解读与 API 插件设计思路
01. OpenAPI 规范介绍
对于企业现有的 API,想要接入到 LLMOps 项目中,一般来说,有两种策略:
- 创建一个内置插件,在内置插件中,使用 requests 包对特定的 API 接口发起请求,工具的参数就是 API 请求的参数,这种实现方法设计起来非常简单,使用
requests包发起特定的请求即可,但是一个接口就要新增一个 Python 文件、一个 Yaml 描述文件,并且每次新增都需要重新启动项目,非常繁琐。 - 使用一种规范用来描述API接口,然后程序根据这个规范来提取数据,涵盖了:请求地址、请求参数、请求方法等信息,从而向特定的API接口发起请求,这种方式对比内置插件会更灵活,通过一个程序实现对任意的 API 接口实现请求,只需要传递不同的规范描述即可。
显然对于企业现有的 API,使用内置插件的方式不合适,因为定制性不强,对于 LLMOps 项目来说,每个用户可能都有自己的 API 服务,使用内置插件,所有用户都可以访问到,而且让用户拥有调整代码的权限,也非常不安全,所以一般考虑第 2 种形式。
对于API接口的描述有很多规范,其中OpenAPI规范使用的最频繁/常见,文档链接:https://spec.openapis.org/oas/latest.html。
OpenAPI 规范是一个标准的、与具体编程语言无关的 RESTful API 规范,OpenAPI 规范使得人类和计算机都能在 “不接触任何程序源代码和文档、不监控网络通信” 的情况下了解一个服务的作用,简单来说OpenAPI就是一个 API 接口服务的规范描述信息,和具体的实现方式、编程语言无关,例如可以用 JSON 描述、也可以使用 YAML 等。
例如下方就是一个OpenAPI规范的 JSON 接口描述:
json
{
"openapi": "3.1.0",
"info": {
"title": "获取天气预报数据",
"description": "检索某个位置当前的天气预报",
"version": "v1.0.0"
},
"servers": [
{
"url": "https://weather.example.com"
}
],
"paths": {
"/location": {
"get": {
"description": "获取特定位置的天气预报信息",
"operationId": "GetCurrentWeather",
"parameters": [
{
"name": "location",
"in": "query",
"description": "需要获取天气预报的城市名",
"required": true,
"schema": {
"type": "string"
}
}
],
"deprecated": false
}
}
},
"components": {
"schemas": {}
}
}从上面的 JSON 数据中,我们可以得到以下的信息(程序同样可以获取到):
- 基础服务的 API 地址是
https://weather.example.com。 /location是一个获取特定位置天气预报的一个服务,方法为get。/location接口可以传递一个参数location用于代表需要检索天气的城市,该参数是必填的,类型为字符串。
在OpenAPI规范的数据结构中,使用了大量字段进行描述,其中固定字段如下(原文未列出具体固定字段,此处保留原文逻辑):
对于OpenAPI规范,完整的描述非常庞大,而且还有特别多的字段,例如:Link、Header、Media Type、Request Body、Tag、XML、License 等,可以在 apifox 提供的翻译文档或者 OpenAPI 官网中查看。
- apifox OpenAPI 文档:https://openapi.apifox.cn/
- OpenAPI 官方文档:https://spec.openapis.org/oas/latest.html
不过对于 LLMOps 项目中的API插件,我们要记录的信息不多,仅仅包含一小部分,例如:基础服务API地址、对应服务的路径、对应服务的方法及传递的参数即可,而且绝大部分接口只有GET和POST方法,所以其实可以将OpenAPI的规范进行简化,只保留最关键的信息,如下:
json
{
"servers": [
{
"url": "https://weather.example.com"
}
],
"paths": {
"/location": {
"get": {
"description": "获取特定位置的天气预报信息",
"operationId": "GetCurrentWeather",
"parameters": [
{
"name": "location",
"in": "query",
"description": "需要获取天气预报的城市名",
"required": true,
"schema": {
"type": "string"
}
}
]
}
},
"/ip": {
"post": {
"description": "根据传递的ip地址获取所在地",
"operationId": "GetIpLocation",
"parameters": [
{
"name": "ip",
"in": "query",
"description": "需要查询地址的标准ip,例如201.142.15.5",
"required": true,
"schema": {
"type": "string"
}
}
]
}
}
}
}这样在前端 UI 页面,我们就可以传递OpenAPI Schema相关信息用来描述企业现有的 API,并在后端进行相应的校验与解析。
02. 基于 OpenAPI 规范的 API 插件
对于企业现有接口,接口请求方式无非是 Get、Post、Delete、Put、Options 等,附加的数据无非是 Header、Query、Body、Cookie 中,对于 API 接口,我们可以使用 OpenAPI 规范来进行描述,符合 OpenAPI 规范的 JSON,我们可以轻易使用程序读取其请求接口地址、请求方法、请求参数及详情、路径的描述,从而发起特定的请求。
基于这个思想,我们可以来构建一个APITools,该工具传入OpenAPI规范的数据,从而创建一个请求工具,该请求工具会根据传递的配置构建一个函数,接收特定的参数,并向特定的 API 接口发起请求。
这个思想就是LLMOps中创建自定义API工具的思路,因为对于企业现有的 API,我们往往更希望通过手动在页面配置的方式进行热集成,而不是在后端代码中添加一个工具,然后重新运行项目。
所以本质上就是创建一个类/函数,该类/函数接收OpenAPI Schema数据,并返回一个自定义工具,该自定义工具调用时会向OpenAPI Schema数据里描述的接口发起请求,并且该自定义工具参数就是OpenAPI Schema描述的参数。
映射的伪代码如下:
python
# 1.定义通用API插件
class ApiTool(BaseTool):
url: xxx
parameters: xxx
def _run(xxx):
xxx
# 2.构建openapi-schema规范数据
openapi_schema_weather = {xxx} # 查询天气预报接口
openapi_schema_search = {xxx} # 网络搜索工具
# 3.基于openapi创建工具
api_tool_weather = ApiTool(openapi_schema_weather)
api_tool_search = ApiTool(openapi_schema_search)
# 4.调用工具并传递接口参数,从而向特定的 API 发起请求
api_tool_weather.invoke(xxx)
api_tool_search.invoke(xxx)思路已经有了,核心部分就是如何设计一套程序,将OpenAPI-Schema转换成LangChain-Tool,在这部分存在几个需要解决的问题:
- 一个OpenAPI-Schema描述中存在多个paths,其中一个path代表一个 API 或者一个工具,如何将一个OpenAPI-Schema转换成多个
provider+tool的工具包; - OpenAPI-Schema描述的信息是 JSON 字典,如何将其转换成LangChain-Tool中的
name(String)、description(String)、args_schema(BaseModel类)等信息,对于args_schema类,pydantic 包是否提供了从json->BaseModel类的便捷性方法; - OpenAPI-Schema描述的信息是 JSON 字典,如何将
servers+paths描述信息绑定到requests上,从而实现不同工具发起不同的请求并携带不同的参数; - 对于 LLMOps 项目来说,这是一个多用户的系统,如何设计一套存储方案,可以存储多个用户的多个OpenAPI-Schema规范信息,并相互进行隔离。
总结
- 企业 API 接入 LLMOps 优先选择基于规范描述的方式,而非内置插件,前者更灵活且适配多用户隔离需求;
- OpenAPI 规范是通用的 RESTful API 描述标准,可简化后仅保留
servers、paths等核心字段适配 LLMOps 场景; - API 插件核心设计思路是将 OpenAPI-Schema 转换为 LangChain-Tool,需解决多路径解析、JSON 转 BaseModel、请求参数绑定、多用户数据隔离四大核心问题。