Skip to content

项目全景与架构设计

一句话介绍

DocMind 是一个面向研发团队内部技术文档Agentic RAG + MCP 知识问答系统,解决"文档分散、格式混杂、查找低效"的研发效能痛点。

业务场景

目标用户:中大型研发团队(50-200 人),文档分散在 Confluence、飞书、Git 仓库、本地 PDF 等多个平台。

核心痛点

  1. 新人 onboarding 慢——需要翻遍几十篇文档才能找到一个 API 参数配置,老员工反复被打断
  2. 精确查找难——"Spring Boot 3.4 的 server.shutdown 默认值是什么?"纯向量检索容易被语义相近内容干扰
  3. 文档格式碎片化——架构设计是 PDF,API 文档是 Markdown,运维手册是 Word,会议纪要是网页
  4. 知识孤岛——文档只在 Web UI 能搜,开发者不想离开 IDE

DocMind 的解决方式

痛点方案
精确匹配 API 名/参数/版本号BM25 关键词检索(评测:事实类 Recall +0.20)
语义性问题("微服务拆分原则")向量检索 + RRF 融合
复杂问题跨多篇文档Query Decomposition 拆解 + 并行子问题检索
多格式文档MinerU 统一解析为 Markdown → Markdown-Aware Chunker
IDE 内直接查知识库MCP 标准协议暴露给 Cursor / VS Code
答案不能编造技术细节Self-Reflection 评分 + 条件重写(忠实度 +0.037)

项目定位

不同于传统 RAG 的 "query → 向量检索 → 拼接 → 生成" 固定管线,DocMind 让 LLM 作为 Agent 动态决定调用哪些检索工具、调用几次,并对最终答案进行自反思重写。同时通过 MCP 协议对外暴露工具能力,开发者在 IDE 中可直接调用团队知识库而无需切换到 Web UI。

技术选型与理由

技术选型为什么选它(面试话术)
后端框架Spring Boot 3.4 + Spring AI 1.1.4Spring AI 原生支持 Function Calling,和 MCP Server 集成零额外代码
向量数据库Milvus 2.3支持 COSINE 相似度 + 标量过滤联合查询,适合多知识库场景
全文检索Lucene 8.11 (SmartCN)内嵌式 BM25,不需要额外部署 Elasticsearch,对中文有原生分词支持
重排序DashScope gte-rerankCross-Encoder 级别精排,HTTP API 调用,免部署 Python 推理服务
嵌入模型text-embedding-v3 (1024维)阿里云原生,和 DashScope 统一计费,中文效果好
缓存Redis 7同时承担热点缓存 + 跨会话用户记忆两个角色,减少组件数量
对象存储MinIOS3 兼容,私有化部署,文档原件持久化
文档解析MinerU 云端 API(PDF / PPT / 图片 / 网页 URL 统一入口)+ Apache PDFBox(PDF 兜底)+ Apache POI(DOCX)layout-aware 多栏 / 表格 / 公式 / OCR / 网页正文抽取;统一输出 Markdown 后进入 RAG,下游链路无需感知格式差异
前端Vue 3 + TypeScript + Element Plus类型安全 + 成熟 UI 组件库,开发效率高

整体架构

用户提问


┌──────────────────────────────────────────────────────┐
│ DocMindAgent (ReAct 编排器)                           │
│                                                      │
│  ① Query Understanding (LLM 一次调用:改写+分类+拆解) │
│  ② Scope Routing (Tier-0 规则 + Tier-1 LLM 范畴判定) │
│  ③ Path Decision + Tool Selection (规则引擎,零LLM)   │
│  ④ Supervisor-Worker Retrieval (多策略编排)           │
│     ┌────────────────────────────────────────┐       │
│     │ Supervisor (编排决策层)                  │       │
│     │  • IterationDecider — 继续/切策略/终止  │       │
│     │  • ObservationEvaluator — 质量感知降级  │       │
│     │  • PlanGenerator + PlanExecutor (复杂)  │       │
│     ├────────────────────────────────────────┤       │
│     │ Workers (执行层)                        │       │
│     │  ├─ RetrievalWorker (Vector+BM25+RRF)  │       │
│     │  ├─ WebWorker (Tavily)                 │       │
│     │  ├─ MemoryWorker (Redis)               │       │
│     │  └─ AnalysisWorker (多文档对比)         │       │
│     └────────────────────────────────────────┘       │
│  ⑤ Safety Check + Prompt Assembly                    │
│  ⑥ LLM Streaming + Self-Reflection + CRAG 评分      │
└──────────────────────────────────────────────────────┘


SSE 流式响应 → 前端逐步渲染

核心设计决策

1. 为什么用 Agent 而不是固定流水线?

问题:技术文档问答场景中查询类型差异极大——"Spring Boot 3.4 的 server.shutdown 默认值"需要精确 BM25 匹配,"微服务拆分的最佳实践"需要语义检索 + 大召回量,"昨天故障的根因分析"需要联网查最新信息。固定管线无法兼顾。

