项目记录

企业级 RAG 平台架构:一次提问背后的完整链路

从数据契约到评测门禁,梳理企业内部知识问答系统一次提问背后的完整工程链路。

发布于
本文目录 · 5 节

大部分人写 RAG 都停在同一个位置:本地存一个向量库,similarity_search 拿 top3, 塞进 prompt,让模型回答。Demo 阶段这没问题。但只要有人问一句 「这个答案的依据是什么?」,或者「今天更新了制度,为什么明天才生效?」, 这套代码就撑不住了。

这篇文章不讲原理,只讲一件事:一个敢上线的企业内部知识问答系统, 一次提问背后到底跑通了哪些层。读完你应该能说清这个项目的骨架, 也能对照检查自己的 RAG 项目缺了哪一块。

先给结论

能上线的 RAG = 数据契约 + 意图路由 + 混合检索 + 重排 + 置信度与引用 + 版本与隔离 + 门禁验收。

少任何一块,系统都能跑,但都会在某个具体场景下崩掉:

缺失的层什么时候崩
数据契约有人往知识目录里丢了一个没有分类的 PDF,检索时分类过滤直接失效
意图路由用户问「你好」也被送进向量库,召回一堆无关文档,模型硬答
混合检索用户搜具体编号(如「报销单号 R-2024-001」),纯语义检索召不回
重排top20 里正确答案排在第 15 位,被 top_k=5 直接截断
置信度与引用知识库里没有答案,模型照样流畅编造
版本与隔离更新制度后新旧两版同时可召回,同一个人一天内听到两种说法
门禁验收改一行阈值,FAQ 准确率从 92% 掉到 71%,没人发现

一句话描述这个项目

SHUN RAG:面向企业内部知识问答的 LangChain + Milvus Hybrid + FastAPI 平台。 业务已经收敛为单一场景 enterprise_knowledge,覆盖 HR 制度、IT 支持、账号安全和财务报销。

技术栈本身不新鲜,值得看的是它的分层方式。

四层架构

text
第一层  用户入口        static/         问答页 + 管理页 + 状态页
第二层  FastAPI 路由    qa_core/api/    pages / chat / admin / kb_versions
第三层  核心引擎        qa_core/        18 个子包:意图、检索、生成、缓存、治理、质量
第四层  外部依赖        MySQL / Redis / etcd / MinIO / Milvus / LLM 供应商

第一层刻意做得很薄。 前端是原生 HTML/CSS/JS(static/),没有前端框架包袱, 因为它不需要承担业务逻辑,只负责把 WebSocket 事件流渲染成对话。

第二层只做接入。 app.py 全文不到 150 行,只干四件事:建应用、配 CORS 与静态资源、 启动预热、注册路由。意图识别、检索策略、Prompt 拼接、Milvus 查询一律不在这里出现。 入口越薄,「系统里有没有旧链路、有没有技术降级旁路」这件事就越容易确认。

第三层是全部内容所在。 qa_core/ 下的子包各管一件事, 其中主链路是 intent → retrieval → retrieval.strategy → pipeline → prompts:

text
qa_core/
  api/           HTTP / WebSocket 入口
  application/   QAService 编排(API 与 pipeline 之间的唯一通道)
  intent/        规则 + BERT 模型 + 网关仲裁
  retrieval/     Milvus 混合检索、动态检索计划、重排、过滤
  pipeline/      主流程、步骤、置信度、引用、事件流
  prompts/       按意图与风险类别组织的 Prompt 档位
  indexing/      解析、归一化、切分、指纹、增量
  quality/       入库质量门禁与冲突检测
  governance/    知识库版本、数据域隔离
  cache/         L1 进程 + L2 Redis,namespace epoch 失效
  memory/        会话历史与反馈
  graphrag/      GraphRAG 扩展
  observability/ LangSmith Trace

第四层是三个独立服务。 MySQL 存控制面(版本、反馈、会话),Redis 做缓存, Milvus 存向量(依赖 etcd 做元数据、MinIO 做对象存储)。它们由 compose 统一编排, 互相之间没有隐藏依赖。

一次提问经历了什么

text
 1. 浏览器 WebSocket 连到 /api/stream
 2. API 层限流、校验参数
 3. QAService 解析场景,做数据域隔离(tenant / dataset / visibility / role)
 4. 意图识别:先规则收口(问候、越界、直答直接返回),再判断 FAQ / 知识查询 / 追问
 5. 检索计划:意图 + 短问题保护 + 规则分数 + 风险类别 → top_k、阈值、查哪些集合
 6. 查询改写与变体生成(生成 1~3 个等价问法)
 7. 命中 FAQ 快路径 → 精确匹配成功则直出
 8. 文档混合检索:dense + sparse 双路召回,带元数据过滤
 9. CrossEncoder 重排,选出最终上下文
10. 三段置信度计算 + 强制引用来源;证据不足则拒答或提示人工确认
11. LLM 流式生成,逐 token 推给前端
12. 落库:会话历史、检索结果缓存

第 4、5、10 步是这个项目和 toy 项目真正的分界线。

三个值得抄的设计决策

一、业务先收敛,技术再展开。 项目原本是多场景模板,现在主动收敛成单场景 enterprise_knowledge。 HR / IT / 财务只是场景内部的 source 分类,不是三套业务。 收敛之后,规则、评测集、门禁阈值才有稳定的调参基准—— 多场景同时演进时,任何一次调参都是在拆东墙补西墙。

二、启动失败是一种设计。 LLM Key、Milvus、MySQL、本地模型、场景配置、active 知识库版本, 任何一项缺失,服务直接启动失败,而不是降级运行。 同时 BERT、BGE、Reranker 和 Milvus collection 预热完成前不接收流量。 宁可起不来,也不要「看起来能跑但答错」。

三、检索分数不等于答案可信度。 Milvus 返回的 score 只说明候选和 query 的相关性排序, 不说明答案是否被证据支撑。所以这里有生成前证据置信度、生成后支撑度、 最终合并置信度三段计算,并且引用来源是强制校验的。

这个系列拆成 8 篇,跟着一次提问的路径走:

  1. 架构总览与阅读地图(本篇)
  2. 部署、前置校验与启动预热
  3. 知识入库:数据契约、切分与增量指纹
  4. 意图识别、网关仲裁与动态检索策略
  5. Milvus 混合检索与 CrossEncoder 重排
  6. 主链路编排、置信度与「该拒答就拒答」
  7. 两级缓存、知识库版本与多租户隔离
  8. 质量门禁、评测闭环与交付验收 下一篇从最实际的地方开始:怎么把这套东西在本地完整跑起来, 以及那几个容器到底各自在负责什么。