Agent实战 #03:RAG模块从理论到工程落地
2026/7/25 17:41:20 网站建设 项目流程

Agent实战系列|RAG模块:从理论到工程落地(本文)


RAG 不是新东西,但把它做到生产可用,难度远超大多数人的预期。本文基于 Dream-SaaS 项目的 RAG 模块实战经验,从架构设计、混合检索、分块策略到 Prompt Engineering,拆解一个 Java Agent 系统中 RAG 模块的真实落地过程——包括踩过的坑和做过的取舍。

为什么要自己做 RAG 模块

大模型有三个绕不开的问题:知识过时、缺乏私有知识、幻觉。RAG(Retrieval-Augmented Generation)的核心思想很简单——"先检索,再回答",相当于给 LLM 开卷考试。

道理简单,做起来不简单。在 Dream-SaaS 这个 Java Agent 系统中,RAG 面临一组现实约束:

  • Java 生态缺乏成熟的 RAG 框架(Python 有 LangChain、LlamaIndex,Java 没有对标物)
  • 企业内部知识库格式混杂(PDF、Word、Markdown),解析质量参差不齐
  • 检索质量直接决定回答质量,纯向量召回在专有名词场景下表现糟糕
  • 需要与 Agent 编排层解耦,同时又要提供足够的扩展点

这些问题不是选一个框架就能解决的。最终的决定是:基于 Spring AI 自建 RAG 模块,分两层架构实现。


两层架构:为什么这么拆

Dream-SaaS 的 RAG 模块被拆成了两个独立的微服务:

模块职责关键能力
dream-ai-rag独立 RAG 微服务向量存储、文档解析、关键词检索(ILIKE)、基础索引
dream-ai-agent-rag-serviceAgent 集成层混合检索(BM25+RRF)、Rerank、Prompt 组装、评测对比

另有一个dream-ai-agent-ragdoc模块承载前沿探索(GraphRAG、AgenticRAG)。

这个拆分的核心逻辑是职责分离

  • dream-ai-rag做的是"脏活累活":文档解析、切片、向量化、存储。它的接口通过RagLearningPort暴露,是一个简单的门面
  • dream-ai-agent-rag-service做的是"精细活":混合检索、多路打分融合、Rerank 精排、检索质量评测。它通过RagFragmentRetriever接口承载完整管线

为什么不合一?因为检索策略的迭代速度远快于存储层。你可能每周都在调 BM25 权重、Rerank 模型、RRF 参数,但不可能每周重构向量存储。拆开之后,改检索策略不影响底层,换向量数据库也不影响上层。


核心技术点一:混合检索与 RRF 融合

纯向量检索有个致命弱点:专有名词召回率低。用户问"BM25 算法的原理",向量模型可能返回"文本搜索方法"——语义相近但没有提到 BM25。关键词检索恰好相反,精确匹配强但无法理解同义词。

混合检索的思路是两路都要,然后融合。Dream-SaaS 实现了四种检索模式:

模式召回策略适用场景
VECTOR_ONLY仅向量相似度通用语义问答
BM25_ONLY仅 BM25 关键词专有名词/精确匹配
HYBRID向量 + BM25 + RRF 融合需要兼顾语义和关键词
HYBRID_RERANK混合 + DashScope Rerank 精排问答默认模式,召回宽、排序准

这些模式定义在RagRetrievalMode枚举中,是整个检索管线的核心开关。

RRF 融合:让两路分数"说上话"

向量检索输出余弦相似度(0~1),BM25 输出的是无界分数。两者量纲完全不同,怎么融合?**RRF(Reciprocal Rank Fusion)**的巧妙之处在于:不看分数,只看排名。

RRF_score(d) = Σ weight_i × 1 / (k + rank_i(d))

LegacyRagRetrievalAdapter中,核心流程是:

  1. 向量召回:多取一些(fetchK ≥ finalK×2),给融合留余地
  2. BM25 打分:在向量候选集上做进程内 BM25 打分(不是全库关键词检索)
  3. RRF 融合:两路排名按权重累加,得到融合排序
  4. Rerank 精排:调用 DashScope Rerank API 做交叉编码重排
  5. 组装输出:每个 RagFragment 携带四路分数(vectorScore / bm25Score / fusedScore / rerankScore)

