实现高级RAG(Advanced RAG)--RetrievalAugmentor--LangChain4j
高级RAG解决的痛点:
解决基础 RAG(Naive RAG)在面对复杂业务场景时暴露出的“找不到、找不准、用不好”等核心痛点,我们可以把基础 RAG 的痛点归纳为三个阶段:检索前(提问阶段)、检索中(寻找阶段)和检索后(生成阶段)。高级 RAG 正是通过引入更复杂的机制,精准干预了这三个阶段。
Advanced RAG流程(高级 RAG 流程):
RetrievalAugmentor检索增强器
实现高级RAG的核心就是配置RetrievalAugmentor(检索增强器)
在配置AiServices的时候,官方包里有对应的获取retrievalAugmentor方法
package dev.langchain4j.service; public abstract class AiServices<T> { ... public AiServices<T> retrievalAugmentor(RetrievalAugmentor retrievalAugmentor) { if (this.contentRetrieverSet) { throw IllegalConfigurationException.illegalConfiguration("Only one out of [retriever, contentRetriever, retrievalAugmentor] can be set"); } else { this.retrievalAugmentorSet = true; this.context.retrievalAugmentor = (RetrievalAugmentor)ValidationUtils.ensureNotNull(retrievalAugmentor, "retrievalAugmentor"); return this; } } ... }所以可以直接注入AIservice:
注意:这里“MyAiService”必须是接口,LangChain4j 内部使用了 Java 的动态代理
当你调用AiServices.builder(MyService.class).build()时,自动为你生成了一个代理实现类。
/** LangChain4j 的 AiServices 原生支持响应式流。 如果返回值是 Flux<String>, LangChain4j 会自动将其识别为流式传输(Streaming) */ public interface MyAiService{ public Flux<String> streamChat(自定义参数); public String chat(自定义参数); } =============================================================================== MyAiService aiService = AiServices.builder(MyAiService.class) .... .retrievalAugmentor(retrievalAugmentor)//放到这里 .build();如何配置 RetrievalAugmentor?
RetrievalAugmentor主要组件:
可以通过 LangChain4j 配合以下核心组件实现
| QueryTransformer | 优化检索词 | 解决“用户不会提问”问题 |
| QueryRouter | 查询路由 | 允许同一个系统同时向不同的介质(向量库、关系型数据库、Web 搜索引擎)发起检索 |
| ContentRetriever | 检索器 | |
| ContentAggregator | 聚合检索结果(重排序 | 对这些杂乱的数据进行合并、去重、筛选和重新排序。 |
| ContentInjector | 将检索到的内容注入 prompt | “高质量上下文” |
代码实现:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package dev.langchain4j.rag; public interface RetrievalAugmentor { AugmentationResult augment(AugmentationRequest var1); } | V public class DefaultRetrievalAugmentor implements RetrievalAugmentor{...} ===============================上面是源码========================================== | V RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder() .queryRouter(...) .queryTransformer(...) .contentAggregator(...) .contentInjector(...) .build();1.QueryTransformer(优化检索词)
langchain4j包中实现查询转换的接口
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package dev.langchain4j.rag.query.transformer; import dev.langchain4j.rag.query.Query; import java.util.Collection; public interface QueryTransformer { Collection<Query> transform(Query var1); }支持多种场景:
- 查询改写(Query Rewriting)- 把一个查询改写成另一个更好的形式
- 查询扩展(Query Expansion)- 把一个查询扩展成多个相关查询
- 查询压缩(Query Compression)- 从对话历史中提取核心查询
- 查询分解(Query Decomposition)- 把复杂查询拆分成多个子查询
代码实现:
//核心就是编写个专门用来优化检索词的prompt
//核心就是编写个专门用来优化检索词的prompt private static final PromptTemplate LG_AGENT_PROMPT = PromptTemplate.from(""" 编写优化检索词的提示词 """); public class MyQueryTransformer implements QueryTransformer { @Override public Collection<Query> transform(Query query) { // 你的转换逻辑 ... } }MyQueryTransformer queryTransformer = new MyQueryTransformer(); RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder() .queryRouter(...) .queryTransformer(queryTransformer) // 这里 .contentAggregator(...) .contentInjector(...) .build();放进去后,当用AIservice调用接口中的方法时: 自动调用 queryTransformer.transform(query)
2、QueryRouter(查询路由)
langchain4j包里的queryRouter方法:
package dev.langchain4j.rag; public class DefaultRetrievalAugmentor implements RetrievalAugmentor { ... public static class DefaultRetrievalAugmentorBuilder { ... public DefaultRetrievalAugmentorBuilder queryRouter(QueryRouter queryRouter) { this.queryRouter = queryRouter; return this; } ... } }接收的是QueryRouter类型的参数,这是个接口:
package dev.langchain4j.rag.query.router; public interface QueryRouter { Collection<ContentRetriever> route(Query var1); }直接实现这个接口:
核心:构建prompt,让llm返回该问题属于哪种场景:
常见的 RAG 查询路由场景:
1.向量检索:语义相似性、模糊匹配、概念理解
2.关键词检索:精确匹配、专有名词、编号、错误码
3.结构化查询:聚合、统计、范围查询、排序
4.图查询(Graph/Neo4j)
根据返回的场景从所有检索器(Collection<ContentRetriever>)中筛选出 对应的 检索器
代码实现:
public class MyQueryRouter implements QueryRouter { private static final PromptTemplate QUERY_ROUTE_PROMPT = PromptTemplate.from(...) @Override public Collection<ContentRetriever> route(Query query) { //路由问题,获取结果 //判断执行哪个检索器 } }RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder() .queryRouter(new MyQueryRouter(检索器集合...)) .queryTransformer(queryTransformer) .contentAggregator(...) .contentInjector(...) .build();3.ContentAggregator(聚合检索结果(去重、重排序))
核心是:RAG重排序
什么是重排序?
重排序(Reranking)是在通过混合检索(或其他方式)获得初步检索结果(候选文本块)后,再通过更強的模型(通常是Cross-Encoder或专用Reranker模型)对这些候选文本块进行重新打分和排序,将真正最相关、最有价值的内容排在前面。
重排序作用:
过滤噪声:初步检索往往会召回数十条文档,其中可能混杂着相关性较低的"噪声"内容。
提升答案精度,
优化上下文窗口利用率:把最相关的放前面
实现方式:
| 原理 | 使用 | |
| RRF(Reciprocal Rank Fusion,倒数排名融合) | 不看各个检索模型给出的绝对评分(Score),只看文档在各个检索结果列表中的相对排名(Rank),并通过倒数求和的方式进行多路召回结果的融合。 | 当向DefaultContentAggregator传入多个检索器或使用了QueryTransformer导致产生了多个查询时,LangChain4j 内部就会自动触发 RRF 算法为你融合排名 |
| ReRank模型 | 基于语义模型重新打分使用深度学习模型计算相关性 | 三种实现方式: 1.用云服务API eg:Cohere 2.去Hugging Face下载到本地 3.将重排模型独立部署(Hugging Face的TEI) |
用哪个?
| 只用RRF | DefaultContentAggregator(内置RRF) |
| 只用ReRank | 自己实现聚合器,调用scoringModel |
| 二者兼得 | ReRankingContentAggregator |
项目里先用 RRF 将多路检索(如向量+全文)的结果融合成一路,再用 ReRank 模型进行高精度的深度打分。
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package dev.langchain4j.rag.content.aggregator; public interface ContentAggregator { List<Content> aggregate(Map<Query, Collection<List<Content>>> var1); }package dev.langchain4j.rag.content.aggregator; public class ReRankingContentAggregator implements ContentAggregator { ... }LangChain4j 官方已经内置了ReRankingContentAggregator。 只需要直接通过ReRankingContentAggregator.builder().scoringModel(...).build()就能实现重排序。
// 聚合器 ContentAggregator contentAggregator = new ProgressAwareContentAggregator( ReRankingContentAggregator.builder() .scoringModel(scoringModel)//ReRank模型 .maxResults(5) .build());RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder() .queryRouter(new MyQueryRouter(检索器集合...)) .queryTransformer(queryTransformer) .contentAggregator(contentAggregator) .contentInjector(...) .build();4、ContentInjector (将检索到的内容注入 prompt)
直接用的 LangChain4j 的 DefaultContentInjector。
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package dev.langchain4j.rag.content.injector; public class DefaultContentInjector implements ContentInjector { .... public DefaultContentInjector() { this(DEFAULT_PROMPT_TEMPLATE, (List)null); } public DefaultContentInjector(PromptTemplate promptTemplate, List<String> metadataKeysToInclude) { this.promptTemplate = (PromptTemplate)Utils.getOrDefault(promptTemplate, DEFAULT_PROMPT_TEMPLATE); this.metadataKeysToInclude = Utils.copy(metadataKeysToInclude); } }只需要:放进RetrievalAugmentor 里就行,底层自动完成,内容注入
//将检索到的内容注入到 LLM 的 Prompt 中 ContentInjector contentInjector = new DefaultContentInjector();RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder() .queryRouter(new MyQueryRouter(检索器集合...)) .queryTransformer(queryTransformer) .contentAggregator(contentAggregator) .contentInjector(contentInjector) .build(); // 注入到 AI Service 中 MyAiService aiService = AiServices.builder(MyAiService.class) ... .retrievalAugmentor(retrievalAugmentor) .build();