# ZP Agent Backend pre indexovanie, hybridné vyhľadávanie a RAG nad repozitárom záverečných prác `zpwiki`. Projekt načítava Markdown dokumenty, spracuje YAML metadata, rozdelí obsah na tokenové chunky a vytvorí SQLite index kombinujúci FTS5 fulltextové vyhľadávanie a embeddingy. Nad hybridným vyhľadávaním je vytvorená RAG vrstva, ktorá pripravuje kontext, zdroje a pravidlá pre odpoveď jazykového modelu. API je integrované so školským OpenWebUI ako OpenAPI Tool Server. ## Implementované - načítanie Markdown súborov a YAML front matter, - normalizácia názvov, autorov, tagov, kategórií a `published`, - tokenové chunkovanie pomocou `tiktoken`, - konfigurovateľná maximálna veľkosť chunku, overlap a minimálna veľkosť chunku, - zachovanie názvu dokumentu a hierarchie nadpisov v chunku, - SQLite databáza a FTS5 fulltextový index, - BM25 vyhľadávanie s podporou diakritiky a prefixových výrazov, - vyhľadávacie stratégie `all_terms`, `prefix_terms` a `any_term`, - embeddingy pre každý chunk pomocou modelu `intfloat/multilingual-e5-small`, - uloženie embeddingov priamo v SQLite, - vektorové vyhľadávanie pomocou cosine similarity, - hybridné vyhľadávanie FTS5 + embeddings pomocou RRF, - nižšia váha pre slabú `any_term` FTS stratégiu, - zachovanie presných `all_terms` a `prefix_terms` výsledkov bez vektorového šumu, - obmedzenie počtu výsledkov z jedného dokumentu pomocou `max_per_document`, - filtrovanie publikovaných dokumentov, - generovanie `source_url` pre dohľadateľnosť výsledkov, - RAG vrstva s pripraveným kontextom, zdrojmi a pravidlami pre grounded odpoveď, - pravidlá proti používaniu neoverených informácií a zamieňaniu rôznych typov údajov, - FastAPI endpointy `/health`, `/rag`, `/search`, `/sync` a `/webhook/gitea`, - autorizácia vyhľadávania pomocou `X-API-Key` alebo Bearer tokenu, - autorizácia `/sync` pomocou samostatného API kľúča, - CORS konfigurácia pre OpenWebUI, - integrácia s OpenWebUI cez OpenAPI Tool Server, - Gitea webhook s HMAC-SHA256 podpisom a kontrolou udalosti a repozitára, - zámok proti súbežnému reindexovaniu, - atomická výmena databázy po úspešnom reindexovaní, - persistentná Hugging Face cache v Docker volume, - warm-up embeddingového modelu, - automatizované a integračné testy vrátane RAG endpointu. ## Architektúra ``` text zpwiki ↓ Markdown + YAML ↓ normalizácia dokumentov ↓ tokenové chunkovanie ↓ SQLite ├── dokumenty a metadata ├── chunky ├── FTS5 └── embeddingy ↓ hybridné vyhľadávanie ├── FTS5 / BM25 └── embeddingové vyhľadávanie ↓ RRF fusion ↓ RAG kontext + zdroje ↓ FastAPI /rag ↓ OpenWebUI / ZP Agent ↓ jazykový model ↓ odpoveď so source_url ``` ## Štruktúra ``` text zp-agent/ ├── app/ │ └── main.py ├── scripts/ │ ├── common.py │ ├── scan_zpwiki.py │ ├── build_chunks.py │ ├── build_sqlite_index.py │ ├── embedding_utils.py │ ├── rag_utils.py │ ├── rebuild_index.py │ ├── search_db.py │ └── search_utils.py ├── test/ ├── data/ ├── Dockerfile ├── docker-compose.yml ├── requirements.txt ├── requirements-dev.txt └── README.md ``` Projekt očakáva repozitáre v tejto štruktúre: ``` text ~/DP/ ├── zpwiki/ └── zp-agent/ ``` ## Konfigurácia V koreňovom priečinku vytvor `.env`: ``` dotenv WEBHOOK_SECRET= SYNC_API_KEY= SEARCH_API_KEY=<ďalšia náhodná hodnota s minimálne 32 znakmi> EXPECTED_GITEA_REPOSITORY=KEMT/zpwiki WEBHOOK_PULL_GIT=false # Voliteľné EMBEDDING_MODEL=intfloat/multilingual-e5-small EMBEDDING_BATCH_SIZE=32 ``` Tajomstvá je možné vygenerovať príkazom: ``` bash openssl rand -hex 32 ``` Súbor `.env` sa nesmie commitovať. Chunkovanie je možné konfigurovať pomocou premenných prostredia: ``` dotenv CHUNK_MAX_TOKENS=450 CHUNK_OVERLAP_TOKENS=70 CHUNK_MIN_TOKENS=80 CHUNK_TOKEN_ENCODING=cl100k_base ``` ## Spustenie cez Docker ``` bash docker compose up -d --build ``` Kontrola služby: ``` bash curl http://127.0.0.1:8000/health ``` Swagger UI: ``` text http://127.0.0.1:8000/docs ``` Logy: ``` bash docker compose logs -f zp-agent-api ``` Zastavenie: ``` bash docker compose down ``` Hugging Face cache je uložená v persistentnom Docker volume, takže sa embeddingový model pri bežnom reštarte kontajnera nemusí znova sťahovať. ## Reindexovanie Celý proces načíta dokumenty, vytvorí chunky, obnoví FTS5 index a vytvorí embedding pre každý chunk: ``` bash docker compose run --rm zp-agent-api python scripts/rebuild_index.py ``` Vzniknú súbory: ``` text data/documents.json data/chunks.json data/zp_index.sqlite ``` Databáza obsahuje dokumenty, chunky, FTS5 index, metadata a embeddingy. Pri automatickom reindexovaní sa používa zámok proti súbežnému spusteniu a atomická výmena databázy po úspešnom vytvorení nového indexu. ## Hybridné vyhľadávanie Vyhľadávanie kombinuje: ``` text dotaz ├── FTS5 / BM25 └── embeddingové vyhľadávanie ↓ RRF fusion ↓ výsledky ``` FTS5 používa stratégie: ``` text all_terms prefix_terms any_term ``` Presné lexikálne výsledky majú prednosť pred slabšími sémantickými zhodami. Pri slabšej `any_term` stratégii sa znižuje jej váha a väčší význam môže dostať embeddingové vyhľadávanie. Test z terminálu: ``` bash docker compose run --rm zp-agent-api python scripts/search_db.py "rag agent" --limit 5 ``` Pred volaním API načítaj premenné z `.env`: ``` bash set -a source .env set +a ``` Vyhľadávanie cez zabezpečené API: ``` bash curl -X POST http://127.0.0.1:8000/search -H "Content-Type: application/json" -H "Authorization: Bearer $SEARCH_API_KEY" -d '{ "query": "strojový preklad", "limit": 3, "published_only": false, "max_per_document": 1 }' ``` API používa hybridný engine: ``` text hybrid_fts5_embeddings ``` `max_per_document=1` zabezpečí, že sa pri požiadavke na viac výsledkov preferujú rôzne dokumenty namiesto viacerých chunkov z rovnakého dokumentu. ## RAG Nad hybridným retrievalom je implementovaná RAG vrstva v `scripts/rag_utils.py`. Endpoint `/rag` vyhľadá relevantné dokumenty a pripraví: - textový kontext pre jazykový model, - metadata použitých zdrojov, - názov a autora dokumentu, - `source_url`, - interné retrieval informácie, - pravidlá pre grounded odpoveď. RAG pravidlá požadujú, aby model odpovedal iba podľa poskytnutých zdrojov. Zároveň rozlišujú údaje, ktoré sa môžu ľahko zameniť, napríklad autora dokumentu, osobu, o ktorej dokument pojednáva, rok začiatku štúdia a rok záverečnej práce. Príklad: ``` bash curl -X POST http://127.0.0.1:8000/rag -H "Content-Type: application/json" -H "Authorization: Bearer $SEARCH_API_KEY" -d '{ "query": "V akom roku robil Ján Holp diplomovú prácu?", "limit": 5, "published_only": false, "max_per_document": 1 }' ``` ## OpenWebUI ZP Agent je pripojený do OpenWebUI ako OpenAPI Tool Server. OpenWebUI používa: ``` text OpenAPI URL: http://localhost:8000/openapi.json Authentication: Bearer Token: SEARCH_API_KEY ``` V reálnom nasadení musí byť URL API dostupná z prostredia, v ktorom beží OpenWebUI; `localhost` je vhodný iba vtedy, ak sa OpenWebUI pripája k API z rovnakého hostiteľa. OpenAPI schéma určená pre integráciu vystavuje RAG nástroj, ktorý môže jazykový model použiť pri otázkach nad ZP Wiki. Tok požiadavky: ``` text používateľ ↓ OpenWebUI ↓ jazykový model ↓ ZP Agent /rag ↓ hybrid retrieval ↓ RAG kontext + zdroje ↓ jazykový model ↓ odpoveď ``` Príklad otázky: ``` text Použi ZP Agent a zisti, v akom roku robil Ján Holp diplomovú prácu. ``` Výsledná odpoveď obsahuje stručnú informáciu získanú zo ZP Wiki a príslušný `source_url`. ## Manuálna synchronizácia Manuálne reindexovanie cez zabezpečený endpoint: ``` bash curl -X POST http://127.0.0.1:8000/sync -H "Content-Type: application/json" -H "X-API-Key: $SYNC_API_KEY" -d '{"pull_git": false}' ``` ## Gitea webhook Endpoint: ``` text POST /webhook/gitea ``` Webhook overuje: - HMAC-SHA256 podpis, - typ Gitea udalosti, - očakávaný repozitár, - stav reindexovacieho zámku. Očakávaný repozitár: ``` dotenv EXPECTED_GITEA_REPOSITORY=KEMT/zpwiki ``` Ak je povolené: ``` dotenv WEBHOOK_PULL_GIT=true ``` pred reindexovaním sa vykoná: ``` bash git pull --ff-only ``` ## Bezpečnosť Vyhľadávanie podporuje API key: ``` text X-API-Key: ``` aj Bearer autentifikáciu: ``` text Authorization: Bearer ``` Bearer autentifikácia sa používa najmä pri integrácii s OpenWebUI. Endpoint `/sync` používa samostatný `SYNC_API_KEY` a webhook samostatný `WEBHOOK_SECRET`. Tajomstvá sa ukladajú do `.env`, ktorý nesmie byť súčasťou Git repozitára. ## Testy Inštalácia testovacích závislostí: ``` bash pip install -r requirements-dev.txt ``` Bežné automatizované testy: ``` bash pytest -q test ``` Aktuálny stav: ``` text 77 passed, 2 skipped ``` Testy vrátane kontroly reálnych dát a databázy: ``` bash RUN_LIVE_TESTS=1 pytest -q test ``` Aktuálny stav: ``` text 79 passed ``` Testovacia sada pokrýva indexovanie, vyhľadávanie, hybridný retrieval, API, autentifikáciu, RAG utility, RAG endpoint a OpenAPI integráciu.