基于大模型的文档知识库实现流程

1. 前言

大模型技术的横空出世给行业及技术应用带来了全新的变革。今天要讲的场景是“知识库”、“企业搜索”相关场景。在大模型出现以前,我们针对这样的业务往往采用传统的搜索引擎如ES来解决,或者更高级一些,引入NLP和KG技术,可以做出较智能的搜索、问答,如采用知识图谱技术的KBQA。

随着大模型技术的不断迭代,如今我们有了全新的方式去探索建设一个行业垂类或者企业内部知识库。而根据笔者观察,这也是大模型技术落地最常见的一个方向。

网上层出不穷的分析文章多如牛毛,但往往还是从算法、代码层面入手,这里我们尝试以更浅显直接的语言对这一应用场景进行阐述、归纳,本文会涉及少量代码讲解,更容易帮助读者理解。

看完本篇文章你会对如何应用大模型实现个人/企业知识库的核心流程有基本认知。

2. 实现流程简析

2.1 核心功能

首先提出一个最基础的“文档知识库项目”的核心功能,包括以下内容:

    1. 文档上传,生成后台知识库
    1. 检索,返回知识内容
  1. 其实还会涉及如对话、历史记录等相关功能,但并非核心,暂且不表。

2.2 技术选型

后端:Python

开发框架:Langchain

大模型在线服务:智谱ChatGLM

向量数据库:Chroma

2.3 技术流程

核心技术流程如下

    1. 构建知识库: 首先将用户上传的一批文档(如PDF、TXT等),默认按照段落分割成N个Chunks(块)
    1. 知识库向量化:又称为Embedding,将所有Chunks处理为向量数据,以便计算机理解,这些数据会存入专用的向量数据库
    1. 检索向量化:将用户的Query也转化为向量,然后在向量知识库中去匹配出向量计算最相似的top K个段落
    1. 提交LLM生成回答:将用户query、匹配出的文本打包交给大模型,由大模型理解、归纳形成总结后的完整答案,即检索结果

这也是现在大家都在讲的RAG(检索增强生成/Retrieval-augmented Generation),相当于给大模型外挂了知识库。

下面将依次对以上流程进行详细说明

3. 详细过程说明

3.1 前置内容

在step by step的过本篇技术流程前,还有些前置内容需要了解,我们要介绍Langchain、大模型接入的基础概念。

3.1.1 什么是LangChain

先明确一点,Langchain是一个大语言模型开发框架,是用于大模型相关业务的一个开发套件。LangChain 本身不提供LLM,本质上就是对各种大模型提供的 API 的套壳,是为了方便我们使用这些 API,搭建起来的一些框架、模块和接口。 LangChain 将 LLM 模型(对话模型、embedding模型等)、向量数据库、交互层 Prompt、外部知识、外部代理工具整合到一起,进而可以自由构建 LLM 应用。

LangChain 主要由以下 6 个核心模块组成:

模型输入/输出(Model I/O):与语言模型交互的接口

数据连接(Data connection):与特定应用程序的数据进行交互的接口

链(Chains):将组件组合实现端到端应用

记忆(Memory):用于链的多次运行之间持久化应用程序状态

代理(Agents):扩展模型的推理能力,用于复杂的应用的调用序列

回调(Callbacks):扩展模型的推理能力,用于复杂的应用的调用序列

看到这里你或许不会太明白,没关系,后续我们将给出零星的代码,让你真正理解LangChain是怎么用起来的。

3.1.2 大模型服务接入

目前的大模型接入有两种方式,一种是本地模型私有化部署应用,一种是接厂商的API,这两种没有本质的区别,为了更方便跑起来,本文所介绍的是接入厂商API的方式,不用考虑本地GPU算力要求。

以下给出调用ChatGLM在线服务的示例

from zhipuai_llm import ZhipuAILLM
zhipuai_model = ZhipuAILLM(model="chatglm_std", temperature=0, zhipuai_api_key=zhipuai.api_key)
zhipuai_model.generate(['你好'])

# temperature参数,控制输出的随机性,取值0到1,默认为0,值越大,大模型输出结果越发挥
你好👋!我是人工智能助手 智谱清言,可以叫我小智🤖,很高兴见到你,欢迎问我任何问题。

3.2 构建知识库

