开发ChatGPT插件-示例插件

探索ChatGPT,学习开发一款插件,成为先锋者,一同成长。加入「阿杰与AI」公众号,关注AI发展趋势,等待AI机会出现。

  • 1.如何访问插件?
  • 2.开发ChatGPT插件-介绍
  • 3.开发ChatGPT插件-入门
  • 4.开发ChatGPT插件-认证
  • 5.开发ChatGPT插件-示例插件
  • 6.开发ChatGPT插件-审核
  • 7.开发ChatGPT插件-线上插件及常见问题

示例插件

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

在开发过程中,您可以在本地计算机上或通过GitHub CodespacesReplitCodeSandbox等云开发环境运行插件。

插件快速入门

OpenAI创建了插件快速入门,作为开发人员在不到 5 分钟内启动和运行插件的起点。如果您还没有运行过插件并且想熟悉运行一个插件所需的最少步骤,请考虑从插件quickstart repo开始。

了解如何构建一个无需授权的简单待办事项列表插件

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


{
    "schema_version": "v1",
    "name_for_human": "TODO Plugin (no auth)",
    "name_for_model": "todo",
    "description_for_human": "Plugin for managing a 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",
        "is_user_authenticated": false
    },
    "logo_url": "PLUGIN_HOSTNAME/logo.png",
    "contact_email": "support@example.com",
    "legal_info_url": "https://example.com/legal"
}

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

接下来,OpenAI可以定义 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()

最后,OpenAI需要设置和定义 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

了解如何使用服务级别身份验证构建一个简单的待办事项列表插件

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


{
    "schema_version": "v1",
    "name_for_human": "TODO Plugin (service auth)",
    "name_for_model": "todo",
    "description_for_human": "Plugin for managing a TODO list, you can add, remove & view 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",
        "is_user_authenticated": false
    },
    "logo_url": "https://example.com/logo.png",
    "contact_email": "support@example.com",
    "legal_info_url": "https://example.com/legal"
}

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

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

接下来,OpenAI可以定义 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()

最后,OpenAI需要设置和定义 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

了解如何构建简单的运动统计插件

这个插件是一个简单的运动统计 API 的例子。在考虑构建什么时,请牢记OpenAI的域政策和使用政策。

首先,定义一个ai-plugin.json包含以下字段的文件:


{
    "schema_version": "v1",
    "name_for_human": "Sport Stats",
    "name_for_model": "sportStats",
    "description_for_human": "Get current and historical stats for sport players and games.",
    "description_for_model": "Get current and historical stats for sport players and games. Always display results using markdown tables.",
    "auth": {
        "type": "none"
    },
    "api": {
        "type": "openapi",
        "url": "PLUGIN_HOSTNAME/openapi.yaml",
        "is_user_authenticated": false
    },
    "logo_url": "PLUGIN_HOSTNAME/logo.png",
    "contact_email": "support@example.com",
    "legal_info_url": "https://example.com/legal"
}

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

接下来,OpenAI为一个简单的体育服务插件定义一个模拟 API。


import json
import requests
import urllib.parse
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")
HOST_URL = "https://example.com"
@app.get("/players")
async def get_players():
    query = request.args.get("query")
    res = requests.get(
        f"{HOST_URL}/api/v1/players?search={query}&page=0&per_page=100")
    body = res.json()
    return quart.Response(response=json.dumps(body), status=200)
@app.get("/teams")
async def get_teams():
    res = requests.get(
        "{HOST_URL}/api/v1/teams?page=0&per_page=100")
    body = res.json()
    return quart.Response(response=json.dumps(body), status=200)
@app.get("/games")
async def get_games():
    query_params = [("page", "0")]
    limit = request.args.get("limit")
    query_params.append(("per_page", limit or "100"))
    start_date = request.args.get("start_date")
    if start_date:
        query_params.append(("start_date", start_date))
    end_date = request.args.get("end_date")
    if end_date:
        query_params.append(("end_date", end_date))
    seasons = request.args.getlist("seasons")
    for season in seasons:
        query_params.append(("seasons[]", str(season)))
    team_ids = request.args.getlist("team_ids")
    for team_id in team_ids:
        query_params.append(("team_ids[]", str(team_id)))

    res = requests.get(
        f"{HOST_URL}/api/v1/games?{urllib.parse.urlencode(query_params)}")
    body = res.json()
    return quart.Response(response=json.dumps(body), status=200)
