前两篇解决了"怎么调模型"和"怎么编排流程",但模型有个绕不开的局限:它不知道你的私有文档,知识也停留在训练截止的那一天。RAG(检索增强生成)就是为此而生。这一篇讲 RAG 的前半场——索引:如何把一堆文档变成一个可以按语义检索的知识库;检索与问答是下一篇的主题。

一、为什么需要 RAG#

直接问模型公司内部的规章制度、上周刚发布的产品文档,它要么答不上来,要么一本正经地编(幻觉)。根源有三个:

  1. 知识滞后:模型的知识冻结在训练截止日;
  2. 幻觉:对不知道的东西,模型倾向于编造而不是承认不知道;
  3. 微调太贵:为了让模型"记住"你的文档去做续训,成本高、更新慢,文档一变又要重训。

RAG 的思路简单直接:不改模型,改输入。把你的文档做成一个可检索的知识库,用户提问时先从库里检索出最相关的几段内容,连同问题一起塞进提示词,让模型"看着资料回答"。

整个体系分成两条流程:

  • 索引流程(离线,本篇):文档加载 → 切分成块 → 向量化 → 存入向量数据库;
  • 检索流程(在线,下篇):把问题向量化 → 与库中的块做相似度比对 → 选出 Top-K → 交给模型生成答案。

下面沿着索引流程的四步走一遍。

二、第一步:文档加载#

LangChain 用统一的 Document 对象承载文档内容——page_content 是正文,metadata 是来源、位置等元数据。各种格式的文件通过对应的文档加载器(Document Loader)转成 Document。

结构化程度高的文本(txt、md)加载很直接:

Python
from langchain_community.document_loaders import TextLoader

docs = TextLoader(file_path="sample.md", encoding="utf-8").load()
print(docs[0].page_content[:100])

对 Markdown、Word 这类有结构的文档,Unstructured 系列加载器提供两种模式:

Python
from langchain_community.document_loaders import UnstructuredMarkdownLoader

# single 模式:整个文件合成一个 Document
docs = UnstructuredMarkdownLoader(file_path="sample.md", mode="single").load()

# elements 模式:按标题、段落等结构元素拆成多个 Document
docs = UnstructuredMarkdownLoader(file_path="sample.md", mode="elements").load()

Word 文档换成 UnstructuredWordDocumentLoader,用法完全一样。至于 PDF——简单版式的用 pypdf 系加载器就够了;但扫描件、多栏排版、带表格公式的复杂 PDF,业界通行做法是先交给专门的解析服务(如 MinerU、Marker 这类工具)转成 Markdown,再按普通文本加载。别在"用通用加载器硬啃复杂 PDF"上浪费时间,解析质量决定了整个知识库的上限。