ChatGPT插件开发实战

1.概述

ChatGPT是一款由OpenAI推出的先进对话模型,其强大的自然语言处理能力使得它成为构建智能对话系统和人机交互应用的理想选择。为了进一步拓展ChatGPT的功能和适应不同领域的需求,OpenAI提供了插件开发平台,让开发者可以定制化和扩展ChatGPT的能力。

2.内容

OpenAI 插件将 ChatGPT 连接到第三方应用程序。这些插件使 ChatGPT 能够与开发人员定义的 API 进行交互,从而增强 ChatGPT 的功能并允许其执行广泛的操作。插件使 ChatGPT 能够执行以下操作:

  • 检索实时信息;例如,体育赛事比分、股票价格、最新新闻等。
  • 检索知识库信息;例如,公司文档、个人笔记等。
  • 协助用户采取行动;例如,预订航班、订餐等。

插件开发人员公开一个或多个 API 端点,并附有标准化清单文件和 OpenAPI 规范。这些定义了插件的功能,允许 ChatGPT 使用文件并调用开发人员定义的 API。

AI 模型充当智能 API 调用者。给定 API 规范和何时使用 API 的自然语言描述,模型会主动调用 API 来执行操作。例如,如果用户询问“我应该在巴黎哪里住几晚?”,模型可能会选择调用酒店预订插件 API,接收 API 响应,并结合 API 数据生成面向用户的答案及其自然语言能力。

2.1 插件流程

要构建插件,了解端到端流程是非常有必要的,流程如下所示:

  1.  创建清单文件并将其托管在yourdomain.com/.well-known/ai-plugin.json
    • 该文件包含有关您的插件的元数据(名称、徽标等)、有关所需身份验证的详细信息(身份验证类型、OAuth URL 等)以及您想要公开的端点的 OpenAPI 规范。
    • 该模型将看到 OpenAPI 描述字段,这些字段可用于为不同字段提供自然语言描述。
    • 建议在开始时仅公开 1-2 个端点,并使用最少数量的参数,以最大程度地缩短文本长度。插件描述、API 请求和 API 响应都插入到与 ChatGPT 的对话中。这不利于模型的上下文限制。
  2. 在ChatGPT页面后台系统中注册插件
    • 从顶部下拉列表中选择插件模型,然后选择“插件”、“插件商店”,最后选择“开发自己的插件”。
    • 如果需要身份验证,请提供 OAuth 2client_id和client_secret/或 API 密钥。
  3. 激活插件
    • 必须自己手动激活插件
    • 如果需要 OAuth,用户将通过 OAuth 重定向到您的插件进行登录。

    • 能够与另外 100 个用户共享自己的插件
  4. 开始对话
    • OpenAI 将在发送给 ChatGPT 的消息中注入插件的简洁描述,最终用户看不到。这将包括插件描述、端点和示例。
    • 当用户提出相关问题时,如果看起来相关,模型可能会选择从您的插件调用 API 调用;对于POST请求,我们要求开发者构建用户确认流程以避免破坏行为。
    • 该模型会将 API 调用结果合并到对用户的响应中。
    • 该模型可能会在其响应中包含从 API 调用返回的链接。
    • 该模型还可以在 Markdown 中格式化来自 API 的数据,并且 ChatGPT UI 将自动呈现 Markdown。

2.2 快速使用

创建插件需要 3 个步骤:

  • 构建 API
  • 以 OpenAPI yaml 或 JSON 格式记录 API
  • 创建一个 JSON 清单文件,用于定义插件的相关元数据

每个插件都需要一个ai-plugin.json文件,该文件需要托管在API的域名上。例如,名为example.com的公司将通过https://example.com域名使插件的JSON文件可访问,因为这是他们API托管的位置。当通过ChatGPT UI安装插件时,在后端我们会查找位于/.well-known/ai-plugin.json的文件。/.well-known文件夹是必需的,并且必须存在于自己的域名上,以便ChatGPT可以与自己的插件连接。如果找不到文件,则无法安装插件。对于本地开发,可以使用HTTP,但如果指向远程服务器,则需要使用HTTPS。

所需ai-plugin.json文件的最小定义如下所示:

