Skip to main content
Glama
Mily-Lv
by Mily-Lv

RAG-MCP-SERVER

플러그인이 가능하며 전체 링크를 관측할 수 있는 모듈형 RAG 검색 서비스입니다. MCP(Model Context Protocol) 도구 형태로 검색 기능을 외부에 노출하므로 Claude Desktop, GitHub Copilot 등의 MCP Client에서 직접 호출할 수 있습니다.

핵심 설계 목표는 RAG 엔지니어링에서 자주 마주치는 두 가지 구체적인 문제를 해결하는 것입니다.

  1. 링크 추적 어려움 — 검색 결과가 잘못된 경우 문제가 리콜, 퓨전, rerank 중 어디에서 발생했는가? 인덱스와 쿼리 두 링크를 합친 10개 단계의 소요 시간, 후보 수, 점수와 순위 변화를 단계별로 기록하며, Dashboard에서 이를 시각적으로 되돌아볼 수 있습니다.

  2. 감각에 의존한 튜닝 — Embedding 모델을 바꾸면 실제로 좋아지는지 나빠지는지? Hit Rate@K / MRR와 Ragas Faithfulness / Context Precision을 함께 평가하고, 고정 테스트 세트 기반으로 회귀합니다. 주관적 판단이 아닌 지표로 보정합니다.


목차


Related MCP server: mcp-rag-assistant

아키텍처 개요

                    ┌──────────────────────────────────────────┐
  文档 (PDF/DOCX/    │           Ingestion Pipeline             │
  MD/TXT)      ───▶ │  load → split → transform → embed →      │
                    │  upsert                                  │
                    └────────────────┬─────────────────────────┘
                                     │  SHA256 指纹 + SQLite 摄取历史
                                     │  (文档级增量索引 / 幂等)
                                     ▼
                    ┌──────────────────────────────────────────┐
                    │   ChromaDB (Dense)  +  BM25 (Sparse)     │
                    └────────────────┬─────────────────────────┘
                                     ▼
                    ┌──────────────────────────────────────────┐
  查询          ───▶│            Query Engine                  │
                    │  query_processing → dense ┐              │
                    │                            ├→ RRF fusion │
                    │                    sparse ┘      │       │
                    │                                  ▼       │
                    │                              rerank      │
                    │                    (失败回退至 RRF 顺序) │
                    └────────────────┬─────────────────────────┘
                                     ▼
             ┌───────────────┬───────────────┬──────────────────┐
             │  MCP Server   │  CLI Scripts  │  Dashboard       │
             │  (3 tools)    │  (5 scripts)  │  (Streamlit 6页) │
             └───────────────┴───────────────┴──────────────────┘

  贯穿全程:TraceContext(trace → stage)写入 logs/traces.jsonl

플러그인 가능한 기반

각 핵심 단계마다 통일된 Base 인터페이스를 정의하고, Factory + YAML 구성만으로 변경할 수 있어 구성 요소 교체 시 코드 수정이 필요 없습니다.

단계

인터페이스

구현된 Provider

LLM

BaseLLM

openai / azure / deepseek / kimmi / ollama

Vision LLM

BaseVisionLLM

openai / azure / kimi

Embedding

BaseEmbedding

openai / azure / siliconflow / bge / ollama

Vector Store

BaseVectorStore

chroma

Splitter

BaseSplitter

recursive

Reranker

BaseReranker

llm / cross_encoder(BGE)

Evaluator

BaseEvaluator

custom / ragas / composite

Loader

BaseLoader

pdf / docx / markdown / text

모든 OpenAI 호환 엔드포인트는 provider: "openai" + 사용자 지정 base_url로 연동할 수 있으며, 새 코드를 추가할 필요가 없습니다.


핵심 기능

하이브리드 검색: BM25 sparse 검색은 고유 명사의 정확한 일치를 담당하고, Dense vector 검색은 의미적 일치를 담당합니다. 양방향 리콜 후 RRF 융합을 거쳐 최종적으로 Reranker가 재정렬합니다. rerank 백엔드에 실패하면 자동으로 RRF 융합 순서로 폴백하며, 일시적인 타임아웃이 전체 링크를 중단시키지 않습니다.

증분 인덱스와 멱등성: SHA256 내용 스패커 + SQLite ingestion_history 테이블로 문서 레벨 증분 처리합니다. 중복 수집은 건너뛰고, 내용이 변경된 경우에만 인덱스를 재구성하므로 중복 수집으로 인한 오염 데이터가 발생하지 않습니다.

멀티모달: PyMuPDF다 PDF에 포함된 이미지를 추출하고 원래 위치를 보존하며, Vision LLM이 생성한 이미지 설명을 Chunk에 붙여 넣어 텍스트 전용 RAG 링인을 그대로 재사용합니다. MCP 응답은 ImageContent로 이미지를 반환합니다.