@app.get("/stats")
async def get_stats():
    query_params = [("page", "0")]
    limit = request.args.get("limit")
    query_params.append(("per_page", limit or "100"))
    start_date = request.args.get("start_date")
    if start_date:
        query_params.append(("start_date", start_date))
    end_date = request.args.get("end_date")
    if end_date:
        query_params.append(("end_date", end_date))
    player_ids = request.args.getlist("player_ids")
    for player_id in player_ids:
        query_params.append(("player_ids[]", str(player_id)))
    game_ids = request.args.getlist("game_ids")
    for game_id in game_ids:
        query_params.append(("game_ids[]", str(game_id)))
    res = requests.get(
        f"{HOST_URL}/api/v1/stats?{urllib.parse.urlencode(query_params)}")
    body = res.json()
    return quart.Response(response=json.dumps(body), status=200)
@app.get("/season_averages")
async def get_season_averages():
    query_params = []
    season = request.args.get("season")
    if season:
        query_params.append(("season", str(season)))
    player_ids = request.args.getlist("player_ids")
    for player_id in player_ids:
        query_params.append(("player_ids[]", str(player_id)))
    res = requests.get(
        f"{HOST_URL}/api/v1/season_averages?{urllib.parse.urlencode(query_params)}")
    body = res.json()
    return quart.Response(response=json.dumps(body), 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=5001)
if name == "__main__":
    main()

最后,OpenAI定义OpenAI的 OpenAPI 规范:


openapi: 3.0.1
info:
    title: Sport Stats
    description: Get current and historical stats for sport players and games.
    version: "v1"
servers:
    - url: PLUGIN_HOSTNAME
paths:
    /players:
        get:
            operationId: getPlayers
            summary: Retrieves all players from all seasons whose names match the query string.
            parameters:
                - in: query
                  name: query
                  schema:
                      type: string
                  description: Used to filter players based on their name. For example, ?query=davis will return players that have 'davis' in their first or last name.
            responses:
                "200":
                    description: OK
    /teams:
        get:
            operationId: getTeams
            summary: Retrieves all teams for the current season.
            responses:
                "200":
                    description: OK
    /games:
        get:
            operationId: getGames
            summary: Retrieves all games that match the filters specified by the args. Display results using markdown tables.
            parameters:
                - in: query
                  name: limit
                  schema:
                      type: string
                  description: The max number of results to return.
                - in: query
                  name: seasons
                  schema:
                      type: array
                      items:
                          type: string
                  description: Filter by seasons. Seasons are represented by the year they began. For example, 2018 represents season 2018-2019.
                - in: query
                  name: team_ids
                  schema:
                      type: array
                      items:
                          type: string
                  description: Filter by team ids. Team ids can be determined using the getTeams function.
                - in: query
                  name: start_date
                  schema:
                      type: string
                  description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or after this date.
                - in: query
                  name: end_date
                  schema:
                      type: string
                  description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or before this date.
            responses:
                "200":
                    description: OK
    /stats:
        get:
            operationId: getStats
            summary: Retrieves stats that match the filters specified by the args. Display results using markdown tables.
            parameters:
                - in: query
                  name: limit
                  schema:
                      type: string
                  description: The max number of results to return.
                - in: query
                  name: player_ids
                  schema:
                      type: array
                      items:
                          type: string
                  description: Filter by player ids. Player ids can be determined using the getPlayers function.
                - in: query
                  name: game_ids
                  schema:
                      type: array
                      items:
                          type: string
                  description: Filter by game ids. Game ids can be determined using the getGames function.
                - in: query
                  name: start_date
                  schema:
                      type: string
                  description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or after this date.
                - in: query
                  name: end_date
                  schema:
                      type: string
                  description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or before this date.
            responses:
                "200":
                    description: OK
    /season_averages:
        get:
            operationId: getSeasonAverages
            summary: Retrieves regular season averages for the given players. Display results using markdown tables.
            parameters:
                - in: query
                  name: season
                  schema:
                      type: string
                  description: Defaults to the current season. A season is represented by the year it began. For example, 2018 represents season 2018-2019.
                - in: query
                  name: player_ids
                  schema:
                      type: array
                      items:
                          type: string
                  description: Filter by player ids. Player ids can be determined using the getPlayers function.
            responses:
                "200":
                    description: OK

