news 2026/5/12 0:34:36

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 0:34:08

本地AI伴侣NightClaw:构建具身化、有记忆的智能体框架

1. 项目概述&#xff1a;打造一个真正“活着”的本地AI伴侣如果你和我一样&#xff0c;厌倦了每次打开聊天窗口都要重新自我介绍&#xff0c;也受够了那些冷冰冰、只会执行命令的“智能助手”&#xff0c;那么NightClaw这个项目可能会让你眼前一亮。这不是又一个套壳的ChatGPT应…

作者头像 李华
网站建设 2026/5/12 0:33:36

用IDL+ENVI处理MODIS数据?手把手教你用CASA模型估算区域NPP(附完整代码)

基于CASA模型的区域NPP估算实战指南&#xff1a;从MODIS数据到生产力图谱 遥感生态学研究中最令人兴奋的挑战之一&#xff0c;就是将卫星观测数据转化为可操作的生态指标。作为衡量生态系统健康程度的关键参数&#xff0c;净初级生产力(NPP)的准确估算对于理解碳循环、评估植被…

作者头像 李华
网站建设 2026/5/12 0:31:55

3分钟搞懂LLM、Agent、Skills、MCP,AI圈必备干货!

本文详细解读了AI领域中的四个核心概念&#xff1a;LLM&#xff08;大语言模型&#xff09;作为"大脑"擅长理解和生成文本&#xff1b;Agent作为"项目经理"能自主执行任务并调用工具&#xff1b;Skills是具体的"手脚"&#xff0c;每个技能只做一…

作者头像 李华
网站建设 2026/5/12 0:30:47

榨干Jetson Nano性能:从电源模式、交换空间到虚拟环境的深度调优实战

榨干Jetson Nano性能&#xff1a;从电源模式、交换空间到虚拟环境的深度调优实战 当你的Jetson Nano在运行复杂AI模型时开始频繁卡顿&#xff0c;或是因内存不足而崩溃&#xff0c;这往往不是硬件本身的局限&#xff0c;而是资源管理策略需要优化。本文将揭示一套经过实战验证的…

作者头像 李华
网站建设 2026/5/12 0:29:25

暗黑2网页存档编辑器:5分钟快速上手终极指南

暗黑2网页存档编辑器&#xff1a;5分钟快速上手终极指南 【免费下载链接】d2s-editor 项目地址: https://gitcode.com/gh_mirrors/d2/d2s-editor 想要轻松修改暗黑破坏神2的角色属性和装备吗&#xff1f;d2s-editor是一款功能强大的网页存档编辑器&#xff0c;让你无需…

作者头像 李华