只需三步,搭建基于知识库的专属ChatGPT

只需三步,搭建基于知识库的专属ChatGPT

Meta AI 的研究人员引入了一种叫做检索增强生成(Retrieval Augmented Generation,RAG)的方法来完成知识密集型的任务。RAG 会接受输入并检索出一组相关/支撑的文档,并给出文档的来源(例如维基百科)。这些文档作为上下文和输入的原始提示词组合,送给文本生成器得到最终的输出。RAG 让语言模型不用重新训练就能够获取最新的信息,基于检索生成产生可靠的输出。RAG 的应用范围广泛且多样,从改进客户服务聊天机器人、促进详细和个性化的内容推荐,到支持业务环境中的复杂决策流程。

下面我们将以“大数据与智能计算”专业课的课件作为我们自己的知识库,通过 vLLM 框架、llama_index 和和 fastapi 搭建一个属于我们自己的专属的问答系统。

【step1】将 vLLM 部署为模仿 OpenAI API 协议的服务器

第一步为将 vLLM 部署为模仿 OpenAI API 协议的服务器,这使得 vLLM 可以用作使用 OpenAI API 的应用程序的直接替代品。关于该步骤,以及关于 vLLM 的使用说明,这篇文章里面有更加详细的讲解。

使用 vLLM 部署本地 LLM 指南-CSDN博客

这次我使用的 LLM 为 OpenChat-3.5-0106,这个模型是由 mistralai/Mistral-7B-v0.1 微调得到的,参数量为 7B,模型内容链接如下:

openchat/openchat-3.5-0106 · Hugging Face

首先输入以下命令启动服务器,关于该命令的详解可以看上面的 CSDN 博客。

python -m vllm.entrypoints.openai.api_server \
    --model "/root/autodl-tmp/kdy/models/openchat-3.5-0106" \
		--served-model-name "openchat" \

终端输出如下,服务器启动成功:

只需三步,搭建基于知识库的专属ChatGPT_第1张图片

【step2】使用 fastapi 创建 RESTful API端点

首先需要安装 llama_index 和 fastapi。

下面这篇文章里详解了在本地部署 LLM 的教程,较为简明,也不需要使用 fastapi 创建 API 端点,如果只是对 llama_index 感兴趣或是初步接触 RAG 技术,可以先看下面这篇文章在本地进行一个简单的实现,虽然本地的速度慢的很

使用 LlamaIndex 部署本地 Mistral-7b 大模型实现 RAG-CSDN博客

我使用了 fastapi 提供的流式响应功能。该功能可以逐个发送生成器生成的值,而不是一次性发送所有内容。

代码命名为 fastapi_test.py,代码内容如下:

from llama_index import VectorStoreIndex, SimpleDirectoryReader
from llama_index import ServiceContext
from llama_index.llms.openai_like import OpenAILike
from llama_index import set_global_service_context
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from fastapi.responses import StreamingResponse

openai_api_key = "EMPTY"
openai_api_base = "http://localhost:8000/v1"
llm = OpenAILike(
    api_key=openai_api_key,
    api_base=openai_api_base,
    model="openchat",
    is_chat_model=True,  
    context_window=32768, 
)

service_context = ServiceContext.from_defaults(llm=llm, embed_model="local:BAAI/bge-small-zh-v1.5", chunk_size=1024)
set_global_service_context(service_context)

documents = SimpleDirectoryReader("/root/autodl-tmp/kdy/RAG/data").load_data()
index = VectorStoreIndex.from_documents(documents)
index.storage_context.persist(persist_dir="/root/autodl-tmp/kdy/RAG/storage")
query_engine = index.as_query_engine(streaming=True, similarity_top_k=5)

app = FastAPI()
# 定义请求模型
class QueryModel(BaseModel):
    question: str

# POST端点
@app.post("/query/")
async def answer_query(query: QueryModel):
    if query.question:
        response = query_engine.query(query.question)
        def generator():
            for item in response.response_gen:
                yield item  # 逐个生成值
        return StreamingResponse(generator())  # 使用流式响应
    raise HTTPException(status_code=400, detail="No question provided")

