大型语言模型(LLM)虽然强大,能够快速生成智能且自然的响应,但其训练数据的局限性是主要瓶颈。例如,GPT-4 的知识截止日期为 2021 年 9 月,这意味着模型无法知晓此日期之后的事件或发展。此外,LLM 在信息准确性和幻觉方面存在持续问题——它们可能生成连贯但事实上不正确的回答。最后,LLM 缺乏具体的利基领域知识,只能在通用水平上生成响应。
为了解决这些问题并使 LLM 适用于特定和信息密集型任务,可以将 LLM 连接到 AI 知识库。AI 知识库是由有组织的数据组成的信息存储库,包括产品文档、文章、消息和其他材料。本文将详细解释如何创建一个知识库,并将其连接到 LLM,使其能够生成事实正确和具体的响应。
什么是 RAG?
RAG(Retrieval-Augmented Generation,检索增强生成)是一种技术,它使 LLM 能够访问知识库中的相关文档。它允许 LLM 根据访问的文档生成准确的响应。RAG 技术的工作原理如下:
- 查询处理:首先,将用户的自然语言查询转换为向量表示。
- 检索:在知识库中搜索与该查询最相关的文档片段。
- 上下文构建:将最合适的搜索结果作为上下文添加到提示(Prompt)中,并添加指令,例如:'仅使用以下段落中的信息来回答以下问题'。
- 生成:如果使用的 LLM 模型未针对指令进行调整,则需要添加示例(Few-Shot Learning)来演示预期输入和输出。包含指令、搜索结果和输出格式的提示文本将发送到 LLM 模型。
- 响应:LLM 使用来自上下文的信息生成准确、具体的响应。
RAG 的核心组件
RAG 系统主要由两个核心组件组成:信息检索组件和文本生成器 LLM。
- 检索器(Retriever):这是一个特定的查询编码器和基于矢量的文档搜索索引。现代系统中,向量数据库常被用作高效的检索器。向量数据库存储数据的向量嵌入,它是反映其语义含义的数据的数值表示。例如,单词 "HELLO" 可能被表示为
[0.23, 0.001, 0.707]。知识库可以是向量数据存储,检索器会将查询转换为向量,并使用相似性搜索来查找相关信息。流行的向量数据库包括 Chroma、FAISS 和 Pinecone。 - 文本生成器 LLM:使用的 LLM 模型取决于具体目的。例如,如果最终解决方案要求严格的数据隐私,则不建议使用云端的 OpenAI GPT 模型。出于开源和可控的目的,可以使用 Mistral 7B 等本地部署模型。在此示例中,我们将结合 LangChain 框架,这是一个经常用于创建基于 RAG 应用程序的工具。
创建 AI 知识库的步骤
要利用 RAG 技术,首先需要构建一个结构化的 AI 知识库。这通常涉及四个关键步骤:
- 数据收集与准备:收集原始文档(PDF、TXT、Markdown 等)。此步骤主要是手动执行的数据清洗工作,确保内容无乱码、格式统一。
- 文本分块(Chunking):由于大多数 LLM 的上下文窗口有限(例如 Mistral 7B 最多支持 8192 个 Token),需要将长文档拆分为固定大小的块。最常用的尺寸是 512 到 1024 个字符或 Token。
- 创建向量嵌入:使用专门的嵌入模型(Embedding Model)将文本块转换为向量。常用的模型包括
bge-large-en-1.5或all-MiniLM-L6-v2。 - 存储嵌入:将生成的向量存储在向量数据库中,以支持快速的相似度检索。
这些步骤完成后,即可创建 AI 知识库,随后连接到 LLM 以构建完整的 AI 解决方案。
文本分块策略详解
在 RAG 管道中,文本分块是必不可少的预处理步骤。它将冗长的文本文档划分为更易于理解的片段,以便 LLM 能有效处理。分块旨在提高检索准确性并优化模型对上下文窗口的使用。
常见的分块方法包括:
- 固定长度分块:将文本划分为由预定数量的标记或单词组成的部分。简单直接,但可能切断语义。
- 语义分块:沿着语义线划分文本,例如按段落或句子边界分割,保持上下文的完整性。
- 滑动窗口(Sliding Window):使用重叠块来保证围绕块边界的上下文得到维护,防止关键信息在分割时丢失。
使用 LangChain 的代码示例:
langchain.document_loaders TextLoader
langchain.text_splitter RecursiveCharacterTextSplitter
loader = TextLoader()
documents = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=,
chunk_overlap=,
length_function=,
is_separator_regex=
)
chunks = splitter.split_documents(documents)
()


