Staged RAG MCP Server
A production-grade, two-level Retrieval-Augmented Generation server built on the Model Context Protocol (MCP)
Search smart. Retrieve less. Answer better.
Created by Shashidhar Reddy Nalamari • nalamarishashidharreddy@gmail.com
______________________________________________________________________
目录
- 基本安装 - 使用可选提供程序进行安装 - 开发安装
- 配置文件 - 服务器配置 - 嵌入配置 - 发电配置 - 分块配置 - 检索配置 - 存储配置 - 摄入配置 - 日志记录配置 - 知识库配置 - 环境变量 - 完整配置参考
- 标准模式(流式HTTP) - STDIO模式 - 使用知识库监视器
- 谷歌双子座(默认) - 开放人工智能 - Ollama(当地) - 拥抱脸 - Azure OpenAI - 一起AI - LM工作室(本地) - 切换提供商 - 确定性回退
- 检索工具 - 搜索_总结 - get_文档 - get_document_chunk - 高级搜索工具 - 混合搜索 - 多查询搜索 - find_类似物 - 文档管理工具 - ingest_文档 - ingest_batch - update_document - 删除文档 - 元数据工具 - get_document_metadata - 观察性工具 - collection_stats - list_collections - 解释检索 - 检索日志 - 知识库工具 - kb_status - 惊讶
- 概述 - 运作原理 - 支持的文件类型 - 自动生成的标签 - PDF支持 - 清单跟踪 - 文件夹组织 - 初始同步与后台监视器
- 数据摄取管道 - 文档模型 - 分块策略 - 摘要生成 - 嵌入和索引
- 语义搜索 - BM25关键字搜索 - 混合搜索 - 多查询融合 - 相似性搜索 - 及排名
- 什么是收藏 - 创建收藏 - 多重收集策略 - 收款统计
- 文档存储(JSON) - 矢量索引(NumPy) - BM25索引(内存中) - 审核日志(jsonl) - KB清单(JSON)
______________________________________________________________________
概述
分级RAG MCP服务器 是一个复杂的检索增强生成系统,通过 模型上下文协议(MCP)与将整个文档转储到LLM上下文窗口的传统RAG系统不同,Staged RAG使用 搜索然后展开 策略:首先检索轻量级摘要以识别相关文档,然后选择性地仅获取重要文档的完整内容。
这种方法大大减少了令牌消耗,提高了响应质量,并提供了每个检索决策的完全可审计性。
为什么要分阶段进行RAG?
传统的RAG系统有一个根本问题:它们检索K个文档,并将所有文档推送到LLM上下文中,无论每个文档是否真正相关。这导致:
- 浪费的代币:无关文档占用了昂贵的上下文窗口空间
- 分散注意力:重要信息在噪音中丢失
- 不透明:用户看不到为什么选择某些文档
- 更高的延迟:处理不必要的文本会减慢响应速度
分段RAG通过引入两级检索管道来解决这些问题:
Level 1: Search Summaries → Compact summaries + scores (cheap)
↓ (evaluate)
Level 2: Get Full Documents → Complete text + chunks (selective)
↓ (optional)
Level 2.5: Get Specific Chunk → Single chunk (surgical)LLM(或用户)检查1级结果,并决定哪些文件需要完全扩展。这意味着您只需为实际需要的代币付费。
关键差异
| 功能 | 传统RAG | 分级RAG MCP |
|---|---|---|
| 检索策略 | 单遍:检索和注入 | 双遍:总结→ 扩展 |
| 令牌效率 | 低--上下文中的所有K个文档 | 高--仅扩展了相关文档 |
| 透明度 | 黑框 | 每次检索的完整审计跟踪 |
| 搜索模式 | 通常为一种(语义) | 语义、BM25、混合、多查询 |
| 协议 | 自定义API | MCP标准-适用于任何MCP客户端 |
| 知识库 | 仅手动摄取 | 使用文件监视器自动同步文件夹 |
| 嵌入提供商 | 通常一个 | 7个提供商:Gemini、OpenAI、Ollama、HuggingFace、Azure、Together、LM Studio |
______________________________________________________________________
主要特点
岩芯回收
- 两级分阶段检索 --首先搜索摘要(级别1),然后展开所选文档(级别2),可选单块检索(级别2.5)
- 语义搜索 --使用最先进的嵌入模型进行向量相似性搜索
- BM25关键字搜索 --通过Okapi BM25进行基于TF IDF的经典关键字匹配
- 混合搜索 --语义和关键字分数与归一化权重的可配置混合
- 多查询融合 --使用交互排名融合(RRF)或最大分数聚合运行多个查询并合并结果
- 类似文档发现 --查找语义上与参考文档相似的文档
文档管理
- 单文档摄入 --添加带有标题、文本、标签、元数据和可选预先计算的摘要的文档
- 批量摄取 --在一次操作中批量摄取多达50个文档
- 文档更新 --修改元数据、标签或全文(触发自动重分块和重嵌入)
- 文档删除 --通过自动索引清理(矢量索引+BM25重建)删除文档
- 令牌计数 --用于预算管理的自动令牌计数
知识库(自动同步文件夹)
- 文件夹监视器 --基于后台轮询的文件监视器(无特定于操作系统的事件依赖关系)
- 自动摄入 --将文件放入
knowledge_base/它们会立即被编入索引 - 变化检测 --基于SHA-256哈希的变化检测;修改后的文件会自动重新索引
- 删除跟踪 --删除的文件会自动从矢量存储中清除
- PDF支持 --通过以下方式提取完整PDF文本
pypdf具有元数据标题提取功能 - 清单跟踪 --持久JSON清单在服务器重启时将文件映射到文档ID
- 自动标记 --文件会自动按扩展名、源和子文件夹位置标记
- 文本清理 --智能清理PDF工件:页码、CamelCase连接、过多空白
嵌入提供者
- 谷歌双子座 --默认提供程序使用
gemini-embedding-001(3072个尺寸) - 开放人工智能 —
text-embedding-3-small/text-embedding-3-large - 奥拉玛 --本地模型,如
nomic-embed-text(不需要API密钥) - 拥抱脸 --本地
sentence-transformers或HuggingFace推理API - Azure OpenAI --企业Azure OpenAI部署
- 一起AI --共同嵌入AI模型
- LM工作室 --通过LM Studio进行本地OpenAI兼容嵌入
- 确定性回退 -当没有API可用时,基于SHA-256的确定性向量
可观测性
- 审计日志 --每个工具调用都记录到JSONL中,并带有时间戳、参数、结果计数、文档ID和延迟
- 检索说明 --检查任何查询文档对的余弦相似度、BM25分数和术语重叠
- 收款统计 --文档计数、令牌总数、标签/源分布、索引大小
- 检索日志 --使用工具和会话筛选器查询最近的审核条目
- 速率限制 -内置请求调步(80 RPM)以保持在API免费层内
建筑
- MCP标准 --通过FastMCP建立在模型上下文协议之上;适用于VS Code、Claude Desktop和任何MCP客户端
- 提供商无关嵌入 --工厂模式与懒惰的进口;仅在选中时加载可选依赖项
- 线程安全 --所有存储、索引和记录器都使用线程锁来实现并发安全
- 防撞工具 --每个MCP工具都被包裹在错误捕获装饰器中;服务器永远不会因工具故障而崩溃
- 可通过YAML配置 --基本配置+本地覆盖;机密的环境变量
- 零外部服务 --不需要数据库服务器;所有内容都存储为本地JSON+NumPy文件
______________________________________________________________________
建筑
系统架构图
┌──────────────────────────────────────────────────────────────────────────┐
│ MCP CLIENT │
│ (VS Code / Claude / Custom) │
└──────────────────────────┬───────────────────────────────────────────────┘
│ MCP Protocol (HTTP / STDIO)
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ MCP SERVER (FastMCP) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ server.py — Tool Registry │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ │
│ │ │ Retrieval │ │ Management │ │ Observability │ │ │
│ │ │ Tools │ │ Tools │ │ Tools │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ search_ │ │ ingest_ │ │ collection_stats │ │ │
│ │ │ summaries │ │ document │ │ list_collections │ │ │
│ │ │ get_ │ │ ingest_batch │ │ explain_retrieval │ │ │
│ │ │ documents │ │ update_ │ │ retrieval_log │ │ │
│ │ │ get_document │ │ document │ │ │ │ │
│ │ │ _chunk │ │ delete_ │ │ │ │ │
│ │ │ hybrid_ │ │ document │ │ │ │ │
│ │ │ search │ │ │ │ │ │ │
│ │ │ multi_query │ │ │ │ │ │ │
│ │ │ _search │ │ │ │ │ │ │
│ │ │ find_similar │ │ │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────┬───────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ SERVICE LAYER (service.py) │
│ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────────────────┐ │
│ │ RAGService │ │ KBManager │ │ SummaryGenerator │ │
│ │ (Singleton) │ │ (Singleton) │ │ (Gemini / Local Fallback) │ │
│ └───────┬────────┘ └───────┬────────┘ └────────────────────────────┘ │
│ │ │ │
└──────────┼───────────────────┼───────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────────────────────────────────────────────────────────────────┐
│ CORE LAYER │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ DocumentStore│ │ VectorIndex │ │ BM25Scorer │ │ ChunkManager│ │
│ │ (JSON) │ │ (NumPy) │ │ (rank-bm25) │ │ (Sentence) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────────┘ └─────────────┘ │
│ │ │ │
│ ┌──────┴─────────────────┴──────────────────────────────────────────┐ │
│ │ EmbeddingEngine │ │
│ │ ┌─────────┐ ┌────────┐ ┌────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Gemini │ │ OpenAI │ │ Ollama │ │ HuggingFace│ │ Together │ │ │
│ │ └─────────┘ └────────┘ └────────┘ └────────────┘ └────────────┘ │ │
│ │ ┌──────────────┐ ┌──────────┐ ┌──────────────────────────────┐ │ │
│ │ │ Azure OpenAI │ │ LMStudio │ │ Deterministic Fallback │ │ │
│ │ └──────────────┘ └──────────┘ └──────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────────┐ │
│ │ FileWatcher │ │ KBManifest │ │ AuditLogger │ │
│ │ (Polling) │ │ (JSON) │ │ (JSONL) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ data/store/ │ │ data/index/ │ │ data/logs/ │
│ *.json │ │ *.npz │ │ audit.jsonl │
└──────────────┘ └──────────────┘ └──────────────────┘两级检索管道
Staged RAG的核心创新是两级检索管道:
User Query
│
▼
┌─────────────────┐
│ LEVEL 1: │
│ search_ │ Lightweight — returns only
│ summaries() │ summaries + similarity scores
└────────┬────────┘
│
▼
┌─────────────────┐
│ EVALUATE: │ LLM or user inspects summaries
│ Inspect scores │ and decides which docs to expand
│ & summaries │
└────────┬────────┘
│
┌──────────┼──────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ SKIP │ │ LEVEL 2: │ │ LEVEL 2: │ Selective — only fetch
│ (low │ │ get_ │ │ get_ │ documents that scored
│ score) │ │ documents│ │ documents│ above threshold
└──────────┘ └────┬─────┘ └────┬─────┘
│ │
▼ ▼
┌─────────────────┐
│ LEVEL 2.5: │ Surgical — retrieve specific
│ get_document_ │ chunks within a document
│ chunk() │ by index or semantic query
└────────┬────────┘
│
▼
┌─────────────────┐
│ SYNTHESIZE: │ Generate answer using only
│ Answer with │ the relevant retrieved content
│ citations │
└─────────────────┘数据流
Document Ingestion Flow:
─────────────────────────
Input Text ──→ Token Count Check ──→ Chunking (sentence-based)
│ │
▼ ▼
Summary Generation ──────────────→ Document Model (Pydantic)
│ │
▼ ▼
Embedding (provider) ───────────→ Vector Index (NumPy cosine)
│ │
▼ ▼
BM25 Index (rebuild) ───────────→ JSON Document Store
│ │
▼ ▼
Audit Log Entry ────────────────→ Return doc_id + metadata
Search Flow:
────────────
Query ──→ Embedding (provider) ──→ Vector Index Search (cosine)
│ │
▼ ▼
(Optional) BM25 Score ──────────→ Score Fusion (weighted)
│ │
▼ ▼
Filter (tags, min_score) ───────→ Ranked SummaryResults
│ │
▼ ▼
Audit Log Entry ────────────────→ Return SearchResponse组件架构
| 组件 | 文件 | 责任 |
|---|---|---|
| MCP服务器 | server.py | 工具注册、错误包装、服务器生命周期 |
| RAG服务 | service.py | 核心编排:摄取、搜索、更新 |
| 配置 | config.py | YAML加载、设置数据类、合并逻辑 |
| 文档存储 | core/document_store.py | JSON支持的每个集合文档持久性 |
| 向量索引 | core/vector_index.py | 基于NumPy的具有NPZ持久性的余弦相似性指数 |
| BM25评分器 | core/bm25.py | Okapi BM25关键字评分使用 rank-bm25 |
| 区块管理器 | core/chunk_manager.py | 基于句子的重叠文本分块 |
| 嵌入引擎 | core/embeddings.py | 具有速率限制和回退功能的多提供商嵌入 |
| 摘要生成器 | core/summary_generator.py | 基于Gemini的摘要,具有提取回退功能 |
| 键盘管理 | core/kb_manager.py | 文件监视器编排、摄取、重新同步 |
| 文件监视器 | core/file_watcher.py | 基于轮询的目录监视器,具有哈希跟踪功能 |
| KB清单 | core/kb_manifest.py | 文件到文件id映射持久化 |
| 审计记录器 | logging/audit.py | 具有自动截断功能的线程安全JSONL审计日志 |
| 嵌入器工厂 | embeddings/factory.py | 懒惰的进口供应商工厂 |
| 提示 | prompts.py | 分阶段检索工作流的提示模板 |
| 工具集 | utils.py | 文本清理、句子分割、分块、向量运算 |
______________________________________________________________________
项目结构
staged-rag-mcp/
│
├── LICENSE # Custom license (free use, no false ownership claims)
├── README.md # This file — comprehensive project documentation
├── HOWTOUSE.md # Detailed usage guide with examples
├── pyproject.toml # Python project metadata and dependencies
├── config.yaml # Base configuration (version controlled)
├── config.local.yaml # Local overrides (not version controlled)
├── .env # Environment variables (API keys — not committed)
│
├── src/
│ └── staged_rag/ # Main package
│ ├── __init__.py # Package version and exports
│ ├── server.py # MCP server: tool registration, lifecycle
│ ├── service.py # RAG service: core business logic
│ ├── config.py # Configuration loading and dataclasses
│ ├── prompts.py # Prompt templates for staged retrieval
│ ├── utils.py # Text processing utilities
│ │
│ ├── core/ # Core engine components
│ │ ├── __init__.py
│ │ ├── document_store.py # JSON-backed document persistence
│ │ ├── vector_index.py # NumPy cosine similarity index
│ │ ├── bm25.py # BM25 keyword scorer
│ │ ├── chunk_manager.py # Sentence-based text chunking
│ │ ├── embeddings.py # Multi-provider embedding engine
│ │ ├── summary_generator.py # AI summary with local fallback
│ │ ├── kb_manager.py # Knowledge base folder orchestrator
│ │ ├── kb_manifest.py # File → doc_id manifest tracker
│ │ └── file_watcher.py # Polling-based file system watcher
│ │
│ ├── embeddings/ # Embedding provider implementations
│ │ ├── __init__.py
│ │ ├── base.py # Abstract base class (EmbeddingBase)
│ │ ├── configs.py # Provider configuration dataclasses
│ │ ├── factory.py # Lazy-import provider factory
│ │ ├── gemini.py # Google Gemini provider
│ │ ├── openai.py # OpenAI provider
│ │ ├── ollama.py # Ollama local provider
│ │ ├── huggingface.py # HuggingFace (local + API)
│ │ ├── azure_openai.py # Azure OpenAI provider
│ │ ├── together.py # Together AI provider
│ │ └── lmstudio.py # LM Studio local provider
│ │
│ ├── models/ # Pydantic data models
│ │ ├── __init__.py
│ │ ├── document.py # Document and DocumentChunk models
│ │ ├── search.py # SearchResponse and SummaryResult models
│ │ └── audit.py # AuditLogEntry model
│ │
│ ├── tools/ # MCP tool implementations
│ │ ├── __init__.py # Tool re-exports
│ │ ├── retrieval.py # search_summaries, get_documents, get_document_chunk
│ │ ├── advanced.py # hybrid_search, multi_query_search, find_similar
│ │ ├── management.py # ingest, update, delete, kb_status, kb_resync
│ │ ├── metadata.py # get_document_metadata
│ │ └── observability.py # collection_stats, explain_retrieval, retrieval_log
│ │
│ ├── resources/ # MCP resource providers
│ │ ├── __init__.py
│ │ └── providers.py # Document and collection resource URIs
│ │
│ └── logging/ # Logging and audit
│ ├── __init__.py
│ └── audit.py # Thread-safe JSONL audit logger
│
├── data/ # Runtime data directory
│ ├── store/ # Document store (JSON per collection)
│ │ └── default.json
│ ├── index/ # Vector indexes (NPZ per collection)
│ │ └── default.npz
│ ├── logs/ # Audit logs
│ │ └── audit.jsonl
│ ├── mock/ # Mock data for testing
│ │ └── documents.json
│ └── kb_manifest.json # Knowledge base manifest
│
├── knowledge_base/ # Drop files here for auto-ingestion
│ └── README.md
│
├── scripts/ # Utility scripts
│ ├── seed_mock_data.py # Seed document store with sample data
│ ├── full_test.py # Run comprehensive test suite
│ ├── inspect_state.py # Inspect current data store state
│ ├── run_eval.py # Run retrieval evaluation
│ ├── test_mcp_search.py # Test MCP search functionality
│ └── test_search.py # Test search functionality
│
└── tests/ # Automated tests
├── test_agent_flow.py # End-to-end agent flow tests
├── test_document_store.py # Document store unit tests
├── test_embeddings.py # Embedding provider tests
├── test_tools.py # Tool function tests
└── test_vector_index.py # Vector index unit tests______________________________________________________________________
先决条件
| 要求 | 最低版本 | 注意事项 | |
|---|---|---|---|
| python | 3.11+ | 用途 match 声明, `X \ | Y` 类型工会 |
| 点 | 21.0+ | 适用 pyproject.toml 支持 | |
| API密钥 | - | 至少有一个嵌入提供程序API密钥(请参阅 嵌入提供者) |
可选先决条件
| 工具 | 安装所需 | |
|---|---|---|
| 奥拉玛 | 本地嵌入模型 | 奥拉玛 |
| LM工作室 | 本地LM工作室嵌入 | lmstudio.ai |
| Git | 版本控制 | git-scm.com |
______________________________________________________________________
安装
基本安装
# Clone the repository
git clone https://github.com/reddynalamari/staged-rag-mcp.git
cd staged-rag-mcp
# Create virtual environment
python -m venv .venv
# Activate virtual environment
# Windows:
.venv\Scripts\activate
# macOS/Linux:
source .venv/bin/activate
# Install the package in editable mode
pip install -e .使用可选提供程序进行安装
# Install with OpenAI support
pip install -e ".[openai]"
# Install with Ollama support
pip install -e ".[ollama]"
# Install with HuggingFace support
pip install -e ".[huggingface]"
# Install with Together AI support
pip install -e ".[together]"
# Install with FAISS support (faster vector search)
pip install -e ".[faiss]"
# Install ALL optional providers
pip install -e ".[all-providers]"
# Install with development tools
pip install -e ".[dev]"开发安装
# Install everything (all providers + dev tools)
pip install -e ".[all-providers,dev]"
# Verify installation
python -c "from staged_rag import __version__; print(f'Staged RAG MCP v{__version__}')"______________________________________________________________________
配置
配置文件
系统使用分层的YAML配置:
- 内置默认值 --硬编码
config.py config.yaml--基础项目配置(版本控制)config.local.yaml--本地覆盖(gitignored,具有最高优先级)
设置按顺序合并:默认值→ config.yaml → config.local.yaml。以后文件中的任何键都会覆盖以前文件中的相同键。
服务器配置
server:
name: staged-rag # Server name (shown in MCP clients)
transport: streamable-http # Transport protocol: "streamable-http" or "stdio"
host: 127.0.0.1 # Bind address for HTTP transport
port: 8090 # Port for HTTP transport运输选项:
streamable-http--基于HTTP的传输;服务器在host:port上作为web服务运行stdio--标准I/O传输;当MCP客户端生成服务器进程时使用
嵌入配置
embedding:
provider: gemini # Provider name (see Embedding Providers section)
model: gemini-embedding-001 # Model name (provider-specific)
dimensions: 3072 # Embedding vector dimensions
batch_size: 32 # Max texts per embedding batch
provider_config: {} # Additional provider-specific config发电配置
generation:
model: gemini-2.5-flash-lite # Model for summary generation
summary_max_sentences: 4 # Max sentences in generated summaries分块配置
chunking:
chunk_size: 200 # Target tokens per chunk
chunk_overlap: 20 # Overlap tokens between adjacent chunks
min_chunk_size: 50 # Minimum tokens for a chunk to be kept分块是如何工作的:
- 文本在句末标点处被拆分为句子(
.,!,?) - 句子被累积,直到块达到
chunk_size代币 - 当达到块边界时,最后一个
chunk_overlap代币转移到下一个区块 - 小于的块
min_chunk_size令牌将被丢弃(除非它们是唯一的内容)
检索配置
retrieval:
default_top_k: 5 # Default number of results per search
max_top_k: 50 # Maximum allowed top_k (safety cap)
default_collection: default # Default collection name
hybrid_semantic_weight: 0.7 # Semantic score weight in hybrid search
hybrid_keyword_weight: 0.3 # Keyword score weight in hybrid search
min_similarity_score: 0.0 # Minimum score threshold for results存储配置
storage:
data_dir: ./data # Root data directory
store_dir: ./data/store # Document JSON store directory
index_dir: ./data/index # Vector index (NPZ) directory
log_dir: ./data/logs # Audit log directory创建的存储文件:
data/store/.json--每个集合一个包含所有文档的JSON文件data/index/.npz--每个集合一个NumPy压缩文件,包含所有向量data/logs/audit.jsonl--仅附加审核日志
摄入配置
ingestion:
max_document_tokens: 50000 # Maximum tokens per document (rejects larger)
max_batch_size: 50 # Maximum documents per batch ingest
auto_summary: true # Auto-generate summaries on ingestion日志记录配置
logging:
audit_file: ./data/logs/audit.jsonl # Audit log file path
max_log_entries: 10000 # Auto-truncate after this many entries
log_level: INFO # Python logging level知识库配置
knowledge_base:
enabled: true # Enable/disable the folder watcher
kb_dir: ./knowledge_base # Directory to watch for documents
manifest_file: ./data/kb_manifest.json # Manifest tracking file
collection: default # Target collection for KB documents
poll_interval: 5.0 # Seconds between file system scans
max_file_size: 10485760 # Maximum file size in bytes (10 MB)环境变量
创建一个 .env 项目根目录中的文件:
# Required for Gemini (default provider)
GEMINI_API_KEY=your_gemini_api_key_here
# Alternative: Google API Key
GOOGLE_API_KEY=your_google_api_key_here
# For OpenAI provider
OPENAI_API_KEY=your_openai_api_key_here
# For Azure OpenAI provider
EMBEDDING_AZURE_OPENAI_API_KEY=your_azure_api_key
EMBEDDING_AZURE_DEPLOYMENT=your_deployment_name
EMBEDDING_AZURE_ENDPOINT=https://your-resource.openai.azure.com/
EMBEDDING_AZURE_API_VERSION=2024-02-01
# For Together AI provider
TOGETHER_API_KEY=your_together_api_key
# For HuggingFace Inference API
HUGGINGFACE_API_KEY=your_hf_api_key完整配置参考
Click to expand — Complete config.yaml with all options and defaults
# ============================================================
# Staged RAG MCP Server — Full Configuration Reference
# ============================================================
server:
name: staged-rag # Server display name
transport: streamable-http # "streamable-http" or "stdio"
host: 127.0.0.1 # HTTP bind address
port: 8090 # HTTP port
embedding:
provider: gemini # gemini | openai | ollama | huggingface
# | azure_openai | together | lmstudio
model: gemini-embedding-001 # Model name (varies by provider)
dimensions: 3072 # Output vector dimensions
batch_size: 32 # Batch size for embedding calls
provider_config: {} # Provider-specific overrides:
# ollama_base_url: http://localhost:11434
# openai_base_url: https://api.openai.com/v1
# huggingface_base_url: https://api-inference.huggingface.co/...
# azure_kwargs:
# azure_deployment: my-deployment
# azure_endpoint: https://my-resource.openai.azure.com/
# api_version: 2024-02-01
generation:
model: gemini-2.5-flash-lite # Summary generation model
summary_max_sentences: 4 # Max sentences in summaries
chunking:
chunk_size: 200 # Target tokens per chunk
chunk_overlap: 20 # Overlap between chunks
min_chunk_size: 50 # Minimum tokens per chunk
retrieval:
default_top_k: 5 # Default results per search
max_top_k: 50 # Maximum allowed top_k
default_collection: default # Default collection name
hybrid_semantic_weight: 0.7 # Semantic weight in hybrid search
hybrid_keyword_weight: 0.3 # Keyword weight in hybrid search
min_similarity_score: 0.0 # Score threshold
storage:
data_dir: ./data
store_dir: ./data/store
index_dir: ./data/index
log_dir: ./data/logs
ingestion:
max_document_tokens: 50000 # Max tokens per document
max_batch_size: 50 # Max documents per batch
auto_summary: true # Auto-generate summaries
logging:
audit_file: ./data/logs/audit.jsonl
max_log_entries: 10000
log_level: INFO # DEBUG | INFO | WARNING | ERROR
knowledge_base:
enabled: true # true = active, false = disabled
kb_dir: ./knowledge_base
manifest_file: ./data/kb_manifest.json
collection: default
poll_interval: 5.0 # Seconds between scans
max_file_size: 10485760 # 10 MB______________________________________________________________________
快速开始
5分钟后起床跑步:
# 1. Clone and install
git clone https://github.com/reddynalamari/staged-rag-mcp.git
cd staged-rag-mcp
python -m venv .venv
.venv\Scripts\activate # Windows
pip install -e .
# 2. Set your API key
echo GEMINI_API_KEY=your_key_here > .env
# 3. Seed sample data (optional)
python scripts/seed_mock_data.py
# 4. Start the server
python -m staged_rag.server服务器启动于 http://127.0.0.1:8090.连接任何MCP客户端以开始查询。
______________________________________________________________________
如何使用指南
有关包含真实世界示例、每个功能的详细演练和广泛故障排除的全面、分步使用指南,请参阅 HOWTOUSE.md.
本指南包括:
| 第节 | 你将学到什么 |
|---|---|
| 入门指南 | 安装、API密钥设置、服务器启动、验证 |
| 配置 | 所有配置选项,包括调优指南和完整示例 |
| 连接MCP客户端 | VS Code(HTTP/STDIO)、克劳德桌面、自定义客户端 |
| 文件摄入 | 单次、批量、标记和基于收集的摄入 |
| 两级检索 | 分阶段搜索的完整演练→ 扩展→ 块工作流 |
| 高级搜索 | 混合搜索、多查询融合、相似文档、标签过滤 |
| 文档管理 | 更新、删除、元数据检查 |
| 知识库 | 自动同步文件夹设置、PDF、子文件夹、重新同步、自动标记 |
| 集合 | 创建、组织和监视隔离的文档命名空间 |
| 可观测性 | 解释检索评分、审核日志、系统健康监控 |
| 嵌入提供者 | 所有7个提供商的详细设置及其示例 |
| 提示工程 | LLM集成的系统、评估和分析提示 |
| 真实世界用例 | 团队知识库、研究论文、代码库文档、支持、个人笔记、多租户 |
| 性能调优 | 搜索质量,API成本降低,大量收集处理 |
| 故障排除 | 40+个问题,提供分步解决方案和错误消息参考 |
| 常见问题解答 | 最常见问题的答案 |
新加入舞台RAG? 从 HOWTOUSE.md 指南——它旨在让你在几分钟内从零到高效。
______________________________________________________________________
Python快速测试
from staged_rag.tools import search_summaries, get_documents
# Level 1: Search summaries
results = search_summaries("machine learning", top_k=3)
for r in results["results"]:
print(f" [{r['similarity_score']:.2f}] {r['title']}: {r['summary'][:80]}...")
# Level 2: Expand top result
if results["results"]:
top_doc_id = results["results"][0]["doc_id"]
full = get_documents([top_doc_id], include_chunks=True)
print(f"\nFull text ({full['total_tokens']} tokens):")
print(full["documents"][0]["full_text"][:200])______________________________________________________________________
运行服务器
标准模式(流式HTTP)
python -m staged_rag.server输出:
2026-02-09 10:00:00 [staged_rag.server] INFO: Starting Staged RAG MCP Server...
2026-02-09 10:00:01 [staged_rag.server] INFO: Server listening on http://127.0.0.1:8090STDIO模式
对于生成服务器进程的MCP客户端(例如,Claude Desktop):
# In config.local.yaml
server:
transport: stdio然后,客户端使用以下命令启动服务器:
python -m staged_rag.server使用知识库监视器
# In config.yaml or config.local.yaml
knowledge_base:
enabled: true输出包括:
2026-02-09 10:00:02 [staged_rag.server] INFO: Knowledge-base watcher active – monitoring ./knowledge_base
2026-02-09 10:00:02 [staged_rag.core.kb_manager] INFO: Running initial KB sync...
2026-02-09 10:00:03 [staged_rag.core.kb_manager] INFO: Initial KB sync complete: {'created': 3, 'modified': 0, 'deleted': 0, 'errors': 0}______________________________________________________________________
嵌入提供者
Staged RAG通过工厂模式和延迟导入支持7个嵌入提供者。您只需要实际使用的提供程序的依赖关系。
谷歌双子座(默认)
embedding:
provider: gemini
model: gemini-embedding-001
dimensions: 3072# Required environment variable
GEMINI_API_KEY=your_key
# No additional pip install needed (google-genai is a core dependency)| 型号 | 尺寸 | 备注 |
|---|---|---|
gemini-embedding-001 | 768–3072 | 可配置的输出维度 |
开放人工智能
embedding:
provider: openai
model: text-embedding-3-small
dimensions: 1536OPENAI_API_KEY=your_key
pip install -e ".[openai]"| 型号 | 尺寸 | 备注 |
|---|---|---|
text-embedding-3-small | 1536 | 快速、经济高效 |
text-embedding-3-large | 3072 | 质量更高 |
text-embedding-ada-002 | 1536 | 旧型号 |
Ollama(当地)
embedding:
provider: ollama
model: nomic-embed-text
dimensions: 768
provider_config:
ollama_base_url: http://localhost:11434# No API key needed — runs locally
pip install -e ".[ollama]"
ollama pull nomic-embed-text| 型号 | 尺寸 | 备注 |
|---|---|---|
nomic-embed-text | 768 | 通用性良好 |
all-minilm | 384 | 轻量化 |
mxbai-embed-large | 1024 | 更高质量 |
拥抱脸
本地模式(句子转换):
embedding:
provider: huggingface
model: all-MiniLM-L6-v2
dimensions: 384pip install -e ".[huggingface]"API模式(HuggingFace推理):
embedding:
provider: huggingface
model: sentence-transformers/all-MiniLM-L6-v2
dimensions: 384
provider_config:
huggingface_base_url: https://api-inference.huggingface.co/pipeline/feature-extraction/sentence-transformers/all-MiniLM-L6-v2HUGGINGFACE_API_KEY=your_key
pip install -e ".[openai]" # Uses OpenAI-compatible clientAzure OpenAI
embedding:
provider: azure_openai
model: text-embedding-3-small
dimensions: 1536
provider_config:
azure_kwargs:
azure_deployment: my-embedding-deployment
azure_endpoint: https://my-resource.openai.azure.com/
api_version: 2024-02-01EMBEDDING_AZURE_OPENAI_API_KEY=your_key
pip install -e ".[openai]"一起AI
embedding:
provider: together
model: togethercomputer/m2-bert-80M-8k-retrieval
dimensions: 768TOGETHER_API_KEY=your_key
pip install -e ".[together]"LM工作室(本地)
embedding:
provider: lmstudio
model: text-embedding-nomic-embed-text-v1.5
dimensions: 768
provider_config:
openai_base_url: http://localhost:1234/v1# No API key needed — runs locally via LM Studio
pip install -e ".[openai]" # Uses OpenAI-compatible client切换提供商
要切换提供商,请更新 config.local.yaml:
embedding:
provider: ollama
model: nomic-embed-text
dimensions: 768然后重新启动服务器。现有文件保留其嵌入内容;新文档使用新提供程序。
重要:如果更改嵌入提供程序或模型,现有的向量索引将变得不兼容。删除data/index/*.npz重新摄取文档或使用kb_resync()用于知识库文档。
确定性回退
如果嵌入API不可用(没有API密钥、速率受限、网络错误),则引擎返回到确定性向量生成器:
- 输入文本的SHA-256哈希→ seed
- 具有该种子的NumPy随机正态分布→ 向量
- L2标准化→ 单位向量
这确保了服务器永远不会因嵌入失败而崩溃。确定性向量产生一致的(但质量较低的)相似性得分。
______________________________________________________________________
MCP工具参考
所有工具都通过模型上下文协议公开,可以从任何MCP客户端调用。每个工具都被包裹在一个错误捕获装饰器中(@_safe_tool)--如果工具抛出异常,则返回 {"error": "ExceptionType: message"} 而不是使服务器崩溃。
检索工具
搜索_总结
一级检索 --每次搜索的入口点。返回具有相似性得分的紧凑摘要。
search_summaries(
query: str,
top_k: int = 5,
collection: str = "default",
min_score: float = 0.0,
tags_filter: list[str] | None = None
) → dict参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
query | str | *必需的* | 自然语言搜索查询 |
top_k | int | 5 | 结果数量(上限为 max_top_k) |
collection | str | "default" | 要搜索的收藏 |
min_score | float | 0.0 | 最小相似性得分阈值(0.0–1.0) |
tags_filter | list[str] | None | 仅返回至少有一个匹配标签的文档 |
退货:
{
"query": "machine learning algorithms",
"results": [
{
"doc_id": "a1b2c3d4-...",
"title": "Introduction to ML",
"summary": "This document covers fundamental machine learning concepts...",
"similarity_score": 0.92,
"token_count": 1500,
"tags": ["ml", "tutorial"],
"collection": "default"
}
],
"total_candidates": 25,
"search_time_ms": 145.3
}______________________________________________________________________
get_文档
二级检索 --获取所选文档ID的完整文档文本。
get_documents(
doc_ids: list[str],
include_chunks: bool = False,
collection: str = "default"
) → dict参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
doc_ids | list[str] | *必需的* | 要检索的文档ID |
include_chunks | bool | False | 包括单个块数据 |
collection | str | "default" | 要搜索的收藏 |
退货:
{
"documents": [
{
"doc_id": "a1b2c3d4-...",
"title": "Introduction to ML",
"full_text": "Complete document text...",
"source": "manual",
"token_count": 1500,
"tags": ["ml", "tutorial"],
"metadata": {"author": "John"},
"chunks": [
{
"chunk_index": 0,
"text": "First chunk text...",
"token_count": 195,
"start_char": 0,
"end_char": 1024
}
]
}
],
"total_tokens": 1500
}______________________________________________________________________
get_document_chunk
2.5级检索 --通过索引或语义查询检索单个块。
get_document_chunk(
doc_id: str,
chunk_index: int | None = None,
chunk_query: str | None = None,
collection: str = "default"
) → dict参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
doc_id | str | *必需的* | 文档ID |
chunk_index | int | None | 特定块索引(从0开始) |
chunk_query | str | None | 语义查询以找到最相关的块 |
collection | str | "default" | 收藏名称 |
提供其中之一chunk_index或chunk_query不是两者都有。
返回值(按索引):
{
"doc_id": "a1b2c3d4-...",
"chunk_index": 2,
"total_chunks": 8,
"text": "The chunk text content...",
"token_count": 195,
"has_previous": true,
"has_next": true
}返回(通过查询,包括相关性得分):
{
"doc_id": "a1b2c3d4-...",
"chunk_index": 5,
"total_chunks": 8,
"text": "Most relevant chunk...",
"token_count": 180,
"has_previous": true,
"has_next": true,
"relevance_score": 0.87
}______________________________________________________________________
高级搜索工具
混合搜索
将语义(向量)搜索与BM25关键字匹配相结合,以提高召回率。
hybrid_search(
query: str,
top_k: int = 5,
collection: str = "default",
semantic_weight: float = 0.7,
keyword_weight: float = 0.3
) → dict参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
query | str | *必需的* | 搜索查询 |
top_k | int | 5 | 结果数量 |
collection | str | "default" | 收藏名称 |
semantic_weight | float | 0.7 | 向量相似度权重(0.0–1.0) |
keyword_weight | float | 0.3 | BM25关键字匹配权重(0.0–1.0) |
它是如何工作的:
- 运行语义搜索→ 获取余弦相似性得分(已经为0-1)
- 运行BM25关键字搜索→ 将分数归一化为0-1(除以最大值)
- 计算加权组合:
score = (semantic_weight × sem_score) + (keyword_weight × kw_score) - 权重会自动归一化为1.0
- 返回按综合得分排名的top_k结果
最适合: 关键字精确匹配的查询与语义含义一起重要。
______________________________________________________________________
多查询搜索
运行多个查询并使用排名融合合并结果。
multi_query_search(
queries: list[str],
top_k: int = 5,
collection: str = "default",
fusion_method: str = "rrf"
) → dict参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
queries | list[str] | *必需的* | 两个或多个搜索查询 |
top_k | int | 5 | 最终结果数量 |
collection | str | "default" | 收藏名称 |
fusion_method | str | "rrf" | 融合方法: "rrf" (互惠排名融合)或 "max" (最高分) |
融合方法:
rrf(互惠排名融合):score = Σ 1/(60 + rank + 1)对于文档出现的每个查询。出现在多个查询结果中的文档得到增强。max:获取每个文档的所有查询的最大相似性得分。
最适合: 捕捉一个主题的多个方面。例如,同时搜索“神经网络”、“深度学习”、“反向传播”。
______________________________________________________________________
find_类似物
查找与参考文档类似的文档。
find_similar(
doc_id: str,
top_k: int = 5,
exclude_same_source: bool = False,
collection: str = "default"
) → dict参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
doc_id | str | *必需的* | 参考文档ID |
top_k | int | 5 | 类似文件的数量 |
exclude_same_source | bool | False | 从同一来源排除文档 |
collection | str | "default" | 收藏名称 |
它是如何工作的:
- 检索参考文档的摘要(或标题)
- 嵌入摘要文本
- 在向量索引中搜索最近邻
- 从结果中排除参考文档本身
______________________________________________________________________
文档管理工具
ingest_文档
将单个文档添加到系统中。
ingest_document(
title: str,
text: str,
source: str = "manual",
collection: str = "default",
tags: list[str] | None = None,
metadata: dict | None = None,
summary: str | None = None
) → dict参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | str | *必需的* | 文档标题(不得为空) |
text | str | *必需的* | 文档全文(不得为空) |
source | str | "manual" | 源标识符(URL、路径、标签) |
collection | str | "default" | 目标集合 |
tags | list[str] | None | 可搜索标签 |
metadata | dict | None | 任意键值元数据 |
summary | str | None | 预先计算的摘要(如果提供,则跳过自动生成) |
加工管道:
- 验证标题和文本是否为非空
- 对照检查令牌计数
max_document_tokens - 生成摘要(Gemini API→ 本地提取回退)
- 将文本分割成重叠的片段
- 创建
Document具有UUID的模型 - 保存到JSON存储
- 嵌入摘要(或标题)→ 向矢量索引追加销售
- 重建BM25索引
- 写入审核日志条目
退货:
{
"doc_id": "a1b2c3d4-e5f6-...",
"title": "My Document",
"collection": "default",
"chunk_count": 8,
"token_count": 1500,
"summary": "Generated or provided summary...",
"status": "indexed"
}______________________________________________________________________
ingest_batch
在一次操作中批量摄取多个文档。
ingest_batch(
documents: list[dict],
collection: str = "default"
) → dict参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
documents | list[dict] | *必需的* | 文档字典列表(每个字典都必须有 title 和 text) |
collection | str | "default" | 目标集合 |
每个文档字典都支持与 ingest_document: title, text, source, tags, metadata, summary.
退货:
{
"total": 10,
"succeeded": 9,
"failed": 1,
"results": [
{"doc_id": "...", "title": "Doc 1", "status": "indexed"},
{"title": "Doc 2", "status": "error", "error": "Text must not be empty"}
],
"total_tokens_indexed": 12500
}______________________________________________________________________
update_document
更新现有文档的元数据、文本或摘要。
update_document(
doc_id: str,
text: str | None = None,
title: str | None = None,
tags: list[str] | None = None,
metadata: dict | None = None,
summary: str | None = None,
collection: str = "default"
) → dict除以下参数外的所有参数 doc_id 是可选的——只有提供的字段才会更新。
行为:
title,tags:直接更换metadata:与现有元数据进行浅层合并text:触发完整的重新分块、重新摘要和重新嵌入summary(无text):更新摘要并重新嵌入
______________________________________________________________________
删除文档
删除文档和所有相关数据。
delete_document(
doc_id: str,
collection: str = "default"
) → dict清理的内容:
- 从JSON存储中删除文档
- 从NumPy索引中删除向量
- 在没有文档的情况下重建BM25索引
- 已记录审核日志条目
______________________________________________________________________
元数据工具
get_document_metadata
在不加载全文的情况下检索文档元数据(轻量级)。
get_document_metadata(
doc_id: str,
collection: str = "default"
) → dict退货:
{
"doc_id": "a1b2c3d4-...",
"title": "Document Title",
"source": "manual",
"collection": "default",
"tags": ["tag1", "tag2"],
"token_count": 1500,
"chunk_count": 8,
"created_at": "2026-02-08T10:30:00+00:00",
"updated_at": "2026-02-09T14:20:00+00:00",
"metadata": {"author": "Jane Doe"}
}______________________________________________________________________
观察性工具
collection_stats
获取集合的全面统计数据。
collection_stats(
collection: str = "default"
) → dict退货:
{
"collection": "default",
"document_count": 25,
"total_tokens": 37500,
"avg_tokens_per_doc": 1500.0,
"total_chunks": 200,
"tag_distribution": {"ml": 10, "tutorial": 5, "source:knowledge_base": 8},
"source_distribution": {"manual": 15, "knowledge_base:notes.md": 5, "batch": 5},
"oldest_document": "2026-01-15T08:00:00+00:00",
"newest_document": "2026-02-09T12:00:00+00:00",
"index_size_bytes": 245760
}______________________________________________________________________
list_collections
枚举所有具有摘要统计信息的集合。
list_collections() → dict退货:
{
"collections": [
{"name": "default", "document_count": 25, "total_tokens": 37500, "description": ""},
{"name": "research", "document_count": 10, "total_tokens": 18000, "description": ""}
]
}______________________________________________________________________
解释检索
了解文档在给定查询中排名的原因。
explain_retrieval(
query: str,
doc_ids: list[str],
collection: str = "default"
) → dict退货:
{
"query": "neural networks",
"explanations": [
{
"doc_id": "a1b2c3d4-...",
"title": "Deep Learning Basics",
"cosine_similarity": 0.92,
"bm25_score": 15.7,
"top_matching_terms": ["neural", "networks"],
"query_doc_term_overlap": 0.8,
"explanation_text": "Combined semantic and keyword scores to rank this document."
}
]
}______________________________________________________________________
检索日志
查询审核日志中最近的检索事件。
retrieval_log(
last_n: int = 10,
tool_filter: str | None = None,
session_id: str | None = None
) → dict参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
last_n | int | 10 | 最近要返回的条目数 |
tool_filter | str | None | 按工具名称过滤(例如。, "search_summaries") |
session_id | str | None | 按会话ID筛选 |
______________________________________________________________________
知识库工具
kb_status
检查知识库监视器的当前状态。
kb_status() → dict退货:
{
"kb_dir": "/path/to/knowledge_base",
"collection": "default",
"watcher_running": true,
"manifest": {
"total_files": 12,
"total_indexed": 11,
"total_errors": 1,
"last_scan": "2026-02-09T12:00:00+00:00"
}
}______________________________________________________________________
惊讶
强制知识库文件夹完全重新同步。
kb_resync() → dict发生了什么:
- 所有来自KB的文档都将从矢量存储中删除
- 清单已完全清除
- 执行新的初始同步(完整文件夹扫描+摄取)
退货:
{
"created": 12,
"modified": 0,
"deleted": 0,
"errors": 0
}______________________________________________________________________
知识库系统
概述
知识库(KB)系统提供基于文件夹的自动文档摄取。而不是手动呼叫 ingest_document 对于每个文件,您只需将文件放入 knowledge_base/ 目录,服务器自动处理一切。
运作原理
knowledge_base/
├── notes.md ← File appears
├── report.pdf
└── data/
└── analysis.txt
│
▼
┌─────────────────┐
│ File Watcher │ Polls every N seconds
│ (Polling) │ Computes SHA-256 hashes
└────────┬────────┘
│
┌────────┴────────┐
│ Diff Engine │ Compares with known state
│ Created? │ Created → ingest
│ Modified? │ Modified → re-ingest
│ Deleted? │ Deleted → remove
└────────┬────────┘
│
┌────────┴────────┐
│ KB Manager │ Reads file, cleans text
│ _ingest_file() │ Derives title and tags
│ │ Calls RAGService.ingest
└────────┬────────┘
│
┌────────┴────────┐
│ KB Manifest │ Records file → doc_id
│ (JSON) │ Persists across restarts
└─────────────────┘支持的文件类型
| 类别 | 扩展 |
|---|---|
| 文本 | .txt, .md, .markdown, .rst |
| 数据 | .json, .yaml, .yml, .csv, .tsv |
| 代码 | .py, .js, .ts, .java, .c, .cpp, .h, .go, .rs |
| 网络 | .html, .htm, .xml |
| 配置 | .log, .cfg, .ini, .toml |
| 文件 | .pdf (通过pypdf提取文本) |
自动生成的标签
从知识库中获取的每个文件都会收到自动标签:
source:knowledge_base--识别KB来源的文档filetype:--文件扩展名(例如。,filetype:md,filetype:py)folder:--子文件夹名称(例如。,folder:research,folder:docs)
PDF支持
PDF文件会自动处理:
- 文本提取 通过
pypdf(逐页) - 标题提取 来自PDF
/Title元数据字段 - 文本清理 --删除页码,修复CamelCase连接,规范空白
- 回退标题 --第一行实质性文本,或已清理的文件名
限制:
- 仅图像/扫描的PDF不产生文本(在清单中记录为“错误”)
- 跳过无法用空密码解密的加密PDF
- 非常大的PDF可能会影响
max_file_size限制(默认值为10 MB)
清单跟踪
这 kb_manifest.json file持久化文件到文档的映射:
{
"version": 1,
"kb_dir": "/absolute/path/to/knowledge_base",
"files": {
"notes.md": {
"relative_path": "notes.md",
"doc_id": "a1b2c3d4-e5f6-7890-...",
"content_hash": "sha256_hex_string",
"file_size": 2048,
"indexed_at": "2026-02-09T10:00:00+00:00",
"updated_at": "2026-02-09T10:00:00+00:00",
"status": "indexed"
},
"broken.pdf": {
"relative_path": "broken.pdf",
"doc_id": "",
"content_hash": "sha256_hex_string",
"file_size": 5242880,
"indexed_at": "2026-02-09T10:00:00+00:00",
"updated_at": "2026-02-09T10:00:00+00:00",
"status": "error",
"error": "No extractable text found in PDF"
}
},
"stats": {
"total_files": 2,
"total_indexed": 1,
"total_errors": 1,
"last_scan": "2026-02-09T10:00:00+00:00"
}
}状态值:
indexed--成功摄入并可搜索skipped--文件可读但为空error--无法处理文件(原因在error现场)
文件夹组织
使用子文件夹按主题组织文档。子文件夹名称变为可搜索标签:
knowledge_base/
├── engineering/
│ ├── architecture.md → tags: [filetype:md, source:knowledge_base, folder:engineering]
│ ├── testing-guide.md → tags: [filetype:md, source:knowledge_base, folder:engineering]
│ └── apis/
│ └── rest-design.md → tags: [filetype:md, source:knowledge_base, folder:engineering, folder:apis]
├── product/
│ ├── roadmap.md → tags: [filetype:md, source:knowledge_base, folder:product]
│ └── specs/
│ └── feature-x.pdf → tags: [filetype:pdf, source:knowledge_base, folder:product, folder:specs]
└── meeting-notes.txt → tags: [filetype:txt, source:knowledge_base]使用标签过滤器搜索:
# Find only engineering documents
results = search_summaries("API design", tags_filter=["folder:engineering"])
# Find only PDF documents
results = search_summaries("specification", tags_filter=["filetype:pdf"])初始同步与后台监视器
| 阶段 | 时间 | 发生了什么 |
|---|---|---|
| 初始同步 | 服务器启动 | 完整文件夹扫描;比较清单与文件系统;摄入新内容、重新摄入已修改内容、删除已删除内容 |
| 后台监视器 | 每一个 poll_interval 秒 | 增量扫描;检测并处理自上次扫描以来的更改 |
后台监视器在守护进程线程中运行,并在服务器关闭时自动停止。
______________________________________________________________________
文档生命周期
数据摄取管道
Input Text
│
├─→ Token Count Check (max_document_tokens)
│ │ fail → return error
│ │ pass ↓
├─→ Summary Generation
│ ├─→ Gemini API (if available + auto_summary=true)
│ │ │ fail → local fallback
│ └─→ Local Extractive Fallback
│ ├─→ Clean text (PDF noise, CamelCase, whitespace)
│ ├─→ Split sentences
│ ├─→ Filter noise (short, numeric, header-like)
│ └─→ Select top N substantive sentences
│
├─→ Chunking (sentence-based)
│ ├─→ Split into sentences
│ ├─→ Accumulate until chunk_size tokens
│ ├─→ Carry over chunk_overlap tokens
│ └─→ Filter chunks .json)
│ ├─→ Vector Index (data/index/.npz)
│ └─→ BM25 Index (in-memory rebuild)
│
└─→ Audit Log (data/logs/audit.jsonl)文档模型
每个文档都存储为Pydantic Document 型号:
class Document(BaseModel):
doc_id: str # UUID4 — stable unique identifier
title: str # Human-readable title
source: str # Origin: path, URL, or label
full_text: str # Complete document text
summary: str # 2–4 sentence summary for Level 1 retrieval
chunks: list[DocumentChunk] # Overlapping text segments
tags: list[str] # Searchable tags (e.g., ["ml", "tutorial"])
collection: str # Collection name (default: "default")
token_count: int # Token count of full_text
created_at: datetime # UTC creation timestamp
updated_at: datetime # UTC last-update timestamp
metadata: dict # Arbitrary key-value metadata每个区块:
class DocumentChunk(BaseModel):
chunk_index: int # 0-based position in document
text: str # Chunk text content
token_count: int # Token count of this chunk
start_char: int # Start character offset in full_text
end_char: int # End character offset in full_text分块策略
系统使用 基于句子的重叠组块:
- 句子分割 --文本在句末标点处被拆分(
.,!,?),单个换行符连接到段落中 - 积累 --句子累积成一大块,直到
chunk_size已达到令牌 - 重叠 --当达到块边界时,最后一个
chunk_overlap令牌会传递到下一个块,提供上下文连续性 - 最小尺寸 --小于的块
min_chunk_size令牌将被丢弃(除非它们是唯一的内容)
摘要生成
摘要对于一级检索效率至关重要。系统提供两条路径:
路径1:Gemini API(默认)
- 发送带有结构化提示的完整文档文本
- 生成2-4个句子的摘要
- 关注关键事实和文件意图
路径2:局部提取回退
- 在以下情况下自动激活:
- 未配置API密钥 - API调用失败(速率限制、网络错误等)
- 管道:
1. 干净的文本(PDF工件、CamelCase连接、空白) 1. 拆分成句子 1. 过滤噪音(太短,主要是数字、全大写标题、样板) 1. 从开头选择前N个实质性句子(主题陈述位置)
嵌入和索引
嵌入 --使用配置的嵌入提供程序将文档的摘要(或标题,如果摘要为空)编码为固定维度的浮点向量。
向量索引 --矢量存储在NumPy压缩文件中(.npz).搜索使用归一化余弦相似度:
similarity(q, d) = (q · d) / (|q| × |d|)向量在存储前进行L2归一化,因此相似性降低为点积。
BM25指数 --内存中的Okapi BM25索引是由文档标题、摘要和全文构建的。每次摄取、更新或删除时都会重建此索引,以确保一致性。
______________________________________________________________________
搜索和检索
语义搜索
主搜索模式。嵌入查询,然后使用余弦相似度找到最近的文档向量。
优势: 理解意思,处理同义词,跨语言工作 缺点: 可能会错过精确的术语匹配,取决于嵌入质量
BM25关键字搜索
使用Okapi BM25算法的经典词频逆文档频率评分。
优势: 精确的术语匹配,快速,无需API调用 缺点: 没有语义理解,对词汇不匹配敏感
混合搜索详细信息
将语义和BM25分数与可配置的权重相结合:
final_score = (semantic_weight × cosine_sim) + (keyword_weight × normalized_bm25)哪里:
cosine_sim∈\[0,1\]——向量搜索中的余弦相似度normalized_bm25=bm25_score / max(bm25_scores)--标准化为\[0,1\]- 权重会自动归一化:如果
semantic_weight=0.7和keyword_weight=0.3,它们除以它们的总和
多查询融合
运行多个独立查询并合并结果:
互惠秩融合(RRF):
score(d) = Σ_{q ∈ queries} 1 / (60 + rank_q(d) + 1)出现在多个查询结果中的文档累积了更高的RRF分数。常数60减弱了等级差异的影响。
最高分数:
score(d) = max_{q ∈ queries} similarity_q(d)只需在所有查询中获得最高的相似性得分。
相似性搜索
find_similar 使用参考文档的摘要作为查询:
- 检索参考文档
- 嵌入其摘要(或标题)
- 搜索最近邻的向量索引
- 从结果中排除参考文档
- 可选择排除具有相同内容的文档
source
及排名
所有分数在返回给客户端之前都被限制在\[0.0,1.0\]。这确保了无论采用何种评分方法,都能得到一致的解释。
______________________________________________________________________
集合
什么是收藏
集合是独立的文档命名空间。每个系列都有自己的:
- JSON文档存储(
data/store/.json) - 矢量索引(
data/index/.npz) - BM25索引(内存中)
不同收藏中的文档在搜索过程中不会相互作用。
创建收藏
集合是隐式创建的——只需摄入一个具有新集合名称的文档:
ingest_document(
title="My Doc",
text="Content...",
collection="research" # Creates "research" collection automatically
)多重收集策略
推荐模式:
| 模式 | 何时使用 |
|---|---|
单身 default 集合 | 小项目、原型设计 |
基于主题(research, docs, code) | 具有不同文档类别的中型项目 |
基于租户(user_123, org_456) | 多租户应用程序 |
时间(2026_q1, 2026_q2) | 时间序列文档档案 |
收款统计
监控集合 collection_stats:
stats = collection_stats("research")
# Check document count, token usage, tag distribution, etc.______________________________________________________________________
存储后端
文档存储(JSON)
- 地点:
data/store/.json - 格式: JSON对象映射
doc_id→ 完整文档记录 - 螺纹安全:
threading.Lock用于并发访问 - 缓存: 每个集合的内存缓存(延迟加载)
矢量索引(NumPy)
- 地点:
data/index/.npz - 格式: NumPy压缩存档
doc_ids阵列和vectors矩阵 - 操作:
upsert,delete,search(余弦相似度) - 螺纹安全:
threading.Lock对于所有操作 - 坚持不懈: 每次突变后自动保存到磁盘
BM25索引(内存中)
- 图书馆:
rank-bm25(霍加皮BM25) - 重建触发器: 每次文档摄取、更新或删除
- 语料库: 每份文档的标题+摘要+全文
- 没有毅力 --在服务初始化时从文档存储中重建
审核日志(jsonl)
- 地点:
data/logs/audit.jsonl - 格式: 每行一个JSON对象(仅可追加)
- 自动截断: 超过时删除最旧的条目
max_log_entries - 螺纹安全:
threading.Lock为写作
KB清单(JSON)
- 地点:
data/kb_manifest.json - 目的: 跟踪已摄入的文件、它们的文档ID和内容哈希
- 坚持不懈: 更新每个文件事件(创建、修改、删除)
______________________________________________________________________
可观察性和审计
审计日志
每次工具调用都会记录以下内容:
{
"timestamp": "2026-02-09T12:34:56.789+00:00",
"tool": "search_summaries",
"params": {
"query": "machine learning",
"top_k": 5,
"collection": "default"
},
"result_count": 3,
"doc_ids": ["a1b2c3d4-...", "e5f6g7h8-...", "i9j0k1l2-..."],
"latency_ms": 145.3
}检索说明
对于任何查询文档对, explain_retrieval 提供:
- 余弦相似度 --原始语义相似度得分
- BM25得分 --原始关键字匹配分数
- 术语重叠率 --文档中找到的查询词的比例
- 最匹配的术语 --哪些查询词匹配
收集监控
跟踪系统健康状况 collection_stats:
- 文档数量和随时间的增长
- 代币预算消耗
- 标签和来源分布分析
- 索引文件大小
______________________________________________________________________
与MCP客户端集成
VS代码/GitHub副本
添加到您的VS代码MCP设置(.vscode/mcp.json 或用户设置):
{
"mcpServers": {
"staged-rag": {
"command": "python",
"args": ["-m", "staged_rag.server"],
"cwd": "/path/to/staged-rag-mcp",
"env": {
"GEMINI_API_KEY": "your_key_here"
}
}
}
}对于HTTP传输:
{
"mcpServers": {
"staged-rag": {
"url": "http://127.0.0.1:8090/mcp"
}
}
}克劳德桌面版
添加到Claude Desktop的MCP配置(claude_desktop_config.json):
{
"mcpServers": {
"staged-rag": {
"command": "python",
"args": ["-m", "staged_rag.server"],
"cwd": "/path/to/staged-rag-mcp",
"env": {
"GEMINI_API_KEY": "your_key_here"
}
}
}
}自定义MCP客户端
任何兼容MCP的客户端都可以连接。服务器公开了17个工具:
| 类别 | 工具 |
|---|---|
| 检索 | search_summaries, get_documents, get_document_chunk |
| 高级搜索 | hybrid_search, multi_query_search, find_similar |
| 管理层 | ingest_document, ingest_batch, update_document, delete_document |
| 元数据 | get_document_metadata |
| 可观察性 | collection_stats, list_collections, explain_retrieval, retrieval_log |
| 知识库 | kb_status, kb_resync |
______________________________________________________________________
分阶段RAG的快速工程设计
这 prompts.py 该模块为在LLM应用程序中实现分阶段检索工作流提供了结构化提示。
系统提示
from staged_rag.prompts import staged_rag_system_prompt
prompt = staged_rag_system_prompt("What are the benefits of microservices?")生成:
You are a knowledge assistant with access to a staged RAG system.
RETRIEVAL PROTOCOL:
1) SEARCH: call search_summaries with the user's question.
2) EVALUATE: read summaries and scores.
3) EXPAND: call get_documents for relevant docs only.
4) SYNTHESIZE: answer using retrieved content.
5) CITE: reference doc_ids for claims.
USER QUESTION: What are the benefits of microservices?评估摘要提示
from staged_rag.prompts import evaluate_summaries_prompt
prompt = evaluate_summaries_prompt(json.dumps(summaries, indent=2))指示法学硕士审查搜索结果,并决定是展开还是跳过。
深度分析提示
from staged_rag.prompts import deep_analysis_prompt
prompt = deep_analysis_prompt(
question="What are the benefits?",
context=retrieved_text
)指示法学硕士根据检索到的上下文和引用综合答案。
______________________________________________________________________
测试
运行测试
# Run all tests
pytest tests/ -v
# Run with coverage
pytest tests/ --cov=staged_rag --cov-report=term-missing
# Run specific test file
pytest tests/test_document_store.py -v
# Run specific test
pytest tests/test_vector_index.py::test_upsert_and_search -v测试结构
| 测试文件 | 它测试什么 |
|---|---|
test_document_store.py | JSON文档存储上的CRUD操作 |
test_vector_index.py | 矢量更新、删除、搜索、持久化 |
test_embeddings.py | 嵌入提供程序初始化和编码 |
test_tools.py | MCP工具功能签名和响应 |
test_agent_flow.py | 端到端检索工作流 |
编写新测试
import pytest
from staged_rag.core.document_store import DocumentStore
def test_save_and_retrieve(tmp_path):
store = DocumentStore(tmp_path)
doc = {"doc_id": "test-1", "title": "Test", "full_text": "Content"}
store.save("default", doc)
result = store.get("default", "test-1")
assert result is not None
assert result["title"] == "Test"
def test_delete_nonexistent(tmp_path):
store = DocumentStore(tmp_path)
result = store.delete("default", "nonexistent")
assert result is None______________________________________________________________________
性能考量
速率限制
嵌入引擎包括内置的速率起搏:
- 默认限制: 每分钟80个请求(RPM)
- 窗口: 60秒滑动窗
- 行为: 接近极限时自动休眠
- 目的: 保持在自由层API配额范围内
代币预算管理
- 在接收时跟踪每个文档的令牌计数
collection_stats报告每个集合的总令牌数和平均令牌数max_document_tokens拒绝超出预算的文件- 1级(摘要)回报
token_count因此,LLM可以做出明智的扩张决策
嵌入缓存
- 嵌入在NumPy中持久化
.npz文件——从不为现有文档重新计算 - 只有新的或更新的文档触发嵌入API调用
- BM25索引在内存中重建(快速,无API调用)
指标表现
- 矢量搜索: O(n)线性扫描,带NumPy矩阵乘法——适用于高达~100K文档的集合
- BM25搜索: O(n×m),其中n=文档,m=查询词
- 文档查找: 从内存缓存中查找O(1)哈希映射
对于较大的集合(100000多个文档),请考虑FAISS可选依赖关系:
pip install -e ".[faiss]"______________________________________________________________________
安全
API密钥管理
- 将API密钥存储在
.env文件(gitignored) - 密钥通过以下方式加载
python-dotenv启动时 - 切勿在配置文件中硬编码密钥
- 在CI/CD管道中使用环境变量
数据隐私
- 所有数据都存储在本地(没有外部数据库)
- 以明文JSON存储的文档内容——如果需要,在静止时加密
- 审计日志可能包含查询文本——通过配置保留
max_log_entries - KB清单包含文件路径和哈希值(无内容)
访问控制
- 默认情况下,MCP服务器不实现身份验证
- 使用HTTP传输时,通过以下方式限制访问:
- 绑定到 127.0.0.1 (仅限本地主机,默认) - 使用带有身份验证的反向代理 - 部署在VPN或防火墙后面
______________________________________________________________________
故障排除
安装问题
| 问题 | 解决方案 |
|---|---|
pip install -e . 失败 | 确保Python 3.11+: python --version |
ModuleNotFoundError: google.genai | 快跑 pip install google-genai |
ModuleNotFoundError: fastmcp | 快跑 pip install fastmcp>=0.5.0 |
ModuleNotFoundError: openai | 快跑 pip install -e ".[openai]" 适用于OpenAI/Azure/LMStudio |
运行时问题
| 问题 | 解决方案 |
|---|---|
| 服务器无法启动 | 检查 .env 具有有效的API密钥;检查端口未使用 |
| “文档超过最大令牌数” | 增加 ingestion.max_document_tokens 在配置中 |
| 嵌入API速率限制 | 等待重试;减少 batch_size;使用本地提供商 |
| 确定性回退活动 | 设置有效的API密钥或使用Ollama进行本地嵌入 |
| 空搜索结果 | 检查集合名称;尝试 min_score=0.0;验证索引文件 |
| KB文件未被索引 | 确保 knowledge_base.enabled: true;检查文件扩展名支持 |
| PDF提取失败 | 安装 pypdf: pip install pypdf>=4.0.0 |
搜索质量问题
| 问题 | 解决方案 |
|---|---|
| 无关结果 | 使用 hybrid_search 关键词助推;改进文档摘要 |
| 缺少明显匹配项 | 尝试 multi_query_search 具有查询变化 |
| 低相似性得分 | 检查配置和提供者之间的嵌入维度是否匹配 |
| 提供者更改后得分不一致 | 删除 data/index/*.npz 并重新摄取所有文档 |
数据问题
| 问题 | 解决方案 |
|---|---|
| 索引不同步 | 删除 data/index/.npz 并重新摄入 |
| KB清单已过时 | 运行 kb_resync() 强制全面重建 |
| 审核日志太大 | 减少 logging.max_log_entries 或删除 audit.jsonl |
| 损坏的JSON存储 | 从备份还原;文档存储是 data/store/.json |
______________________________________________________________________
贡献
欢迎投稿!请遵循以下指南:
- 分叉 存储库
- 创建 特征分支:
git checkout -b feature/my-feature - 制造 您的更改具有清晰、描述性的提交
- 测试 您的更改:
pytest tests/ -v - 提交 带有明确描述的拉取请求
开发设置
git clone https://github.com/reddynalamari/staged-rag-mcp.git
cd staged-rag-mcp
python -m venv .venv
.venv\Scripts\activate
pip install -e ".[all-providers,dev]"
pytest tests/ -v代码风格
- 遵循PEP 8惯例
- 对所有函数签名使用类型提示
- 为公共函数和类添加文档字符串
- 保持功能集中和小型化
- 使用有意义的变量和函数名
出资协议
提交贡献即表示您同意 许可证 文件。您的贡献是根据相同的许可条款提供的,您授予原作者使用它们的永久权利。
______________________________________________________________________
路线图
计划的功能
- \[\]用于大规模集合的FAISS矢量索引后端
- \[\]超大型文档的流式摄取
- \[\]多模态嵌入(图像+文本)
- \[\]KB文件事件的Webhook通知
- \[\]非MCP客户端的REST API和MCP
- \[\]文档版本控制和历史记录
- \[\]进出口收款
- \[\]用于收款管理的Web UI仪表板
- \[\]可插拔摘要生成器(OpenAI、Anthropic、本地LLM)
- \[\]扫描PDF的OCR支持
- \[\]增量向量索引更新(避免完全重建)
- \[\]收藏级访问控制
- \[\]分布式存储后端(S3、GCS)
______________________________________________________________________
作者
沙希达·雷迪·纳拉马里
本项目由Shashidhar Reddy Nalamari设计、架构和开发。两级分阶段检索架构、多提供者嵌入系统、知识库自动同步引擎和完整的MCP服务器实现是作者的原创作品。
______________________________________________________________________
许可证
该项目发布于 自定义许可证 允许出于任何目的(个人、商业、教育)免费使用,但有两个关键条件:
- 需要归因 --你必须赞扬原作者(Shashidhar Reddy Nalamari)
- 无虚假所有权声明 --您不能声称此项目是您自己的原创作品
看 许可证 文件以获取完整条款。
快速摘要
| 允许 | 不允许 |
|---|---|
| 用于任何目的(免费) | 声明为您自己的创作 |
| 修改和分发 | 删除作者归因 |
| 商业用途(免费) | 在软件上注册IP |
| 创作衍生作品 | 在投资组合中以原创形式呈现,不含信用 |
| 学习和教学 | 提交自己的学分 |
______________________________________________________________________
致谢
本项目基于并感谢以下开源项目:
- FastMCP --用于构建MCP服务器的Python框架
- 模型上下文协议 --Anthropic的协议规范
- 派丹蒂克 --数据验证和序列化
- 数值Python --矢量运算的数值计算
- 排名bm25 --Python中的BM25实现
- 谷歌genai --谷歌生成人工智能SDK
- pypdf --PDF文本提取
- python dotenv --环境变量管理
- Yaml --YAML配置解析
- 句子变换器 --局部拥抱人脸嵌入模型
嵌入提供者架构模式的灵感来自 mem0ai/mem0 项目的嵌入器抽象。
______________________________________________________________________
Staged RAG MCP Server
Search smart. Retrieve less. Answer better.
Copyright © 2026-present Shashidhar Reddy Nalamari. All Rights Reserved.
Released under a custom license — see LICENSE for details.