{
    "schema_version": "v1",
    "name_for_human": "TODO List",
    "name_for_model": "todo",
    "description_for_human": "Manage your TODO list. You can add, remove and view your TODOs.",
    "description_for_model": "Help the user with managing a TODO list. You can add, remove and view your TODOs.",
    "auth": {
        "type": "none"
    },
    "api": {
        "type": "openapi",
        "url": "http://localhost:3333/openapi.yaml"
    },
    "logo_url": "http://localhost:3333/logo.png",
    "contact_email": "support@example.com",
    "legal_info_url": "http://www.example.com/legal"
}

请注意,列在“公共”下的项目将在插件商店对用户可用。

以下是具有不同身份验证方法的示例:

# App-level API keys
type ManifestServiceHttpAuth  = BaseManifestAuth & {
  type: 'service_http';
  authorization_type: HttpAuthorizationType;
  verification_tokens: {
    [service: string]?: string;
  };
}

# User-level HTTP authentication
type ManifestUserHttpAuth  = BaseManifestAuth & {
  type: 'user_http';
  authorization_type: HttpAuthorizationType;
}

type ManifestOAuthAuth  = BaseManifestAuth & {
  type: 'oauth';

  # OAuth URL where a user is directed to for the OAuth authentication flow to begin.
  client_url: string;

  # OAuth scopes required to accomplish operations on the user's behalf.
  scope: string;

  # Endpoint used to exchange OAuth code with access token.
  authorization_url: string;

  # When exchanging OAuth code with access token, the expected header 'content-type'. For example: 'content-type: application/json'
  authorization_content_type: string;

  # When registering the OAuth client ID and secrets, the plugin service will surface a unique token.
  verification_tokens: {
    [service: string]?: string;
  };
}

上述提到的清单文件中某些字段的长度存在限制,这些限制可能会发生变化。还对API响应体强加了最大长度限制,目前为100,000个字符,但这也可能随着时间变化而调整。

总体而言,最佳实践是尽可能简洁地描述和响应,因为模型有有限的上下文窗口。

2.3 OpenAPI定义

接下来的步骤是构建OpenAPI规范以文档化API。ChatGPT中的模型除了OpenAPI规范和清单文件中定义的内容外,对你的API一无所知。这意味着,如果你有一个庞大的API,你不需要将所有功能暴露给模型,而是可以选择特定的端点。例如,如果你有一个社交媒体API,你可能希望让模型通过GET请求访问网站内容,但防止模型能够对用户的帖子进行评论,以降低垃圾信息的可能性。

OpenAPI规范是包裹在您的API之上的包装器。一个基本的OpenAPI规范如下所示:

openapi: 3.0.1
info:
  title: TODO Plugin
  description: A plugin that allows the user to create and manage a TODO list using ChatGPT.
  version: 'v1'
servers:
  - url: http://localhost:3333
paths:
  /todos:
    get:
      operationId: getTodos
      summary: Get the list of todos
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/getTodosResponse'
components:
  schemas:
    getTodosResponse:
      type: object
      properties:
        todos:
          type: array
          items:
            type: string
          description: The list of todos.

首先定义规范版本、标题、描述和版本号。当在ChatGPT中运行查询时,它会查看信息部分中定义的描述,以确定插件是否与用户查询相关。

OpenAPI规范中的限制:

  • API规范中每个API端点描述/摘要字段的最大长度为200个字符
  • API规范中每个API参数描述字段的最大长度为200个字符

2.4 运行插件

一旦为API、清单文件和API的OpenAPI规范创建完成,现在可以通过ChatGPT UI连接插件了。我们创建的插件可能运行在两个不同的地方,要么是在开发环境的本地,要么是在远程服务器上。

如果在本地运行API的版本,可以将插件界面指向自己的本地主机服务器。要将插件与ChatGPT连接,请导航到插件商店,然后选择“开发自己的插件”。输入自己的本地主机和端口号(例如localhost:3333)。请注意,目前仅支持本地开发的auth类型为none。

