work hard work smart

专注于AI+Java后端开发。 不断总结,举一反三。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

LangChain4j RAG 核心组件与组合方式

Posted on 2026-06-20 15:28  work hard work smart  阅读(11)  评论(0)    收藏  举报

LangChain4j RAG 核心组件与组合方式

在使用 LangChain4j 构建 RAG 应用时,经常会看到一组组件名,比如 RetrievalAugmentorQueryTransformerQueryRouterContentRetrieverContentAggregatorContentInjector

这些组件名看起来很多,容易让人觉得框架封装很深,不知道真实原理是什么。

其实 LangChain4j 并没有改变 RAG 的本质,它只是把 RAG 的几个固定步骤抽象成了标准组件。理解这些组件之后,就能知道一次 RAG 问答到底是怎么执行的。

本文介绍 LangChain4j RAG 的核心组件,以及这些组件如何组合成一个完整的检索增强问答流程。


一、RAG 的真实执行流程

不管使用什么框架,RAG 的核心流程都差不多:

用户问题
   |
   v
问题改写 / 查询增强
   |
   v
选择合适的数据源
   |
   v
执行内容检索
   |
   v
合并、去重、重排序检索结果
   |
   v
把检索结果注入 Prompt
   |
   v
调用大模型生成答案

如果不使用框架,可能会写成类似这样的代码:

String rewrittenQuery = rewrite(userQuestion);

List<Document> vectorDocs = esVectorSearch(rewrittenQuery);
List<Document> fullTextDocs = esFullTextSearch(rewrittenQuery);
List<Document> sqlDocs = text2SqlSearch(rewrittenQuery);
List<Document> graphDocs = graphSearch(rewrittenQuery);

List<Document> finalDocs = mergeAndRerank(
        vectorDocs,
        fullTextDocs,
        sqlDocs,
        graphDocs
);

String prompt = injectContext(userQuestion, finalDocs);

String answer = llm.chat(prompt);

LangChain4j 做的事情,就是把上面这些步骤拆成标准组件,然后通过 RetrievalAugmentor 统一编排。


二、LangChain4j RAG 的整体结构

LangChain4j 中,一个完整的 RAG 流程通常由下面几个组件组成:

RetrievalAugmentor
   |
   |-- QueryTransformer
   |-- QueryRouter
   |       |
   |       |-- ContentRetriever 1
   |       |-- ContentRetriever 2
   |       |-- ContentRetriever 3
   |
   |-- ContentAggregator
   |-- ContentInjector

可以简单理解为:

组件 作用
RetrievalAugmentor RAG 总编排器,负责串起完整检索增强流程
QueryTransformer 问题改写器,对用户问题进行压缩、改写或扩展
QueryRouter 问题路由器,决定当前问题应该走哪些检索器
ContentRetriever 内容检索器,真正从 ES、数据库、向量库、图数据库等数据源检索内容
ContentAggregator 内容聚合器,对多路检索结果进行合并、去重、重排序
ContentInjector 内容注入器,把检索结果注入到最终 Prompt 中

这些组件并不是都负责搜索。真正搜索的是 ContentRetriever,其他组件负责搜索前后的流程编排。


三、RetrievalAugmentor:RAG 总编排器

RetrievalAugmentor 是 LangChain4j RAG 流程的核心入口。

它负责把问题改写、问题路由、内容检索、结果聚合和上下文注入串起来。

常见组合方式如下:

RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
        .queryTransformer(queryTransformer)
        .queryRouter(queryRouter)
        .contentAggregator(contentAggregator)
        .contentInjector(contentInjector)
        .build();

这段代码表示:

  • 使用 queryTransformer 对用户问题进行改写
  • 使用 queryRouter 决定问题应该走哪些检索器
  • 使用 contentAggregator 对多路检索结果进行聚合和重排序
  • 使用 contentInjector 把最终内容注入 Prompt
  • 使用 DefaultRetrievalAugmentor 串起完整 RAG 流程

所以 RetrievalAugmentor 本身不直接查 ES,也不直接查数据库。它更像一个流程控制器。


四、QueryTransformer:问题改写器

QueryTransformer 负责在真正检索之前,对用户原始问题进行处理。

用户的问题往往比较口语化,比如:

这个怎么弄?

如果直接拿这个问题去检索,效果可能不好。问题改写器可以结合上下文,把它改写成更适合检索的问题。

例如改写成:

如何打开汽车零重力座椅?

它的作用可以理解为:

用户原始问题
   |
   v
LLM 改写 / 压缩 / 补全
   |
   v
更适合检索的问题

在 RAG 中,问题改写很重要。因为检索效果很大程度上取决于 query 的质量。


五、QueryRouter:问题路由器

QueryRouter 负责判断一个问题应该走哪些检索器。

在简单 RAG 中,可能只有一个知识库检索器,不需要路由。

但是在复杂系统中,数据源可能有很多种:

  • ES 向量检索
  • ES 全文检索
  • 关系型数据库检索
  • 图数据库检索
  • 外部接口检索
  • 文件系统检索

