Modular RAG MCP Server
Modular RAG MCP Server
一个可插拔、可观测的模块化 RAG(检索增强生成)服务框架。通过 MCP(Model Context Protocol)标准协议对外暴露工具接口,让 Claude、Copilot 等 AI 助手直接调用私有知识库;配套 Streamlit 管理面板与自动化评估体系,实现从文档摄取到在线检索的完整闭环。
📖 目录
🏗️ 项目简介
本项目把 RAG 的核心环节——混合检索(Hybrid Search + RRF)、多模态视觉处理(Image Captioning)、RAG 评估(Ragas + Custom)——与当下热门的应用协议 MCP(Model Context Protocol) 串联为一个完整、可运行的工程系统。
设计定位:
全链路可插拔:LLM / Embedding / Reranker / Splitter / VectorStore / Evaluator 每个能力维度都定义了抽象接口 + 工厂模式,通过
config/settings.yaml一键切换后端,零代码修改即可适配不同 Provider 与部署环境。白盒化可观测:Ingestion 与 Query 两条链路的每一个中间状态(耗时、输入输出、错误)都被
TraceContext记录到logs/traces.jsonl,并通过 6 页面 Streamlit Dashboard 可视化呈现,慢查询诊断不再靠猜。标准协议接入:遵循 MCP 协议(stdio + JSON-RPC 2.0),可直接对接 Claude Desktop、GitHub Copilot、Cursor 等任意 MCP Client,一次开发、多处调用。
数据驱动的质量闭环:集成 Custom(Hit Rate / MRR)与 Ragas(Faithfulness 等)双评估体系,配合 Golden Test Set 回归基线,让检索质量的每次调优都有数据可依。
一段话讲清楚它做什么
丢进一篇 PDF → 系统将其解析、分块、LLM 精炼、生成图片描述、向量化编码、写入多路存储 → 一个可检索的知识库就绪。之后任何 MCP Client 都能用自然语言查询它,系统做 jieba 分词 + Dense/Sparse 并行混合检索 + RRF 融合排序 + 可选重排,最终返回带引用来源的 Markdown 结构化答案,必要时还能附带检索到的图片。
✨ 核心能力
模块 | 能力 | 说明 |
Ingestion Pipeline | PDF → Markdown → Chunk → Transform → Embedding → Upsert | 6 阶段全链路数据摄取,SHA256 幂等去重,支持多模态图片描述 |
Hybrid Search | Dense(向量)+ Sparse(BM25)+ RRF 融合 + 可选 Rerank | 两段式检索架构:粗排召回 + 精排重排 |
MCP Server | 标准 MCP 协议暴露 Tools |
|
Multimodal | Image-to-Text 策略 | Vision LLM 自动为文档图片生成描述并缝合进 Chunk,"搜文字出图" |
Observability | 全链路白盒化追踪 | 双链路(摄取/查询)独立打点,JSON Lines 持久化 |
Dashboard | Streamlit 6 页面管理平台 | 总览 / 数据浏览 / 摄取管理 / 摄取追踪 / 查询追踪 / 评估面板 |
Evaluation | Custom + Ragas 双评估体系 | Golden Test Set 回归测试,拒绝"凭感觉"调优 |
可插拔架构 | 6 大抽象接口 + 工厂模式 | LLM ×4 / Embedding ×3 / Reranker ×2 / Splitter / VectorStore / Evaluator |
🏛️ 系统架构
总体设计
系统由离线摄取与在线查询两大链路构成,共享同一套存储层:
┌─────────────────────────────────────────────────────────────────────────┐
│ Modular RAG MCP Server │
│ │
│ ┌──────────────────────────────┐ ┌──────────────────────────────┐ │
│ │ 离线 Ingestion 链路(摄取) │ │ 在线 Query 链路(查询) │ │
│ │ │ │ │ │
│ │ ① 文件完整性检查 (SHA256) │ │ ① MCP 工具调用 (stdio) │ │
│ │ ② PDF 解析 (MarkItDown) │ │ ② 查询预处理 (jieba 分词) │ │
│ │ ③ 递归分块 (chunk=1000) │ │ ③ 并行混合检索 (Dense+Sparse)│ │
│ │ ④ 变换管线 (Refine+Enrich+ │ │ ④ RRF 融合排序 (k=60) │ │
│ │ Caption) │ │ ⑤ 可选 Rerank │ │
│ │ ⑤ 向量编码 (Dense+Sparse) │ │ ⑥ 响应构建 (Markdown+引用+ │ │
│ │ ⑥ 多路存储 (3 个后端) │ │ 图片) │ │
│ └──────────────────────────────┘ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 存储层:ChromaDB(稠密向量)+ BM25 Index(稀疏索引) │ │
│ │ + ImageStorage(图片索引)+ SQLite(摄取历史/幂等) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘五层分层架构
代码按依赖方向分为五层,层间通过抽象基类解耦,上层依赖下层接口而非具体实现:
┌──────────────────────────────────────────────────────────┐
│ AI 助手(Claude / Copilot / Cursor 等 MCP Client) │
└──────────────────────────┬───────────────────────────────┘
│ MCP Protocol (stdio JSON-RPC 2.0)
┌──────────────────────────▼───────────────────────────────┐
│ src/mcp_server/ 【协议服务层】 │
│ server.py → protocol_handler.py → tools/(3 个 Tool) │
└──────────────────────────┬───────────────────────────────┘
│ 调用
┌────────────┬─────────────▼─────────────┬─────────────────┐
│ │ │ │
│ src/ │ src/core/query_engine/ │ src/ │
│ ingestion/ │ 【检索引擎层】 │ observability/ │
│ 【业务管道】│ HybridSearch → RRF → │ 【可观测性层】 │
│ Pipeline │ Rerank → Response │ Dashboard + │
│ 6 阶段 │ │ Evaluation │
└────────────┴─────────────┬─────────────┴─────────────────┘
│ 依赖抽象
┌──────────────────────────▼───────────────────────────────┐
│ src/libs/ 【可插拔库层】 │
│ LLM | Embedding | Loader | Splitter | VectorStore │
│ | Reranker | Evaluator(Base 类 + Factory 模式) │
└──────────────────────────┬───────────────────────────────┘
│ 读写
┌──────────────────────────▼───────────────────────────────┐
│ src/core/ 【基础设施层】 │
│ settings.py | types.py | trace/(配置 + 数据类型 + 追踪) │
└──────────────────────────────────────────────────────────┘层级依赖规则:上层可调用下层,下层绝不引用上层;src/libs/ 是纯抽象层,不依赖任何业务层;src/core/ 只含纯数据与配置,零业务逻辑。
链路一:离线摄取(Ingestion Pipeline)
IngestionPipeline::run() 将一篇 PDF 逐步转换为可检索的向量与索引,共 6 个阶段:
阶段 | 组件 | 作用 |
① 完整性检查 |
| 计算 SHA256 哈希,查询历史记录,已处理文件自动跳过(幂等) |
② 文档加载 |
| MarkItDown 转 Markdown + PyMuPDF 提取图片,插入 |
③ 文档分块 |
| chunk_size=1000 / overlap=200,生成确定性 Chunk ID,继承源数据 |
④ 变换管线 |
| LLM 精炼去噪、生成 title/summary/tags、为图片生成描述;LLM 失败自动降级为规则模式 |
⑤ 向量编码 |
| Dense:Embedding API(2560 维);Sparse:jieba 分词 + BM25 词频统计(本地零成本) |
⑥ 多路存储 |
| 分别写入 ChromaDB、JSON 倒排索引、SQLite 图片索引;三路并行独立,一路失败不影响其他 |
幂等性设计:SHA256 文件级去重 + Chunk ID 确定性生成({doc_id}_{index:04d}_{content_hash[:8]}),同一文件重复提交自动跳过,--force 可强制重处理。
链路二:在线查询(Query Pipeline)
MCP Client 发出 JSON-RPC 请求,经 ProtocolHandler 分发到对应 Tool,核心链路如下:
MCP Client (stdio)
└─ tools/call: query_knowledge_hub
└─ QueryProcessor → jieba 分词 + 停用词过滤 + 过滤语法解析
└─ HybridSearch → DenseRetriever + SparseRetriever 并行(ThreadPool)
└─ RRFFusion (k=60) → Σ 1/(k + rank_i),排名级融合,无需分数归一化
└─ Reranker(可选) → Cross-Encoder / LLM 精排,失败保持原序
└─ ResponseBuilder → Markdown 内容 + [1][2] 引用标记 + 多模态图片
└─ TraceCollector → 写入 logs/traces.jsonl
└─ MCP Response → TextContent + 可选 ImageContent并行与降级:Dense 与 Sparse 通过 ThreadPoolExecutor 并行执行,总耗时取二者最大值;任一路失败,另一路结果直接作为最终结果,两路皆失败才报错——单点故障不阻断整体查询。
可观测性与评估
TraceContext:每个阶段独立计时,记录
{name, elapsed_ms, details},最终持久化为logs/traces.jsonl(每行一条合法 JSON,可按类型/时间/集合过滤)。Streamlit Dashboard(6 页面):系统总览 / 数据浏览 / 摄取管理 / 摄取追踪(阶段瀑布图)/ 查询追踪(Dense vs Sparse 对比)/ 评估面板。
评估体系:
CustomEvaluator(Hit Rate、MRR 等确定性指标)+RagasEvaluator(Faithfulness、Answer Relevancy、Context Precision 等 LLM-as-Judge 语义指标),由CompositeEvaluator并行组合、EvalRunner基于 Golden Test Set 批量运行。
💡 技术亮点
🔌 全链路可插拔架构:LLM / Embedding / Reranker / Splitter / VectorStore / Evaluator 均定义抽象基类 + 工厂模式,通过 settings.yaml 一键切换 Provider(LLM ×4、Embedding ×3、Reranker ×2),替换组件不修改任何上游代码。
🔍 混合检索 + RRF 融合:BM25 稀疏检索解决专有名词精确匹配,Dense Embedding 解决同义词语义匹配,两者互补;RRF 基于排名而非原始分数融合(RRF_score = Σ 1/(k+rank)),免去跨路分数归一化,输出完全确定性。
🖼️ 多模态图像处理:采用 Image-to-Text 策略,Vision LLM(Qwen3-VL)自动为文档图片生成描述并缝合进 Chunk,复用纯文本 RAG 链路即可实现"搜文字出图";图片以 base64 编码随 MCP 响应返回。
📡 MCP 生态集成:遵循 MCP 标准协议(stdio + JSON-RPC 2.0),stdout 专用于协议消息、所有日志重定向 stderr;阻塞 I/O 用 asyncio.to_thread() 放入线程池;主线程预加载 chromadb/onnxruntime 规避 import 死锁。可直接对接任意 MCP Client,零前端开发。
🧪 三层测试体系:Unit(无外部依赖,Mock-First)/ Integration(跨模块协作,LLM 相关用 marker 可选运行)/ E2E(全链路端到端),覆盖独立模块逻辑、模块间交互与完整链路。
⛓️ 幂等与优雅降级:SHA256 文件级去重 + 确定性 Chunk ID 保证重复摄取不产生脏数据;LLM 失败回退规则、Dense/Sparse 互备、Rerank 失败保序,任何单点故障都不中断主链路。
📊 数据驱动迭代:Golden Test Set 回归基线 + 双评估体系,让检索调优(如调整 RRF k、chunk_size)有量化指标可循。
🛠️ MCP 工具
工具名 | 描述 | 输入参数 |
| 知识库混合检索(核心工具) |
|
| 列出所有集合及统计信息 |
|
| 按文档 ID 获取标题/摘要/标签 |
|
🧰 技术栈
┌───────────────────────────────────────────────────────────────┐
│ 交互层:Streamlit(Dashboard)· MCP Protocol(JSON-RPC over │
│ stdio)· Python 3.10+ · asyncio │
├───────────────────────────────────────────────────────────────┤
│ RAG 核心: │
│ 检索 Dense: Qwen3-Embedding-4B(2560 维) │
│ Sparse: 自实现 BM25(jieba 分词) │
│ 融合 RRF(k=60,排名级融合) │
│ 重排 Cross-Encoder / LLM Rerank(可插拔) │
│ 生成/变换 DeepSeek-V3.2 等(OpenAI 兼容) │
├───────────────────────────────────────────────────────────────┤
│ 存储层:ChromaDB(向量)· BM25 JSON Index(稀疏)· │
│ SQLite(图片索引 / 摄取历史)· 文件系统 │
├───────────────────────────────────────────────────────────────┤
│ 可观测性:TraceContext · JSON Lines · Ragas · CustomEvaluator│
│ · pytest(三层测试)· Golden Test Set │
└───────────────────────────────────────────────────────────────┘关键依赖:mcp(官方 SDK)、chromadb、langchain-text-splitters、markitdown[pdf] + PyMuPDF、jieba、openai(兼容 SiliconFlow / Azure / DeepSeek)、ollama、ragas、streamlit、sentence-transformers(可选重排)。
支持的 Provider:LLM ×4(OpenAI 兼容 / Azure / DeepSeek / Ollama)、Embedding ×3、Rerank ×2(LLM / Cross-Encoder)、Vision LLM(Qwen3-VL 等)。
📂 项目结构
MODULAR-RAG-MCP-SERVER/
├── config/
│ ├── settings.yaml # 全局配置中心(LLM/Embedding/检索/摄取…)
│ └── prompts/ # 各阶段 Prompt 模板(精炼/元数据/图片描述/重排)
├── documents/ # 待摄取原始文档
├── data/db/ # 运行时数据(chroma/ bm25/ 图片索引/ 摄取历史)
├── logs/traces.jsonl # 全链路追踪 JSON Lines
├── scripts/ # CLI 入口(ingest/query/evaluate/start_dashboard/探测脚本)
├── src/
│ ├── core/ # 【基础设施层】settings、types、query_engine、response、trace
│ ├── ingestion/ # 【业务管道层】pipeline、chunking、embedding、storage、transform
│ ├── libs/ # 【可插拔库层】llm、embedding、loader、splitter、vector_store、reranker、evaluator
│ ├── mcp_server/ # 【协议服务层】server、protocol_handler、tools/
│ └── observability/ # 【可观测性层】logger、dashboard(6 页)、evaluation
├── tests/ # unit / integration / e2e / fixtures(Golden Test Set)
├── DEV_SPEC.md # 开发规格文档
├── main.py # 项目主入口(注册为 mcp-server CLI)
└── pyproject.toml # 项目元数据与依赖🛠️ 我是怎么做的:Harness 工程方法
这个项目的构建遵循 Harness 工程思路——不是"让一个模型一次写完所有代码",而是 Initializer + Coding Loop 的最小闭环:
先拆解 → 再循环执行 → 靠外部状态保持连续性 → 靠确定性验证保证质量
核心哲学是五个字:拆、循环、记、验、守。
1. 拆:Initializer 把需求拆成可验证的 Feature
不直接写代码,先把需求整理成后续 Agent 可反复执行的任务蓝图(AppSpec.md),再由 Initializer 把大任务拆成一组细粒度、可执行、可验证的 Feature,产出 feature_list.json 状态文件。
本项目被拆成 24 个独立 Feature(F-001 → F-024),每个 Feature 明确四件事:
{
"id": "F-005",
"name": "VectorStore 抽象层",
"description": "BaseVectorStore 抽象类 + VectorStoreFactory + ChromaStore 实现",
"files": ["src/libs/vector_store/*.py"],
"depends_on": ["F-001", "F-002"],
"verification": "python -m pytest tests/unit/test_vector_store.py -v",
"acceptance": "ChromaStore 可 upsert/query/delete/get_by_ids,cosine 排序正确"
}从 F-001(核心数据类型)到 F-024(端到端集成测试),依赖关系清晰、按拓扑序执行:先打通 F-001 types / F-002 settings / F-007 integrity 等无依赖地基,再到 F-013 pipeline 编排、F-017 hybrid search、F-020 MCP Server,最后是 F-021~F-024 工具与端到端验收。
2. 循环:Coding Loop 每轮只做一件事
每轮启动一个全新的 Coding Agent,只完成一个明确的小任务,靠外部状态接力:
┌──────────────────────────────────────────────────────────┐
│ Coding Loop 单轮协议 │
│ │
│ ① 启动全新 Agent(无历史上下文) │
│ ② Agent 读取 3 个外部文件: │
│ - feature_list.json → 知道当前该做哪个 Feature │
│ - progress.md → 知道前面完成了什么、踩了什么坑 │
│ - 目标代码文件 → 了解当前代码状态 │
│ ③ Agent 完成一个 Feature │
│ ④ 确定性验证 Gate(不是 Agent 自评): │
│ - pytest 必须 PASS │
│ - mypy/pyright 类型检查必须 PASS │
│ - ruff lint 必须 PASS │
│ ⑤ 验证通过后,由系统(非 Agent)更新状态: │
│ - feature_list.json: status → completed │
│ - progress.md: 追加交接记录 │
│ - git commit: 提交代码 + 验证结果 │
│ ⑥ Agent 立即退出,下一轮由新 Agent 接力 │
└──────────────────────────────────────────────────────────┘3. 记:外部状态是系统的核心记忆
不把连续性寄托在模型上下文里,每个 Agent 启动时从三份持久化信息快速接手:
外部状态 | 作用 |
| 所有 Feature 的定义、依赖、状态、验证命令;状态由验证结果决定,不由 Agent 自评,永远反映真实进度 |
| 每轮完成后的交接记录(做了什么、结果如何、踩了什么坑),是新 Agent 的 onboarding 材料 |
Git Log | 每个 Feature 一个 commit, |
4. 验:确定性验证 Gate
每个 Feature 都有明确的验证命令和预期输出,pytest pass ≠ 代码完成,pytest pass = 代码至少通过了预定义的确定性检查。通过才更新状态、提交、进入下一轮;失败则记录失败原因,由下一轮新 Agent 接手修复——质量不由"AI 说做好了就是做好了"决定。
5. 守:Human-in-the-Loop 关键节点
高度自动化但并非无人值守,在以下节点设计人工确认断点:
断点时机 | 触发条件 | 本项目实例 |
高风险依赖选择 | Feature 涉及关键依赖引入 | PDF Loader 选择 MarkItDown vs pdfminer |
架构决策点 | 跨多个 Feature 的接口设计 | LLM / Embedding 抽象基类签名 |
失败重试 > 2 次 | 同一 Feature 连续失败 | Pipeline 编排器集成反复失败时人工介入 |
关键界面变更 | 修改已有公共 API | VectorStore 接口变更影响所有 consumer |
集成验证前 | 最后几个 Feature 完成 | 端到端验收通过后再正式发布 |
为什么这么做
传统方式让一个 Agent 一次做完所有事,产出的代码无法验证、无法复现、无法交接。而 Initializer + Coding Loop 最小闭环让每个 Feature 都有明确的输入文件、输出文件、验证命令和依赖关系,把"靠 AI 记忆"变成"靠外部状态 + 确定性验证",把一个复杂项目稳扎稳打地交付出来。
🚀 快速开始
# 1. 克隆并进入项目
git clone <repo-url>
cd MODULAR-RAG-MCP-SERVER
# 2. 安装依赖(Python 3.10+)
pip install -e ".[dev]"
# 3. 配置 API Key:从模板复制(config/settings.yaml 已被 git 忽略,请勿提交真实密钥)
cp config/settings.yaml.example config/settings.yaml
# 然后编辑 config/settings.yaml,填写 LLM / Embedding 的 API Key 与 base_url
# 4. 摄取文档:将 PDF 放入 documents/,执行
python scripts/ingest.py --path documents/ --collection knowledge_hub
# 5. 查询验证
python scripts/query.py --query "你的问题" --collection knowledge_hub
# 6. 启动 Dashboard
python scripts/start_dashboard.py
# 7. 启动 MCP Server(供 Claude / Copilot 等 Client 调用)
mcp-server配置 Provider 切换、新文档格式扩展等,只需修改
settings.yaml或基于src/libs/的抽象接口新增实现——可插拔架构让这些改动不影响任何上游代码。
📚 配套文档
文档 | 内容 |
开发规格文档:项目概述、核心特点、技术选型、测试方案、系统架构、排期与可扩展性 |