如果插件在远程服务器上运行,则需要首先选择“开发自己的插件”进行设置,然后再选择“安装未验证的插件”将其安装到您的环境中。只需将插件的清单文件添加到yourdomain.com/.well-known/路径中,然后开始测试自己的API。但是,请注意,对于清单文件的后续更改,您需要将新更改部署到公共站点上,这可能需要较长的时间。在这种情况下,我们建议设置一个本地服务器作为自己的API的代理,这样可以快速原型化OpenAPI规范和清单文件的更改。

3.示例插件

为了开始构建,官方提供了一组涵盖不同身份验证模式和用例的简单插件。从简单的无身份验证待办事项列表插件到更强大的检索插件,这些示例让我们了解了希望通过插件实现的目标。

在开发过程中,可以在计算机上本地运行该插件,也可以通过GitHub Codespaces、Replit或CodeSandbox等云开发环境运行该插件。

3.1 了解如何构建一个无需身份验证的简单待办事项列表插件

首先,查看无身份验证页面,然后定义一个ai-plugin.json包含以下字段的文件

{
    "schema_version": "v1",
    "name_for_human": "TODO List (No Auth)",
    "name_for_model": "todo",
    "description_for_human": "Manage your TODO list. You can add, remove and view your TODOs.",
    "description_for_model": "Plugin for managing a TODO list, you can add, remove and view your TODOs.",
    "auth": {
        "type": "none"
    },
    "api": {
        "type": "openapi",
        "url": "PLUGIN_HOSTNAME/openapi.yaml"
    },
    "logo_url": "PLUGIN_HOSTNAME/logo.png",
    "contact_email": "support@example.com",
    "legal_info_url": "https://example.com/legal"
}

请注意,PLUGIN_HOSTNAME应该是插件服务器的实际主机名。

接下来,我们可以定义 API 端点来为特定用户创建、删除和获取待办事项列表项。

import json

import quart
import quart_cors
from quart import request

# Note: Setting CORS to allow chat.openapi.com is only required when running a localhost plugin
app = quart_cors.cors(quart.Quart(__name__), allow_origin="https://chat.openai.com")

_TODOS = {}


@app.post("/todos/<string:username>")
async def add_todo(username):
    request = await quart.request.get_json(force=True)
    if username not in _TODOS:
        _TODOS[username] = []
    _TODOS[username].append(request["todo"])
    return quart.Response(response='OK', status=200)


@app.get("/todos/<string:username>")
async def get_todos(username):
    return quart.Response(response=json.dumps(_TODOS.get(username, [])), status=200)


@app.delete("/todos/<string:username>")
async def delete_todo(username):
    request = await quart.request.get_json(force=True)
    todo_idx = request["todo_idx"]
    if 0 <= todo_idx < len(_TODOS[username]):
        _TODOS[username].pop(todo_idx)
    return quart.Response(response='OK', status=200)


@app.get("/logo.png")
async def plugin_logo():
    filename = 'logo.png'
    return await quart.send_file(filename, mimetype='image/png')


@app.get("/.well-known/ai-plugin.json")
async def plugin_manifest():
    host = request.headers['Host']
    with open("ai-plugin.json") as f:
        text = f.read()
        # This is a trick we do to populate the PLUGIN_HOSTNAME constant in the manifest
        text = text.replace("PLUGIN_HOSTNAME", f"https://{host}")
        return quart.Response(text, mimetype="text/json")


@app.get("/openapi.yaml")
async def openapi_spec():
    host = request.headers['Host']
    with open("openapi.yaml") as f:
        text = f.read()
        # This is a trick we do to populate the PLUGIN_HOSTNAME constant in the OpenAPI spec
        text = text.replace("PLUGIN_HOSTNAME", f"https://{host}")
        return quart.Response(text, mimetype="text/yaml")


def main():
    app.run(debug=True, host="0.0.0.0", port=5002)


if __name__ == "__main__":
    main()

最后,我们需要设置和定义 OpenAPI 规范以匹配本地或远程服务器上定义的端点。无需通过规范公开 API 的全部功能,而是可以选择让 ChatGPT 仅访问某些功能。

还有许多工具可以自动将您的服务器定义代码转换为 OpenAPI 规范,因此无需手动执行此操作。对于上面的 Python 代码,OpenAPI 规范将如下所示:

openapi: 3.0.1
info:
    title: TODO Plugin
    description: A plugin that allows the user to create and manage a TODO list using ChatGPT. If you do not know the user's username, ask them first before making queries to the plugin. Otherwise, use the username "global".
    version: "v1"
servers:
    - url: PLUGIN_HOSTNAME
paths:
    /todos/{username}:
        get:
            operationId: getTodos
            summary: Get the list of todos
            parameters:
                - in: path
                  name: username
                  schema:
                      type: string
                  required: true
                  description: The name of the user.
            responses:
                "200":
                    description: OK
                    content:
                        application/json:
                            schema:
                                $ref: "#/components/schemas/getTodosResponse"
        post:
            operationId: addTodo
            summary: Add a todo to the list
            parameters:
                - in: path
                  name: username
                  schema:
                      type: string
                  required: true
                  description: The name of the user.
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: "#/components/schemas/addTodoRequest"
            responses:
                "200":
                    description: OK
        delete:
            operationId: deleteTodo
            summary: Delete a todo from the list
            parameters:
                - in: path
                  name: username
                  schema:
                      type: string
                  required: true
                  description: The name of the user.
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: "#/components/schemas/deleteTodoRequest"
            responses:
                "200":
                    description: OK

components:
    schemas:
        getTodosResponse:
            type: object
            properties:
                todos:
                    type: array
                    items:
                        type: string
                    description: The list of todos.
        addTodoRequest:
            type: object
            required:
                - todo
            properties:
                todo:
                    type: string
                    description: The todo to add to the list.
                    required: true
        deleteTodoRequest:
            type: object
            required:
                - todo_idx
            properties:
                todo_idx:
                    type: integer
                    description: The index of the todo to delete.
                    required: true
View Code

3.2 了解如何构建具有服务级别身份验证的简单待办事项列表插件

首先,查看服务级别身份验证页面,然后定义一个ai-plugin.json包含以下字段的文件:

 

{
    "schema_version": "v1",
    "name_for_human": "TODO List (service auth)",
    "name_for_model": "todo",
    "description_for_human": "Manage your TODO list. You can add, remove and view your TODOs.",
    "description_for_model": "Plugin for managing a TODO list, you can add, remove and view your TODOs.",
    "auth": {
        "type": "service_http",
        "authorization_type": "bearer",
        "verification_tokens": {
            "openai": "Replace_this_string_with_the_verification_token_generated_in_the_ChatGPT_UI"
        }
    },
    "api": {
        "type": "openapi",
        "url": "https://example.com/openapi.yaml"
    },
    "logo_url": "https://example.com/logo.png",
    "contact_email": "support@example.com",
    "legal_info_url": "https://example.com/legal"
}

请注意,服务级别身份验证插件需要验证令牌。设置服务访问令牌后,该令牌是在 ChatGPT Web UI 中的插件安装过程中生成的。

还需要将“Example.com”更新为远程服务器的名称。

接下来,我们可以定义 API 端点来为特定用户创建、删除和获取待办事项列表项。端点还检查用户是否经过身份验证。

import json

import quart
import quart_cors
from quart import request

app = quart_cors.cors(quart.Quart(__name__))

# This key can be anything, though you will likely want a randomly generated sequence.
_SERVICE_AUTH_KEY = "REPLACE_ME"
_TODOS = {}

def assert_auth_header(req):
    assert req.headers.get(
        "Authorization", None) == f"Bearer {_SERVICE_AUTH_KEY}"

@app.post("/todos/<string:username>")
async def add_todo(username):
    assert_auth_header(quart.request)
    request = await quart.request.get_json(force=True)
    if username not in _TODOS:
        _TODOS[username] = []
    _TODOS[username].append(request["todo"])
    return quart.Response(response='OK', status=200)

@app.get("/todos/<string:username>")
async def get_todos(username):
    assert_auth_header(quart.request)
    return quart.Response(response=json.dumps(_TODOS.get(username, [])), status=200)