方案:Supervisor-Worker 多 Agent 架构。SupervisorAgent 根据查询分类结果选择执行策略——简单/中等查询走迭代 ReAct 循环(IterationDecider 控制终止条件),复杂查询走 Plan-and-Execute 模式(PlanGenerator 生成多步计划,PlanExecutor 按依赖拓扑并行执行)。4 个 Worker(Retrieval/Web/Memory/Analysis)封装具体执行能力。

降级:多层降级链——LLM 工具调用失败 → 规则路由;召回为空 → WebWorker 补充;召回分数低 → HyDE 增强(打桩);多文档冲突 → AnalysisWorker 分析;PlanGenerator 失败 → 2 步 fallback 计划。

2. 为什么同时用向量检索和 BM25?

问题:技术文档中充满精确标识符——API 名、参数名、版本号、错误码。纯向量检索对"text-embedding-v3"可能匹配到所有 embedding 相关内容,无法精确定位。纯 BM25 对"怎么优化检索效果"这种概念性问题又无能为力。

方案:混合检索 + RRF 融合。评测数据:事实类(含 API 名/参数)Recall@5 从 0.73(纯向量)提升到 0.93(+BM25 融合),BM25 贡献了 +0.20。

3. 为什么需要自反思重写(Self-Reflection)?

问题:技术文档场景下 LLM 编造不存在的 API 参数或错误的配置值是致命的——开发者如果照着错误配置部署,可能引发线上事故。

方案:生成后小模型从四维度(事实一致性/完整性/来源匹配/表达质量)评分;不通过时将审查发现的具体问题注入 rewrite prompt,主模型重新流式生成修正版。评测数据:忠实度 +0.037,被重写的 8 条查询中包括修正了 Milvus HNSW 参数描述错误、补回了索引刷新关键步骤等。

4. MCP 协议的价值是什么?

问题:开发者日常工作在 IDE 中,切换到 Web UI 查文档打断心流。知识库能力被锁在一个入口里。

方案:通过 Spring AI MCP Server 将 doc_search、keyword_search 等工具标准化暴露。实际验证:用 Cursor IDE 作为 MCP 客户端对接,开发者在编辑器内直接用自然语言查询团队知识库,检索结果作为编程辅助的上下文来源。同一套工具代码既服务内部 Agent 循环,也对外暴露标准接口,零代码重复。

5. 为什么 Scope Routing 用两层设计?

问题:Phase 2 的意图分类(factoid / procedural / comparison 等)默认每种都要查知识库,导致"总结上面的对话"这类元对话被错误送入检索流程,召回完全无关切片。

方案:两层范畴判定——Tier-0 用纯正则(MetaIntentDetector)零 LLM 成本识别高确定性模式(问候/致谢/显式对话引用),Tier-1 复用 QueryUnderstanding 的同一次 LLM 调用输出 scope 字段。任何一层失败默认走 KNOWLEDGE_QUERY,保证向后兼容。

为什么不只用 LLM? Tier-0 正则能处理约 20% 的非知识查询("你好/谢谢/总结上面"),这些模式确定性极高,用 LLM 是浪费。两层叠加:高确定性的零成本处理,模糊边界的才调 LLM。

6. 为什么 Path Decision 用规则引擎不用 LLM?

问题:工具选择(doc_search / keyword_search / web_search / recall_memory)本质是 4 个布尔信号(是否模糊、是否精确、是否时效、是否需要记忆)到 N 个工具的确定性映射。

方案:RetrievalPlanner 纯规则引擎,4 个 if 判断 <1ms。比 LLM Function Calling(~300ms、结果不稳定、不可单元测试)更适合这个规则空间极小的场景。PathDecision record 统一输出路径决策,reason 字段记录机器可读的判定原因,写入 trace 可追溯。

项目规模

维度数值
后端 Java 文件143 个(~18,200 行)
前端 Vue/TS 文件35 个(~6,900 行)
核心 RAG 组件29 个文件
数据库表14 张(7 张核心业务 + 7 张离线评测)
动态配置参数39 个(热配,无需重启)
Prompt 模板8 个
MCP 工具5 个 / 6 端点(内外双路径)
优化迭代20 次,每次有评测数据
评测数据集52 条 × 7 类 × 4 档 × 3 轮

完整文件级地图见 14-项目工程全景.md

部署架构

┌─── docker-compose.dev.yml ──────────────────────┐
│  Redis 7     :6379    缓存 + 用户长期记忆        │
│  MinIO       :9000    S3 兼容文档原件存储         │
│  etcd        :2379    Milvus 元数据              │
│  Milvus      :19530   向量数据库(COSINE, HNSW) │
└─────────────────────────────────────────────────┘
  MySQL 8      :3306    业务数据 + BM25 FULLTEXT(独立运行)
  Spring Boot  :8080    后端 API + MCP Server
  Vue 3 + Vite :5173    前端(proxy /api → 8080)