了解如何构建简单的 OAuth 待办事项列表插件

要创建 OAuth 插件,OpenAI首先定义一个ai-plugin.jsonauth 类型设置为的文件oauth


{
    "schema_version": "v1",
    "name_for_human": "TODO OAuth",
    "name_for_model": "todo_oauth",
    "description_for_human": "Plugin for managing a 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": "oauth",
        "client_url": "PLUGIN_HOSTNAME/oauth",
        "scope": "",
        "authorization_url": "PLUGIN_HOSTNAME/auth/oauth_exchange",
        "authorization_content_type": "application/json",
        "verification_tokens": {
            "openai": "Replace_this_string_with_the_verification_token_generated_in_the_ChatGPT_UI"
        }
    },
    "api": {
        "type": "openapi",
        "url": "PLUGIN_HOSTNAME/openapi.yaml",
        "is_user_authenticated": false
    },
    "logo_url": "PLUGIN_HOSTNAME/logo.png",
    "contact_email": "contact@example.com",
    "legal_info_url": "http://www.example.com/legal"
}

接下来,OpenAI需要定义OpenAI的 OAuth 服务。此 OAuth 示例不适用于生产用例,而是强调简单的 OAuth 流程是什么样子的,以便开发人员可以获得构建生产解决方案的经验。


import json
import quart
import quart_cors
from quart import request
app = quart_cors.cors(quart.Quart(__name__), allow_origin="*")

_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):
    print(request.headers)
    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"]
    # fail silently, it's a simple plugin
    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("manifest.json") as f:
        text = f.read()
        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()
        text = text.replace("PLUGIN_HOSTNAME", f"https://{host}")
        return quart.Response(text, mimetype="text/yaml")
@app.get("/oauth")
async def oauth():
    query_string = request.query_string.decode('utf-8')
    parts = query_string.split('&')
    kvps = {}
    for part in parts:
        k, v = part.split('=')
        v = v.replace("%2F", "/").replace("%3A", ":")
        kvps[k] = v
    print("OAuth key value pairs from the ChatGPT Request: ", kvps)
    url = kvps["redirect_uri"] + f"?code={OPENAI_CODE}"
    print("URL: ", url)
    return quart.Response(
        f'<a href="{url}">Click to authorize</a>'
    )

# Sample names
OPENAI_CLIENT_ID = "id"
OPENAI_CLIENT_SECRET = "secret"
OPENAI_CODE = "abc123"
OPENAI_TOKEN = "def456"
@app.post("/auth/oauth_exchange")
async def oauth_exchange():
    request = await quart.request.get_json(force=True)
    print(f"oauth_exchange {request=}")
    if request["client_id"] != OPENAI_CLIENT_ID:
        raise RuntimeError("bad client ID")
    if request["client_secret"] != OPENAI_CLIENT_SECRET:
        raise RuntimeError("bad client secret")
    if request["code"] != OPENAI_CODE:
        raise RuntimeError("bad code")
    return {
        "access_token": OPENAI_TOKEN,
        "token_type": "bearer"
    }

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

最后,与OpenAI的其他示例一样,OpenAI基于端点定义了一个简单的 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.**探索ChatGPT,学习开发一款插件,成为先锋者,一同成长。加入「阿杰与AI」公众号,关注AI发展趋势,等待AI机会出现。**

*   1.如何访问插件?
*   2.开发ChatGPT插件-介绍
*   3.开发ChatGPT插件-入门
*   4.开发ChatGPT插件-认证
*   5.开发ChatGPT插件-示例插件
*   6.开发ChatGPT插件-审核
*   7.开发ChatGPT插件-线上插件及常见问题

