当前位置: 首页 > news >正文

07-advanced-rag-patterns 高级 RAG:查询改写、路由、过滤、重排和来源返回

LangChain4j 进阶实战:第 7 篇,高级 RAG 模式,压缩、路由、过滤、重排和来源返回

1. 为什么基础 RAG 还不够

基础 RAG 的流程是:

用户问题 -> 向量检索 -> 取 TopK -> 塞给模型 -> 生成回答

这个流程能跑通,但在真实业务里很快会遇到问题:

  • 多轮对话里,用户问“他什么时候出生”,检索不到“他”是谁。
  • 知识分散在多个库里,不知道该查哪个。
  • 用户只能看自己有权限的数据。
  • 检索出来的 TopK 里有不少无关片段。
  • 用户想知道答案来自哪里。
  • 有些问题根本不需要检索,却也走了一遍 RAG,浪费成本。
  • 内部知识库没有最新信息,需要补充网络搜索。

这就是高级 RAG 的价值。

我的总结先放前面:高级 RAG 的本质不是“加更多组件”,而是把检索过程从粗暴 TopK 变成可控流程。你要决定查不查、查哪里、怎么改写问题、怎么过滤、怎么排序、怎么解释来源。

2. Query Compression:解决多轮对话指代问题

实现查询改写的核心写法:

QueryTransformerqueryTransformer=newCompressingQueryTransformer(chatModel);returnDefaultRetrievalAugmentor.builder().queryTransformer(queryTransformer).contentRetriever(contentRetriever).build();

完整案列