MCP 도구:

Tool

용도

query_knowledge_hub

하이브리드 검색 + rerank 후 참조(이미지 포함) 포함된 결과를 반환

list_collections

모든 collection 및 문서/청크 통계 나열

get_document_summary

지정된 문서의 요약과 청크 개요를 반환

Dashboard(Streamlit 6페이지): 시스템 개요 / 데이터 탐색 / 수집 관리 / 수집 추적 / 질의 추적 / 평가 패널.


빠른 시작

환경 요구 사항

Python ≥ 3.10.

설치

git clone <your-repo-url>
cd RAG-MCP-SERVER

python -m venv .venv
# Windows
.venv\Scripts\activate
# Linux / macOS
source .venv/bin/activate

pip install -e ".[dev]"

모든 dependency에는 상한 버전이 지정되어 있습니다. mcp는 <2.0으로 고정되어 있습니다(2.x에서 CallToolResult.isError 등 필드 이름이 변경됨). langchain-community는 <0.4로 고정되어 있습니다(0.4에서 chat_models.vertexai를 제거했으며,로 인해 ragas 임포트에 실패할 수 있음).

구성

cp config/settings.yaml.example config/settings.yaml

config/settings.yaml을 편집해 자신의 API Key를 입력하세요. 이 파일은 .gitignore에 의해 무시되므로 커밋하지 마세요.

문서 인수

python scripts/ingest.py --path ./your_docs --collection my_kb
python scripts/ingest.py --path ./your_docs --collection my_kb --force   # 强制重建
python scripts/ingest.py --path ./your_docs --dry-run                    # 只看会处理哪些文件

query

python scripts/query.py -q "你的问题" -c my_kb --top-k 5 --verbose

--verbose 옵션을 지정하면 dense / sparse / fusion / rerank 각 단계의 중간 결과가 출력됩니다.

Dashboard 시작

python scripts/start_dashboard.py

MCP Client 연동

Claude Desktop을 예로 들면, claude_desktop_config.json에 다음을 추가합니다:

{
  "mcpServers": {
    "rag-mcp-server": {
      "command": "<绝对路径>/.venv/Scripts/python.exe",
      "args": ["<绝对路径>/main.py"]
    }
  }
}

구성 설명

주요 구성 구간(전체 주석은 config/settings.yaml.example 참조):

retrieval:
  dense_top_k: 20
  sparse_top_k: 20
  fusion_top_k: 10
  rrf_k: 60
  # 路由开关,用于 A/B 基线:只测 dense 则 enable_sparse: false,反之亦然
  enable_dense: true
  enable_sparse: true

rerank:
  enabled: true
  provider: "llm"           # 走已配置的 LLM,零额外依赖
  # provider: "cross_encoder"  # 本地 BGE cross-encoder,需 pip install sentence-transformers
  top_k: 5

evaluation:
  enabled: true
  provider: "composite"     # 同时跑检索指标与生成指标
  backends: ["custom", "ragas"]
  metrics: ["hit_rate", "mrr", "faithfulness", "context_precision"]

embedding.dimensions는 최초 문서를 수집한 후에는 변경할 수 없습니다. 이미 존재하는 Chroma collection이 벡터 차원과 결속되어 있기 때문입니다.


관측 가능성

모든 수집과 조회는 트레이스를 생성하여 logs/traces.jsonl에 기록하고, 구조는 trace → stages[]입니다. 각 stage에는 elapsed_ms와 해당 단계의 data가 기록됩니다.

링크

단계

Ingestion

load → split → transform → embed → upsert

Query

query_processing → dense_retrieval → sparse_retrieval → fusion → rerank

순위 변화 추적

각 단계가 끝난 뒤의 점수 목록만으로는 '이 단계가 정말로 정렬을 개선했는지, 어떤 chunk를 개선했는지'를 알 수 없습니다. 그 때문에 fusion과 rerank 단계에서는 순위 변화를 추가로 기록합니다(src/core/query_engine/rank_tracking.py).

  • 약속은 1-based이며, rank_delta = rank_before - rank_after로 표현합니다. 양수면 순위 상승

  • fusion의 rank_before는 해당 chunk가 두 경로 중에서 본 최고 순위를 가져와, 'RRF가 단일 경로 이용보다 위에 올렸는가?'를 설명합니다. 동시에 dense_rank / sparse_rank를 기록해 어느 경로에서 왔는지 보여준다.

  • rerank의 rank_before는 reranker입력 값인 fusion 목록에서의 위치를 그대로 씁니다. 이것이 reranker가 누구를 올리고 내리고 하는 정확히 보여줍니다.

  • 새로 진입한 chunk는 임의의 순위 상승을 나타내지 않고 None을 보고합니다.

  • 단계 수준의 aggregate: moved_up / moved_down / unchanged / new / max_gain / max_drop / dropped

