ChatGPT知识库插件架构

ChatGPT 官方有一个开源的插件项目:chatgpt-retrieval-plugin 其官方简介为:

The ChatGPT Retrieval Plugin lets you easily find personal or work documents by asking questions in natural language.

简单理解就是一个知识库插件,可以让ChatGPT用自然语言搜索个人或企业的文档。

该项目为如何实现一个企业内部的知识库插件提供了很好的示范,由于该项目只是一个示例的作用,其代码质量一般,不建议直接在生产环境使用。

虽然是一个ChatGPT的插件,但是对于使用其它大模型来实现知识库也有借鉴意义,由于插件的形式不需要修改模型,所以非常灵活。

ChatGPT插件#

要理解知识库插件的架构,首先要理解ChatGPT插件的基本架构。

要编写ChatGPT的插件,首先要将插件的功能以 Restful API 的形式组织好,然后以ChatGPT规定的格式提供你插件说明和API说明。

后面就是神奇的地方,在与ChatGPT对话时,ChatGPT会智能的决策对话中的问题中是否与插件相关,如果相关,会自动调用插件的API完成相应的操作,并将API输出内容加工后反馈在回答中。

例如其官方的 TODO List 插件示例,插件以自然语言向ChatGPT说明其用途:

Help the user with managing a TODO list. You can add, remove and view your TODOs.

并且提供三个API接口,通过 GET,POST,DELETE 3个接口实现 TODO List 的增删和查询功能。

API接口需要用OpenAPI的定义方式进行说明,里面也需要用自然语言对API进行一些说明,TODO List 的 API 说明可以看官方这个示例:https://platform.openai.com/docs/plugins/examples

下面是示例的API声明,使用了OpenAPI的声明格式,ChatGPT会读取这个文件来理解插件中API的含义。

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

当在对话中输入:“Add study to my todo list”,ChatGPT会自动调用POST接口将 “study” 这项任务加入到 TODO List 中。

由于ChatGPT具备一定的理解能力,所以虽然上面的插件说明是英文的,但如果使用中文与其对话也能触发插件,例如:“我的代办列表中有哪些事项?”。

更神奇的是如果你说:“将买菜加入到代办列表的第一项”,ChatGPT会回复插件的API不具备插入到第一项的功能,并通过添加接口将“买菜”加入到列表的末尾。

下面的图描述了ChatGPT插件的运作机制:

知识库插件#

知识库插件的说明为:

Plugin for searching through the user’s documents (such as files, emails, and more) to find answers to questions and retrieve relevant information. Use it whenever a user asks something that might be found in their personal information.

其只声明了一个查询的API,为便于阅读截取一部分入下,详细声明见:https://github.com/openai/chatgpt-retrieval-plugin/blob/main/.well-known/openapi.yaml

openapi: 3.0.2
info:
  title: Retrieval Plugin API
  description: A retrieval API for querying and filtering documents based on natural language queries and metadata
  version: 1.0.0
  servers:
    - url: https://your-app-url.com
paths:
  /query:
    post:
      summary: Query
      description: Accepts search query objects array each with query and optional filter. Break down complex questions into sub-questions. Refine results by criteria, e.g. time / source, don't do this often. Split queries if ResponseTooLargeError occurs.
      operationId: query_query_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QueryRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QueryResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
      security:
        - HTTPBearer: []

插件设置好之后,就可以在对话中说:“在我的文档中查找:如何做蛋炒饭”,ChatGPT会自动调用插件接口查询相关内容,并将内容整理后输出。

ChatGPT并不是简单的将查询结果输出(否则就跟普通的搜索没有区别),而是对内容进行一定的加工处理后再输出出来。

为了便于ChatGPT加工,这个插件会将知识库文件拆分成若干段落,并对每一个段落计算一个向量,当ChatGPT查询时,插件服务会根据查询内容返回向量相似度最高的一些段落,ChatGPT对这些内容进行加工后返回问题的答案。

下面是知识库插件的架构说明:

插件运行时需要使用一个向量数据库来存储和查询知识库的内容片段,插件代码中支持相当多的向量数据库,我在测试时使用的是Redis Search。

当调用接口向知识库中添加文档时,插件服务会将文档拆分成片段,之后调用OpenAI提供的API接口生成文档片段的Embedding向量,所以整个项目跑起来还需要提供OpenAI的API Token。

经过对代码的修改,可以实现用第三方库生成Embedding向量,这样就不需要将每段文字都传给OpenAI,更适合对这方面有顾虑的使用场景,使用时也不需要调用OpenAI的API了。

我使用的是这个开源模型:https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2 ,其生成的向量有384个维度且对中文支持一般,而OpenAI的API生成的向量有1536个维度,理论上效果更好一些。

相关连接#

ChatGPT插件官方文档:https://platform.openai.com/docs/plugins/introduction

ChatGPT官方简单示例:https://platform.openai.com/docs/plugins/examples

知识库插件代码仓库:https://github.com/openai/chatgpt-retrieval-plugin

comments powered by Disqus