P R O J E K T YK O N T A K T

[03]

DOCCHAT RAG MCP

TIER A · ROK 2025 · STATUS: DZIAŁA · JĘZYKI: PYTHON

Ręcznie pisany RAG z atrybucją źródła, wydany jako serwer MCP

[FIG. 1] MISJA

Piszę pipeline RAG ręcznie, bez frameworka, kiedy kontrola nad chunkowaniem i atrybucją jest ważniejsza niż szybkość startu — i projektuję systemy działające w całości lokalnie, w których dane nie opuszczają maszyny. Claude Desktop nie umie przeszukiwać prywatnych dokumentów, a wrzucanie wrażliwych plików do chmury odpada, więc ten RAG przy każdej odpowiedzi wskazuje źródło do poziomu pliku, strony i zakresu znaków. PyMuPDF i python-docx tną dokumenty po naturalnych granicach, z hierarchią priorytetów nagłówek→akapit→zdanie (1000 znaków, overlap 200), zamiast ciąć na ślepo co N znaków; sentence-transformers liczy embeddingi MiniLM lokalnie, a retrieval po cosine zwraca top-k z atrybucją. Retrieval trzymam oddzielony od modelu, więc wymiana LLM nie dotyka pipeline'u. Rozwiązanie wydaję jako serwer MCP, czyli narzędzie podpinane do cudzego klienta: oficjalne low-level SDK, sześć narzędzi z JSON Schema po transporcie STDIO, handshake initialize + tools/list zweryfikowany end-to-end. Ten sam rdzeń wydałem też jako darmową aplikację na Streamlit (LangChain + Qdrant + Ollama). Domyślnie zero wywołań API — wszystko liczy się na moim sprzęcie.

[FIG. 2] ARCHITEKTURA

najedź na blok, by zobaczyć opis

[FIG. 3] WYZWANIA

[+][CH-01]

Repo wyglądało na zielone, a nie startowało: pięć plików importowało pakiet src/models — dataklasy Document, DocumentChunk, SearchResult — którego w repozytorium w ogóle nie było, więc python -m src.mcp_server_official padał na pierwszym imporcie. Root cause okazał się wzorzec w .gitignore: models/ miał ignorować cache modeli ML, a połknął katalog z kodem źródłowym przy pierwszym git add (drugi wzorzec, test_*.py, zjadł testy). Odtworzyłem cały pakiet „od kontraktu” — czytając każde miejsce użycia i rekonstruując pola oraz metody, których reszta kodu oczekiwała — poprawiłem wzorce na zakotwiczone (/models/) i dołożyłem test importu w smoke-testach, żeby ta klasa błędu już się nie prześlizgnęła. Przy okazji wyleciały zależności-widma: fastmcp, psutil i watchdog zadeklarowane bez ani jednego importu, a brakujący pydantic-settings — realnie importowany — wszedł do zależności.

[+][CH-02]

Mieszane PDF-y i DOCX-y nie mają wspólnej struktury — naiwne cięcie co N znaków rozrywało zdania w połowie i gubiło kontekst. Docstring obiecywał LangChaina, ale splitting napisałem ręcznie, w dwóch formatowo-świadomych wariantach: PDF łamie na podwójnym enterze, końcu zdania i pojedynczym enterze, śledząc numer strony dla atrybucji; DOCX ma system priorytetów granic — nagłówek wygrywa z akapitem, akapit ze zdaniem — i pilnuje przynależności chunka do sekcji. Overlap 200 znaków sprawia, że fakt na styku chunków nie ginie. Efekt: odpowiedź zawsze umie wskazać, z której strony i sekcji pochodzi.

[+][CH-03]

SentenceTransformer jest synchroniczny i CPU-bound — wywołany wprost w handlerze async zablokowałby event-loop serwera MCP i Claude Desktop widziałby zamrożone narzędzia. Zdjąłem model z pętli: ładowanie i encode idą przez pulę wątków (run_in_executor), dokumenty indeksują się wsadowo przez asyncio.gather z return_exceptions, a błąd embeddingu degraduje do wektora zerowego zamiast wywracać indeksowanie. Wyłapałem też utajnioną minę w martwym module: logger pisał na stdout, co w serwerze STDIO złamałoby ramkowanie JSON-RPC — logi należą na stderr, a niepodłączony kod poszedł do kosza (−565 linii), bo martwy moduł to dług, nie wartość.

[FIG. 4] WARSTWA AI

Model językowy siedzi po stronie klienta — Claude Desktop przez MCP albo Ollama w wersji free — a mój kod odpowiada za retrieval: embeddingi MiniLM, ranking cosine i atrybucję źródła. Ten podział jest celowy: wymiana modelu nie dotyka pipeline'u, a dane nigdy nie opuszczają maszyny.

[FIG. 2A] ANATOMIA CHUNKINGU

wejście: zwykły PDF/DOCX — zero preprocessingu po stronie usera

[FIG. 5] GALERIA

DOCCHAT RAG MCP — fig 5
[FIG. 5] 01/02
DOCCHAT RAG MCP — fig 6
[FIG. 6] 02/02

[FIG. 6] STACK & LINKI

MCP SDK (OFICJALNE, LOW-LEVEL)SENTENCE-TRANSFORMERSPYMUPDFPYTHON-DOCXPYDANTICLANGCHAINQDRANTOLLAMASTREAMLIT