Skip to main content
Glama

rag-mcp-server

基于 PyTorch 官方文档的 RAG 知识检索 MCP Server:双向量库(FAISS + Chroma)混合检索,DeepSeek 生成带引用的回答,能力通过 FastMCP 暴露为标准工具,可被 Claude 等 MCP 客户端直接调用。

功能特性

  • 双向量库:FAISS(IndexFlatIP 精确余弦,主检索)+ Chroma(持久化 / where 过滤 / 备份对照),统一 md5(chunk_id) 对齐,构建后自动校验双库 top-5 重合率 ≥ 90%

  • 章节感知分块:按 API entry 语义切分,超长块递归切分 + overlap,保证代码签名不被拦腰截断

  • 混合检索:向量(BGE)+ 关键词(BM25)双路,RRF(k=60) 融合,规避两路分数量纲不可比

  • 带引用回答:DeepSeek 基于检索上下文生成,强制 [n] 引用 + Sources,越界引用后校验剔除;资料不足正确降级拒绝,不编造

  • MCP Serversearch / ask / list_topics / stats 四个标准工具,lifespan 只加载一次模型

  • ReAct 检索 Agent + 自纠正验证:模型自行决定检索策略,跨多主题迭代搜索(search / list_topics 工具调用);独立验证器逐条检查 [n] 引用是否支持论断,不支持的反馈回 Agent 重搜修正

架构

PyTorch 官方文档 (HTML)
        │  loader.py  解析 <dl> 签名+描述
        ▼
      Document ──► chunker.py  章节感知分块 (447 chunks / 96 API topics)
        │  embedder.py  BGE 文档编码 (无 instruction, 384 维)
        ▼
┌────────────────────────── 建库 ──────────────────────────┐
│   FAISS (IndexFlatIP) 主检索       Chroma 持久化/过滤      │
│   同批 chunk,md5 chunk_id 对齐,双库重合率 ≥90% 校验      │
└───────────────────────────────────────────────────────────┘
        ▲
        │  embed_query (查询加检索前缀)
        │
问题 ──► HybridRetriever = 向量 top-k + BM25 top-k ──► RRF 融合 ──► top-k Chunk
        │
        ▼
 Generator.generate: 编号[1]..[n] 组装 context ──► DeepSeek ──► 回答 + [n] 引用
        │                        ▲
        │               引用校验 _extract_citations (越界/非数字剔除)
        ▼
 FastMCP tools: search / ask / list_topics / stats   (lifespan 单次加载)
        │
        ▼
 ReAct Agent (demo --agent): 工具调用迭代检索 → 全局来源表 → 验证器检查 [n] 支持性 → 不通过则重搜修正

技术栈

Python 3.13 · FAISS · Chroma · sentence-transformers (BGE) · rank_bm25 · LangChain · DeepSeek · FastMCP

快速开始

1. 环境

python -m venv .venv
# Windows: .venv\Scripts\activate    Linux/macOS: source .venv/bin/activate
pip install -r requirements.txt

BGE 模型需从 HuggingFace 下载,国内可设镜像(config.py 已默认写入 HF_ENDPOINT=https://hf-mirror.com)。

2. 配置密钥

复制 .env.example.env,填入 DeepSeek API Key:

cp .env.example .env   # 填入 DEEPSEEK_API_KEY

也可直接用环境变量 DEEPSEEK_API_KEY,不需要 .env 文件。

3. 构建索引

PYTHONPATH=src python scripts/build_index.py

输出示例:n_chunks=447, dim=384, dual_store_overlap=0.93+

索引数据在 data/(已被 .gitignore 排除,可随时重建,幂等)。

4. 命令行问答

# 单次提问
PYTHONPATH=src python -m ragmcp.cli.demo "How to create a Linear layer in PyTorch?"

# 交互式(输入 exit 退出)
PYTHONPATH=src python -m ragmcp.cli.demo

4.1 ReAct 检索 Agent

# 模型自行决定检索策略,跨多主题迭代搜索
PYTHONPATH=src python -m ragmcp.cli.demo --agent "How to train a model with Adam on a DataLoader using CrossEntropyLoss?"

5. 启动 MCP Server

PYTHONPATH=src python -m ragmcp.server.mcp_server    # stdio transport

注册进 Claude Code / Cursor 等客户端后,即可通过标准工具调用:

工具

说明

search

混合检索(向量 + BM25),返回 top-k 来源片段与分数

ask

端到端问答,返回带 [n] 引用的回答 + Sources

list_topics

知识库覆盖的 API 主题列表

stats

知识库统计(分块数 / 文档数 / 主题数 / 维度)

客户端验收脚本:

PYTHONPATH=src python scripts/test_mcp_client.py

6. 测试

PYTHONPATH=src python -m pytest tests/ -v

目录结构

rag-mcp-server/
├── scripts/
│   ├── download_docs.py    # 下载 PyTorch 文档页
│   ├── build_index.py      # 全量构建双向量库(幂等)
│   └── test_mcp_client.py  # MCP stdio 客户端验收
├── src/ragmcp/
│   ├── config.py           # pydantic-settings 配置
│   ├── ingestion/          # loader(HTML/PDF) chunker(章节感知) embedder(BGE)
│   ├── storage/            # faiss_store chroma_store indexer(双写+对齐校验)
│   ├── retrieval/          # keyword(BM25) hybrid(RRF+加权融合)
│   ├── generation/         # generator(DeepSeek + [n]引用 + 降级)
│   ├── agent/              # rag_agent(ReAct 迭代检索, 工具调用)
│   ├── service/            # rag_service(编排 search/ask/list_topics/stats)
│   ├── server/             # mcp_server(FastMCP 4 工具) lifespan(单次加载)
│   └── cli/                # demo(命令行问答)
├── tests/                  # chunker / keyword / hybrid / generator
├── data/                   # gitignore:raw / chroma / faiss
├── requirements.txt
└── .env.example

关键设计

  1. 双向量库分工:FAISS 快、精确余弦、无持久化;Chroma 落盘、where 过滤、备份对照。统一 md5(source|index) 的 chunk_id 对齐,构建后双库 top-5 重合率校验,证明双库结果一致。

  2. BGE 检索姿势:文档编码不加 instruction、查询编码加前缀 "Represent this sentence for searching relevant passages: ",配合 normalize_embeddings=True 使 IndexFlatIP 内积 = 余弦。

  3. RRF 融合:向量分(-11)与 BM25 分(0几十)量纲不可比,直接加权无意义;RRF 只看排名(k=60,Cormack 2009),跨打分器鲁棒。

  4. 引用后校验:LLM 会幻觉出 context 里不存在的编号,_extract_citations 只保留 1<=n<=total 的合法引用,Sources 才可信。

  5. 无答案降级_low_confidence 阈值(0.02,实测校准)+ SYSTEM_PROMPT 规则 3 双保险,资料不足明确拒绝,不编造。

  6. lifespan 单次加载:BGE 模型(~130MB)+ FAISS 索引在服务启动时加载一次,所有工具调用复用,stdio 会话不重载。

  7. ReAct 全局来源表:Agent 多次检索的 chunk 去重后进全局来源表,每次 search 返回的片段用全局绝对编号 [n],最终回答引用的 [n] 精确反查——跨主题多轮检索的引用依然可信。

  8. 自纠正验证循环:Agent 给出答案后由独立验证器 LLM(temperature 0)逐条判断每个 [n] 引用是否真的支持论断;不支持的引用连同理由反馈回 Agent 重搜/改写(max_verify 轮),把引用校验从「格式校验」升级为「语义支持性校验」。