首先一步是收集用户的知识文档原文件,如操作手册、Wiki、文献等等,读者可自行类比。这里我们先介绍Langchain对文件的引入功能

from langchain.document_loaders import UnstructuredMarkdownLoader

loader = UnstructuredMarkdownLoader("../data_base/knowledge_db/SQL基础教程.pdf")
pages = loader.load()
...

以上是简单的Python代码,我们可以可到通过引入了langchain的document\_loaders的module,就实现了对用户的“SQL基础教程.pdf”文件的入库。同理,通过工程化可以实现文件批量导入。

其次,是对文件进行分割。同样使用LangChain来实现,可按照字符数量长度等一系列参数进行分割,这里咱们只需要要了解是根据规则把文档进行分割即可,不用吃透具体怎么切分的。

#导入文本分割器
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 知识库中单段文本长度
CHUNK_SIZE = 500

# 知识库中相邻文本重合长度
OVERLAP_SIZE = 50

分割完后的效果如下所示:

['前\u3000言\n本书面向完全没有编程和系统开发经验的初学者,介绍了关系数据库以及用来操作关\n系数据库的 SQL 语言的使用方法。各个章节结合具体示例进行解说,并在每章的结尾安排\n了习题,用来检验读者对该章内容的理解程度。大家可以从第 1 章开始,亲自验证示例程序,\n循序渐进地掌握 SQL 的基础知识和技巧。另外,本书还将重要知识点总结为法则,方便读\n者在学习完本书之后随时查阅。']

3.3 知识库向量化

取得分割后的文档,我们需要将其向量化,也就是NLP领域常见的Embedding。所谓Embedding,不求甚解理解就是将文字转换成向量形式,这样机器处理的其实是矩阵运算而不是一个个文字。对文档进行Embedding通常有2种手段。

  • • 采用大模型在线接口:如OpenAI或者ChatGLM的接口
  • • 本地部署Embedding模型:如M3E、Moka等

方便起见,我们直接使用ChatGLM的接口服务,还是采用LangChain对接:

from zhipuai_embedding import ZhipuAIEmbeddings
# embedding = HuggingFaceEmbeddings(model_name="moka-ai/m3e-base") 这是引入本地部署模型的方式
embedding = ZhipuAIEmbeddings() # 引入在线embedding服务

query1 = "旅游"
query2 = "火箭"
query3 = "航天"
emb1 = embedding.embed_query(query)

Embedding以后,我们的文档究竟变成什么样了呢,就上述几个词给出示例:

旅游 生成的为长度 1024  embedding , 其前 30 个值为: [-0.06620011  0.11531917 -0.0707648  -0.06679663 -0.00087593  0.23256738
 -0.20461585 -0.07101158 -0.00645613  0.32598388 -0.01385093  0.1837097
 -0.06796395  0.17077696 -0.0064841  -0.17289667  0.03198481  0.12143167
 -0.05484311  0.34414008 -0.01926355  0.11278316 -0.12705955  0.04611887
 -0.20642205 -0.2279439   0.06027906  0.11457541 -0.02675474 -0.29482642]

“旅游”向量化以后会转换成一个1024维的向量,具体多少维和你使用的大模型本身有关。

之后,在把每个文档向量化以后,我们将采用数据库将这些结果存起来。这里我们使用一款名为Chroma的轻量级向量数据库,LangChain也集成了这个数据库方便使用。

from langchain.vectorstores import Chroma
from zhipuai_embedding import ZhipuAIEmbeddings
persist_directory = '../../data_base/vector_db/chroma' # 数据库持久化路径

vectordb = Chroma.from_documents(
    documents=split_docs[:100], 
    embedding=embedding,
    persist_directory=persist_directory  # =将persist_directory目录保存到磁盘上
)

如上所示,经过zhipu的embedding接口处理过的数据存在了本地的chroma目录中

3.4 检索向量化

前面我们用大量的篇幅讲了文档向量化,其实主要的目的是将文字转换成机器能处理的矩阵形式来进行后续计算。 首先先看以下的科普内容。每个词语都会有一个向量来表示,我们就可以利用向量计算对文本进行处理。最常见的的做法,计算两个文本的余弦相似度,来判断文本之间的的相似度,如:

旅游 和 火箭 向量之间的余弦相似度为:[[0.51281132]]
旅游 和 航天 向量之间的余弦相似度为:[[0.56792957]]
火箭 和 航天 向量之间的余弦相似度为:[[0.75873898]]