这时就需要路由器判断:这个问题到底应该去哪里查?

例如可以定义三类路由策略:

路由策略 适合场景 对应检索器
knowledge_base 非结构化知识问答、文档问答、语义搜索 ES / 向量库 / 文档库检索器
relational_db 订单、车辆、用户、库存等结构化数据查询 SQL 检索器
graph_db 实体关系、路径分析、关联查询 图数据库检索器

一个简化的路由器可以这样理解:

class ExampleQueryRouter implements QueryRouter {

    private final ContentRetriever vectorRetriever;
    private final ContentRetriever fullTextRetriever;
    private final ContentRetriever sqlRetriever;
    private final ContentRetriever graphRetriever;

    @Override
    public Collection<ContentRetriever> route(Query query) {
        String strategy = classify(query.text());

        return switch (strategy) {
            case "knowledge_base" -> List.of(vectorRetriever, fullTextRetriever);
            case "relational_db" -> List.of(sqlRetriever);
            case "graph_db" -> List.of(graphRetriever);
            default -> List.of(vectorRetriever, fullTextRetriever);
        };
    }
}

这里的 classify() 可以是规则判断,也可以是调用大模型做意图识别。


六、ContentRetriever:真正执行检索的组件

ContentRetriever 是 LangChain4j RAG 中真正负责检索内容的组件。

不同数据源可以实现不同的 ContentRetriever

检索器 数据源 作用
vectorRetriever ES / 向量数据库 向量检索,召回语义相似内容
fullTextRetriever Elasticsearch 全文检索,召回关键词匹配内容
sqlRetriever 关系型数据库 通过 Text2SQL 查询结构化数据
graphRetriever 图数据库 查询实体关系、路径和图结构
apiRetriever 外部 API 调用业务系统获取实时数据

也就是说,ES 搜索只是 ContentRetriever 的一种实现。

LangChain4j 的 RAG 框架并不关心底层是 ES、MySQL、Neo4j 还是外部接口。只要它实现了 ContentRetriever 接口,就可以被接入到 RAG 流程中。


七、ES 向量检索和全文检索在 RAG 中的位置

在 RAG 系统中,ES 检索经常会分成两路:

ES 向量检索
ES 全文检索

这两路都属于 ContentRetriever


1. ES 向量检索

向量检索的核心流程是:

用户问题
   |
   v
embedding 模型生成问题向量
   |
   v
用问题向量到 ES dense_vector 字段中做相似度搜索
   |
   v
返回语义相似的知识片段

简化代码如下:

ContentRetriever vectorRetriever = ElasticsearchContentRetriever.builder()
        .restClient(restClient)
        .embeddingModel(embeddingModel)
        .indexName("knowledge_chunks")
        .maxResults(5)
        .minScore(0.5)
        .build();

向量检索适合处理表达不同但语义相近的问题。

例如用户问:

ES 能不能做语义搜索?

文档里写的是:

Elasticsearch 支持基于 dense_vector 的相似度召回。

关键词不完全一样,但向量检索可能仍然能召回。


2. ES 全文检索

全文检索的核心流程是:

用户问题
   |
   v
分词
   |
   v
倒排索引查找
   |
   v
BM25 等相关性评分
   |
   v
返回关键词匹配度高的知识片段

简化代码如下:

ContentRetriever fullTextRetriever = ElasticsearchContentRetriever.builder()
        .restClient(restClient)
        .indexName("knowledge_chunks")
        .maxResults(5)
        .build();

全文检索适合处理专有名词、编号、错误码、精确短语等问题。

例如:

ERROR_CODE_10086 是什么意思?

这种问题通常更依赖关键词精确匹配,全文检索会比单纯向量检索更稳定。


八、多路检索:为什么要组合多个 Retriever

单一路径检索往往不够稳定。

只用全文检索,可能遇到这些问题:

  • 同义词召回效果差
  • 用户表达和文档表达不一致时容易漏召回
  • 对自然语言问题不够友好

只用向量检索,也可能遇到这些问题:

  • 对错误码、编号、类名、方法名不够稳定
  • 结果可解释性弱
  • 依赖 embedding 模型质量

所以更常见的做法是多路检索:

用户问题
   |
   +--> ES 向量检索
   |
   +--> ES 全文检索
   |
   +--> SQL 检索
   |
   +--> 图数据库检索

然后再交给 ContentAggregator 做统一处理。


九、ContentAggregator:多路结果聚合和重排序

当一个问题走了多个检索器之后,会得到多批结果。

例如:

ES 向量检索结果
ES 全文检索结果
SQL 检索结果
图数据库检索结果

这些结果不能直接全部塞给大模型,因为可能存在:

  • 重复内容
  • 不相关内容
  • 排序不准确
  • 结果数量太多

所以需要 ContentAggregator 进行聚合。

常见处理方式包括:

  1. 合并多路结果
  2. 根据文档 ID 或 chunk ID 去重
  3. 对分数做归一化
  4. 使用 rerank 模型重新排序
  5. 过滤低分内容
  6. 截取 Top N 条结果

简化代码如下:

ContentAggregator contentAggregator = ReRankingContentAggregator.builder()
        .scoringModel(scoringModel)
        .minScore(0.6)
        .maxResults(5)
        .build();

可以理解为:

多路召回结果
   |
   v
去重 + 过滤 + rerank
   |
   v
最适合注入 Prompt 的 Top N 内容

十、ContentInjector:把检索内容注入 Prompt

ContentInjector 负责把聚合后的检索内容注入到大模型 Prompt 中。

简化代码如下:

ContentInjector contentInjector = new DefaultContentInjector();

它的作用可以理解为:

用户问题 + 检索到的知识片段
   |
   v
组装成最终 Prompt
   |
   v
发送给大模型

例如用户问题是:

如何打开零重力座椅?

检索到的知识片段是:

零重力座椅可通过中控屏座椅菜单开启,也可以通过语音助手控制。

ContentInjector 会把这些内容拼到 Prompt 中,让大模型基于资料回答,而不是完全依赖模型自身记忆。

这也是 RAG 能减少幻觉的关键步骤。


十一、组件组合后的完整执行链路

把这些组件组合起来后,完整流程可以画成这样:

用户问题
   |
   v
QueryTransformer
问题改写 / 查询增强
   |
   v
QueryRouter
判断应该走哪些检索器
   |
   +------------------------------+
   |                              |
   v                              v
ContentRetriever              ContentRetriever
ES 向量检索                    ES 全文检索
   |                              |
   +--------------+---------------+
                  |
                  v
        ContentAggregator
        多路结果聚合、过滤、rerank
                  |
                  v
        ContentInjector
        把检索结果注入 Prompt
                  |
                  v
        ChatModel / StreamingChatModel
        大模型生成答案

如果问题需要查结构化数据,可以路由到 SQL 检索器。

如果问题需要查实体关系,可以路由到图数据库检索器。

如果问题需要查文档知识库,可以路由到向量检索和全文检索。


十二、AiServices 如何使用 RetrievalAugmentor

构建好 RetrievalAugmentor 之后,需要把它交给 LangChain4j 的 AI Service。

示例代码如下:

Assistant assistant = AiServices.builder(Assistant.class)
        .chatModel(chatModel)
        .chatMemoryProvider(chatMemoryProvider)
        .systemMessageProvider(memoryId -> "你是一个专业助手,请基于资料回答问题。")
        .retrievalAugmentor(retrievalAugmentor)
        .build();

这里的关键是:

.retrievalAugmentor(retrievalAugmentor)

它表示:在调用大模型之前,先执行 RAG 检索增强流程。

也就是说,大模型最终看到的不是单纯的用户问题,而是类似这样的内容:

系统提示词
+ 用户问题
+ 检索到的相关资料
+ 历史会话记忆

然后再生成最终答案。

如果使用流式模型,也可以替换成:

Assistant assistant = AiServices.builder(Assistant.class)
        .streamingChatModel(streamingChatModel)
        .retrievalAugmentor(retrievalAugmentor)
        .build();

十三、不要被框架封装迷惑

LangChain4j 的组件名很多,但真实原理并不复杂。

可以把它还原成普通代码:

Query transformedQuery = queryTransformer.transform(originalQuery);

Collection<ContentRetriever> retrievers = queryRouter.route(transformedQuery);

List<Content> contents = new ArrayList<>();
for (ContentRetriever retriever : retrievers) {
    contents.addAll(retriever.retrieve(transformedQuery));
}

List<Content> finalContents = contentAggregator.aggregate(transformedQuery, contents);

Prompt prompt = contentInjector.inject(finalContents, originalQuery);

String answer = chatModel.chat(prompt);

框架只是帮我们把这套流程标准化了。

真正需要关注的是:

  1. 问题有没有被正确改写
  2. 路由器有没有选对检索器
  3. 检索器底层查的是什么数据源
  4. 多路结果有没有正确合并和重排序
  5. 注入到 Prompt 的内容是否准确、足够、不过量

十四、总结

LangChain4j RAG 的核心不是某一个搜索引擎,而是一套检索增强生成的流程抽象。

它把 RAG 拆成了几个核心组件:

QueryTransformer:问题改写
QueryRouter:问题路由
ContentRetriever:内容检索
ContentAggregator:结果聚合和重排序
ContentInjector:上下文注入
RetrievalAugmentor:统一编排

其中,ES 搜索只是 ContentRetriever 的一种实现。

在真实应用中,ES 通常可以分成两路:

ES 向量检索:负责语义相似召回
ES 全文检索:负责关键词匹配召回

它们可以和 SQL 检索、图数据库检索、外部 API 检索一起,由 QueryRouter 选择,再由 RetrievalAugmentor 串联成完整的 RAG 流程。

所以理解 LangChain4j RAG 的关键是:

不要先看框架名字,而是先还原真实流程。

只要抓住这条主线:

改写问题 -> 路由检索器 -> 执行检索 -> 聚合重排 -> 注入 Prompt -> 大模型回答

再复杂的 LangChain4j RAG 封装,也就能看明白了。