실제 trace 발취:

stage=fusion   elapsed=0.2ms
  rank_changes: {moved_up: 3, moved_down: 1, unchanged: 1, max_gain: 2, dropped: 18}
  rank=2  before=4  delta=+2   dense_rank=4  sparse_rank=4

stage=rerank   elapsed=12231ms
  rank_changes: {moved_up: 1, moved_down: 1, unchanged: 3, max_gain: 1}
  rank=1  before=2  delta=+1

Dashboard의 '조회 추적' 페이지에서 이 내용을 단계별 워터폴 차트와 순위 변화표로 렌더링합니다.


평가 체계

python scripts/evaluate.py --collection my_kb
python scripts/experiment.py --variants dense,sparse,hybrid,hybrid_rerank
  • 검색 지표(CustomEvaluator): Hit Rate@K, MRR —— 테스트 세트의 expected_chunk_ids를 ground truth로 제공해야 합니다.

  • 생성 지표(RagasEvaluator): Faithfulness, Answer Riding | Context Precision

  • CompositeEvaluator: 두 종류의 백엔드를 동시에 실행하고 결과를 통합합니다. 각 backend는 공유 metrics 목록에서 자신의 지표를 선택하며, 단일 backend의 실패는 나머지 햐에 영향하지 않습니다.

scripts/experiment.py는 서로 다른 검색 변형을 A/B 비교하여 각 변형의 지표와 지연을 출력합니다. 이를 통해 "rerank를 붙이면 과연 12초의 비용이 들 값인가"를 판단.


테스트

계층화된 테스트로, 총 1456개 사례:

pytest tests/unit                      # 1298 passed, 1 skipped
pytest tests/integration -m "not llm"  #   94 passed, 10 skipped
pytest tests/e2e -m "not llm"          #   30 passed,  2 skipped

-m "not llm"은 실제 LLM API 호출이 필요한 사례를 제외합니다. 특정 Provider의 자격 증명이 없으면 해당 사례는 실패가 아니라 skip 및 사유 표시를 합니다.

중요한 부분마다 집중적으로 커버:

관심 대상

테스트

RRF fusion

test_fusion_rrf.py

rerank fallback path

test_reranker_fallback.py

idempotent write

test_vector_upserter_idempotency.py

순위 변화 추적

test_rank_tracking.py

토크나이저 인덱스/조회 일치성

test_sparse_encoder.py / test_query_processor.py

Chroma client 동시 생성

test_chroma_client.py

vector store 계약

test_vector_store_contract.py


프로젝트 구조

src/
├── core/
│   ├── query_engine/       # 混合检索:dense / sparse / RRF fusion / rerank
│   │   └── rank_tracking.py  # 排名变化计算(融合与重排共用)
│   ├── response/           # 响应组装、引用生成、多模态拼装
│   ├── trace/              # TraceContext:trace → stage
│   ├── tokenization.py     # BM25 分词器(索引端与查询端唯一实现)
│   └── settings.py         # YAML 配置加载与校验
├── ingestion/
│   ├── chunking/ embedding/ storage/ transform/
│   ├── pipeline.py         # 五阶段摄取流水线
│   └── document_manager.py # 文档删除(跨 Chroma / BM25 / 图片 / 摄取历史)
├── libs/                   # 可插拔底座:base_*.py + *_factory.py
│   ├── llm/ embedding/ loader/ reranker/ splitter/ vector_store/ evaluator/
├── mcp_server/             # MCP 协议与 3 个 Tool
└── observability/
    ├── dashboard/          # Streamlit 六页
    └── evaluation/         # ragas / composite / eval_runner

scripts/   ingest / query / evaluate / experiment / start_dashboard
config/    settings.yaml.example + prompts/
tests/     unit / integration / e2e

data/(Chroma, BM25 인덱스, 추출 이미지, 수집 이력)와 logs/(trace)는 모두 런타임에 생성되는 로컬 산출물입니다. .gitignore에 포함되어 저장소에 배포되지 않으며, 최초 실행 시 자동으로 만들어집니다.


라이선스

MIT

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Provides RAG-based knowledge retrieval and document management as MCP tools, supporting hybrid search, reranking, and retrieval process visualization.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    A pluggable, observable modular RAG framework that exposes query knowledge hub, list collections, and get document summary tools via MCP, enabling AI assistants to perform hybrid search and document retrieval with reranking.
    2
    MIT