外观
项目全景与架构设计
一句话介绍
DocMind 是一个面向研发团队内部技术文档的 Agentic RAG + MCP 知识问答系统,解决"文档分散、格式混杂、查找低效"的研发效能痛点。
业务场景
目标用户:中大型研发团队(50-200 人),文档分散在 Confluence、飞书、Git 仓库、本地 PDF 等多个平台。
核心痛点:
- 新人 onboarding 慢——需要翻遍几十篇文档才能找到一个 API 参数配置,老员工反复被打断
- 精确查找难——"Spring Boot 3.4 的
server.shutdown默认值是什么?"纯向量检索容易被语义相近内容干扰 - 文档格式碎片化——架构设计是 PDF,API 文档是 Markdown,运维手册是 Word,会议纪要是网页
- 知识孤岛——文档只在 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.4 | Spring AI 原生支持 Function Calling,和 MCP Server 集成零额外代码 |
| 向量数据库 | Milvus 2.3 | 支持 COSINE 相似度 + 标量过滤联合查询,适合多知识库场景 |
| 全文检索 | Lucene 8.11 (SmartCN) | 内嵌式 BM25,不需要额外部署 Elasticsearch,对中文有原生分词支持 |
| 重排序 | DashScope gte-rerank | Cross-Encoder 级别精排,HTTP API 调用,免部署 Python 推理服务 |
| 嵌入模型 | text-embedding-v3 (1024维) | 阿里云原生,和 DashScope 统一计费,中文效果好 |
| 缓存 | Redis 7 | 同时承担热点缓存 + 跨会话用户记忆两个角色,减少组件数量 |
| 对象存储 | MinIO | S3 兼容,私有化部署,文档原件持久化 |
| 文档解析 | 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_chunk | content_hash(SHA-256 增量索引)+ parent_chunk_id(Parent Document Retrieval)+ vector_id(MySQL↔Milvus 1:1)+ FULLTEXT INDEX(BM25 预召回) |
kb_knowledge_base | version(chunk 变更自增,语义缓存失效依据)+ status 异步处理状态机 |
qa_message | 4 个 JSON 字段结构化存储推理过程:sources / agent_trace / mcp_calls / reflection_log + confidence_score / confidence_band |
sys_ai_config | 39 个热配参数(6 组:rag/llm/cache/safety/功能开关/agent),admin 面板实时调整,ConcurrentHashMap + AtomicReference 无锁更新 |
sys_user | RBAC + 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 判断失效。