越接近于 1 越相似,越接近 0 越不相似。 可以看到,算法判断火箭和航天意义相近,而旅游和火箭、航天意义一般,符合实际。 所以我们检索的流程,是将用户的Query也处理成一个向量,再去匹配向量知识库中已有的Chunks,然后召回最相似匹配的几个结果,比如TOP 3,TOP 5。

以下我们以“什么是数据库”为query给出执行过程,选择输出TOP 3的内容。

question = "什么是数据库"
docs = vectordb.similarity_search(question,k=3)

打印检索到的内容

检索到的第0个内容: 
 15 ●
1-1 数据库是什么
1-1
第1章 数据库和SQL
数据库是什么
学习重点
● 数据库是将大量数据保存起来,通过计算机加工而成的可以进行高效访问
的数据集合。
● 用来管理数据库的计算机系统称为数据库管理系统(DBMS)。
● 通过使用DBMS,多个用户便可安全、简单地操作大量数据。
● 数据库有很多种类,本书将介绍如何使用专门的SQL语言来操作关系数
据库。
● 
--------------
检索到的第1个内容: 
 ● 16
第1章 数据库和SQL
问的数据集合称为数据库(Database,DB)。将姓名、住址、电话号码、
邮箱地址、爱好和家庭构成等数据保存到数据库中,就可以随时迅速获
取想要的信息了。用来管理数据库的计算机系统称为数据库管理系统
(Database Management System,DBMS)
A。
系统的使用者通常无法直接接触到数据库。因此,在使用系统的时候
往往意识不到数据库的存在。其
--------------
检索到的第2个内容: 
 数据库的结构
学习重点
● RDBMS通常使用客户端/服务器这样的系统结构。
● 通过从客户端向服务器端发送SQL语句来实现数据库的读写操作。
● 关系数据库采用被称为数据库表的二维表来管理数据。
● 数据库表由表示数据项目的列(字段)和表示一条数据的行(记录)所组
成,以记录为单位进行数据读写。
● 本书将行和列交汇的方格称为单元格,每个单元格只能输入一个数据。
--------------

这样,我们就找到了这本书中和“什么是数据库”最相似的3个段落内容。

3.5 提交LLM生成回答

在上一步,我们得到3个段落后,会连同Query“什么是数据库”一起提交给LLM,让大模型帮我们总结归纳,生成最终的答案。如下所示。

首先我们通过LangChain构建一个Prompt,以及一个检索链,本文对Prompt不过多展开。

from langchain.prompts import PromptTemplate
template = """基于以下已知信息,简洁和专业的来回答用户的问题。
 已知信息:{context}
 问题: {question}"""
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],
                                 template=template)

from langchain.chains import RetrievalQA

qa_chain = RetrievalQA.from_chain_type(llm,
                                       retriever=vectordb.as_retriever(),
                                       return_source_documents=True,
                                       chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})

如下,用户检索“什么是数据库”,经过一系列检索链路,可以得到以下回答。

question = "什么是数据库?"
result = qa_chain({"query": question})
print("大模型文档知识库回答 question 的结果:")
print(result["result"])
大模型文档知识库回答 question 的结果:
" 数据库是将大量数据保存起来,通过计算机加工而成的可以进行高效访问的数据集合。它通常由关系数据库管理系统(RDBMS)进行管理。通过使用数据库管理系统(DBMS),多个用户可以安全、简单地操作大量数据。数据库有很多种类,包括关系数据库、文本数据库等。在实际应用中,数据库广泛应用于银行账户管理、手机电话簿、网上商城购物等场景。使用数据库管理系统(DBMS)可以克服文本文件或电子制表软件在多人共享数据、操作大量数据格式、实现读写自动化等方面的不足,提高数据管理和处理的效率。"

这样,我们就实现了从文档的入库 => 向量化检索 => 大模型回答的整个流程。

4. 以百度文心APP示例分析

最后,我们使用一个典型的文档知识库应用来分析,以加深整个技术实现的认识。 从上到下,依次是Prompt设计、知识库上传、模型与参数配置选项,基本都能对应到上文LangChain的相关模块功能,大家可以有个感性认识。

图片

 

参考资料:

动手学大模型应用开发

datawhalechina.github.io/llm-universe/