注意一个设计取舍:这里的 BM25 是在向量候选集上打分的,不是像 Elasticsearch 那样全库倒排索引。好处是实现简单、速度快;代价是"向量完全没召回的专有名词"补不回来。这是一个典型的工程权衡。


核心技术点二:BM25 与 Rerank 的实现细节

进程内 BM25:轻量但有边界

Bm25Ranker是一个纯 Java 实现的轻量 BM25 打分器,没有依赖外部索引。核心参数:

参数作用
k11.2词频饱和系数,控制 TF 增长的天花板
b0.75长度归一化强度,0=不考虑长度,1=充分惩罚长文档

分词采用简单的"非字母数字切分+小写"策略。对英文效果不错,但中文是短板——连续中文整句可能被当成一个 token。生产环境需要替换为 jieba 或 ICU 分词器。

// Bm25Ranker 的打分核心 for (String term : qTerms) { int f = tf.getOrDefault(term, 0); if (f == 0) continue; // IDF:稀有词权重大 double idf = Math.log(1 + (n - dfi + 0.5) / (dfi + 0.5)); // TF 饱和 + 长度归一 double denom = f + K1 * (1 - B + B * len / avgLen); sum += idf * (f * (K1 + 1)) / denom; }

DashScope Rerank:交叉编码精排

DashScopeTextRerankService负责调用阿里云 DashScope 的 Rerank API。它的设计有两个值得注意的点:

  • 优雅降级:API Key 未配置或调用失败时,返回RerankOutcome.skipped(),保持原始排序,不阻断流程
  • 结构化输出:返回orderedIds(排序后的 ID 列表)+scoresById(每个 ID 的 relevance_score),方便上层按需使用

核心技术点三:分块策略对比

分块策略直接影响检索质量。DocumentChunker支持三种可切换的策略:

策略原理优势劣势
TOKEN按 Token 数切分 + 智能标点截断精确对齐模型上下文窗口无 overlap,可能切断语义
PARAGRAPH按空行分段保留文档自然结构段落长度不可控
SEMANTIC按句子边界聚合到目标长度语义完整性最好句子边界检测可能不准

TOKEN 策略底层用的是 Spring AI 的TokenTextSplitter,它的智能标点截断机制是一个亮点:

// 取前 chunkSize 个 token 后,找最后一个标点截断 if (tokens.size() > chunkSize) { int lastPunct = getLastPunctuationIndex(chunkText); if (lastPunct != -1 && lastPunct > minChunkSizeChars) { chunkText = chunkText.substring(0, lastPunct + 1); } }

这个设计的意义在于:尽量在句号、问号、换行处截断,避免从句子中间切断。实验表明,保持语义边界的分块能让检索召回的片段更完整,最终生成的答案也更连贯。

一个值得注意的问题:TokenTextSplitter默认使用 CL100K_BASE 编码(对齐 OpenAI 模型),并且没有实现 overlap。相邻块之间没有共享内容,在对话上下文场景可能丢失连贯性。如果你的场景需要 overlap,需要自行扩展。


源码亮点:RagFragment 与四路分数设计

整个检索管线的输出统一封装在RagFragment中,它是一个 record,携带了完整的溯源信息和四路分数:

