最详细的RAG设计流程讲解—langchain4j框架
这期带来一篇关于 RAG(Retrieval-Augmented Generation)功能实现逻辑的完整拆解

大部分人觉得RAG只是简单的检索+生成两步走。但如果真的做过一个能上线、能抗压、能解决实际问题的系统,就会发现。rag功能需要设计一条完整的工程链路,示例代码使用java的langchain4j框架。

官网:LangChain4j 中文文档 | LangChain4j 中文文档
中文社区版:LangChain4j 中文教程 | LangChain4j 中文文档
目录
数据处理
我们要处理的数据源文件一般是多种多样的。常见输入包括:
- Word(.docx)
- PPT
- HTML
- Markdown
- 扫描件(甚至 OCR)
在大部分ai框架中,一般具有一些默认的处理的方法。但是针对不同类型,我们一般还是自己实现一下。下面举例一下langchain4j处理html文件的逻辑。
自定义一个DocumentTransformer处理器,用于处理文件
/**
* 自定义html文档转换器
*/
public class HtmlToTextDocumentTransformer implements DocumentTransformer {
@Override
public Document transform(Document document) {
return Document.from(removeHtmlTags(document.text()));
}
// 使用正则表达式清除 HTML 标签
public static String removeHtmlTags(String html) {
if (html == null || html.isEmpty()) {
return "";
}
// 定义正则表达式
String regex = "<[^>]+>";
// 替换所有匹配的标签为空字符串
return html.replaceAll(regex, "").trim();
}
@Override
public List<Document> transformAll(List<Document> documents) {
List<Document> list = new ArrayList<Document>();
documents.stream().forEach(document -> {
list.add(this.transform(document));
});
return list;
}
}
加载html文件
Document htmlDoc = Document.from(
"<html><body><p>manba <b>out</b>!</p></body></html>"
);
// 文档转换器
DocumentTransformer transformer = new HtmlToTextDocumentTransformer();
Document cleanedDoc = transformer.transform(htmlDoc);
System.out.println(cleanedDoc.text());
最终输出了
manba out!
分块策略
切太大检索不精确,切太小上下文丢失,不同文档需要不同的策略。langchain4j提供了一个叫DocumentSplitter的工具。给我们自定义各种切割规则。
// 设置分割器
DocumentSplitter splitter = DocumentSplitters.recursive(
200, // 每块约200字符
20 // 重叠区防止知识断裂
);
List<TextSegment> segments = splitter.split(document); // 文档瞬间变拼盘
具体有哪些类型的DocumentSplitter,可以查看langchain4j中文社区
RAG(检索增强生成) | LangChain4j 中文文档
问题改写
用户问“那他怎么配置”,不进行上下文补充,根本不知道用户在询问什么
QueryTransformer负责在检索前对用户问题进行优化,LangChain4j的DefaultRetrievalAugmentor允许配置多个QueryTransformer来组合使用。
| 实现/策略 | 描述 | 适用场景 |
|---|---|---|
CompressingQueryTransformer | 利用ChatModel将对话历史和当前问题压缩成一个独立、清晰的问题 | 多轮对话中,解决指代不明(如“那他怎么配置?”) |
ExpandingQueryTransformer | 利用ChatModel将一个查询扩展为多个相关的查询 | 增加检索的召回率,从不同角度寻找相关信息 |
RepeatingQueryTransformer | 在高级RAG管道中重复使用检索查询 | 配合ReRankingContentAggregator等组件使用 |
| 自定义实现 | 实现QueryTransformer接口,编写特定逻辑(如同义词替换) | 针对特定业务场景的个性化查询优化 |
示例:使用压缩型查询转换器
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.rag.query.transformer.CompressingQueryTransformer;
import dev.langchain4j.rag.query.Query;
// 假设已有ChatLanguageModel实例 (如OpenAiChatModel)
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey("your-api-key")
.modelName("gpt-3.5-turbo")
.build();
// 1. 创建压缩型转换器
QueryTransformer compressor = new CompressingQueryTransformer(model);
// 2. 模拟对话历史和当前问题
List<ChatMessage> chatMemory = List.of(
SystemMessage.from("你是一个Java技术专家。"),
UserMessage.from("如何在Spring Boot项目中配置OpenAI API Key?"),
AiMessage.from("可以在application.properties中设置`openai.api.key`。")
);
String currentQuery = "那怎么把它设置为环境变量呢?";
// 3. 转换查询
Query originalQuery = Query.from(currentQuery, chatMemory);
Collection<Query> transformedQueries = compressor.transform(originalQuery);
transformedQueries.forEach(q -> System.out.println("优化后的问题: " + q.text()));
// 输出可能为: "如何在Spring Boot项目中把OpenAI API Key设置为环境变量?"
检索策略
纯向量检索对精确匹配很弱,用户问一个订单号,向量见多可能完全找不到,混合检索怎么融合,需不需要进行重排序等等,都要进行取舍
LangChain4j的DefaultRetrievalAugmentor提供了一套模块化的RAG管道,允许我们灵活组合查询转换、多路检索和结果重排等高级策略。
-
混合检索:结合关键词匹配(BM25)和语义相似度(向量检索),LangChain4j通过与
Azure AI Search等向量数据库集成,原生支持混合搜索。 -
结果重排:使用
ReRankingContentAggregator,在合并多路检索结果后,通过一个ScoringModel对内容进行重新打分排序,将最相关的结果排在前面
示例:构建一个带查询压缩、混合检索和重排的RAG管道
import dev.langchain4j.rag.DefaultRetrievalAugmentor;
import dev.langchain4j.rag.content.aggregator.ReRankingContentAggregator;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.query.transformer.CompressingQueryTransformer;
import dev.langchain4j.model.scoring.ScoringModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.model.embedding.EmbeddingModel;
public class AdvancedRagPipeline {
public static void main(String[] args) {
// 1. 配置组件 (实际需要具体实现类)
ChatLanguageModel chatModel = ...; // 你的ChatModel
EmbeddingStore embeddingStore = ...; // 你的向量存储,如InMemoryEmbeddingStore
EmbeddingModel embeddingModel = ...; // 你的嵌入模型
// 2. 配置混合检索器 (此处示例为向量检索,混合检索需特定集成)
ContentRetriever vectorRetriever = EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.maxResults(15)
.minScore(0.6)
.build();
// 3. 配置ScoringModel用于重排 (以Cohere为例)
ScoringModel scoringModel = CohereScoringModel.builder()
.apiKey("your-cohere-api-key")
.modelName("rerank-english-v3.0")
.build();
// 4. 构建高级检索增强器
DefaultRetrievalAugmentor augmentor = DefaultRetrievalAugmentor.builder()
// 添加查询转换器:将多轮对话压缩为独立查询
.queryTransformer(new CompressingQueryTransformer(chatModel))
// 添加内容检索器:可以有多个,进行多路召回
.contentRetriever(vectorRetriever)
// 添加内容聚合器:对多路结果进行重排
.contentAggregator(new ReRankingContentAggregator(scoringModel))
.build();
System.out.println("高级RAG管道构建完成。");
}
}
回话记忆
当我们不断进行绘画时,上下文内容越来越长。token成本越来越大。我们需要选择带那几次的上下文。如何控制上下文取舍等等。这些都是要考虑的事情。
ChatMemory接口负责管理对话历史,LangChain4j提供了两种开箱即用的策略来控制上下文长度,控制Token消耗和延迟。
| 策略 | 描述 | 特点 |
|---|---|---|
MessageWindowChatMemory | 保留最近N条消息的滑动窗口 | 简单高效,但无法精确控制Token消耗 |
TokenWindowChatMemory | 保留最近N个Token,并尽可能保留完整消息 | 可精确控制Token数,是生产环境的推荐选择 |
示例:使用TokenWindowChatMemory
import dev.langchain4j.memory.chat.TokenWindowChatMemory;
import dev.langchain4j.model.openai.OpenAiTokenizer;
// 1. 创建基于Token窗口的聊天记忆,最大容量为4000个Token
ChatMemory chatMemory = TokenWindowChatMemory.builder()
.maxTokens(4000, new OpenAiTokenizer("gpt-3.5-turbo"))
.id("user-session-123") // 可为不同用户/会话设置不同ID
.build();
// 2. 模拟对话
chatMemory.add(UserMessage.from("LangChain4j是什么?"));
chatMemory.add(AiMessage.from("LangChain4j是一个Java库..."));
chatMemory.add(UserMessage.from("它支持RAG吗?"));
// 3. 获取记忆中的消息(会自动进行淘汰)
List<ChatMessage> messagesToSend = chatMemory.messages();
System.out.println("即将发送给LLM的消息数量: " + messagesToSend.size());
// 4. 如需持久化,可实现ChatMemoryStore接口并注入到ChatMemory中
// ChatMemoryStore persistentStore = new MyPersistentStore();
// ChatMemory chatMemory = MessageWindowChatMemory.builder()
// .id("user-123")
// .maxMessages(10)
// .chatMemoryStore(persistentStore)
// .build();
MessageWindowChatMemory适合快速验证,TokenWindowChatMemory适合成本敏感的生产环境。ChatMemory只管理发送给LLM的"记忆",不保证存储完整的对话"历史"
以上就是本期关于使用langchain4j实现企业级RAG功能设计的全部内容。如果这篇博客对你有帮助,可以点赞,收藏加关注一下,你们的支持就是我们更新的最大动力。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)