使用GPT3.5,LangChain,Milvus和python构建一个本地知识库

本篇文章获得同事刘工的授权刊登。原文发表于2023年7月13日。

 

引言

  • 介绍本地知识库的概念和用途

在现代信息时代,我们面临着海量的数据和信息,如何有效地管理和利用这些信息成为一项重要的任务。本地知识库是一种基于本地存储的知识管理系统,旨在帮助用户收集、组织和检索大量的知识和信息。它允许用户在本地环境中构建和管理自己的知识资源,以便更高效地进行信息处理和决策。

本地知识库通常采用数据库、索引和搜索技术,以构建一个结构化的存储系统,使用户可以快速地访问和查询所需的信息。

  • 引出使用GPT-3.5、LangChain和Milvus构建本地知识库的动机

当面临一个知识类问题时,我们往往需要利用自己获取到的信息加以总结,对海量信息中包含的要点进行快速地查询和了解,而现在出现的GPT-3.5技术则能够使得用户向语言模型提问并得到一个回答。LangChain则是对大语言模型技术所用到的一些功能进行了统一的封装,这使得我们可以利用本地的知识资源,以获得我们需要的信息,Milvus则是一个可以存储这种类型数据的向量数据库。

ChatGPT

ChatGPT 是由 OpenAI 开发的一种高级语言模型,可以根据给定的提示生成类似人类语言的文本,从而实现对话、文本摘要和问答等多种功能。

出于演示的目的,我们将专注于OpenAI的"gpt-3.5-turbo-16k"模型,因为它目前价格合适,速度较快,回答比较准确。

如果想深入了解chatgpt相关信息,请参考下面链接:(请使用VPN)

     1 . https://platform.openai.com/docs/api-referencehttps://platform.openai.com/docs/api-reference

LangChain

LangChain 是一个库(以 Python、JavaScript 或 TypeScript 提供),提供了一组用于处理语言模型、文本嵌入和文本处理任务的工具和实用程序。 它通过组合语言模型、向量存储和文档加载器等各种组件来简化创建聊天机器人、处理文档检索和执行问答操作等任务。 

使用GPT3.5,LangChain,Milvus和python构建一个本地知识库_第1张图片

我们将专注于创建一个问答聊天机器人,其中包含上面图中所展示的绿色的部分。

如果想深入了解LangChain相关信息,请参考下面链接:

Get started | ️ LangchainGet started with LangChainhttps://python.langchain.com/docs/get_started

Milvus

Milvus 是一个数据库,用于存储、索引和管理由深度神经网络和其他机器学习 (ML) 模型生成的大量嵌入向量。

跟faiss不同,Milvus需要在本地安装相关服务。安装文档如下:

Install Milvus Standalone with Docker Compose (CPU) Milvus documentationLearn how to install Milvus stanalone with Docker Compose v2.3.x.https://milvus.io/docs/install_standalone-docker.md在安装Milvus之前,需要先安装docker,安装文档如下:

Install Docker Engine on CentOS | Docker DocsLearn how to install Docker Engine on CentOS. These instructions cover the different installation methods, how to uninstall, and next steps.https://docs.docker.com/engine/install/centos/

安装完成之后当使用SDK连接Milvus时,其默认端口是:19530

相关文档如下:

Manage Milvus Connections Milvus documentationLearn how to connect to a Milvus server v2.3.x.https://milvus.io/docs/manage_connection.md

Document loaders

Document loaders是langchain中的一个组件,它的功能是从文件中读取数据,比如PDF,csv,url,txt等。经过loaders加载后的数据Document主要由两部分组成,即page_content和metadata。metadata中存储了文件名,第几页等基础信息,page_content中存储了该页的内容。

其基础用法如下:、

from langchain.document_loaders import PyPDFLoader

loader = PyPDFLoader(r"loRA _refer.pdf")
print(loader.load())

代码中所展示的文档可以从这里下载:​​​​​​​https://arxiv.org/pdf/2106.09685.pdficon-default.png?t=N6B9https://arxiv.org/pdf/2106.09685.pdf

代码解释:

  1. PyPDFLoader是document_loaders 中加载pdf的组件,这段代码将pdf加载为一个loader对象,并打印了其中内容,可以看出打印为一个列表,这个列表中存放了一个Document对象。

除了PyPDFLoader,langchain还提供了CSVLoader,HTMLLoader,JSONLoader,MarkdownLoader,File Directory,ExcelLoader,Microsoft Word,Microsoft PowerPoint,GitHub,EPub,Images,WebBaseLoader,URL等多种加载器,具体可查看其document_loaders文档:

Document loaders | ️ LangchainHead to Integrations for documentation on built-in document loader integrations with 3rd-party tools.https://python.langchain.com/docs/modules/data_connection/document_loaders/

WebBaseLoader

WebBaseLoaderr可以从网页中加载文本内容,其基本用法如下:

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Milvus
from langchain.document_loaders import WebBaseLoader
from langchain.text_splitter import CharacterTextSplitter

loader = WebBaseLoader([
    "https://milvus.io/docs/overview.md",
])

docs = loader.load()

Document transformers