public record RagFragment( String text, // 片段正文 String documentId, // 来源文档 ID String filename, // 来源文件名 Integer chunkIndex, // 块序号 String knowledgeBaseId,// 知识库 ID String chunkStrategy, // 分块策略 Double vectorScore, // 向量相似度分 Double bm25Score, // BM25 关键词分 Double fusedScore, // RRF 融合分(对外主分) Double rerankScore, // Rerank 精排分 Map metadata // 扩展元数据 ) {}

为什么要把四路分数都暴露出来?因为评测需要RagCompareService可以用四种模式各跑一遍,对比同一 query 下不同策略的命中情况和耗时。没有这些分数的透传,检索质量调优就是盲人摸象。


工程踩坑实录

坑一:文档解析质量是隐性瓶颈

项目使用 Apache Tika 做文档解析(TikaDocumentReader),支持近百种格式。但实际体验中:

  • PDF 的表格、多列布局经常导致文本提取顺序混乱
  • BodyContentHandler(-1)不限制字符数,超大文档有 OOM 风险
  • 元数据(作者、页码)没有被暴露给下游Document,只注入了 source 字段

应对方案:对 PDF 场景考虑使用 Spring AI 的PagePdfDocumentReaderParagraphPdfDocumentReader替代通用 Tika 解析。

坑二:BM25 的中文分词问题

前文提到Bm25Ranker的 tokenize 方法对中文不友好。一个真实的案例:查询"向量数据库选型",可能被当成一个 token 处理,导致 BM25 完全失效。

当前缓解方式:dream-ai-rag 模块里另有一条 ILIKE 扫描通路(KeywordSearchService),用 N-gram 凑召回。但这是两套独立的稀疏通路,没有融合。

坑三:检索质量调优缺乏度量

早期没有评测体系时,"检索质量好不好"全靠肉眼看。后来引入了RagEvaluationServiceRagCompareService,配合预置的rag-queries.json测试集,实现了:

  • 四种检索模式的 A/B 对比
  • 每次检索的耗时上报(RagRetrievalMonitorReporter
  • 参考 RAGAS 框架的四维评估(Context Precision / Recall / Faithfulness / Answer Relevance)

前沿探索:AgenticRAG 的状态图编排

dream-ai-agent-ragdoc模块中,已经实现了两个前沿方向的探索代码:

AgenticRAG:自适应检索策略

AgenticRagOrchestrator基于阿里通义 Graph 框架构建了一个状态图(StateGraph),实现了"查询分解 → 多策略检索 → 评估充分性 → 自适应切换"的闭环:

START → [Plan] → [Retrieve] → [Evaluate] ↓ ↓ 不充分→切策略 充分→[Generate] → END

关键设计:检索策略会在 vector → graph → keyword 之间循环切换,最多迭代 3 轮。每一轮都会用RetrievalEvaluator评估检索结果是否充分。

GraphRAG:知识图谱增强

KnowledgeGraphService提供了图结构的检索通路,通过KnowledgeGraphPromptBuilderGraphSubgraphAnswerPromptBuilder将图谱查询结果融入 Prompt。这为多跳推理场景(如"A 公司的 CEO 和 B 公司有什么合作")提供了可能。


架构全景图

最后用一张图总结整个 RAG 模块的数据流:


几点思考

1. RAG 的 80% 工作在数据侧。文档解析质量、分块策略选择、元数据管理——这些"不性感"的工作决定了系统 80% 的上限。模型和算法只是在这个上限内做优化。

2. 混合检索是必选项,不是可选项。纯向量检索在专有名词、编号、代码片段等场景下的召回率不够用。BM25 + RRF 融合的实现成本不高,但收益显著。

3. 评测体系是检索质量的护栏。没有评测的 RAG 系统就是在裸奔。哪怕只是一个简单的测试集 + 四模式对比,也比"肉眼看效果"强十倍。

4. Java 生态的 RAG 基建在补课。Spring AI 提供了基础能力(向量存储、文档解析、分块),但混合检索、Rerank、评测这些生产级能力需要自建。好消息是 Spring AI 的扩展性足够好,自建的成本可控。


RAG 不是银弹,但在当前大模型的能力边界下,它仍然是让 AI 系统"懂业务"最务实的方案。Dream-SaaS 的 RAG 模块还在持续迭代中——AgenticRAG 的自适应检索、GraphRAG 的多跳推理、以及中文分词的优化,都是接下来要重点攻克的方向。

有问题评论区见,欢迎交流~


Agent实战系列导航

编号标题核心内容状态
#00从0到上线14个Agent,4C8G部署全记录14个AI工具上线、极简部署、避坑清单✅已发
#01Dream-SaaS 整体架构拆解28 个 Java 模块 + 5 个独立部署 App 的宏观架构✅已发
#02一个 Agent 的 5 层工程化能力Code Review Agent 从 Demo 到生产的工程细节✅已发
#03RAG 模块:从理论到工程落地混合检索、分块策略、四路分数设计(本文)📝本文
#04多Agent协作:Supervisor DAG编排任务分解→并行调度→checkpoint→HITL→Reflexion自反思🔜即将更新
#05多模态Agent实战Vision LLM 截图理解→Bug工单/设计Token/结构化JSON⏳规划中
#06生产级Agent:可观测+评估+GuardrailsOpenTelemetry链路追踪、评估体系、输出守卫链⏳规划中

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询