LangChain builds a local knowledge base question answering application based on ChatGPT

1. Build a Q&A application using a local knowledge base

The previous article built a domain-specific model based on LangChainprompt Promptsmanagement. If you read it, you can feel that it is still very powerful, but it is still a bit difficult for you to answer ChatGPTsome specific domain content . For example, let me introduce what is :GPTChatGPTLangChain

from langchain.llms import OpenAI
import os

openai_api_key=os.environ["OPENAI_API_KEY"]
llm = OpenAI(model_name="gpt-3.5-turbo", openai_api_key=openai_api_key)
my_text = "介绍下 langChain "
print(llm(my_text))

insert image description here

It can be seen that the answer does not seem to be what we want. There are also questions and answers in some specific scenarios. For example, in the school student question answering system, students ask what is the Monday course? , GPTIt's a bit difficult to ask me to answer directly, so how to solve this situation?

Now that you have LangChainit, it is very simple to implement:

For example: the existing knowledge content is placed dataunder the directory, which has the following content:

insert image description here
The txtdocument records LangChainthe introduction of :

insert image description here
pdfIntroduced langchainbelow Prompts:

insert image description here
csvRecorded student course information:

insert image description here

Next, load and vectorize the knowledge content first.

3.1 Text loading and Embedding vector persistence

import os
# 向量数据库
from langchain.vectorstores import Chroma
# 文档加载器
from langchain.document_loaders import TextLoader, CSVLoader, PyPDFLoader
# 文本转换为向量的嵌入引擎
from langchain.embeddings.openai import OpenAIEmbeddings
# 文本拆分
from langchain.text_splitter import RecursiveCharacterTextSplitter

openai_api_key = os.environ["OPENAI_API_KEY"]

knowledge_base_dir = "./data"

doc = []
for item in os.listdir(knowledge_base_dir):
    if item.endswith("txt"):
        loader = TextLoader(file_path=os.path.join(knowledge_base_dir, item), encoding="utf-8")
        doc.append(loader.load())
    elif item.endswith("csv"):
        loader = CSVLoader(file_path=os.path.join(knowledge_base_dir, item), encoding="utf-8")
        doc.append(loader.load())
    elif item.endswith("pdf"):
        loader = PyPDFLoader(file_path=os.path.join(knowledge_base_dir, item))
        doc.append(loader.load())

print("提取文本量:", len(doc))
# 拆分
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=400)
docs = []
for d in doc:
    docs.append(text_splitter.split_documents(d))
    print("拆分文档数:", len(docs))
# 准备嵌入引擎
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)
# 向量化
# 会对 OpenAI 进行 API 调用
vectordb = Chroma(embedding_function=embeddings, persist_directory="./cut")
for d in docs:
    vectordb.add_documents(d)
# 持久化
vectordb.persist()

After running, you can ./cutsee the persistent vector content in :

insert image description here

3.2 Building Q&A

from langchain import OpenAI
# 向量数据库
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 文本转换为向量嵌入引擎
from langchain.embeddings.openai import OpenAIEmbeddings
import os

openai_api_key = os.environ["OPENAI_API_KEY"]
llm = OpenAI(temperature=0, openai_api_key=openai_api_key)

# 准备好你的嵌入引擎
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)

vectordb = Chroma(embedding_function=embeddings, persist_directory="./cut")
# 创建您的检索引擎
qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=vectordb.as_retriever())

query = "介绍下什么是 langchain?"
res = qa.run(query)
print('问题:', query, 'LLM回答:', res)

query = "介绍下 langchain 中的 prompts ?"
res = qa.run(query)
print('问题:', query, 'LLM回答:', res)

query = "周一需要上什么课?"
res = qa.run(query)
print('问题:', query, 'LLM回答:', res)

query = "周三上午需要上什么课?"
res = qa.run(query)
print('问题:', query, 'LLM回答:', res)

insert image description here
It can be seen that the answer has been given accurately.

Normally, when we use ChatGPT, we return it in the form of stream, and here we can also modify it to the form of stream:

from langchain import OpenAI
# 向量数据库
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 文本转换为向量的嵌入引擎
from langchain.embeddings.openai import OpenAIEmbeddings
# 流式回调
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
import os

openai_api_key = os.environ["OPENAI_API_KEY"]
llm = OpenAI(temperature=0, openai_api_key=openai_api_key, streaming=True, callbacks=[StreamingStdOutCallbackHandler()])

# 嵌入引擎
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)

vectordb = Chroma(embedding_function=embeddings, persist_directory="./cut")
# 创建您的检索引擎
qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=vectordb.as_retriever())

query = "介绍下什么是 langchain?"
qa.run(query)

insert image description here

Next, cooperate with tornadothe high-performance asynchronous non-blocking webframework to realize interface call question and answer.

2. Deploy WEB services

Install tornadothe framework:

pip install tornado -i https://pypi.tuna.tsinghua.edu.cn/simple

Create a question and answer service interface server.py:

from tornado.concurrent import run_on_executor
from tornado.web import RequestHandler
import tornado.gen
import utils_response
from langchain import OpenAI
# 向量数据库
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 文本转换为向量的嵌入引擎
from langchain.embeddings.openai import OpenAIEmbeddings
import os

class QA(RequestHandler):
    # 准备模型
    openai_api_key = os.environ["OPENAI_API_KEY"]
    llm = OpenAI(temperature=0, openai_api_key=openai_api_key)
    # 准备好你的嵌入引擎
    embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)
    vectordb = Chroma(embedding_function=embeddings, persist_directory="./cut")
    # 检索引擎
    qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=vectordb.as_retriever())
    print("检索引擎已加载!")

    def prepare(self):
        self.executor = self.application.pool

    @tornado.gen.coroutine
    def get(self):
        questions = self.get_query_argument('questions')
        if not questions or questions == '':
            return utils_response.fail(message='问题为空')
        result = yield self.detection(questions)
        self.write(result)

    @run_on_executor
    def detection(self, questions):
        # 开始检测
        res = self.qa.run(questions)
        return utils_response.ok(res)

Routing configuration, and start the service app.py:

import tornado.web
import tornado.ioloop
import tornado.httpserver
import os
from concurrent.futures.thread import ThreadPoolExecutor
from server import QA

## 配置
class Config():
    port = 8081
    base_path = os.path.dirname(__file__)
    settings = {
    
    
        # "debug":True,
        # "autore load":True,
        "static_path": os.path.join(base_path, "resources/static"),
        "template_path": os.path.join(base_path, "resources/templates"),
        "autoescape": None
    }

# 路由
class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            ("/qa", QA),
            ("/(.*)$", tornado.web.StaticFileHandler, {
    
    
                "path": os.path.join(Config.base_path, "resources/static"),
                "default_filename": "index.html"
            })
        ]
        super(Application, self).__init__(handlers, **Config.settings)
        self.pool = ThreadPoolExecutor(10)


if __name__ == '__main__':
    app = Application()

    httpserver = tornado.httpserver.HTTPServer(app)

    httpserver.listen(Config.port)

    print("start success", "prot = ", Config.port)

    print("http://localhost:" + str(Config.port) + "/")

    tornado.ioloop.IOLoop.current().start()

insert image description here
Use the following PostManto test:

insert image description here
The answer has been successfully obtained.

Guess you like

Origin blog.csdn.net/qq_43692950/article/details/131367024