ITPub博客

首页 > 数据库 > 数据库开发技术 > LLMs:GPT 使用 OpenAPI 规范进行函数调用

LLMs:GPT 使用 OpenAPI 规范进行函数调用

原创 数据库开发技术 作者:HelloTech技术派 时间:2024-02-02 08:30:00 0 删除 编辑

大部分互联网都是由 RESTful API 提供支持的。本文演示如何使用 GPT 智能调用 API。它利用 OpenAPI 规范和链式函数调用。

本文分为两个主要部分:

  1. 如何将示例 OpenAPI 规范转换为 Chat Completions API 的函数定义列表。
  2. 如何使用 Chat Completions API 根据用户指令智能调用这些函数。

建议先熟悉函数调用➡️(如何使用大模型调用函数)。

安装依赖

# 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 规范转换为函数定义

OpenAPI 规范包括有关 API 、它们支持的操作、它们接受的参数、它们可以处理的请求以及它们返回的响应的详细信息。规范以 JSON 格式定义。

规范中的 endpoints 包括以下操作:

  • 列出所有事件
  • 创建新事件
  • 按 ID 检索事件
  • 按 ID 删除事件
  • 按 ID 更新事件名称

规范中的每个操作都有一个 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,我们需要执行以下操作:

  1. 解析 JSON 引用:在 OpenAPI 规范中,通常使用 JSON 引用(也称为 $ref)来避免重复。这些引用指向在多个位置使用的定义。例如,如果多个 API 返回相同的对象结构,则可以定义该结构一次,然后在需要时引用该结构。我们需要解析这些引用并将其替换为它们指向的内容。
  2. 提取函数的名称:我们只需使用 operationId 作为函数名称。或者,我们可以使用端点路径和操作作为函数名称。
  3. 提取描述和参数:我们将遍历 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 调用这些函数

现在有了这些函数定义,可以利用 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技术派;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2017-12-05

  • 博文量
    181
  • 访问量
    73722