@app.delete("/todos/<string:username>")
async def delete_todo(username):
    assert_auth_header(quart.request)
    request = await quart.request.get_json(force=True)
    todo_idx = request["todo_idx"]
    if 0 <= todo_idx < len(_TODOS[username]):
        _TODOS[username].pop(todo_idx)
    return quart.Response(response='OK', status=200)

@app.get("/logo.png")
async def plugin_logo():
    filename = 'logo.png'
    return await quart.send_file(filename, mimetype='image/png')

@app.get("/.well-known/ai-plugin.json")
async def plugin_manifest():
    host = request.headers['Host']
    with open("ai-plugin.json") as f:
        text = f.read()
        return quart.Response(text, mimetype="text/json")

@app.get("/openapi.yaml")
async def openapi_spec():
    host = request.headers['Host']
    with open("openapi.yaml") as f:
        text = f.read()
        return quart.Response(text, mimetype="text/yaml")

def main():
    app.run(debug=True, host="0.0.0.0", port=5002)

if __name__ == "__main__":
    main()
View Code

最后,我们需要设置并定义 OpenAPI 规范以匹配远程服务器上定义的端点。一般来说,无论身份验证方法如何,OpenAPI 规范看起来都是一样的。使用自动 OpenAPI 生成器将减少创建 OpenAPI 规范时出错的可能性。

openapi: 3.0.1
info:
    title: TODO Plugin
    description: A plugin that allows the user to create and manage a TODO list using ChatGPT. If you do not know the user's username, ask them first before making queries to the plugin. Otherwise, use the username "global".
    version: "v1"
servers:
    - url: https://example.com
paths:
    /todos/{username}:
        get:
            operationId: getTodos
            summary: Get the list of todos
            parameters:
                - in: path
                  name: username
                  schema:
                      type: string
                  required: true
                  description: The name of the user.
            responses:
                "200":
                    description: OK
                    content:
                        application/json:
                            schema:
                                $ref: "#/components/schemas/getTodosResponse"
        post:
            operationId: addTodo
            summary: Add a todo to the list
            parameters:
                - in: path
                  name: username
                  schema:
                      type: string
                  required: true
                  description: The name of the user.
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: "#/components/schemas/addTodoRequest"
            responses:
                "200":
                    description: OK
        delete:
            operationId: deleteTodo
            summary: Delete a todo from the list
            parameters:
                - in: path
                  name: username
                  schema:
                      type: string
                  required: true
                  description: The name of the user.
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            $ref: "#/components/schemas/deleteTodoRequest"
            responses:
                "200":
                    description: OK

components:
    schemas:
        getTodosResponse:
            type: object
            properties:
                todos:
                    type: array
                    items:
                        type: string
                    description: The list of todos.
        addTodoRequest:
            type: object
            required:
                - todo
            properties:
                todo:
                    type: string
                    description: The todo to add to the list.
                    required: true
        deleteTodoRequest:
            type: object
            required:
                - todo_idx
            properties:
                todo_idx:
                    type: integer
                    description: The index of the todo to delete.
                    required: true
View Code

4.总结

插件开发平台为开发者提供了一系列API和工具,使其可以自定义ChatGPT的输入输出、增加特定任务的支持以及集成外部数据和服务。开发者可以通过插件实现特定的领域知识、自定义回答模式、定制话题导向等功能,从而让ChatGPT更贴合特定的使用场景和用户需求。

在插件开发过程中,开发者可以借助ChatGPT的先进预训练模型,以及丰富的开发文档和示例代码来快速上手。插件支持多种编程语言,并与现有的ChatGPT API无缝集成,保证了开发的便捷性和灵活性。

值得注意的是,插件开发平台也注重模型的安全性和可控性。OpenAI提供了强大的监管措施和审核流程,确保插件的使用符合社区准则,并防止滥用或不当行为。

总体而言,ChatGPT插件开发平台为开发者提供了一个广阔的创作空间,让他们可以将ChatGPT打造成更具个性和实用性的智能对话系统。通过这个平台,开发者可以将ChatGPT的潜力发挥到极致,为用户提供更加智能、定制化的交互体验。 

posted @ 2023-07-30 21:50  哥不是小萝莉  阅读(1710)  评论(0编辑  收藏  举报