因为在现在的技术条件下,chatgpt或其他的大语言模型均有受到文本长度的限制,所以对于一个大型文件或者很多个大型文件时,若将全部文本一次性发送给chatgpt,则模型往往会报错token超出。在这种情况下,我们则会先将长文档拆分为可以放入模型上下文窗口的较小块。在langchain中内置了很多函数,使我们可以直接进行这个操作。

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Milvus
from langchain.document_loaders import WebBaseLoader
from langchain.text_splitter import CharacterTextSplitter

loader = WebBaseLoader([
    "https://milvus.io/docs/overview.md",
])

docs = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1024, chunk_overlap=20)
docs = text_splitter.split_documents(docs)

代码解释:

  1. text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0): 这行代码创建一个名为text_splitter的对象,它是CharacterTextSplitter类的一个实例。构造函数的参数如下所示:

    1. chunk_size=1000: 这是指定拆分文本片段的大小的参数。在这里,设置为1000,表示每个片段的字符数为1000。

    2. chunk_overlap=0: 这是指定片段之间重叠部分的大小的参数。在这里,设置为0,表示片段之间没有重叠。设置重叠部分有可能会在某些长句子面临被切开情况会很有用,或者上下两句联系较为紧密时起作用。可以根据实际情况来写入,默认可以设置为0.

  2. docs = text_splitter.split_documents(documents): 这行代码使用之前创建的text_splitter对象调用split_documents()方法来将加载的文档拆分成较小的文本片段。拆分后的文本片段将存储在名为docs的变量中。split_documents()方法的参数documents是之前加载的文档。

Text embedding models

embedding (嵌入)这个动作的目的是创建一段文本的矢量表示形式。矢量是一个数学领域的概念,若对这部分不熟悉请学习线性代数这门课程。

矢量在数学上一般以一个有序数组来表示,相关概念见下文:

线性代数基础 | 向量的运算与向量的线性组合 - 知乎向量及其基本运算“ 开局一个向量,构造出新的向量。 ”向量的形式化定义 n个有序数a_1,a_2,\cdots,a_n所组成的数组称为n维向量,这n个数称为该向量的n个分量,第i个数a_i称为第i个分量。 n维向量可写成一行或一列…https://zhuanlan.zhihu.com/p/339974158

文本向量化之后,就可以执行诸如语义搜索之类的操作,在其中我们可以寻找向量空间中最相似的文本片段。

当我们需要查找向量空间的最相似的文本片段时,就必须引入另一个工具,即向量数据库。

Vector stores

存储和搜索非结构化数据的最常见方法之一是将数据embedding并存储生成的embedding向量。然后在查询时检索与查询内容"最相似"的embedding向量。Vector stores(矢量存储)负责存储embedding数据并为您执行矢量搜索。(请注意,在数学中矢量与向量同义)。

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Milvus
from langchain.document_loaders import WebBaseLoader
from langchain.text_splitter import CharacterTextSplitter

loader = WebBaseLoader([
    "https://milvus.io/docs/overview.md",
])

docs = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1024, chunk_overlap=20)
docs = text_splitter.split_documents(docs)
embeddings = OpenAIEmbeddings()

vector_db = Milvus.from_documents(
    docs,
    embeddings,
    connection_args={"host": "127.0.0.1", "port": "19530"},
    collection_name="milvus_docs",
)
  1. 从文档中生成向量,并在Milvus数据库中创建一个名为"milvus_docs"的集合,将对应的向量数据保存到该集合中。

从Milvus数据库搜索相关文本并加入到langchain的问答中

from langchain.vectorstores import Milvus
from langchain.chat_models import ChatOpenAI
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.chains.retrieval_qa.base import RetrievalQA
from typing import Any
from langchain.memory import ConversationBufferMemory
from langchain import PromptTemplate, FAISS


embeddings = OpenAIEmbeddings()

def get_vector_chain() -> Any:
    llm = ChatOpenAI(temperature=0,model_name="gpt-3.5-turbo-16k")
    template = """
    Use the following context (delimited by ) and the chat history (delimited by ) to answer the question:
    ------
    
    {context}
    
    ------
    
    {history}
    
    ------
    {question}
    Answer in the language in which the question was asked:
    """
    prompt = PromptTemplate(
        input_variables=["history", "context", "question"],
        template=template,
    )

    vector_db = Milvus(
        embedding_function=embeddings,
        connection_args={"host": "127.0.0.1", "port": "19530"},
        collection_name="milvus_docs",
    )
    chain = RetrievalQA.from_chain_type(
        llm,
        retriever=vector_db.as_retriever(search_type="similarity", search_kwargs={"k": 2}),
        chain_type="stuff",
        chain_type_kwargs={
            "prompt": prompt,
            "memory": ConversationBufferMemory(
                memory_key="history",
                input_key="question"),

        },
    )

    return chain

chain = get_vector_chain()
answer = chain.run("What is milvus?")
print(answer)
  1. (33行)从Milvus中加载集合名为"milvus_docs"的相关数据进来。请注意这里没有加载文档,没有使用from_documents方法,而在前面的加载中使用了vector_db = Milvus.from_documents( docs, embeddings, connection_args={"host": "127.0.0.1", "port": "19530"}, collection_name="milvus_docs", ),这里是读取和存储的核心,请留意一下。

  2. (40行)搜索前两个最相关的数据发送给RetrievalQA,这里的search_kwargs={"k": 2}是指前两个最相关的。

你可能感兴趣的:(gpt-3,langchain,milvus)