dp-zp-agent/README.md
2026-10-26 19:23:02 +01:00

40 KiB
Raw Blame History

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.

Stabilná baseline retrieval vetva používa klasický hybridný RAG: FTS5/BM25 + embeddingy + RRF. Nad týmto baseline sa vo vetve GraphRAG dopĺňa knowledge graph uložený v Neo4j. Aktuálne je implementovaná grafová infraštruktúra, deterministický builder, vizualizácia grafu a samostatné grafové vyhľadávanie. Grafová vrstva má zdrojovo overený benchmark a zmrazenú opravenú verziu v7 s novým TESTom 80 kombinácií a regresiou 320 historických otázok, kontrolou všetkých vrátených typovaných faktov a samostatným overením generovania odpovedí cez OpenWebUI API. Ďalším krokom je natívne zapojenie grafového nástroja a hlavného agenta do OpenWebUI.

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,
  • lexikálne zvýhodnenie presných alebo veľmi silných zhôd,
  • 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ď,
  • autorizovaný /answer s kontrolou faktických väzieb, citácií a úplnosti, najviac jednou opravou a zostavením finálneho textu z overených údajov; OpenWebUI Pipe, postup je nižšie v časti OpenWebUI,
  • rozšírenie RAG kontextu o začiatok rovnakej sekcie (section-lead expansion) bez globálneho zvýšenia max_per_document,
  • pravidlá proti používaniu neoverených informácií a zamieňaniu rôznych typov údajov,
  • no-answer správanie pri chýbajúcej alebo nedostatočne podloženej informácii,
  • FastAPI endpointy /health, /rag, /search, /sync a /webhook/gitea,
  • striktná validácia API vstupov,
  • 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,
  • bezpečnostné HTTP hlavičky,
  • integrácia s OpenWebUI cez OpenAPI Tool Server,
  • Gitea webhook s HMAC-SHA256 podpisom, limitom veľkosti payloadu 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,
  • Neo4j databáza spúšťaná ako samostatná Docker služba,
  • deterministická tvorba knowledge graphu z documents.json a chunks.json,
  • grafové entity Person, Document, DocumentAuthor, Thesis, Work, Keyword, Topic a Category,
  • grafové vzťahy pre dokumenty, práce, témy, kategórie a autorstvo,
  • uniqueness constraints a opakovateľný rebuild GraphRAG grafu,
  • Neo4j Browser na vizualizáciu a kontrolu vytvoreného grafu,
  • samostatné grafové vyhľadávanie nad Neo4j s rozpoznaním entít, vzťahov, prác, tém a rokov,
  • retrieval evaluácia pre FTS, embeddings a hybridný retrieval,
  • answer-level RAG evaluácia cez OpenWebUI model a RAG tool,
  • oddelené answer-level overrides bez úpravy frozen retrieval benchmarku,
  • príprava rozšírených datasetov pre extended RAG, GraphRAG, robustness a performance experimenty.

Architektúra

zpwiki
  ↓
Markdown + YAML
  ↓
scan_zpwiki.py
  ↓
normalizované dokumenty
  ↓
build_chunks.py
  ↓
tokenové chunky
  ↓
build_sqlite_index.py
  ↓
SQLite
├── dokumenty
├── metadata
├── chunky
├── FTS5
└── embeddingy
  ↓
search_core.py / search_utils.py
  ↓
hybridné vyhľadávanie
├── FTS5 / BM25
├── embeddingové vyhľadávanie
└── RRF fusion + lexical anchor
  ↓
rag_utils.py
├── výber zdrojov
├── section-lead expansion
├── RAG kontext
└── grounding pravidlá
  ↓
FastAPI /rag
  ↓
OpenWebUI / ZP Agent
  ↓
jazykový model
  ↓
odpoveď + source_url

GraphRAG vývojová vetva:

documents.json + chunks.json
  ↓
graphrag.py
  ↓
build_graphrag.py
  ↓
Neo4j
├── Person
├── Document
├── Thesis
├── Work
├── Keyword
├── Topic
├── Category
└── DocumentAuthor
  ↓
grafové vzťahy + vizualizácia
  ↓
graph_search.py
  ↓
grafové entity + cesty + zdrojové dokumenty
  ↓
spojenie s klasickým RAG

V aktuálnom stave je implementované vytvorenie a uloženie knowledge graphu aj samostatné grafové vyhľadávanie. Jeho zapojenie do /rag a OpenWebUI sa ešte dopĺňa.

Synchronizačná vetva:

Gitea push
  ↓
POST /webhook/gitea
  ↓
HMAC + repository + event validácia
  ↓
durable synchronizačná fronta
  ↓
overenie vetvy + git fetch + merge --ff-only
  ↓
nové JSON, SQLite a grafový payload v stagingu
  ↓
koordinované prepnutie SQLite a Neo4j + spoločná verzia dát

Evaluačná vetva:

tools/rag/evaluation/json_files/*.json
  ↓
retrieval evaluator / RAG answer evaluator
  ↓
runner
  ↓
metriky
  ↓
tools/rag/evaluation/results/

tools/rag/evaluation/results/ nie je v nižšie uvedenom stromčeku, pretože obsahuje generované výstupy experimentov.

Štruktúra projektu

Nižšie je funkčná štruktúra projektu. Testovacie súbory, generované výsledky, cache, __pycache__, Git interné súbory a podobné pomocné artefakty sú zámerne vynechané.

zp-agent/
├── app/
│   ├── main.py
│   ├── routes.py
│   └── security.py
│
├── tools/
│   ├── answers/
│   │   ├── answer_delivery.py
│   │   └── openwebui_pipe.py
│   ├── answer_evaluation/
│   │   ├── checked_generation.py
│   │   ├── context_policy.py
│   │   └── evaluate_graph_answers.py
│   ├── retrieval/
│   │   ├── publication.py
│   │   ├── retrieval_contract.py
│   │   └── text_budget.py
│   ├── runtime/
│   │   ├── runtime_assets.py
│   │   ├── runtime_health.py
│   │   ├── tool_health.py
│   │   └── bootstrap_indexes.py
│   ├── sync/
│   │   ├── index_state.py
│   │   ├── sync_indexes.py
│   │   ├── sync_jobs.py
│   │   └── wiki_git.py
│   ├── rag/
│   │   ├── common.py
│   │   ├── scan_zpwiki.py
│   │   ├── build_chunks.py
│   │   ├── build_sqlite_index.py
│   │   ├── embedding_utils.py
│   │   ├── rebuild_index.py
│   │   ├── search_core.py
│   │   ├── search_utils.py
│   │   ├── search_db.py
│   │   ├── rag_utils.py
│   │   ├── rag_document_expansion.py
│   │   ├── rag_query_evidence.py
│   │   ├── rag_supplemental_expansion.py
│   │   ├── run_e5_regression.sh
│   │   ├── run_e5_1_regression.sh
│   │   ├── evaluation/
│   │   │   ├── evaluate_retrieval.py
│   │   │   ├── evaluate_rag_answers.py
│   │   │   ├── retrieval_runner.py
│   │   │   ├── rag_runner.py
│   │   │   ├── metrics.py
│   │   │   ├── rag_metrics.py
│   │   │   └── json_files/
│   │   └── tests/
│   │
│   └── graphrag/
│       ├── graph_db.py
│       ├── graphrag.py
│       ├── build_graphrag.py
│       ├── graph_search.py
│       ├── neo4j_smoke.py
│       ├── evaluation/
│       │   └── graphrag_questions.json
│       └── tests/
│
├── test/
│   ├── test_api.py
│   ├── test_configuration.py
│   └── test_security.py
│
├── scripts/
│   ├── start_project.sh
│   └── verify_project.py
│
├── data/
│   ├── documents.json
│   ├── chunks.json
│   └── zp_index.sqlite
│
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── requirements-dev.txt
└── README.md

answers/ doručuje overené odpovede a obsahuje inštalovateľný OpenWebUI Pipe. answer_evaluation/ obsahuje kontrolu odpovedí, renderer a evaluačné klienty. retrieval/ zdieľa dátový kontrakt, publikačnú politiku a delenie podľa rozpočtu. runtime/ pripravuje model/tokenizér a kontroluje štart a dostupnosť služieb. sync/ spravuje Git, frontu a spoločné publikovanie SQLite/Neo4j indexov. V koreňovom tools/ zostáva iba __init__.py.

Na Gite je z dokumentácie iba tento hlavný README. Vedľajšie návody, pracovné záznamy a textové správy ostávajú lokálne a sú ignorované. JSON/CSV výsledky a pôvodné experimentálne manifesty zostávajú zachované. Historické zmrazené merania sa reprodukujú v ich pôvodnom Git checkoute; presun modulov nemení ich uložené odtlačky ani výsledky.

app/

app/main.py

  • vytvára FastAPI aplikáciu,
  • nastavuje lifespan aplikácie,
  • validuje bezpečnostnú konfiguráciu pri štarte,
  • vykonáva warm-up embeddingovej vrstvy,
  • nastavuje CORS a bezpečnostné hlavičky,
  • pripája API routery,
  • ponecháva OpenAPI schému dostupnú pre OpenWebUI.

Interaktívne Swagger/Redoc rozhranie môže byť v produkčnej konfigurácii vypnuté. Pre integráciu OpenWebUI je dôležitý endpoint:

/openapi.json

app/routes.py

  • definuje /health,
  • definuje verejne publikovaný RAG nástroj /rag,
  • obsahuje interné/zabezpečené /search a /sync,
  • obsluhuje /webhook/gitea,
  • vykonáva validáciu requestov a sanitizáciu chýb,
  • používa reindexovací zámok,
  • kontroluje webhook payload pred spracovaním.

app/security.py

  • spracúva SEARCH_API_KEY,
  • spracúva SYNC_API_KEY,
  • spracúva WEBHOOK_SECRET,
  • vyžaduje dostatočne dlhé a navzájom odlišné tajomstvá,
  • používa bezpečné porovnávanie autentifikačných hodnôt,
  • validuje očakávaný Gitea repozitár a súvisiacu konfiguráciu.

tools/rag/

common.py

Spoločné utility a konfiguračné funkcie používané indexovacou a vyhľadávacou vrstvou.

scan_zpwiki.py

  • prechádza repozitár zpwiki,
  • načítava Markdown a YAML front matter,
  • normalizuje dokumentové metadata,
  • pripravuje dokumenty na ďalšie spracovanie.

build_chunks.py

  • rozdeľuje dokumenty na tokenové chunky,
  • používa tiktoken,
  • zachováva nadpisy, kódové bloky a tabuľky,
  • podporuje tokenový overlap,
  • eviduje token count a hash obsahu chunku.

build_sqlite_index.py

  • vytvára SQLite databázu,
  • ukladá dokumenty, chunky a metadata,
  • vytvára FTS5 index,
  • ukladá embeddingy do SQLite,
  • pripravuje databázu pre hybridný retrieval.

embedding_utils.py

  • načítava embeddingový model,
  • vytvára embeddingy pre dokumenty a query,
  • normalizuje vektory,
  • poskytuje utility pre vektorové vyhľadávanie.

tools/graphrag/

graph_db.py

  • načítava konfiguráciu Neo4j,
  • vytvára Neo4j driver,
  • poskytuje kontrolu spojenia s grafovou databázou.

graphrag.py

  • pripravuje dátový model knowledge graphu,
  • normalizuje grafové identifikátory,
  • extrahuje osoby, práce, roky, témy, kategórie a súvisiace metadata,
  • pripravuje uzly a vzťahy pre zápis do Neo4j.

build_graphrag.py

  • načíta documents.json a chunks.json,
  • vytvorí GraphRAG payload,
  • vytvorí Neo4j uniqueness constraints,
  • zapíše uzly a vzťahy,
  • podporuje opakovateľný rebuild spravovanej časti grafu,
  • vypíše základné počty uzlov, vzťahov a kvalitu extrakcie názvov prác.

graph_search.py

  • rozpoznáva relevantné osoby a obmedzenia v otázke,
  • vyhľadáva práce, témy, roky a spoločné vzťahy v Neo4j,
  • vracia grafové cesty, fakty a zdrojové dokumenty,
  • slúži ako základ budúceho GraphRAG nástroja pre OpenWebUI.

neo4j_smoke.py

Jednoduchý smoke test pripojenia k Neo4j používaný pri kontrole Docker prostredia.

search_core.py

Nízkoúrovňové jadro vyhľadávania:

  • normalizácia query,
  • FTS5 vyhľadávanie,
  • embeddingové vyhľadávanie,
  • scoring,
  • RRF fusion,
  • lexical anchor,
  • metadata,
  • diverzifikácia výsledkov.

search_utils.py

Vyššia orchestration vrstva nad search_core.py a kompatibilné vyhľadávacie utility používané ostatnými časťami projektu.

search_db.py

CLI rozhranie na ručné vyhľadávanie nad lokálnym SQLite indexom.

rag_utils.py

  • zostavuje RAG kontext,
  • pripravuje zdrojové metadata,
  • pridáva source_url,
  • aplikuje grounding pravidlá,
  • podporuje no-answer správanie,
  • rozširuje primárny chunk o začiatok rovnakej sekcie,
  • zachováva väzbu medzi primárnym chunkom a section-lead chunkom.

rebuild_index.py

Orchestruje kompletný rebuild:

scan
→ chunking
→ SQLite/FTS
→ embeddings
→ validácia
→ atomická výmena DB

tools/rag/evaluation/

evaluate_retrieval.py

CLI vstup pre retrieval benchmark. Umožňuje spúšťať DEV, TEST alebo celý dataset a používa strict dataset validáciu.

retrieval_runner.py

Spúšťa jednotlivé retrieval stratégie nad evaluačnými otázkami.

metrics.py

Počíta retrieval metriky, napríklad:

Hit@1
Hit@3
Hit@5
MRR
Recall@5

evaluate_rag_answers.py

CLI vstup pre answer-level RAG evaluáciu. Hodnotí celý tok:

otázka
→ OpenWebUI model
→ ZP Agent tool
→ /rag
→ finálna odpoveď

Podporuje answer-level overrides, aby nebolo potrebné meniť frozen retrieval benchmark pri otázkach, ktoré sú pre generatívnu evaluáciu nejednoznačné.

rag_runner.py

Spúšťa jednotlivé answer-level prípady a komunikuje s OpenWebUI/API.

rag_metrics.py

Počíta answer-level metriky, napríklad:

  • prítomnosť očakávaných faktov,
  • správnosť source_url,
  • should_answer,
  • použitie toolu,
  • strict pass,
  • latency a tokenové údaje.

tools/rag/evaluation/json_files/

questions.json

Hlavný frozen benchmark pre klasický retrieval a RAG. Po zmrazení benchmarku sa nemá meniť len preto, aby sa zlepšil výsledok answer-level evaluácie.

rag_answer_overrides.json

Obsahuje iba answer-level úpravy formulácie alebo očakávaní pri nejednoznačných otázkach. Retrieval benchmark tým ostáva nezmenený.

questions_before_ambiguity_cleanup.json

Historická záloha datasetu pred úpravami nejednoznačností.

questions_before_validation.json

Historická záloha datasetu pred validačnými úpravami.

questions_extended.json

Rozšírený benchmark s presne 2000 otázkami. Je pripravený pre neskoršie rozsiahlejšie experimenty nad klasickým retrievalom a RAG.

Obsahuje kombináciu:

  • faktických otázok,
  • parafráz,
  • otázok bez diakritiky,
  • preklepov,
  • krátkych query,
  • no-answer prípadov,
  • negatívnej verifikácie,
  • multi-document otázok,
  • porovnávania,
  • citation-oriented prípadov.

Tento dataset je zatiaľ pripravený, ale nemá sa používať na priebežné ladenie frozen benchmarku.

GraphRAG benchmark je po oddelení nástrojov uložený samostatne v:

tools/graphrag/evaluation/graphrag_questions.json

Obsahuje 500 otázok zameraných na grafové a viacvzťahové dopyty.

robustness_questions.json

Samostatný dataset s 200 otázkami pre testovanie robustnosti.

Pokrýva napríklad:

  • chýbajúcu diakritiku,
  • preklepy,
  • nekonzistentnú kapitalizáciu a interpunkciu,
  • nerelevantný šum,
  • prompt injection,
  • konfliktné tvrdenia používateľa,
  • tlak na halucinovanie,
  • no-answer grounding,
  • ambiguity/disambiguation,
  • integritu citácií,
  • kombinovaný vstupný šum.

Každá položka obsahuje aj robustness metadata s typom útoku, perturbáciou, očakávaným správaním a závažnosťou.

performance_queries.json

Samostatný workload s 200 query pre budúce performance a stress experimenty.

Profily zahŕňajú:

  • baseline single-document query,
  • krátke query,
  • noisy query,
  • multi-document query,
  • no-answer query,
  • query s väčším retrieval limitom,
  • context-heavy query,
  • opakované hot query.

Súbor samotný performance nemeria. Je to vstup pre budúci samostatný performance runner, ktorý bude volať /rag a merať napríklad:

mean latency
median
P50
P95
P99
requests/second
error rate
timeout rate
cold vs warm latency

Stress režim bude nad rovnakým workloadom zvyšovať paralelizmus a sledovať správanie systému pri rastúcej záťaži.

Konfigurácia

V koreňovom priečinku vytvor .env:

WEBHOOK_SECRET=<náhodná hodnota s minimálne 32 znakmi>
SYNC_API_KEY=<iná náhodná hodnota s minimálne 32 znakmi>
SEARCH_API_KEY=<ďalšia náhodná hodnota s minimálne 32 znakmi>
EXPECTED_GITEA_REPOSITORY=jp170na/zpwiki
EXPECTED_GITEA_BRANCH=master
WEBHOOK_PULL_GIT=true
ZPWIKI_SSH_KEY=/absolutna/cesta/k/ssh_klucu
ZPWIKI_KNOWN_HOSTS=/absolutna/cesta/k/known_hosts

# Voliteľné
EMBEDDING_MODEL=intfloat/multilingual-e5-small
EMBEDDING_BATCH_SIZE=32
OPENWEBUI_API_KEY=<kľúč pre generujúci školský model>
ANSWER_MODEL=model120-fast
ANSWER_LLM_URL=https://ui.tukekemt.xyz/api/chat/completions

# GraphRAG / Neo4j
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=<silné lokálne heslo>
NEO4J_DATABASE=neo4j

Tajomstvá je možné vygenerovať príkazom:

openssl rand -hex 32

Súbor .env sa nesmie commitovať.

Chunkovanie je možné konfigurovať pomocou premenných prostredia:

CHUNK_MAX_TOKENS=450
CHUNK_OVERLAP_TOKENS=70
CHUNK_MIN_TOKENS=80
CHUNK_TOKEN_ENCODING=cl100k_base

Spustenie cez Docker

Potrebný je dostupný Docker Engine, natívny Linux Compose v2 a Buildx. Vo WSL používaj kontext, ku ktorému sa Linux klient vie pripojiť; Windows kontext s npipe tu nie je náhradou Linux socketu. Overenie:

docker --context default info
docker compose version
docker buildx version

Po vytvorení .env, klonu ../zpwiki a SSH konfigurácie spusti:

./scripts/start_project.sh

Skript štandardne používa kontext default, nemení globálny Docker kontext a spravuje iba projekt zp-agent. Iný cieľ nastav cez ZP_DOCKER_CONTEXT. Zostaví image, počká na zdravý Neo4j, overí existujúce indexy a podľa potreby ich koordinovane vytvorí. Až potom spustí API a počká na readiness. Pri opakovanom spustení ponechá konzistentné a aktuálne indexy.

Model aj tokenizér sú súčasťou image v /opt/zp-assets. Bežný štart nevyžaduje internet ani existujúcu používateľskú Hugging Face/tiktoken cache. Prvý build potrebuje balíky a modelové súbory; build overuje aj načítanie pripravených assets so zakázanou sieťou. Prázdny HF cache volume neskrýva model pribalený v image. Git pripojenie je samostatná sieťová operácia; príznaky offline zakazujú sťahovanie modelu, nie synchronizáciu wiki.

Voliteľne môžeš pred buildom pripraviť ignorované lokálne podklady a využiť už stiahnutý model z hostiteľa:

.venv/bin/python -m tools.runtime.runtime_assets prepare

runtime_assets/ obsahuje veľké modelové súbory a do Gitu nepatrí; verzovaný je iba prázdny adresár pre čistý build. Bez pripravených lokálnych podkladov si ich Docker build vytvorí sám. Súbory majú manifest so SHA-256 a model je pripnutý na konkrétny commit. Iný model/revízia vyžaduje nový build s EMBEDDING_MODEL/EMBEDDING_REVISION a zodpovedajúce pripravené assets. API pri nesúlade neprejde potichu na sťahovanie iného modelu.

Kontrola služby:

curl http://127.0.0.1:8000/health

OpenAPI schéma:

http://127.0.0.1:8000/openapi.json

Logy:

docker compose logs -f zp-agent-api

Zastavenie:

docker compose down

Hugging Face cache volume zostáva persistentné; predvolený embeddingový model a tokenizér však štartujú priamo z overených súborov v image.

Neo4j je spustené ako samostatná služba v rovnakom Docker Compose prostredí. Stav služieb je možné skontrolovať:

docker compose ps

Neo4j Browser je lokálne dostupný na:

http://127.0.0.1:7474/browser/

Kontrola spojenia z API kontajnera:

docker compose exec zp-agent-api \
  python -m tools.graphrag.neo4j_smoke

Reindexovanie

Spoločná synchronizácia pripraví dokumenty, chunky, SQLite/FTS5, embeddingy a Neo4j graf z jedného snapshotu wiki. Nová generácia sa aktivuje až po úspešnej príprave oboch indexov:

docker compose run --rm zp-agent-api python -m tools.rag.rebuild_index

Vzniknú súbory:

data/documents.json
data/chunks.json
data/zp_index.sqlite
data/index_version.json

Databáza obsahuje dokumenty, chunky, FTS5 index, metadata a embeddingy.

Manuálny /sync, push webhook a uvedený CLI príkaz používajú rovnakú pipeline. SQLite, Neo4j a odpovede nástrojov evidujú spoločnú verziu dát a Git revíziu. Pri krátkom prepnutí sa nové vyhľadávanie dočasne odmietne; chyba grafu vyvolá rollback. Pri nedokončenom prepnutí po páde zostane vyhľadávanie nedostupné do úspešného opakovania synchronizácie. Podrobnosti a zotavenie: Implementácia je v tools/sync/sync_indexes.py.

Publikovanie a dostupnosť nástrojov

Oba nástroje predvolene vylučujú iba explicitné published=false. Chýbajúca hodnota sa považuje za publikovanú. Parameter published_only=false výslovne zahrnie aj nepublikované dokumenty pri autorizovanom volaní; CLI používa --include-unpublished.

/health uvádza samostatne tools.rag, tools.neo4j a tools.graphrag. Globálne ready vyžaduje oba vyhľadávacie nástroje; pri výpadku Neo4j môže klient ďalej používať RAG podľa tools.rag.ready. Spoločná politika je v tools/retrieval/publication.py.

Hybridné vyhľadávanie

RAG aj GraphRAG používajú spoločné polia status, reason, sources, index_version, complete, truncated a truncation_reasons. Pôvodný RAG kontext aj grafové fakty a cesty zostávajú dostupné. Úplnosť vyhľadania sa odlišuje od pravdivosti odpovede; pri RAG môže byť nezaručená (null). Typované polia a chyby definuje tools/retrieval/retrieval_contract.py.

Vyhľadávanie kombinuje:

dotaz
├── FTS5 / BM25
└── embeddingové vyhľadávanie
        ↓
   RRF fusion
        ↓
   lexical anchor
        ↓
   výsledky

FTS5 používa stratégie:

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:

docker compose run --rm zp-agent-api \
  python -m tools.rag.search_db "rag agent" --limit 5

Pred volaním API načítaj premenné z .env:

set -a
source .env
set +a

Vyhľadávanie cez zabezpečené API:

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": true,
    "max_per_document": 1
  }'

API používa hybridný engine:

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 tools/rag/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.

Ak sa relevantná informácia nenachádza priamo v primárnom chunke, RAG vrstva môže k rovnakému zdroju doplniť začiatok rovnakej sekcie. Táto section-lead expanzia umožňuje zachovať retrieval max_per_document=1, ale zároveň doplniť názov témy, rok alebo inú informáciu umiestnenú na začiatku sekcie.

Pri nedostatočnej podpore má model odmietnuť informáciu domýšľať.

Príklad:

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": true,
    "max_per_document": 1
  }'

GraphRAG

GraphRAG vetva rozširuje existujúci klasický RAG o knowledge graph. Graf sa vytvára deterministicky z už pripravených dokumentov a chunkov a ukladá sa do Neo4j.

Aktuálne používané hlavné typy uzlov:

Person
Document
DocumentAuthor
Thesis
Work
Keyword
Topic
Category

Hlavné vzťahy zahŕňajú napríklad:

Person         -[:HAS_DOCUMENT]-> Document
Person         -[:AUTHORED]-> Thesis
Person         -[:HAS_WORK]-> Work
Thesis/Work    -[:EVIDENCED_BY]-> Document
Document       -[:HAS_KEYWORD]-> Keyword
Document       -[:IN_CATEGORY]-> Category
DocumentAuthor -[:AUTHORED_DOCUMENT]-> Document
Document       -[:DESCRIBES]-> Topic

Knowledge graph sa vytvorí príkazom:

docker compose exec zp-agent-api \
  python -m tools.graphrag.build_graphrag

Builder vytvára constraints, zapisuje uzly a vzťahy a pri štandardnom spustení obnoví iba časť grafu spravovanú ZP Agentom. Zmazanie aj zápis prebiehajú v jednej transakcii s kontrolou počtov, takže chyba zachová pôvodný graf. Opakované spustenie nevytvára duplicitné uzly.

Graf je možné vizuálne kontrolovať v Neo4j Browseri. Samostatné grafové vyhľadávanie je implementované v tools/graphrag/graph_search.py. GraphRAG zatiaľ nezasahuje do stabilného klasického /rag toku; ďalšia etapa spojí grafové výsledky s klasickým RAG a OpenWebUI nástrojom.

Aktuálna evaluácia GraphRAG v7 má DEV 1 046/1 046 a nový TEST 80/80 pre grafové vyhľadávanie aj finálne odpovede. Modelový DEV má 46/46; pri novom TESTe prešlo na prvý pokus 77/80 (96,25 %), po jednej povolenej oprave 80/80 (100 %). Prirodzený text sa skladá z overených štruktúrovaných tvrdení. Sú to nové kombinácie nad známym korpusom a šablónami, nie neznáme osoby ani ľudsky anotovaný benchmark. Samostatná historická regresia má 319/320 (99,69 %); nepodložená odpoveď v zostávajúcom prípade bola odmietnutá pred doručením.

Pri zmrazení v7 prešli vtedajšie testy vrátane živých. V7 opravuje ignorovanie rokov mimo 20xx, kontrolu rozporných tvrdení o úplnosti, počtov, povinných tvrdení a citácií vrátane referenčného Markdownu a HTML. Začiatok štúdia vyžaduje explicitný zdroj, neodvodzuje sa z adresára; opravilo sa 10 očakávaní. Všetky v1–v6 merania ostávajú zachované. Uložené výsledky v7 obsahujú metriky; historické manifesty a raw odpovede ostávajú pri meraniach.

Historická v4 mala grafový strict TEST 320/320, doplnkovú kontrolu kontextových faktov 319/320 a LLM TEST 281/300 (93,67 %). Pôvodné otázky aj výsledky v1–v4 zostávajú zachované. Aktuálne skóre nie je zárukou všeobecnej pravdivosti. V7 benchmark nie je nezávisle anotovaný človekom. Nové ladenie na známych otázkach sa označuje ako regresia, nie ako nový nezávislý TEST.

OpenWebUI

Pripravená kontrolovaná cesta používa samostatný Pipe v tools/answers/openwebui_pipe.py. Administrátor v Admin Panel → Functions → Create vloží celý súbor, uloží ho napríklad ako zp_verified a aktivuje. Vo Valves nastaví:

Pole Hodnota
BACKEND_URL Adresa API dostupná zo servera OpenWebUI, bez /answer
BACKEND_API_KEY Hodnota SEARCH_API_KEY backendu
DEFAULT_ENGINE rag
TIMEOUT_SECONDS 300

Backend používa OPENWEBUI_API_KEY pre generujúci model. ANSWER_LLM_URL je predvolene https://ui.tukekemt.xyz/api/chat/completions a ANSWER_MODEL je model120-fast. Základný model nesmie odkazovať na tento Pipe, aby nevznikla slučka. Kľúče patria do .env alebo Valves. Po aktivácii sú vo výbere dva modely: overený RAG a overený GraphRAG. Pipe odovzdáva poslednú otázku; kontextové odkazy na predchádzajúce správy zatiaľ samostatne nerozvíja.

Tok: Pipe → autorizovaný /answer → retrieval → generovanie → kontroly → renderer → priamy text v UI. Vyžaduje accepted_for_delivery=true; odmietnutý text ani opravné pokusy sa nezobrazia. Iný model finálny text neprepisuje. Inštalácia na školskom serveri čaká na administrátora a sieťové sprístupnenie API. localhost vo funkcii znamená server OpenWebUI, nie vývojový počítač. V spoločnej Compose sieti môže byť URL http://zp-agent-api:8000.

Pôvodný OpenAPI Tool Server poskytuje podklady cez /rag.

OpenWebUI používa OpenAPI schému ZP Agent API a Bearer autentifikáciu pre vyhľadávací/RAG nástroj.

Tok požiadavky:

používateľ
  ↓
OpenWebUI
  ↓
jazykový model
  ↓
ZP Agent /rag
  ↓
hybrid retrieval
  ↓
RAG kontext + zdroje
  ↓
jazykový model
  ↓
odpoveď

Výsledná odpoveď má byť grounded v ZP Wiki a má používať relevantný source_url. Táto pôvodná cesta s voľným následným generovaním sama nezaručuje kontroly doručenia pripraveného Pipe.

Retrieval evaluácia

Frozen retrieval benchmark sa spúšťa napríklad:

python -m tools.rag.evaluation.evaluate_retrieval \
  --split dev \
  --strict-dataset

Podporované režimy:

dev
test
all

Vyhodnocujú sa samostatne:

FTS
vector
hybrid

a metriky:

Hit@1
Hit@3
Hit@5
MRR
Recall@5

TEST split sa nemá používať na priebežné ladenie retrieval konfigurácie.

RAG answer evaluácia

Answer-level evaluácia používa:

python -m tools.rag.evaluation.evaluate_rag_answers --split dev

Evaluuje kompletný model/tool/RAG tok a od retrieval benchmarku je oddelená.

Ak je pôvodná otázka vhodná pre retrieval, ale nejednoznačná pre generatívnu odpoveď, jej answer-level formulácia sa upraví iba cez:

tools/rag/evaluation/json_files/rag_answer_overrides.json

Pôvodný frozen questions.json tým ostáva nezmenený.

Tokenové limity, segmenty a embeddingová cache

Chunkovanie dodrží CHUNK_MAX_TOKENS aj pri dlhom slove, riadku kódu, tabuľke alebo nadpise. Posledné delenie zachová Unicode znaky a obsah; celá cesta nadpisov zostáva v metadátach.

Finálny embeddingový vstup obsahuje názov, autora, tagy, kategórie, obsah aj prefix passage: . Jeho veľkosť sa meria skutočným tokenizérom modelu vrátane špeciálnych tokenov. Nadlimitný vstup sa bez straty obsahu rozdelí na segmenty. Pôvodný chunk, FTS a graf ostávajú zachované. Vektorové vyhľadávanie vyberá najlepšiu zhodu segmentu a každý chunk vráti iba raz. Encoder nadlimitný nesegmentovaný vstup odmietne namiesto tichého orezania.

data/embedding_cache.sqlite uchováva vektory podľa presného vstupu a odtlačku váh, tokenizéra a konfigurácie modelu. Pri zmene relevantných metadát alebo modelu sa vektor prepočíta. Kontrolný súčet zachytí poškodený záznam cache. Prvé vytvorenie novej cache prepočíta všetko; ďalšia obnova nezmenených vstupov použije cache. Výsledok synchronizácie obsahuje embedding_segments, embedding_computed a embedding_reused; počet embedding_chunks stále označuje počet pokrytých pôvodných chunkov. Cache je obnoviteľná optimalizácia; jej odstránenie pri zastavenej synchronizácii vyvolá nový výpočet, nezmení aktívne indexy.

POST /rag predvolene vracia dôkazy iba raz v context a stručné citačné metadáta v sources. Diagnostické expanzie a ich texty sa vrátia len pri explicitnom include_diagnostics: true, oddelene v diagnostics. RAG evaluačný klient diagnostiku pred odoslaním modelu vždy odstráni.

RAG_LLM_MAX_TOKENS (predvolene 12000) obmedzuje celý JSON podkladu vrátane kontextu, pokynov, zdrojov a verzie dát. Nie je to rozpočet celej konverzácie ani výstupu modelu. RAG_LLM_TOKEN_ENCODING má predvolene cl100k_base; ide o explicitný odhad, nie presný tokenizér všetkých vzdialených modelov. Výstup context_budget uvádza použitý tokenizér, limit a skutočný počet podľa tohto odhadu. Pri orezaní nástroj nastaví complete=false, truncated=true a dôvod llm_context_budget.

Manuálna synchronizácia a stav úloh

POST /sync uloží požiadavku do persistentnej fronty a ihneď vráti HTTP 202. Indexovanie prebieha na pozadí; prijatie požiadavky ešte neznamená dokončenie.

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": true}'

Odpoveď obsahuje job_id a status_url, napríklad /sync/jobs/<job_id>. Stav načítaj rovnakým SYNC_API_KEY:

curl http://127.0.0.1:8000/sync/jobs/<job_id> -H "X-API-Key: $SYNC_API_KEY"
curl http://127.0.0.1:8000/sync/status -H "X-API-Key: $SYNC_API_KEY"

Úloha má stav queued, running, succeeded alebo failed. Pole phase rozlišuje čakanie na zámok, Git, budovanie a publikovanie indexov. Úspešná úloha obsahuje v result počty, trvanie a spoločnú verziu SQLite a Neo4j. Prehľad obsahuje aktívne úlohy, posledný úspech a posledné zlyhanie.

Fronta data/sync_jobs.sqlite prežije reštart. Procesový zámok povoľuje iba jedného pracovníka; rozpracovaná úloha sa po páde zopakuje. Push prijatý po začiatku obnovy vytvorí následnú úlohu. Ďalšie čakajúce požiadavky sa zlúčia, pričom požiadavka na stiahnutie z Gitu sa zachová. Konflikt s CLI obnovou spôsobí čakanie, nie stratu požiadavky. Bežná chyba sa skúsi najviac trikrát; následne zostane viditeľný stav failed a možno poslať nový /sync. CLI python -m tools.rag.rebuild_index --pull zostáva synchronný.

Gitea webhook a prístup ku Gitu

Endpoint POST /webhook/gitea overuje HMAC-SHA256 nad pôvodným telom, veľkosť payloadu, udalosť push, repozitár a konkrétnu vetvu z ref:

EXPECTED_GITEA_REPOSITORY=jp170na/zpwiki
EXPECTED_GITEA_BRANCH=master
WEBHOOK_PULL_GIT=true

Push inej vetvy a zmazanie vetvy sa ignorujú bez obnovy. X-Gitea-Delivery zabraňuje opakovanému zaradeniu rovnakého doručenia. Platný push vráti HTTP 202 s odkazom na stav úlohy; nečaká na dokončenie indexovania.

Git aktualizácia overí zhodu origin s očakávaným repozitárom, aktívnu vetvu a neprítomnosť lokálnych zmien vrátane nesledovaných súborov. Potom vykoná:

git fetch --no-tags --no-recurse-submodules origin refs/heads/master:refs/remotes/origin/master
git merge --ff-only refs/remotes/origin/master

Cieľom je aktuálna verzia nakonfigurovanej vetvy; oneskorený webhook neprepína wiki naspäť na starší commit. Odlišná história sa automaticky neprepisuje. Pri chybe Gitu zostávajú predchádzajúce indexy zachované.

Pre tento wiki server treba SSH. V .env nastav absolútne hostiteľské cesty ZPWIKI_SSH_KEY a ZPWIKI_KNOWN_HOSTS. Compose pripája iba tieto dva súbory na čítanie do /run/secrets/; kľúč sa nekopíruje do image ani repozitára. Použi kľúč s prístupom k wiki, ideálne deploy kľúč iba na čítanie, a vopred over záznam servera v known_hosts. SSH používa BatchMode a povinné striktné overenie servera; pri chybe si interaktívne nepýta heslo.

Na Gitea nastav push webhook na verejne dosiahnuteľnú adresu https://<adresa-api>/webhook/gitea s rovnakým WEBHOOK_SECRET. Lokálna adresa 127.0.0.1 nie je zo vzdialeného Gitea servera dosiahnuteľná.

Bezpečnosť

Vyhľadávanie podporuje API key:

X-API-Key: <SEARCH_API_KEY>

aj Bearer autentifikáciu:

Authorization: Bearer <SEARCH_API_KEY>

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.

Bezpečnostná vrstva zároveň kontroluje minimálnu dĺžku tajomstiev, odlišnosť jednotlivých secretov a používa bezpečné porovnávanie hodnôt.

Testy

Inštalácia testovacích závislostí:

pip install -r requirements-dev.txt

Bežné automatizované testy:

pytest -q

Kompletná lokálna kontrola s bežiacim projektom a .env:

.venv/bin/python scripts/verify_project.py --git-network

Skript skompiluje všetky Python súbory v aplikácii, nástrojoch, testoch a skriptoch, overí interné importy a syntax shell skriptov, Git, Compose, API, spoločnú verziu indexov, integritu SQLite a kompletnú regresiu vrátane lokálneho Neo4j. Prepínač --git-network navyše iba načíta hlavičku nastavenej wiki vetvy cez existujúce SSH pripojenie. Počas pytestu sú externé spojenia zablokované a kľúč generujúceho modelu odstránený. Výsledky a log ostávajú lokálne v data/.

Kontrola syntaxe najdôležitejších upravovaných modulov:

python -m py_compile \
  tools/rag/rag_utils.py \
  tools/rag/evaluation/evaluate_rag_answers.py \
  tools/rag/evaluation/rag_runner.py \
  tools/rag/evaluation/rag_metrics.py

Pred checkpointom je vhodné použiť aj:

git diff --check
git status

Pevný počet pytest výsledkov nie je v README uvádzaný, pretože sa s vývojom mení. Aktuálny stav sa má vždy potvrdiť novým spustením testov na konkrétnom commite.

Experimentálne datasety

Pre ďalšie experimenty sú pripravené:

questions_extended.json
graphrag_questions.json
robustness_questions.json
performance_queries.json

graphrag_questions.json je aktuálne určený pre vývoj GraphRAG vetvy. Ostatné datasety zostávajú oddelené od priebežného ladenia frozen klasického baseline.

Najbližší postup

Klasický RAG a GraphRAG sú oddelené do samostatných komponentov v tools/rag/ a tools/graphrag/. Stabilný klasický RAG zostáva referenčným bodom.

1. GraphRAG dátový model

Model je implementovaný. Rozlišuje autora dokumentu, študenta, záverečnú prácu, projekt, tému a kľúčové slovo. Študijný program sa nedopĺňa bez spoľahlivého zdroja.

2. GraphRAG evaluácia

V7 má zmrazený graf, DEV 1 046/1 046 a nový TEST 80/80. Finálne metriky oddeľuje nový TEST od historickej regresie a vyhľadávanie od modelu s kontrolami a rendererom; pôvodné v1–v6 sa nemenia. Pred formálnym porovnaním s klasickým RAG potrebujeme spoločnú metodiku a ideálne otázky anotované nezávislým človekom. Nový nezávislý TEST vyžaduje nové otázky; opakované meranie odhalených otázok je regresia.

3. OpenWebUI nástroje

Pripraviť samostatne inštalovateľný RAG nástroj a GraphRAG nástroj. Každý komponent bude mať vlastný adresár, rozhranie a testy.

4. Hlavný agent

Pridať hlavný prompt/agenta, ktorý podľa otázky vyberie vhodný nástroj a bude vedieť kombinovať ich výsledky.

5. Git MCP server

Pridať samostatný MCP server pre prácu s Git repozitárom a spúšťať ho v Dockeri. Následne ho sprístupniť hlavnému agentovi ako ďalší nástroj.

6. Spojenie GraphRAG s klasickým RAG

Po stabilizovaní grafového vyhľadávania spojiť výsledky grafu s existujúcim FTS5 + embedding vyhľadávaním a porovnať klasický RAG s GraphRAG variantom.

7. Finálne experimenty

Až po stabilizovaní celého systému vykonať širšie porovnanie embedding modelov, robustnosti, výkonu a ostatných finálnych experimentov.