检索增强生成(Retrieval-Augmented Generation,RAG) 是一种将信息检索 与大语言模型生成 深度融合的 AI 技术框架,核心是让模型先查资料、再写答案 ,从根本上解决大模型的知识过时、幻觉、隐私安全三大痛点。
一、核心定位与诞生背景
RAG 本质是为大模型搭建外部动态知识库,让模型不再依赖 "死记硬背" 的训练数据,而是按需检索外部权威信息后生成回答。
- 解决的核心问题:
- 知识截止:大模型训练数据固定,无法获取训练后新信息。
- 幻觉:模型编造虚假、无依据的内容。
- 隐私与成本:企业私有数据不能上传公共模型,全量微调成本极高。
二、核心工作流程(四步闭环)
1. 知识库构建(离线)
- 数据处理:将文档、网页、数据库等非结构化数据清洗、分块(Chunk)。
- 向量化 :用嵌入模型(Embedding Model)将文本转为向量,存入向量数据库(如 Milvus、Pinecone、FAISS)。
- 索引:构建高效检索索引,支持语义相似度查询。
2. 查询处理(在线)
- 用户输入问题 → 转为查询向量 → 向量数据库做语义检索 → 召回 Top-K 最相关的文档片段。
3. 上下文增强
- 将用户问题 + 检索到的参考资料拼接成增强 Prompt,作为大模型输入。
4. 增强生成
- 大模型基于 "问题 + 参考资料" 生成回答,可附带引用来源,提升可信度。
三、核心技术组件
表格
| 组件 | 作用 | 常见选型 |
|---|---|---|
| 嵌入模型 | 文本→向量,决定检索精度 | BGE、text-embedding-ada-002、E5 |
| 向量数据库 | 存储与高效检索向量 | Milvus、Pinecone、Weaviate、FAISS |
| 检索器 | 语义匹配、召回相关片段 | BM25 + 向量混合检索、重排序(Rerank) |
| 生成模型 | 基于上下文生成回答 | GPT、Llama、文心一言、通义千问 |
| 重排序模型 | 对召回结果二次排序,提升相关性 | CrossEncoder、BGE-Reranker |
四、核心优势
- 事实准确、减少幻觉:回答基于检索到的真实资料,可溯源。
- 知识实时更新:无需重新训练模型,仅更新向量库即可接入新数据。
- 隐私安全可控:私有数据本地存储,不泄露给公共模型。
- 成本极低:替代全量微调,仅需轻量级向量化与检索流程。
- 垂直领域适配强:快速接入医疗、法律、企业内部文档等专业知识。
五、主流应用场景
- 企业知识库问答:员工查询内部制度、产品手册、技术文档。
- 智能客服:基于产品文档、FAQ 生成精准回复。
- 法律 / 医疗专业问答:结合最新法规、医学文献生成专业回答。
- 内容创作:基于参考资料生成报告、文案、摘要。
- 多模态 RAG:检索图片、视频、音频等多模态信息并生成回答。
六、RAG vs 微调(Fine-tuning)
表格
| 维度 | RAG | 微调 |
|---|---|---|
| 知识更新 | 实时、低成本(更新向量库) | 慢、高成本(重新训练) |
| 隐私 | 数据本地存储,安全可控 | 数据需上传训练,隐私风险高 |
| 幻觉控制 | 强(基于检索资料) | 弱(依赖训练数据) |
| 适用场景 | 知识频繁更新、私有数据、专业领域 | 通用能力增强、风格对齐 |
七、演进方向
- 高级 RAG:混合检索(BM25 + 向量)、多轮对话 RAG、GraphRAG(知识图谱 + RAG)。
- 端到端优化:检索与生成联合训练、动态上下文窗口、引用验证。
- 多模态 RAG:支持文本、图像、音频、视频的跨模态检索与生成。
1. 文档解析器(DocumentParser):搞定多格式文档读取
1.1 核心作用
将不同格式的二进制文件(PDF/DOCX/PPTX 等)解析为 LangChain4j 统一的 Document 对象(包含文本内容 + 元数据,如文件路径、格式、创建时间等),解决「不同格式文件无法统一处理」的问题。
1.2 常见实现类(按场景选型)
表格
| 解析器类名 | 所属模块 | 支持格式 | 适用场景 | 优缺点 |
|---|---|---|---|---|
TextDocumentParser |
langchain4j 核心模块 | TXT、MD、HTML、JSON 等纯文本 | 纯文本文件解析 | 轻量、无额外依赖;仅支持纯文本 |
ApachePdfBoxDocumentParser |
langchain4j-document-parser-apache-pdfbox | 单页 / 多页 PDF 解析 | 解析精度高;需引入 PDFBox 依赖 | |
ApachePoiDocumentParser |
langchain4j-document-parser-apache-poi | DOC、DOCX、PPT、PPTX、XLS、XLSX | 微软 Office 文件 | 覆盖全 Office 格式;依赖稍重 |
ApacheTikaDocumentParser |
langchain4j-document-parser-apache-tika | 几乎所有格式(PDF/Office/ 图片 / 音频等) | 格式不固定的混合场景 | 万能解析;依赖最大、速度稍慢 |
1.3 核心使用方式(代码实操)
第一步:添加对应依赖(Maven)
xml
<!-- 核心依赖(纯文本解析) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>0.32.0</version> <!-- 建议用最新版 -->
</dependency>
<!-- PDF解析依赖 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-document-parser-apache-pdfbox</artifactId>
<version>0.32.0</version>
</dependency>
<!-- Office解析依赖 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-document-parser-apache-poi</artifactId>
<version>0.32.0</version>
</dependency>
<!-- 万能解析依赖(Tika) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-document-parser-apache-tika</artifactId>
<version>0.32.0</version>
</dependency>
第二步:解析文档的常用 API
java
运行
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
import dev.langchain4j.data.document.parser.apache.poi.ApachePoiDocumentParser;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.util.List;
public class DocumentParserDemo {
public static void main(String[] args) {
// 1. 解析单个纯文本文档(TXT/MD)
Document txtDoc = FileSystemDocumentLoader.loadDocument(
"E:/knowledge/file.txt",
new TextDocumentParser()
);
// 2. 解析单个PDF文档
Document pdfDoc = FileSystemDocumentLoader.loadDocument(
"E:/knowledge/医院信息.pdf",
new ApachePdfBoxDocumentParser()
);
// 3. 解析单个Word文档
Document docxDoc = FileSystemDocumentLoader.loadDocument(
"E:/knowledge/产品手册.docx",
new ApachePoiDocumentParser()
);
// 4. 加载指定目录下所有纯文本文档
List<Document> allTxtDocs = FileSystemDocumentLoader.loadDocuments(
"E:/knowledge",
new TextDocumentParser()
);
// 5. 加载指定目录下仅.txt后缀的文档(按规则过滤)
PathMatcher txtMatcher = FileSystems.getDefault().getPathMatcher("glob:*.txt");
List<Document> filterTxtDocs = FileSystemDocumentLoader.loadDocuments(
"E:/knowledge",
txtMatcher,
new TextDocumentParser()
);
// 6. 递归加载目录+子目录下所有文档
List<Document> recursiveDocs = FileSystemDocumentLoader.loadDocumentsRecursively(
"E:/knowledge",
new ApachePdfBoxDocumentParser() // 解析PDF
);
// 打印解析结果(查看文本内容和元数据)
System.out.println("PDF文档内容:" + pdfDoc.text());
System.out.println("PDF文档元数据:" + pdfDoc.metadata());
}
}
2. 文档分割器(DocumentSplitter):把长文本切成小片段
2.1 核心作用
解析后的文档可能是几万 / 几十万字的长文本,直接向量化会超出大模型上下文窗口,也会降低检索精度。文档分割器的作用是:
- 将长文本分割成固定长度的文本片段(TextSegment);
- 保留重叠部分(如 30 个 token),保证片段间的语义连贯性;
- 若单个单元(段落 / 句子)过长,自动调用子分割器进一步拆分。
2.2 常见实现类(按分割粒度选型)
表格
| 分割器类名 | 分割粒度 | 核心参数 | 适用场景 |
|---|---|---|---|
DocumentByParagraphSplitter |
段落(连续 2 个以上换行符分隔) | 最大长度、重叠长度、分词器 | 结构化文档(如手册、论文) |
DocumentByLineSplitter |
行 | 同上 | 日志、代码文件、表格类文本 |
DocumentBySentenceSplitter |
句子(基于 OpenNLP) | 同上 | 文学文本、对话类内容 |
DocumentByWordSplitter |
单词 | 同上 | 短文本、外文文档 |
DocumentByCharacterSplitter |
字符 | 同上 | 简单文本、无复杂结构的内容 |
DocumentSplitters.recursive() |
递归分割(段落→行→句子→单词) | 同上 | 通用场景(默认推荐) |
2.3 核心概念:Token vs 字符
- 字符:按文字个数计算(如 "你好"=2 个字符),简单但忽略语义;
- Token:按语义单元分割(如 "人工智能"=1 个 token,"机器学习"=1 个 token),更贴合大模型的处理逻辑;
- LangChain4j 中可通过
HuggingFaceTokenizer实现 Token 计算,推荐优先用 Token 而非字符定义片段长度。
2.4 核心使用方式(代码实操)
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentByParagraphSplitter;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddedTextStore;
import dev.langchain4j.model.embedding.InMemoryEmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.tokenizer.HuggingFaceTokenizer;
import java.util.List;
public class DocumentSplitterDemo {
public static void main(String[] args) {
// 1. 加载并解析文档
Document document = FileSystemDocumentLoader.loadDocument(
"E:/knowledge/人工智能.md",
new TextDocumentParser()
);
// 2. 方式1:使用默认递归分割器(推荐)
// 默认:最大300token,重叠30token,递归拆分(段落→行→句子→单词)
List<TextSegment> defaultSegments = DocumentSplitters.recursive()
.maxSegmentSize(300) // 每个片段最大token数
.overlapSize(30) // 片段间重叠token数
.split(document);
// 3. 方式2:自定义按段落分割器(指定Token计算)
DocumentByParagraphSplitter paragraphSplitter = new DocumentByParagraphSplitter(
300, // 最大Token数
30, // 重叠Token数
new HuggingFaceTokenizer() // Token分词器
);
List<TextSegment> paragraphSegments = paragraphSplitter.split(document);
// 4. 分割后直接入库(向量化+存储一步到位)
EmbeddedTextStore embeddingStore = new InMemoryEmbeddingStore<>();
// Ingestor:分割→向量化→存储
EmbeddingStoreIngestor.ingest(document, embeddingStore);
// 打印结果
System.out.println("默认分割后的片段数:" + defaultSegments.size());
System.out.println("第一个片段内容:" + defaultSegments.get(0).text());
System.out.println("向量库中存储的片段数:" + embeddingStore.count());
}
}
2.5 分割器工作流程(关键)
- 初始化分割器,指定最大片段长度 (Token / 字符)和重叠长度;
- 按分割粒度拆分文档(如段落分割器先拆成段落);
- 将小单元(段落 / 句子)组合成片段,不超过最大长度;
- 若单个单元过长(如一个段落 500token),调用子分割器(如行分割器)继续拆分;
- 为每个片段添加唯一
index元数据,保证顺序; - 输出最终的
TextSegment列表,用于后续向量化。
总结(核心关键点)
- 文档解析器 :核心是「格式兼容」,根据文件类型选对应实现(PDF 用 PDFBox、Office 用 POI、混合格式用 Tika),最终输出统一的
Document对象; - 文档分割器:核心是「粒度控制」,优先用默认递归分割器,按 Token 定义片段长度(推荐 300-500token),保留 10%-20% 的重叠长度保证语义连贯;
- 实操关键:解析和分割是 RAG 数据预处理的基础,解析不完整会导致知识缺失,分割不合理会降低检索精度,需根据文档类型(结构化 / 非结构化)调整分割策略。
如果需要,我可以补充「解析 + 分割 + 向量化 + 检索」的完整 RAG 流程代码,或者针对某类文档(如复杂 PDF、多表格 Word)的解析优化方案。