使用LangChain建立检索增强生成(RAG)系统
LLM 最拿手的应用场景之一,非问答聊天机器人莫属。这类应用能针对特定的资料,精准地回答用户问题。背后的核心技术,就是检索增强生成,也就是我们常说的 RAG。
这篇教程的目标很明确:带你手把手从零开始,基于文本数据,搭一个能用的问答应用。过程中,我们会把典型的问答架构拆开揉碎了讲清楚。
什么是 RAG?
RAG 的本质,就是给大模型“开小灶”。
我们知道,大模型的知识面很广,但它的知识储备只停留在训练数据截止的那个时间点,而且仅限于公开信息。如果你想让它回答私有数据(比如公司内部文档)或者最新发布的内容,就必须给它“喂”这些特定信息。把相关信息找出来,塞进模型的输入(也就是提示词)里,这个过程就叫检索增强生成。
LangChain 提供了大量专为构建问答应用,特别是 RAG 应用而设计的组件。这一点非常方便。
有一点需要先说明:本篇聚焦的是非结构化数据(比如纯文本、PDF)的问答。如果你对结构化数据(比如表格、SQL 数据库)的 RAG 感兴趣,后续会有专门的教程介绍。
典型的 RAG 应用由两大块组成:
- :从数据源提取、处理并存储数据。这一步通常是离线完成的。
索引
- :运行时,接收用户提问,从索引里捞出相关的数据,然后交给模型生成答案。
检索与生成
从原始数据到最终答案,整个流程可以概括为以下几个步骤:
索引部分
- :用文档加载器把数据读进来。
加载
- :用文本拆分器把大文档切成小段。这样做有两个好处:一是方便后续检索,二是小段文本更容易放入模型的上下文窗口。
拆分
- :把切好的小段文本进行索引和存储,以便日后搜索。通常的做法是用嵌入模型生成向量,然后存入向量数据库。
存储
检索与生成部分
- :根据用户的提问,用检索器从存储中找出最相关的几个文本块。
检索
- :大模型拿到“问题+相关文本块”后,生成答案。
生成
准备工作
先把大模型和嵌入模型定下来。
这里我们用 BGE 系列的开源模型作为嵌入模型,主要是为了省成本,效果也足够好。
向量数据库方面,选择了 Qdrant。这是一个用 Rust 写的高性能向量数据库,具体细节可以参考《Qdrant:使用Rust编写的开源向量数据库&向量搜索引擎》这篇文章。
好了,接下来就严格按照上面的步骤,用 LangChain 把 RAG 系统搭起来。
索引:加载
LangChain 内置的 DocumentLoader 不算多,不过社区贡献的 PDF loader 很管用。
调用后,我们会拿到一个 Document 对象的列表。每个 Document 都包含两部分:page_content(文本内容)和 metadata(元数据,比如文件名、页码)。
这个列表的长度,就对应 PDF 的页数——可以理解为每一页对应一个 Document。
LangChain 官方集成了很多文档加载器,可以访问这里查看完整列表:https://python.langchain.com/docs/integrations/document_loaders/
索引:拆分
很多大模型的上下文窗口只有 8k 到 32k token。即便现在有超长上下文的模型,让它在几万字里找信息也不靠谱。
所以,我们需要把文档拆成小块,分别生成嵌入向量并存储。这样在运行时,我们只需要检索与问题最相关的几个小块就好。
这里我们用 RecursiveCharacterTextSplitter,把每个 Document 拆成 1000 个字符的块,块与块之间重叠 200 个字符。重叠的目的是避免把关键的上下文割裂开。这个拆分器会递归地用常见分隔符(比如换行符)进行拆分,直到块大小合适为止。对于普通文本,这是最推荐的拆分工具。
另外,我们设置了 add_start_index=True,这样每个拆分出来的块在原始文档中的起始字符索引会被记录在元数据的 start_index 属性里,方便追溯。
索引:存储
现在有 13 个文本块需要索引。最常见的做法是:用嵌入模型生成每个块的向量,然后把这些向量插入到向量数据库里。搜索时,同样先生成查询的向量,然后做相似性搜索(比如余弦相似度),找出最接近的文本块。
初始化好 vector_store 后,我们就把文本块连同向量一起存进数据库。每个块都会关联一个唯一的 ID。
检索和生成:检索
接下来写应用的核心逻辑:接收用户问题,搜索相关文档,把结果和问题一起交给模型,最后返回答案。
LangChain 定义了一个
Retriever
VectorStoreRetriever,它直接调用向量存储的相似性搜索功能。任何 VectorStore 都可以通过 .as_retriever() 轻松转成 Retriever。
检索和生成:生成
最后一步,把所有组件串成一条链:接收问题 → 检索文档 → 构造提示词 → 传给模型 → 解析输出。
LangChain 提供了两个好用的函数:
create_stuff_documents_chain:它定义了如何把检索到的上下文“塞进”提示模板,再交给大模型处理。create_retrieval_chain:它在上面那条链的基础上加上了检索步骤,并且把检索到的上下文跟最终答案一起返回。它的输入键是input,输出包含input、context和answer。
在问答应用中,向用户展示答案的来源是非常有必要的——这能增加可信度,也方便用户核对信息。
-
- 关于宇宙的好的网名有哪些
- 角色扮演 | 1
- 网名