**@PostMapping("/chat") public String queryCompressionRagChat(@RequestParam @NotBlank String userMessage) { log.info("收到查询压缩高级 RAG 聊天请求: {}", userMessage); try { // 创建带有查询压缩高级 RAG 功能的聊天服务 ChatService queryCompressionRagChatService = createQueryCompressionRagChatService(); // 使用查询压缩高级 RAG 服务进行聊天 String response = queryCompressionRagChatService.chat(userMessage); log.info("查询压缩高级 RAG 聊天完成,响应长度: {}", response.length()); return response; } catch (Exception e) { log.error("查询压缩高级 RAG 聊天处理失败: {}", e.getMessage(), e); return "查询压缩高级 RAG 聊天处理失败: " + e.getMessage(); } } /** * 创建带有查询压缩高级 RAG 功能的聊天服务 * * @return 配置了查询压缩高级 RAG 的聊天服务 */ private ChatService createQueryCompressionRagChatService() { // 加载并处理文档 RetrievalAugmentor retrievalAugmentor = createQueryCompressionRetrievalAugmentor(); return AiServices.builder(ChatService.class) .chatModel(chatModel) .retrievalAugmentor(retrievalAugmentor) .chatMemory(MessageWindowChatMemory.withMaxMessages(CHAT_MEMORY_SIZE)) .build(); } /** * 创建查询压缩检索增强器 * * @return 查询压缩检索增强器 */ private RetrievalAugmentor createQueryCompressionRetrievalAugmentor() { // 加载文档 Document document = loadDocument(Path.of(DOCUMENT_PATH), new TextDocumentParser()); // 创建嵌入模型 EmbeddingModel embeddingModel = new BgeSmallEnV15QuantizedEmbeddingModel(); log.info("使用 BgeSmallEnV15QuantizedEmbeddingModel 嵌入模型"); // 创建嵌入存储 EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>(); // 使用 EmbeddingStoreIngestor 简化文档摄入过程 EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() .documentSplitter(DocumentSplitters.recursive(SEGMENT_SIZE, SEGMENT_OVERLAP)) .embeddingModel(embeddingModel) .embeddingStore(embeddingStore) .build(); // 摄入文档 ingestor.ingest(document); log.info("已使用 EmbeddingStoreIngestor 摄入文档,段大小: {},重叠: {}", SEGMENT_SIZE, SEGMENT_OVERLAP); // 创建查询转换器(查询压缩) QueryTransformer queryTransformer = new CompressingQueryTransformer(chatModel); log.info("使用 CompressingQueryTransformer 进行查询压缩"); // 创建内容检索器 ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .maxResults(MAX_RESULTS) .minScore(MIN_SCORE) .build(); log.info("创建内容检索器,最大结果: {},最小分数: {}", MAX_RESULTS, MIN_SCORE); // 创建检索增强器 return DefaultRetrievalAugmentor.builder() .queryTransformer(queryTransformer) .contentRetriever(contentRetriever) .build(); }**

2.1 原理

多轮对话中,用户经常使用指代:

用户:John Doe 有哪些贡献? AI:John Doe 是一位科学家... 用户:他什么时候出生?

如果直接拿“他什么时候出生?”去检索,向量库可能不知道“他”是谁。

Query Compression 会结合历史对话,把问题改写成:

John Doe 什么时候出生?

这就是查询压缩或查询改写。

2.2 适用场景

  • 多轮问答。
  • 用户经常说“刚才那个”“它”“这个方案”。
  • 客服对话。
  • 文档问答连续追问。
  • 智能衣橱里用户追问“那换成休闲一点呢?”

2.3 我的建议

Query Compression 很实用,但会增加一次模型调用。建议只在多轮会话中启用,单轮 FAQ 可以不启用。

3. Query Routing:解决多知识库选择问题

实现知识库路由的核心写法:

Map<ContentRetriever,String>retrieverToDescription=newHashMap<>();retrieverToDescription.put(biographyContentRetriever,"biography of John Doe");retrieverToDescription.put(termsOfUseContentRetriever,"terms of use of car rental company");QueryRouterqueryRouter=newLanguageModelQueryRouter(chatModel,retrieverToDescription);returnDefaultRetrievalAugmentor.builder().queryRouter(queryRouter).build();

案例

public class QueryRoutingAdvancedRagController { private final OpenAiChatModel chatModel; // 硬编码的配置值 private static final String BIOGRAPHY_DOCUMENT_PATH = "./documents/biography-of-john-doe.txt"; private static final String TERMS_OF_USE_DOCUMENT_PATH = "./documents/miles-of-smiles-terms-of-use.txt"; private static final int SEGMENT_SIZE = 300; private static final int SEGMENT_OVERLAP = 0; private static final int MAX_RESULTS = 2; private static final double MIN_SCORE = 0.6; private static final int CHAT_MEMORY_SIZE = 10; /** * 查询路由高级 RAG 聊天接口 - 基于文档检索的聊天(包含查询路由) * * @param userMessage 用户消息,不能为空 * @return 基于文档检索的 AI 回复 */ @PostMapping("/chat") public String queryRoutingRagChat(@RequestParam @NotBlank String userMessage) { log.info("收到查询路由高级 RAG 聊天请求: {}", userMessage); try { // 创建带有查询路由高级 RAG 功能的聊天服务 ChatService queryRoutingRagChatService = createQueryRoutingRagChatService(); // 使用查询路由高级 RAG 服务进行聊天 String response = queryRoutingRagChatService.chat(userMessage); log.info("查询路由高级 RAG 聊天完成,响应长度: {}", response.length()); return response; } catch (Exception e) { log.error("查询路由高级 RAG 聊天处理失败: {}", e.getMessage(), e); return "查询路由高级 RAG 聊天处理失败: " + e.getMessage(); } } /** * 创建带有查询路由高级 RAG 功能的聊天服务 * * @return 配置了查询路由高级 RAG 的聊天服务 */ private ChatService createQueryRoutingRagChatService() { // 加载并处理文档 RetrievalAugmentor retrievalAugmentor = createQueryRoutingRetrievalAugmentor(); return AiServices.builder(ChatService.class) .chatModel(chatModel) .retrievalAugmentor(retrievalAugmentor) .chatMemory(MessageWindowChatMemory.withMaxMessages(CHAT_MEMORY_SIZE)) .build(); } /** * 创建查询路由检索增强器 * * @return 查询路由检索增强器 */ private RetrievalAugmentor createQueryRoutingRetrievalAugmentor() { // 创建嵌入模型 EmbeddingModel embeddingModel = new BgeSmallEnV15QuantizedEmbeddingModel(); // 为传记创建单独的嵌入存储 EmbeddingStore<TextSegment> biographyEmbeddingStore = embed(Path.of(BIOGRAPHY_DOCUMENT_PATH), embeddingModel); ContentRetriever biographyContentRetriever = EmbeddingStoreContentRetriever.builder() .embeddingStore(biographyEmbeddingStore) .embeddingModel(embeddingModel) .maxResults(MAX_RESULTS) .minScore(MIN_SCORE) .build(); // 为使用条款创建单独的嵌入存储 EmbeddingStore<TextSegment> termsOfUseEmbeddingStore = embed(Path.of(TERMS_OF_USE_DOCUMENT_PATH), embeddingModel); ContentRetriever termsOfUseContentRetriever = EmbeddingStoreContentRetriever.builder() .embeddingStore(termsOfUseEmbeddingStore) .embeddingModel(embeddingModel) .maxResults(MAX_RESULTS) .minScore(MIN_SCORE) .build(); // 创建查询路由器 Map<ContentRetriever, String> retrieverToDescription = new HashMap<>(); retrieverToDescription.put(biographyContentRetriever, "biography of John Doe"); retrieverToDescription.put(termsOfUseContentRetriever, "terms of use of car rent
http://www.jsqmd.com/news/799104/

相关文章:

  • 【Midjourney黑莓印相终极指南】:从零复刻经典胶片质感的7步精准调参法
  • 你还在用v1.0联邦框架?2026奇点大会定义新标准:动态拓扑感知、梯度语义压缩、跨异构设备零信任调度——3小时重构你的隐私计算栈
  • 从散热片到电势差:一个完整案例拆解Fluent热电仿真(含Workbench流程与结果分析)
  • CAD图纸导入Altium Designer避坑指南:为什么你的板框总是对不上?
  • 从零构建:为OpenWrt适配MTK闭源WiFi驱动的完整指南
  • 紧急预警:2026年6月起,Gemini API将强制启用新隐私沙箱协议——现有企业集成方案失效倒计时(附ChatGPT平滑迁移Checklist)
  • AI工具搭建自动化视频生成模型剪枝
  • Transformers库实战指南:从核心原理到生产部署的AI工程实践
  • 暗黑破坏神2存档编辑神器:d2s-editor全面解析与实战指南
  • Rust Tokio 异步运行时深度解析:构建高性能并发应用
  • 不诈骗经济学:什么是有产阶级
  • STM32CubeMX配置USB虚拟串口,为什么我的电脑总提示驱动感叹号?Heap Size避坑指南
  • 基于Docusaurus构建现代化技术文档网站的全流程实战指南
  • i.MX RT1052内存全景图:ITCM、DTCM、OCRAM、FlexSPI该怎么用?一份给开发者的分配指南
  • AutoGPT-Next-Web:一键部署AI智能体的Web界面解决方案
  • Python实现本地化PDF合并工具:基于PyPDF2的高效命令行解决方案
  • 多模态思维链(MCT)首次落地,Claude 3.5 Sonnet支持图像→代码→文档联合推理(附可复现测试用例)
  • 别再手动敲命令了!用图形化向导5分钟搞定WebLogic 12c Domain配置(附生产模式选择避坑)
  • 2026年近期,无锡企业如何甄选可靠的等离子金属表面处理服务伙伴 - 2026年企业推荐榜
  • 别再用虚拟机了!在Win10上直接搞定Rational Rose 2003的终极配置手册
  • PL560-590 nm CdSe/CdSe/ZnS QDs,560-600 nm CdSe/ZnS量子点
  • 【AI面试临阵磨枪-48】GraphRAG、多模态 RAG、自适应 RAG 原理
  • 2026年第二季度河北静音梅花刨冰机采购指南 - 2026年企业推荐榜
  • 2026年当下河北实力井盖厂家解析与直供推荐 - 2026年企业推荐榜
  • 春天,从零开始的开源之旅:我的环境搭建与首次PR踩坑全记录
  • 阿里Java面试参考指南(2026最新版)
  • 多模态自指不动点存在性、收敛性与稳定性理论(世毫九实验室原创理论)
  • 开源入门踩坑实录:新手最常遇到的 8 个问题和解决办法
  • MacBook Pro新手指南:不用虚拟机,从下载Windows 10镜像到分区设置,一步步搞定双系统(含MSDN镜像选择建议)
  • 从音箱分频器到手机触控:聊聊RC电路频率响应在真实产品里的那些事儿