documents = SimpleDirectoryReader("/root/autodl-tmp/kdy/RAG/data").load_data()这行代码为读取存放在本地的文档,我这次选取了课程“大数据与智能计算“的 PDF 课件作为示例,如下图所示。

你也可以放更多更大的文件,这里需要注意代码中的service_context = ServiceContext.from_defaults(llm=llm, embed_model="local:BAAI/bge-small-zh-v1.5", chunk_size=1024) 里面的 chunk_size 属性,文档过大时,需要设置该属性讲文档进行切分,详细内容可以看官方文档。

只需三步,搭建基于知识库的专属ChatGPT_第2张图片

端口号可自行选取,我选用了 6006 端口。

输入以下指令启动fastapi进程,注意命令里面的 fastapi_test 就是上面这段脚本的名称。

uvicorn fastapi_test:app --reload --port 6006

下图记录了进程启动以及响应成功的日志,红框框出的即为响应成功的日志。

只需三步,搭建基于知识库的专属ChatGPT_第3张图片

输入以下指令可以向该端点发送 POST 请求

curl -X POST "http://127.0.0.1:6006/query/" -H "accept: application/json" -H "Conten
t-Type: application/json" -d '{"question":"大数据有哪些特点?"}'

很快就收到了响应,如下图所示。

在这里插入图片描述

这里我们回到第一次启动的 openai.api_server 的日志,可以看到 llama_index 帮我补好的完整的 prompt,它包含了 llama_index 帮我们从 PDF 中检索得到的与问题有关的内容。

只需三步,搭建基于知识库的专属ChatGPT_第4张图片

但是使用 curl 指令难免有些复杂,所以我们使用 python 自带的 requests 库进行进一步的封装。

【step3】使用 requests 库发送 HTTP 请求

由于 FastAPI 后端正确设置了流式响应,所以我的Python客户端脚本也需要能够逐步读取这些数据。故需要使用 requests 库的 .iter_content().iter_lines() 方法来逐步读取响应。

.iter_lines() 方法可以逐行来读取响应,但是我觉得逐行还是有点慢,因此我使用了**.iter_content()** 方法来逐字节读取响应。读取方法如下:

for line in response.iter_content(chunk_size=2, decode_unicode=True):
        print(line, end="", flush=True)

chunk_size=2 指定每次迭代读取 2 个字节,可以根据需要设置为合适的大小。

  1. 小的 chunk_size(如1-10字节):适用于需要高实时性的应用,例如逐字节或逐字符显示文本。但这可能会降低性能,因为频繁的网络请求和数据处理可能会增加延迟。
  2. 中等的 chunk_size(如1024字节或1KB):这是一个比较常见的选择,平衡了实时性和性能。对于一般的文本流或逐行读取的情况,这个大小通常足够。
  3. 大的 chunk_size(如4096字节或更多):适用于网络带宽较高且对实时性要求不高的情况,例如大文件下载。较大的块可以减少请求次数,提高整体数据传输效率。

由于我这里是为了实现类似逐字打印文本的效果,考虑使用较小的 chunk_size(如1-10字节)。

最终完整的代码如下:

import argparse
import requests

def query_api(question):
    url = "http://127.0.0.1:6006/query/"
    headers = {
        "accept": "application/json",
        "Content-Type": "application/json"
    }
    payload = {"question": question}
    response = requests.post(url, json=payload, headers=headers, stream=True)
    return response

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Query API with a question.')
    parser.add_argument('-q', '--question', required=True, help='The question to query.')
    args = parser.parse_args()

    response = query_api(args.question)
    if response.encoding is None:
        response.encoding = 'utf-8'
    for line in response.iter_content(chunk_size=2, decode_unicode=True):
        print(line, end="", flush=True)
    print()

运行这段 python 程序,注意命令行参数 -q 就是你要提问的问题,很快就会收到响应,而且为流式响应。

响应界面如下(未加速),这个速度比之前在本地不知快了多少倍

只需三步,搭建基于知识库的专属ChatGPT_第5张图片

你可能感兴趣的:(LLM,实战,NLP,chatgpt,算法,语言模型,RAG,vLLM,llama)