外部 API:DashScope(LLM + Embedding + Rerank)/ MinerU(文档解析)/ Tavily(搜索)/ Langfuse(OTel 协议全链路 trace)

数据库设计概要

核心设计决策
kb_chunkcontent_hash(SHA-256 增量索引)+ parent_chunk_id(Parent Document Retrieval)+ vector_id(MySQL↔Milvus 1:1)+ FULLTEXT INDEX(BM25 预召回)
kb_knowledge_baseversion(chunk 变更自增,语义缓存失效依据)+ status 异步处理状态机
qa_message4 个 JSON 字段结构化存储推理过程:sources / agent_trace / mcp_calls / reflection_log + confidence_score / confidence_band
sys_ai_config39 个热配参数(6 组:rag/llm/cache/safety/功能开关/agent),admin 面板实时调整,ConcurrentHashMap + AtomicReference 无锁更新
sys_userRBAC + BCrypt 密码 + JWT 24h/7d 双 token
eval_*(7 张评测表)离线评测框架:数据集版本管理 + 候选生成 + LLM-as-Judge 自动评分,支持对照实验
mcp_tool_registry工具调用统计(call_count + avg_latency_ms)

完整 schema 设计见 14-项目工程全景.md

面试 Q&A

Q: 这个项目的业务场景是什么?解决什么问题?

A: 面向研发团队的内部技术文档问答。痛点是文档分散在 PDF/Markdown/Word/网页多种格式中,新人 onboarding 需要翻遍几十篇文档找一个配置参数,老员工反复被打断。核心目标是让任何人用自然语言提问,系统精确定位到相关文档段落,并给出有来源引用的答案——在 IDE 里也能直接查。

Q: 项目最大的技术挑战是什么?

A: 两个层面。第一是精确性要求高——技术文档场景下,API 参数、版本号、配置值必须精确,不能"大意对"。所以我做了混合检索(BM25 精确匹配 + 向量语义),再加 Self-Reflection 审核事实一致性,评测显示忠实度从 0.812 提升到 0.849。第二是LLM 不稳定性——方案是"双轨设计",LLM 工具调用失败时自动降级到规则引擎,覆盖 85% 查询场景。

Q: 和市面上的 RAG 方案(如 Dify、RAGFlow)有什么区别?

A: 三个维度:①检索不是固定管线而是 Agent 动态决策——不同类型查询自动走不同检索配方;②答案生成后有 Self-Reflection 条件重写——不只是打分,实际修正答案;③通过 MCP 标准协议暴露给 IDE——开发者不需要离开编辑器就能查知识库,这是 Web-only 方案做不到的。

Q: 为什么不直接用搜索引擎 / Confluence 搜索?

A: 传统搜索返回的是文档链接列表,开发者还要自己点开、定位段落、理解上下文。DocMind 直接返回结构化答案 + 具体段落引用 + 页码,并且支持跨文档综合——比如"对比 IVF 和 HNSW 哪个更适合我们的场景"需要综合两篇不同文档的内容,Query Decomposition 把它拆成两个子问题分别检索再合并回答。

Q: 项目规模多大?是一个人做的吗?

A: 全栈独立开发。后端 143 个 Java 文件(~18,200 行),前端 35 个 Vue/TypeScript 文件(~6,900 行)。14 张数据库表,39 个动态配置参数,8 个 Prompt 模板。经历了 6 个 Phase、20 次评测驱动的迭代优化,每次都有量化对比数据。

Q: 部署架构是什么?

A: 本地开发用 docker-compose 拉起 5 个服务(Redis、MinIO、etcd、Milvus standalone、Milvus attu),MySQL 8 独立运行。后端 Spring Boot 8080 端口,前端 Vite dev server 5173 端口 proxy 到后端。外部依赖是阿里云 DashScope(LLM + Embedding + Rerank 统一计费)、MinerU(可选,多模态文档解析)、Tavily(可选,联网搜索)、Langfuse(可选,可观测性)。所有外部依赖都有降级方案——关掉也能跑。

Q: 数据库怎么设计的?

A: 14 张表(7 张核心业务 + 7 张评测),核心是 kb_chunk。几个设计亮点:第一是 content_hash(SHA-256),100 页文档改 1 字只需 ~1 次 embedding 而非 200-400 次。第二是 parent_chunk_id 外键支撑 Parent Document Retrieval——400 字子块进 Milvus 保精度,1500 字父块只存 MySQL 保上下文。第三是 qa_message 的 4 个 JSON 字段把推理过程结构化存储(agent_trace / mcp_calls / reflection_log / sources),前端可完整回放每步推理。第四是 kb_knowledge_base.version 字段——chunk 变更自增,语义缓存通过比对 version 判断失效。