把一个 RAG 项目做成可插拔的知识基础设施
这篇文章介绍了一个模块化RAG(检索增强生成)知识基础设施项目,其核心价值在于工程化实现而非功能演示。项目采用分层架构设计,重点解决了五个关键问题:分层混合检索(结合BM25、稠密向量、RRF融合和重排)、五阶段文档摄取流水线、多模态图像转文本统一处理、基于状态机的对话编排,以及混合会话存储机制。通过组件解耦设计,支持LLM、Embedding等模块的自由替换,并实现了可观测性和评估闭环。项目将R
把一个 RAG 项目做成可插拔的知识基础设施
很多 RAG 项目看起来都像“能回答问题的 Demo”,但真正难的从来不是把答案跑出来,而是把它做成一套可替换、可观测、可回归、可接入 Agent 生态的知识基础设施。
这个项目最值得写进博客的,不是“我用了哪些框架”,而是它把一整套面试里最容易被问穿的问题,落成了工程事实:
- 检索不是单向量召回,而是 BM25 + Dense Embedding + RRF + Cross-Encoder/LLM Rerank 的分层检索
-
- 摄取不是简单读 PDF,而是 五阶段文档摄取流水线:PDF 解析、语义切分、元数据增强、向量化、幂等入库
-
- 多模态不是额外拼接,而是 Vision LLM 图像转文本 后复用同一条纯文本检索链路
-
- 对话不是单接口拼接,而是 FastAPI + LangGraph 的状态机编排,支持意图识别、指代澄清和并行子查询拆分
-
- 稳定性不是靠运气,而是 Redis + MySQL 混合会话存储,把短期热缓存和长期记忆沉淀分层处理
如果你想快速抓住这个项目的本质,可以先记住一句话:
- 稳定性不是靠运气,而是 Redis + MySQL 混合会话存储,把短期热缓存和长期记忆沉淀分层处理
这不是一个“知识库问答系统”,而是一个能把 RAG 能力发布成标准化上下文服务的工程底座。
下面我就围绕这 5 个侧重点来拆解,它们基本就是这篇博客最该讲透的部分。
一、项目的整体目标
这个仓库的目标不是“做一个能回答问题的接口”,而是做一个可插拔、可替换、可观测、可评估的 RAG 平台。它同时服务两类场景:
- 作为知识检索与问答底座,给用户提供稳定的私有知识查询能力。
-
- 作为学习和面试项目,帮助开发者系统理解 RAG 工程化落地的关键环节。
从架构上看,项目大致分成三层:
- 作为学习和面试项目,帮助开发者系统理解 RAG 工程化落地的关键环节。
- 摄取层:把 PDF 等原始文档处理成结构化 chunk
-
- 检索与生成层:完成混合召回、重排、上下文组装和答案生成
-
- 服务层:通过 MCP Server、FastAPI 后端和 Dashboard 对外提供能力
这种分层的好处很明显:每一层都能独立替换和演进,不会把整个系统绑死在某一个模型、某一种向量库或某个前端形态上。
- 服务层:通过 MCP Server、FastAPI 后端和 Dashboard 对外提供能力
二、为什么说它是“模块化 RAG”
很多 RAG 项目的问题,不在于功能能不能跑,而在于后续怎么改。一旦嵌入模型、重排模型、向量库、切分器、LLM Provider 都写死,项目后期几乎没法做实验。
这个仓库的设计思路是把关键组件都抽象成可替换模块:
-
LLM Provider 可切换
-
- Embedding 可切换
-
- Reranker 可切换
-
- Vector Store 可切换
-
- Splitter 和 Transform 也可配置
这意味着你可以很自然地做三类实验:
- Splitter 和 Transform 也可配置
-
模型实验:切换不同 LLM / Embedding / Reranker 看效果
-
- 策略实验:调整 chunk size、overlap、召回 top_k、rrf 参数
-
- 部署实验:在本地、云端或不同存储后端之间迁移
对工程来说,真正重要的是“系统能不能演进”,而不是“第一次能不能跑通”。
- 部署实验:在本地、云端或不同存储后端之间迁移
这篇文章真正要讲的 5 个技术侧重点
如果只停留在“可插拔”四个字,还是不够。这个项目真正像工程底座的原因,是它把下面 5 个容易被忽略但很关键的能力做成了体系,而不是零散功能:
- 分层检索:BM25 负责关键词,Dense 负责语义,RRF 负责融合,Rerank 负责精排
-
- 五阶段摄取:PDF 解析、语义切分、元数据增强、向量化、幂等入库一条链路打通
-
- 多模态复用:Vision LLM 先把图像转成文本,再走统一文本检索管线
-
- 状态机对话:FastAPI + LangGraph 实现意图识别、澄清、并行子查询与生成编排
-
- 混合会话存储:Redis 负责短期热状态,MySQL 负责长期历史沉淀,保证多轮稳定性
这些点比“我支持几个模型”更像真正的系统设计,也更适合在面试里展开。
- 混合会话存储:Redis 负责短期热状态,MySQL 负责长期历史沉淀,保证多轮稳定性
三、摄取链路:从 PDF 到可检索 Chunk
RAG 的质量,首先取决于数据摄取质量。这个项目的 ingest 流程不是简单地“读文件然后切块”,而是尽量把文档变成可检索、可追踪、可增量更新的结构化数据。
核心思路是:
- 文档标准化:把 PDF 统一转换为 Markdown 风格文本,尽量保留标题、段落、列表等结构信息。
-
- 语义切分:使用更适合 Markdown 的分块策略,避免机械切断语义。
-
- Transform 增强:可插入图像描述、元数据补全、chunk refinement 等处理。
-
- Embedding 与 Upsert:把 chunk 写入向量库,同时保留 metadata,方便后续过滤与追踪。
-
- 增量处理:通过文件 hash、历史记录和状态管理,减少重复摄取成本。
这里最关键的不是“用了什么库”,而是一个工程原则:Loader 负责统一格式,Splitter 负责切块,Transform 负责增强,Storage 负责持久化。
- 增量处理:通过文件 hash、历史记录和状态管理,减少重复摄取成本。
这样一来,后面你想替换任何一步,都不会把整条链路炸掉。
再往深一点看,这条链路其实还体现了三个更成熟的工程思路:
- 幂等摄取:通过文件 hash 和历史状态表做零成本跳过,避免重复解析和重复入库
-
- 结构优先:先把 PDF 变成结构化 Markdown,再交给切块器,减少“纯字符切分”对语义的破坏
-
- 多模态复用:把图片先转成文本描述,再复用同一套文本检索链路,不需要为图像单独搭一条复杂管线
四、检索链路:Hybrid Search + Rerank
如果说摄取决定了“库里有什么”,那检索决定了“能不能找到真正有用的东西”。
这个项目采用的是典型的两段式检索:
1. 粗召回
粗召回阶段同时使用:
-
Sparse Search:例如 BM25,适合关键词精确匹配、专有名词和术语检索
-
- Dense Search:基于 embedding 的语义召回,适合同义表达和模糊问法
然后再用 RRF(Reciprocal Rank Fusion)做融合。这样做的目的很直接:
- Dense Search:基于 embedding 的语义召回,适合同义表达和模糊问法
-
BM25 负责“找得准关键词”
-
- Dense 负责“找得懂语义”
-
- RRF 负责“把两种召回优势合并起来”
2. 精排重排
粗召回拿到候选集之后,再用 rerank 做二次排序。这个步骤非常关键,因为它决定了最终上下文是不是“真的相关”。
从面试角度讲,这里最值得讲的不是“我用了重排”,而是你要能解释清楚:
- 为什么只靠向量召回不够
-
- 为什么粗召回和精排要分开
-
- 为什么 rerank 能提升 top-k 质量但会增加推理成本
这也是典型的工程 trade-off:速度、成本、质量三者不能同时最优,只能做平衡。
- 为什么 rerank 能提升 top-k 质量但会增加推理成本
如果把这个环节再往前推进一步,它其实已经不是“检索功能”,而是一个小型的检索决策系统:
- 当问题更像关键词查询时,BM25 检索权重更高
-
- 当问题更像语义问答时,Dense Embedding 检索收益更高
-
- 当候选集已经比较接近时,Cross-Encoder 或 LLM Rerank 决定最终上下文顺序
-
- 当问题包含多个子意图时,先拆成并行子查询,再汇总上下文
这也是为什么这个项目比普通知识库更像“可控的检索引擎”。
- 当问题包含多个子意图时,先拆成并行子查询,再汇总上下文
五、MCP 集成:让知识库能力变成标准工具
这个项目的另一个亮点,是把 RAG 能力通过 MCP 暴露出去,而不是把它锁死在自定义前端里。
这样做的价值很大:
- GitHub Copilot、Claude 等支持 MCP 的客户端可以直接调用
-
- 不需要为知识库单独开发一套聊天 UI
-
- 你可以把知识检索能力当成标准工具,嵌入到更大的 Agent 系统里
从架构上看,MCP 的意义在于把“问答系统”升级成“上下文服务”。
- 你可以把知识检索能力当成标准工具,嵌入到更大的 Agent 系统里
这会带来一个很实用的结果:
你的知识库不再只是一个网站,而是一个可以被各种 AI 客户端调用的能力层。
六、FastAPI + LangGraph:对话编排不是简单的 if-else
项目里还有一个很实用的后端编排层:FastAPI 负责对外 API,LangGraph 负责内部状态流转。
这套设计解决了几个常见问题:
- 会话怎么维护
-
- 多轮对话怎么做记忆
-
- 用户问题是否需要澄清
-
- 子问题是否需要并行检索
-
- 模型超时或异常时怎么降级
实际 workflow 里,大致会经历这些节点:
- 模型超时或异常时怎么降级
- session:加载会话状态和历史记忆
-
- intent:做意图识别和问题重写
-
- clarify:必要时直接澄清,不进入检索
-
- retrieve:调用 MCP 检索知识
-
- generate:组装上下文并生成回答
-
- output:保存结果与记忆摘要
这套结构比“一个接口里写到底”稳得多,因为它把每个步骤的责任拆开了,也更方便埋点、测试和调试。
- output:保存结果与记忆摘要
更重要的是,这种写法让系统具备了几个高级能力:
- 条件路由:意图不明确时直接进入澄清分支,而不是硬把错误问题送进检索
-
- 并行子查询:复杂问题自动拆分后并发检索,缩短总响应时间
-
- 记忆压缩:把近期历史和长期摘要分开管理,降低 prompt 膨胀风险
-
- 容错降级:生成模型出问题时自动切换候选模型,而不是让整条链路失败
如果把这一段再抽象一下,它其实讲的是一个更重要的命题:多轮 RAG 的难点不在“能不能答”,而在“能不能稳定地答对、答快、答得可解释”。
- 容错降级:生成模型出问题时自动切换候选模型,而不是让整条链路失败
如果说传统后端是“请求来了就处理”,那这里更像“请求来了先判断该走哪条工作流”。
七、可观测性和评估:RAG 不能靠感觉调
很多 RAG 项目最后会卡在一个问题上:为什么这次回答好了,下一次又差了?
这个项目的思路是把链路变成可观察对象:
-
ingestion trace:看文档是怎么被处理的
-
- query trace:看检索、融合、重排的全过程
-
- dashboard:把这些信息可视化
-
- evaluation:用评估集和指标去做回归验证
这样调参就不再是“凭感觉改 chunk size”,而是可以围绕具体指标做实验,比如:
- evaluation:用评估集和指标去做回归验证
-
召回率有没有提升
-
- top-k 命中有没有变好
-
- rerank 后的排序是否更合理
-
- 某类文档是不是总被漏掉
如果没有这套闭环,RAG 最后会变成“玄学工程”;有了这套闭环,优化才有方向。
- 某类文档是不是总被漏掉
这个部分其实最能拉开项目层次。因为真正成熟的 RAG 系统,不只是会回答,还要能回答三个问题:
- 为什么这次命中了这个 chunk
-
- 为什么重排前后顺序变了
-
- 为什么某次改动让指标变好了或者变差了
能回答这三个问题,项目才算进入了“可运营”阶段,而不只是“可演示”阶段。
- 为什么某次改动让指标变好了或者变差了
八、这个项目最值得学习的地方
如果你是想把这个项目写进简历或用于面试,我建议你不要只背功能点,而要把它归纳成三句话:
- 我做了一个模块化 RAG 平台,支持摄取、检索、生成、评估的完整链路。
-
- 我把知识库能力通过 MCP 标准化暴露出去,可以直接接入 Copilot 等 AI 客户端。
-
- 我建立了可观测和可评估机制,让 RAG 优化从经验驱动变成数据驱动。
这三句话比“我用了很多框架”更有说服力,因为它体现的是系统设计能力,而不是 API 调包能力。
- 我建立了可观测和可评估机制,让 RAG 优化从经验驱动变成数据驱动。
结语
我理解这个项目的过程里,最大的收获不是“学会了某个库”,而是对 RAG 工程化有了更完整的认识:
- 文档处理要讲结构
-
- 检索要讲召回与排序的平衡
-
- 服务层要讲标准接口与可扩展性
-
- 评估要讲数据闭环
如果把它当作一个学习项目,它能帮你建立 RAG 的全局认知;如果把它当作一个面试项目,它能帮你讲清楚工程设计和权衡取舍;如果把它当作一个底座,它还能继续扩展成 Agent、知识中台或多模态问答系统。
- 评估要讲数据闭环
这也是我认为它最有价值的地方:它不是一个终点,而是一个可以持续演进的起点。
更多推荐




所有评论(0)