## **插件审核流程**

OpenAI正处于构建插件商店的早期阶段,此页面概述了OpenAI如何考虑插件审查过程的基本概述,以及插件审查过程的早期阶段会是什么样子。

插件审查过程将随着时间的推移发生重大变化。OpenAI乐于接受有关如何改进这些构建插件过程的反馈。

## **OpenAI在插件中寻找什么**

审查过程的目的是确保 ChatGPT 上的插件安全、提供有用的功能并提供高质量的用户体验。从长远来看,OpenAI希望在OpenAI正式化审查过程时例行公事。

在短期内,OpenAI希望为用户提供新的、神奇的体验的插件将提供最大的价值,如果没有大型语言模型的独特功能,这些体验是不可能实现的。

到目前为止,一些最神奇的插件类别是:

*   检索特定于用户或其他难以搜索的知识源(通过 Slack 搜索、搜索用户的文档或其他专有数据库)。
*   与其他插件协同良好的插件(要求模型为您计划一个周末,并让模型将航班/酒店搜索与晚餐预订搜索混合使用)。
*   赋予模型计算能力的插件(Wolfram、OpenAI 代码解释器等)。
*   引入使用 ChatGPT 新方法的插件,例如游戏。

## **插件状态**

在开发插件时,插件可以处于多种状态之一,表明它在审查过程中所处的位置。现在,只有几个插件状态,审核过程主要是手动的。OpenAI希望这会随着插件系统的发展而改变。

| 地位      | 描述                              | 开发者权限 | 用户访问 |
| ------- | ------------------------------- | ----- | ---- |
| 未验证     | 插件启动时的默认状态。                     | 15    | 0    |
| 得到正式认可的 | OpenAI 已审查该插件,并确定该插件已获准供普通用户使用。 | 无限    | 无限   |
| 禁止      | OpenAI 已审查该插件,并确定该插件应被禁止。       | 0     | 0    |

请注意,如果您提交的插件因不符合要求而被拒绝,它仍将处于“未验证”状态。

## **用户类型**

现在,当涉及到插件访问时,OpenAI讨论了三类用户。OpenAI还希望随着插件系统的发展而改变这一点。

| 用户类型          | 描述                                                                                                                          |
| ------------- | --------------------------------------------------------------------------------------------------------------------------- |
| 插件用户          | ChatGPT 用户已获得访问插件的权限,这些插件已通过我们的审核流程并被批准用于一般用途。这些用户必须是 ChatGPT Plus 订阅者。今天只有有限数量的用户可以使用插件,但我们希望随着时间的推移向所有 ChatGPT Plus 用户推出。 |
| 插件开发者         | 已获得开发、使用、测试等能力的 ChatGPT 用户,正在开发的插件。截至今天,这些用户数量很少。我们预计这个数字会增长得非常大,并且我们希望有一个明确的流程(而不是候补名单)来选择成为 ChatGPT 插件开发人员,类似于其他应用程序商店。   |
| 普通 ChatGPT 用户 | 目前,普通的 ChatGPT 用户(包括 Plus 订阅者)没有插件访问权限。我们希望所有 ChatGPT Plus 订阅者最终都能获得访问权限,但截至目前,只有少数用户获得了访问权限。                               |

## **插件商店**

为了让您的插件在插件商店中可用,它需要由 OpenAI 进行审查。在提交您的插件以供审核之前,请确保您的插件符合以下标准:

*   遵守OpenAI的[内容政策](https://openai.com/policies/usage-policies)
*   符合OpenAI的[品牌准则](https://openai.com/brand)
*   您提交的内容中描述的功能
*   提供信息性错误消息
*   具有描述性操作名称
*   提供简单明了的清单文件
*   在插件描述中明确说明地理或功能限制,以避免用户混淆
*   不在插件名称或描述中使用插件、ChatGPT 或 OpenAI 等词

如果缺少任何一个条件,OpenAI将拒绝该插件,您可以在更新后再次提交。

## **提交插件以供审核**

您可以在提交插件约 14 天后收到关于您提交审核的插件的回复。

OpenAI目前正在滚动审查新插件。由于审查过程的手动性质和OpenAI有限的推出,请在OpenAI审查您的插件时耐心等待。[您可以使用插件提交机器人](https://platform.openai.com/docs/plugins/review#)提交插件以供审核。要查看机器人,您需要登录。

要查看插件提交的状态,请确保您已登录并选择此页面右上角的“帮助”。在“消息”下,您将能够看到您提交的插件。当您的插件状态在审核过程中发生变化时,OpenAI会通知您。

## **平台政策**

OpenAI的 API 正在用于为许多行业和技术平台的企业提供支持。从 iOS 应用程序到网站再到 Slack,OpenAI API 的简单性使其可以集成到广泛的用例中。根据下述用例限制,OpenAI允许将OpenAI的 API 集成到所有主要技术平台、应用程序商店等的产品中。

## **插件政策**

除了上面详述的禁止使用OpenAI的模型之外,OpenAI对构建 [插件](https://platform.openai.com/docs/plugins/)的开发人员还有其他要求:

*   插件清单必须有一个明确的描述,与暴露给模型的 API 的功能相匹配。
*   不要在插件清单、OpenAPI 端点描述或插件响应消息中包含不相关、不必要或欺骗性的术语或说明。这包括避免使用其他插件的说明,或尝试控制或设置模型行为的说明。
*   不要使用插件来规避或干扰 OpenAI 的安全系统。
*   不要使用插件来自动与真人对话,无论是通过模拟类似人类的响应还是通过使用预编程的消息进行回复。
*   分发由 ChatGPT 生成的个人通信或内容(例如电子邮件、消息或其他内容)的插件必须表明该内容是由 AI 生成的。

与OpenAI的其他使用政策一样,OpenAI希望OpenAI的插件政策随着OpenAI对插件的使用和滥用的了解而改变。

> 希望通过公众号「阿杰与AI」,能够帮助你了解AI产品,并可以解决一些生活和工作中问题。
>
> 我将分享有关AI的知识和实用建议,希望能够为你带来有价值的认知,一起探索发现AI的机会。

                    required: true

了解如何构建语义搜索和检索插件

ChatGPT检索插件是一个功能更全面的代码示例。该插件的范围很大,因此OpenAI鼓励您通读代码,看看更高级的插件是什么样子的。

检索插件包括:

  • 支持多个矢量数据库提供商
  • 所有 4 种不同的身份验证方法
  • 多种不同的 API 功能

希望通过公众号「阿杰与AI」,能够帮助你了解AI产品,并可以解决一些生活和工作中问题。

我将分享有关AI的知识和实用建议,希望能够为你带来有价值的认知,一起探索发现AI的机会。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
开发OpenWrt插件需要搭建开发环境并进行编译。首先,你需要下载OpenWrt的源码,可以从GitHub上获取,下载地址为https://github.com/openwrt/openwrt.git。你可以使用以下命令进行源码的下载和进入源码目录: git clone https://github.com/openwrt/openwrt.git cd openwrt/ 在源码目录下,你可以开始进行插件开发。OpenWrt使用LuCI作为其Web管理界面,你可以参考LuCI的开发文档,地址为https://github.com/openwrt/luci/wiki/DevelopmentEnvironmentHowTo。该文档提供了详细的步骤和示例,帮助你快速上手插件开发。 在进行插件开发时,你可以选择使用现有的开源镜像进行烧录,也可以进行高度定制化的固件开发。如果你希望进行定制化的固件开发,可以参考上面提到的文档,其中也会有更多关于OpenWrt的编译和开发的文章供你参考。 请注意,OpenWrt插件开发需要一定的编程和系统知识,并且需要对OpenWrt的架构和原理有一定的了解。如果你是初学者,建议先熟悉OpenWrt的基本使用和原理,然后再进行插件开发。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [使用VSCODE远程开发openwrt luci插件](https://blog.csdn.net/gw826943555/article/details/104234226)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [openwrt开发使用-选择芯片编译烧录](https://blog.csdn.net/lyn631579741/article/details/128134899)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿杰的人生路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值