LangChain4j RAG(检索增强生成)—— 小白也能懂的通俗版
🧩先搞懂:RAG 是什么?
RAG = Retrieval-Augmented Generation,中文名「检索增强生成」。核心思想很简单:不让 LLM 凭空瞎编,而是先从你的资料库里找到相关信息,再让 LLM 基于这些资料回答问题。打个比方:LLM 像一个参加开卷考试的学生。RAG 就是帮他翻书找到答案的过程。
⚡核心结论一句话
LangChain4j 提供三种 RAG 风格:Easy RAG(一行代码入门)、Naive RAG(基础向量搜索)、Advanced RAG(模块化管道支持查询转换/路由/重排),按需选择即可。
📋 RAG 三件套一览
| RAG 风格 | 适合场景 | 复杂度 |
|---|---|---|
| Easy RAG | 快速原型、PoC、入门学习 | ⭐ 最低 |
| Naive RAG | 基本文档问答,需要一定定制 | ⭐⭐ |
| Advanced RAG | 生产级高质量 RAG,需要查询优化 | ⭐⭐⭐ 最高 |
一、什么是 RAG?
为什么需要 RAG?
LLM 的知识仅限于训练数据。如果你想让 LLM 了解公司内部的私有文档或最新行业信息,有三种方式:
- RAG:实时检索 + 注入上下文(本文讨论的)
- 微调:用你的数据重新训练模型
- RAG + 微调结合:最佳但最复杂
核心流程
用户提问 → 从资料库检索相关片段 → 把片段拼进提示词 → LLM 基于资料回答信息检索方法对比
| 方法 | 原理 | 特点 |
|---|---|---|
| 全文搜索 | TF-IDF / BM25,匹配关键词频率 | 精确匹配术语,不懂语义 |
| 向量搜索(语义搜索) | 将文本转为向量,按余弦相似度排名 | 理解语义,不怕同义词 🔥 |
| 混合搜索 | 全文 + 向量结合 | 效果最好,目前仅 Azure AI Search 支持 |
二、RAG 的两个阶段
阶段 1:索引(Indexing)—— 离线准备
原始文档 → 清洗 → 分割成小块 → 向量化 → 存入向量数据库通常是离线的(比如周末跑 cron job)。用户上传自定义文档时需要在线执行。
阶段 2:检索(Retrieval)—— 在线响应
用户问题 → 向量化 → 在向量数据库中搜索相似片段 → 注入提示词 → 发给 LLM在线执行,用户提出问题后立即响应。
三、Easy RAG —— 最快上手的方式
只需导入一个依赖,4 步搞定:
// Step 1: 导入 langchain4j-easy-rag 依赖// Step 2: 加载文档List<Document>documents=FileSystemDocumentLoader.loadDocuments("/path/to/docs");// 递归加载子目录: loadDocumentsRecursively()// glob 过滤 PDF: getPathMatcher("glob:**.pdf")// Step 3: 预处理并存入向量数据库(自动分割+嵌入+存储)InMemoryEmbeddingStore<TextSegment>embeddingStore=newInMemoryEmbeddingStore<>();EmbeddingStoreIngestor.ingest(documents,embeddingStore);// Step 4: 创建 AI 服务Assistantassistant=AiServices.builder(Assistant.class).chatModel(chatModel).chatMemory(MessageWindowChatMemory.withMaxMessages(10)).contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore)).build();// Step 5: 开始聊天!Stringanswer=assistant.chat("How to do Easy RAG with LangChain4j?");底层魔法:
- Apache Tika 自动解析各种文件格式
- DocumentSplitter 自动切分成 ≤300 token、重叠 30 token 的小块
- EmbeddingModel 使用 bge-small-en-v1.5(默认),可在 JVM 进程内离线运行,无需外部服务!
四、核心 RAG API 详解
关键概念速查表
| 类/接口 | 作用 |
|---|---|
| Document | 完整文档(如一个 PDF 文件) |
| Metadata | 文档元信息(键值对) |
| DocumentLoader | 从文件系统/URL/云存储加载文档 |
| DocumentParser | 解析不同格式的文件 |
| DocumentTransformer | 清理、过滤、丰富、摘要文档 |
| GraphTransformer | 从非结构化文本提取语义图 |
| TextSegment | 文档切割后的小块 |
| DocumentSplitter | 将文档分割为 TextSegment |
| Embedding | 文本的向量表示 |
| EmbeddingModel | 将文本转为向量 |
| EmbeddingStore | 向量数据库(支持 15+ 种) |
| EmbeddingStoreIngestor | 一站式摄取管线 |
| ContentRetriever | 检索相关内容 |
| RetrievalAugmentor | 高级 RAG 管道编排器 |
Document & Metadata
Documentdoc=Document.from("内容",Metadata.from(Map.of("author","Alice")));doc.text();// 获取文本doc.metadata();// 获取元数据doc.toTextSegment();// 转为 TextSegmentMetadata 三大用途:给 LLM 额外参考、搜索时按元数据过滤、更新同步定位已变更文档。
Document Loader & Parser
10+ 种 Loader:FileSystem、ClassPath、Url、AmazonS3、Azure Blob、GitHub、GCS、Selenium、Playwright、腾讯 COS。
4 种 Parser:
TextDocumentParser— TXT、HTML、MDApachePdfBoxDocumentParser— PDFApachePoiDocumentParser— Word/Excel/PPTApacheTikaDocumentParser—自动检测几乎所有格式(推荐 🔥)
不指定 parser 时会通过 SPI 自动加载,找不到则回退到 TextDocumentParser。
Embedding & EmbeddingStore
// 向量化Embeddingemb=model.embed("你好世界");model.embedAll(List.of(seg1,seg2));// 存入向量库Stringid=store.add(embedding,textSegment);store.addAll(List.of(e1,e2),List.of(s1,s2));// 搜索EmbeddingSearchResultresult=store.search(EmbeddingSearchRequest.builder().queryEmbedding(queryEmb).maxResults(5).minScore(0.75).filter(and(metadataKey("dept").isEqualTo("HR"),metadataKey("year").isGreaterThan(2023))).build());LangChain4j 内置5 个离线嵌入模型,支持15+ 种向量数据库。
EmbeddingStoreIngestor —— 一站式摄取管线
EmbeddingStoreIngestoringestor=EmbeddingStoreIngestor.builder().documentSplitter(splitter)// 可选.textSegmentTransformer(transformer)// 可选.embeddingModel(model)// 必需.embeddingStore(store)// 必需.build();ingestor.ingest(documents);// 一行搞定:分割→转换→嵌入→存储五、朴素 RAG(Naive RAG)
自己控制每一步的基础实现:
// 1. 索引EmbeddingStoreIngestor.ingest(documents,embeddingStore);// 2. 检索Assistantassistant=AiServices.builder(Assistant.class).chatModel(chatModel).contentRetriever(newEmbeddingStoreContentRetriever(embeddingStore,embeddingModel)).build();跟 Easy RAG 的区别在于你自己控制了分割策略、嵌入模型、向量数据库等每一个细节。
六、高级 RAG(Advanced RAG)—— 模块化管道
核心是RetrievalAugmentor,每次 AI 服务调用时被触发来增强 UserMessage:
RetrievalAugmentoraugmentor=DefaultRetrievalAugmentor.builder().queryTransformer(...)// 查询转换.contentRetriever(...)// 内容检索.queryRouter(...)// 查询路由.contentAggregator(...)// 结果聚合.contentInjector(...)// 内容注入.executor(executor)// 并行化.build();五大组件详解
1️⃣ Query Transformer(查询转换器)
| 转换器 | 作用 | 示例 |
|---|---|---|
| DefaultQueryTransformer | 原样传递 | 默认 |
| CompressingQueryTransformer | 压缩为独立查询 | “他住哪?” → “John Doe 住哪?” |
| ExpandingQueryTransformer | 扩展为多个查询 | 多种方式重述问题,提高召回率 |
2️⃣ Content Retriever(内容检索器)
| 检索器 | 数据源 |
|---|---|
| EmbeddingStoreContentRetriever | 向量数据库(最常用 🔥) |
| WebSearchContentRetriever | 网络搜索引擎 |
| SqlDatabaseContentRetriever | SQL 数据库 |
| AzureAiSearchContentRetriever | Azure AI Search(全文+向量混合) |
| Neo4jContentRetriever | Neo4j 知识图谱 |
ContentRetrieverretriever=EmbeddingStoreContentRetriever.builder().embeddingStore(store).embeddingModel(model).maxResults(5).dynamicMaxResults(q->q.length()>100?10:3)// 动态调整.minScore(0.75).dynamicMinScore(q->q.contains("紧急")?0.5:0.75).filter(and(metadataKey("userId").isEqualTo(currentUserId))).build();3️⃣ Query Router(查询路由器)
根据查询类型分发到不同的检索器:
LanguageModelQueryRouterrouter=LanguageModelQueryRouter.builder().chatModel(chatModel).add(hrRetriever,"人力资源相关问题").add(financialRetriever,"财务相关问题").add(techRetriever,"技术问题").build();4️⃣ Content Aggregator(内容聚合器)
合并多个检索器的结果:
| 聚合器 | 说明 |
|---|---|
| DefaultContentAggregator | 简单拼接去重 |
| ReRankContentAggregator | 用重排序模型对结果重新打分 |
5️⃣ Content Injector(内容注入器)
把检索到的内容注入到提示词中(DefaultContentInjector 开箱即用)。
6️⃣ 并行化
多个检索器可以同时执行:
DefaultRetrievalAugmentor.builder().executor(myExecutor).build();七、访问来源(Sources)
查看 LLM 引用了哪些文档:
interfaceAssistant{Result<String>chat(StringuserMessage);}Result<String>result=assistant.chat("怎么申请报销?");Stringanswer=result.content();// AI 的回答List<Content>sources=result.sources();// 引用了哪些片段for(Contentc:sources){System.out.println(c.textSegment().text());// 原文片段System.out.println(c.textSegment().metadata());// 元数据}流式模式监听:
assistant.chat("怎么申请报销?").onRetrieved(sources->System.out.println("检索到 "+sources.size()+" 个片段")).onPartialResponse(token->System.out.print(token)).start();🎯 面试高频追问
Q1:RAG 的三个级别有什么区别?
答:Easy RAG 是一键式入门方案;Naive RAG 是自己控制每一步的基础向量搜索;Advanced RAG 引入 RetrievalAugmentor 管道,支持查询转换/路由/重排等高级优化。
Q2:查询压缩和查询扩展分别解决什么问题?
答:压缩解决"代词消歧"(“他住哪"→"John Doe 住哪”);扩展解决"词汇不匹配"(用多种表述同一问题,提高召回率)。
Q3:向量搜索 vs 全文搜索怎么选?
答:向量搜索理解语义(不怕同义词),全文搜索精确匹配关键词。混合搜索效果最好但目前只有 Azure AI Search 支持。一般先用向量搜索。
Q4:如何在 RAG 中实现权限控制?
答:通过 Metadata Filter——在 ContentRetriever 中设置 filter,如metadataKey("userId").isEqualTo(currentUserId),确保用户只能检索自己有权限的文档。
Q5:RAG 和微调的区别?
答:RAG 是运行时动态检索相关知识,成本低时效性强;微调是把知识学到模型参数里,成本高但推理更快。两者可结合使用。
✅ 总结
RAG 是 LangChain4j 中最实用的功能之一:
- 三种 RAG 风格按需选:Easy RAG 快速入门 → Naive RAG 基础定制 → Advanced RAG 生产级优化
- 丰富的文档生态:10+ 种 Loader、4 种 Parser、自动检测格式
- 灵活的文档处理:DocumentTransformer 清理/过滤/丰富/摘要,GraphTransformer 提取语义图
- 完善的分块体系:DocumentSplitter + TextSegmentTransformer
- 15+ 向量数据库支持:统一接口无缝切换
- 一站式摄取管线:EmbeddingStoreIngestor 一行搞定
- 高级 RAG 管道:RetrievalAugmentor 五大组件全链路覆盖
- 多数据源检索:向量库、Web 搜索、SQL、知识图谱、Azure AI Search
- 查询优化:压缩/扩展查询、LLM 驱动的路由决策
- 结果优化:重排序聚合器、并行化执行
- 来源追溯:Result<T> 获取引用片段,方便审计和溯源
掌握 RAG 之后,你就可以构建真正的企业级文档问答系统了。