dp-zp-agent/README.md

1684 lines
63 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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. Táto vetva pripravuje backend pre Claude Skills a autentizovaný MCP konektor.
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. Aktuálna integrácia pre Claude je opísaná nižšie; historické merania zostávajú zachované.
## 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;
klienti Claude používajú túto istú kontrolovanú cestu,
- 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,
- konfigurovateľné povolené CORS originy,
- bezpečnostné HTTP hlavičky,
- dva Claude Skills a MCP nástroje pre webový Claude, Desktop a Code,
- 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
```text
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
↓
Claude Skills / MCP konektor
↓
jazykový model
↓
odpoveď + source_url
```
GraphRAG vývojová vetva:
```text
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. Obe vyhľadávania sú samostatne dostupné cez API a MCP nástroje pre Claude.
Synchronizačná vetva:
```text
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:
```text
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é.
```text
zp-agent/
├── app/
│ ├── main.py
│ ├── routes.py
│ └── security.py
│
├── tools/
│ ├── answers/
│ │ ├── answer_delivery.py
│ │ └── model_client.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
│
├── packages/
│ └── claude/
│ ├── client.py
│ ├── mcp_server.py
│ ├── rag/SKILL.md
│ ├── graphrag/SKILL.md
│ └── dist/
│
├── scripts/
│ ├── start_project.sh
│ ├── compose.sh
│ ├── build_claude_packages.py
│ └── verify_project.py
│
├── data/
│ ├── documents.json
│ ├── chunks.json
│ └── zp_index.sqlite
│
├── deployment/
│ ├── Dockerfile
│ ├── docker-compose.yml
│ ├── requirements.txt
│ └── requirements-dev.txt
└── README.md
```
`answers/` doručuje overené odpovede a oddeľuje poskytovateľa generujúceho modelu.
`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.
`packages/claude/` obsahuje skills, spoločného HTTP klienta a MCP most.
Samostatné ZIPy v `packages/claude/dist/` obsahujú klienta a nepotrebujú importovať backend.
V koreňovom `tools/` zostáva iba `__init__.py`.
Na Gite je hlavný README a povinné vstupné súbory `SKILL.md`. 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:
```text
/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:
```text
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:
```text
Hit@1
Hit@3
Hit@5
MRR
Recall@5
```
`evaluate_rag_answers.py`
CLI vstup pre answer-level RAG evaluáciu. Hodnotí celý tok:
```text
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:
```text
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:
```text
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`:
```dotenv
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
# Generovanie overenej odpovede: zvoľ jedného poskytovateľa a presný model.
ANSWER_PROVIDER=anthropic
ANTHROPIC_API_KEY=<kľúč Anthropic API; predplatné chatu tento kľúč nenahrádza>
ANSWER_MODEL=<presný identifikátor dostupného Claude modelu>
ANSWER_MAX_TOKENS=4096
# Pre anthropic je predvolená URL https://api.anthropic.com/v1/messages.
# Alternatíva: ANSWER_PROVIDER=openai_compatible, ANSWER_API_KEY, ANSWER_LLM_URL.
# Webový MCP konektor (samostatný kľúč, nikdy SEARCH_API_KEY):
ZP_MCP_ACCESS_TOKEN=<ďalšia náhodná hodnota s minimálne 32 znakmi>
ZP_MCP_PUBLIC_URL=https://mcp.example.com/mcp
ZP_MCP_AUTH_MODE=oauth
ZP_MCP_LOGIN_PASSWORD=<samostatný náhodný prihlasovací kód, minimálne 32 znakov>
# 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:
```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
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:
```bash
docker --context default info
docker compose version
docker buildx version
```
Po vytvorení `.env`, klonu `../zpwiki` a SSH konfigurácie spusti:
```bash
./scripts/start_project.sh
```
Dockerfile, Compose a oba zoznamy závislostí sú v `deployment/`.
`.env`, `.gitignore` a `.dockerignore` zostávajú v koreni projektu.
Príkazy Compose spúšťaj cez `./scripts/compose.sh`, napríklad
`./scripts/compose.sh ps`. Obal nastavuje absolútne cesty, koreň build
kontextu a pôvodný názov projektu `zp-agent`, takže sa nemenia dátové volume.
Funguje aj pri spustení cez absolútnu cestu z iného pracovného priečinka.
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:
```bash
.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:
```bash
curl http://127.0.0.1:8000/health
```
OpenAPI schéma:
```text
http://127.0.0.1:8000/openapi.json
```
Logy:
```bash
./scripts/compose.sh logs -f zp-agent-api
```
Zastavenie:
```bash
./scripts/compose.sh 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ť:
```bash
./scripts/compose.sh ps
```
Neo4j Browser je lokálne dostupný na:
```text
http://127.0.0.1:7474/browser/
```
Kontrola spojenia z API kontajnera:
```bash
./scripts/compose.sh 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:
```bash
./scripts/compose.sh run --rm zp-agent-api python -m tools.rag.rebuild_index
```
Vzniknú súbory:
```text
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](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](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](tools/retrieval/retrieval_contract.py).
Vyhľadávanie kombinuje:
```text
dotaz
├── FTS5 / BM25
└── embeddingové vyhľadávanie
↓
RRF fusion
↓
lexical anchor
↓
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
./scripts/compose.sh run --rm zp-agent-api \
python -m tools.rag.search_db "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": true,
"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
`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:
```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": 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:
```text
Person
Document
DocumentAuthor
Thesis
Work
Keyword
Topic
Category
```
Hlavné vzťahy zahŕňajú napríklad:
```text
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:
```bash
./scripts/compose.sh 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.
Zmrazená 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](tools/graphrag/evaluation/results/final_v7_2026-10-26/summary.json)
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.
Otázky typu „Aké práce má Ján Holp a v ktorých rokoch?“ vracajú jednotlivé
práce aj projekty s názvom, typom a rokom. Záznamy bez samostatného názvu
sa zachovajú a odpoveď uvedie, že názov v zdroji chýba. Dva projekty z rovnakého
roku sa nezlučujú. Kontrola pred doručením vyžaduje pokrytie všetkých dodaných
záznamov vrátane ich vzájomných väzieb a zdrojov; samotný zoznam rokov nestačí.
Otázka pýtajúca sa iba na roky naďalej vracia iba roky. Táto neskoršia oprava
nemení historické výsledky ani manifest v7; ich skóre sa neprenáša na novú verziu.
## Claude Skills a webový MCP konektor
Táto vetva `claude-tools` zachováva spoločný RAG/GraphRAG backend,
Docker, synchronizáciu, kontroly aj historické experimenty. Zdrojové Tools,
Pipe a builder určené výhradne pre OpenWebUI sú odstránené. Zmienky o školskom
OpenWebUI v starších výsledkoch a evaluačných klientoch sú historická metodika,
nie požiadavka na inštaláciu do jeho administrácie.
Tok pre webový Claude:
```text
Claude + dva Skills
-> HTTPS /mcp + OAuth s PKCE (lokálna diagnostika môže používať token)
-> zpwiki_rag / zpwiki_graphrag / zpwiki_health
-> súkromný backend /answer alebo /health
-> SQLite / Neo4j -> zvolený generujúci model -> kontroly -> overený text
```
Skill riadi výber nástroja a interpretáciu výstupu. MCP poskytuje vykonateľné
nástroje a bezpečne drží backendový kľúč na serveri. Do ZIPu nepatria wiki dáta,
databázy, modelové váhy, `.env` ani API kľúče. Samotné nahratie ZIPu backend
nespustí ani nesprístupní localhost internetu.
### Balíky a ich zostavenie
```bash
.venv/bin/python scripts/build_claude_packages.py
```
Verziované a deterministicky zostavené sú:
- `packages/claude/dist/zpwiki-rag.zip`: text, technológie, individuálne fakty,
- `packages/claude/dist/zpwiki-graphrag.zip`: väzby, spoločné témy, roky a počty,
- `packages/claude/dist/packages.json`: SHA-256 archívov.
Každý ZIP má koreňový priečinok s `SKILL.md`, `scripts/query.py`,
`scripts/client.py` a manifestom vnútorných súborov. Funguje po samostatnom
rozbalení, bez importov z tohto repozitára. Pri zmene klienta alebo skillu
zopakuj builder a commitni aktualizované ZIPy aj manifest.
### Webový Claude: postup nasadenia
1. Na hostiteľovi priprav Docker a existujúci backend podľa tohto README.
Over `/health`, dostupnosť oboch indexov a ich spoločnú verziu.
2. Pre `/answer` nastav poskytovateľa modelu v `.env`. `ANSWER_PROVIDER=anthropic`
používa `ANTHROPIC_API_KEY` alebo `ANSWER_API_KEY`, explicitný `ANSWER_MODEL`
a Messages API. Adaptér oddeľuje system prompt a odmieta orezané odpovede.
`openai_compatible` používa `ANSWER_API_KEY`, `ANSWER_LLM_URL`, `ANSWER_MODEL`.
Historické `OPENWEBUI_API_KEY` zostáva podporované pre reprodukciu starých
experimentov. Použitie Claude ako rozhrania samo osebe nemení poskytovateľa.
3. Vygeneruj oddelený `ZP_MCP_ACCESS_TOKEN` aj `ZP_MCP_LOGIN_PASSWORD` cez
dva príkazy `openssl rand -hex 32`. Nastav `ZP_MCP_AUTH_MODE=oauth` a
`ZP_MCP_PUBLIC_URL=https://<tvoja-doména>/mcp`. Prvý token slúži lokálnej
diagnostike; druhý kód zadáva vlastník projektu na prihlasovacej stránke.
Kľúče zostávajú v ignorovanom `.env`. Nesmú byť rovnaké ani zhodné s backendovým kľúčom.
4. Spusti MCP profil. Backend a Neo4j zostávajú rovnaké:
```bash
scripts/compose.sh --profile claude up -d --build
```
5. Priprav DNS a dôveryhodné HTTPS. `deployment/claude.Caddyfile` je príklad
reverzného proxy na tom istom hostiteľovi: zmeň doménu a proxy smeruj na
`127.0.0.1:8001`. MCP je na hostiteľovi zámerne dostupné iba cez loopback.
Proxy musí zachovať cestu `/mcp`, Host a Authorization a dovoliť dlhšie
požiadavky. Doména musí byť dostupná z infraštruktúry Anthropic, nie iba z VPN.
6. V Claude otvor **Customize > Connectors > Add custom connector**, zadaj
meno `ZP Agent` a HTTPS adresu `/mcp`. Vyber **Sign in now** a
**Streamable HTTP**. Ak sa zobrazuje výber OAuth klienta, použi
**Register automatically**. OAuth client ID a client secret nechaj prázdne:
server podporuje Dynamic Client Registration (DCR), nepotrebuje vlastné
hlavičky ani administrátorskú OAuth aplikáciu. Pri pripojení sa otvorí stránka
ZP Agenta; vlož hodnotu `ZP_MCP_LOGIN_PASSWORD` zo svojho lokálneho `.env`
do poľa **Prístupový kód ZP Agenta** a povoľ prístup. Nie je to heslo účtu Claude.
Kód nepatrí do chatu ani do polí OAuth client secret. Po potvrdení sa prehliadač
vráti na `https://claude.ai/api/mcp/auth_callback` a Claude dostane obmedzené tokeny.
7. V **Customize > Skills** nahraj oba ZIPy a zapni ich. Podľa účtu musí byť
zapnuté aj Code execution and file creation. V organizácii môžu konektory
vyžadovať zásah vlastníka; nejde o oprávnenia OpenWebUI.
8. V novom chate zapni konektor, over `zpwiki_health` (indexy, nie kredit generujúceho API), jednu otázku cez každý
nástroj, neexistujúcu osobu a neexistujúci rok. Skontroluj aj výsledný text
Claude: zachovanie citácií a upozornenia na neúplnosť. Schválený backendový
text má Claude prezentovať bezo zmien, ale skill túto vlastnosť UI nevynucuje.
Bez reálnej domény, HTTPS a nastavenia konektora v používateľovom účte môžeme
preukázať lokálnu funkčnosť balíkov, protokolu a backendu; nie hotové webové
nasadenie. Prvé reálne volania odošlú otázku a výsledok do Claude. Výsledky
pôvodných modelov nie sú odhadom presnosti novej konfigurácie.
Oficiálne návody: [Skills](https://support.claude.com/en/articles/12512180-use-skills-in-claude),
[vzdialené konektory a Request headers](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp).
### OAuth a uchovávanie prihlásení
Používa sa OAuth Authorization Code so S256 PKCE, presným Claude callbackom,
viazaním tokenu na konkrétnu adresu `/mcp`, rozsahom `zpwiki:read` a voliteľným
`offline_access`. Discovery a registrácia sú verejné; samotné MCP a `/health`
sú chránené. Prístupový token platí najviac hodinu, refresh grant 30 dní.
Obnova rotuje tokeny; opätovné použitie starého refresh tokenu odvolá celý grant.
Kódy sú jednorazové, platné dve minúty. Prihlásenie vyžaduje súhlas a chráni
ho Secure/HttpOnly/SameSite cookie, CSRF, obmedzenie pokusov a veľkosti požiadaviek.
Toto je prihlásenie jedným projektovým kódom pre vlastníka a poverených ľudí,
nie univerzitné SSO ani správa samostatných používateľských účtov. Vlastník môže
kód odovzdať profesorovi mimo chatu. Zmena prihlasovacieho kódu, statického tokenu
alebo verejnej adresy pri obnove kontajnera zneplatní predchádzajúce OAuth prihlásenia.
Klienti a granty prežijú reštart kontajnera v pomenovanom Docker volume
`claude-oauth`, v súbore `/var/lib/zp-oauth/oauth.sqlite` s oprávneniami 0600.
Prístupové a refresh tokeny aj autorizačné kódy sa ukladajú pod SHA-256 odtlačkami.
Databáza ani prihlasovacie kódy nepatria do ZIPov alebo na Git. Voliteľný režim
`ZP_MCP_AUTH_MODE=bearer` zachováva predchádzajúce pripájanie pomocou
`Authorization: Bearer <ZP_MCP_ACCESS_TOKEN>`, ak účet Claude sprístupňuje beta
funkciu Request headers. Predvolený Compose profil používa OAuth.
Oficiálne požiadavky: [autentizácia konektorov Claude](https://claude.com/docs/connectors/building/authentication).
### Lokálny klient a voliteľný Desktop / Claude Code
Pre skripty nastav `ZP_AGENT_URL` a `ZP_AGENT_API_KEY`, alebo si skopíruj
`packages/claude/config.example.json` mimo repozitára, doplň skutočné hodnoty,
nastav oprávnenia `chmod 600` a do `ZP_AGENT_CONFIG` ulož iba cestu k súboru.
Konfigurácia nemá byť súčasťou skillu ani správy modelu. HTTP je povolené iba
pre loopback; `ZP_AGENT_ALLOW_HTTP=1` je výslovná výnimka pre súkromnú Docker sieť.
```bash
python packages/claude/rag/scripts/query.py --health
python packages/claude/rag/scripts/query.py "Aký je názov diplomovej práce osoby Ján Holp?"
python packages/claude/graphrag/scripts/query.py "Práce Jána Holpa v roku 1999?" --mode retrieve
```
Predvolené `--mode answer` poskytuje iba prijatú overenú odpoveď a nikdy
neodhalí `attempts`, odmietnutý text alebo diagnostiku. `--mode retrieve`
nevolá generujúci model, vráti podklady a explicitne označí potrebu následnej
kontroly finálneho textu. `--health` kontroluje nástroje bez generovania.
URL presmerovania sa nesledujú, chyby neobsahujú tajomstvá a odpoveď je limitovaná.
Pravidlá odpovedania dostane Claude pri inicializácii konektora a v Skills;
výsledky nástrojov neobsahujú pole `presentation_instructions`. Pravidlá určujú
použiť aktuálnu odpoveď bez dodatočných dohadov, zachovať
upozornenie na neúplnosť, nezamieňať autora dokumentu s autorom práce a pri
`no_answer` iba oznámiť nedostatok podkladov. Rozdiely oproti starším odpovediam
sa nevysvetľujú odhadom; verzia dát neoznačuje verziu programu.
Tieto pokyny ovplyvňujú správanie Claude, jeho finálny text však náš backend
nemôže programovo vynútiť. Po aktualizácii už nahratých Skills nahraď ich
novými ZIPmi z `packages/claude/dist/` (verzia 1.3.0) a over správanie v novom chate.
### Nahratie Skills do webového Claude
1. Zapni Code execution and file creation (v novom rozložení Create and edit files)
v Settings > Capabilities, ak ešte nie je zapnuté.
2. Otvor Customize > Skills, potom + > Create skill > Upload a skill.
3. Nahraj samostatne `packages/claude/dist/zpwiki-rag.zip` a `zpwiki-graphrag.zip`.
ZIPy už obsahujú správny koreňový priečinok a netreba ich rozbaľovať.
4. Zapni Skills s názvami `rag` a `graphrag`. Staršie Skills `zpwiki-rag` a
`zpwiki-graphrag`, ak si ich už nahral, vypni alebo odstráň, aby sa neprekrývali.
5. V novom chate zapni existujúci konektor ZP AGENT. Napíš `/`, vyber príslušný
Skill z ponuky a dopíš otázku, napríklad `/rag Aký je názov diplomovej práce
Jána Holpa?` alebo `/graphrag Aké práce má Ján Holp a v ktorých rokoch?`.
Ak ponuka Skills v tvojej verzii rozhrania nie je dostupná, výslovne požiadaj
„Použi skill rag: ...“ alebo „Použi skill graphrag: ...“.
Nahratie a zapnutie sa vykonáva v používateľovom účte; zostavenie ZIPov ani
nasadenie MCP túto časť nevykoná automaticky. Oficiálny postup:
https://support.claude.com/en/articles/12512180-use-skills-in-claude
Lokálny MCP most používa oficiálne SDK:
```bash
.venv/bin/python -m pip install -r deployment/requirements-claude.txt
ZP_AGENT_CONFIG=/sukromna/cesta/zp-agent.json .venv/bin/python packages/claude/mcp_server.py
```
Pre Desktop uprav absolútne cesty v `packages/claude/claude_desktop_config.example.json`
a jeho položku `mcpServers.zpwiki` pridaj k existujúcej konfigurácii bez jej prepisovania.
Pre Claude Code môžeš nainštalovať rozbalené ZIPy do `.claude/skills/` a používať
ich klienty priamo. Lokálny stdio most sa do webového Claude nepridáva; web používa
vyššie opísanú HTTPS službu.
### Kontroly balíkov
```bash
PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -q -p no:cacheprovider test/test_claude_packages.py
PYTHONDONTWRITEBYTECODE=1 .venv/bin/python scripts/check_claude_oauth.py
PYTHONDONTWRITEBYTECODE=1 .venv/bin/python scripts/verify_project.py --no-save --git-network
```
Testy pokrývajú samostatné rozbalenie ZIPov, deterministické SHA-256,
HTTP komunikáciu a autorizáciu backendu, odmietnutý výstup, metadáta úplnosti,
MCP stdio aj autentizovaný HTTP protokol, zlý token a kontrolu hostiteľa,
OAuth discovery, prihlásenie, PKCE, CSRF, presmerovania, scope a resource,
jednorazové kódy, expiráciu, rotáciu, opakovanie a odvolanie tokenov.
`check_claude_oauth.py` overí skutočnú verejnú adresu z `.env`: discovery, DCR,
prihlásenie, PKCE, odmietnutie opakovania kódu, MCP, spoločnú verziu indexov,
obnovu aj odvolanie tokenov. Kódy neukladá do reportu ani nevypisuje; testovací
grant po skončení odvolá. Finálny redirect na účet Claude nenasleduje a nevolá LLM.
Voliteľné `--browser firefox` alebo `--browser chromium` odošle prihlasovací formulár
skutočným prehliadačom s čerstvým profilom (vyžaduje Playwright a jeho browser).
Tento režim nekonštruuje hlavičku Origin ručne a zachytí aj rozdiel medzi HTTP
klientom a HTML formulárom. Prehliadač má sieť povolenú iba na MCP server; test zachytí pokus o návrat
na Claude bez odoslania požiadavky do účtu.
Prihlasovacia stránka používa `Referrer-Policy: same-origin`: zachová pôvod vlastného
formulára a neposiela referrer pri návrate na Claude. Ostatné odpovede používajú
`no-referrer`; kontrola pôvodu aj CSRF zostávajú zapnuté. CSP povoľuje formuláru
iba vlastný server a presne registrovanú callback adresu Claude, aby prehliadač
neblokoval legitímne presmerovanie po prihlásení.
Adaptér Anthropic je testovaný s lokálne nasimulovanými odpoveďami; bez API kľúča
sa nevykonáva platené volanie Anthropic. Verejné HTTPS a samotný účet Claude
sa overia až pri skutočnom pripojení.
## Retrieval evaluácia
Frozen retrieval benchmark sa spúšťa napríklad:
```bash
python -m tools.rag.evaluation.evaluate_retrieval \
--split dev \
--strict-dataset
```
Podporované režimy:
```text
dev
test
all
```
Vyhodnocujú sa samostatne:
```text
FTS
vector
hybrid
```
a metriky:
```text
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:
```bash
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:
```text
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.
```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": true}'
```
Odpoveď obsahuje `job_id` a `status_url`, napríklad `/sync/jobs/<job_id>`.
Stav načítaj rovnakým `SYNC_API_KEY`:
```bash
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`:
```dotenv
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á:
```bash
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:
```text
X-API-Key: <SEARCH_API_KEY>
```
aj Bearer autentifikáciu:
```text
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í:
```bash
pip install -r deployment/requirements-dev.txt
```
Bežné automatizované testy:
```bash
pytest -q
```
Kompletná lokálna kontrola s bežiacim projektom a `.env`:
```bash
.venv/bin/python scripts/verify_project.py --git-network
```
Skript skompiluje všetky Python súbory v aplikácii, nástrojoch, balíkoch, 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ý. HTTP transport testy povoľujú iba svoj dočasný loopback port.
Výsledky ostávajú lokálne v `data/reports/verification/` a pytest logy
v `data/logs/verification/`. Každé spustenie má vlastný časový identifikátor;
existujúci report sa neprepisuje. Skript počká najviac 90 sekúnd na
pripravenosť API po štarte Dockera (`--wait-ready-seconds`).
### Reporty, logy a uchovávanie
Prevádzkové indexy `documents.json`, `chunks.json`, `index_version.json`,
SQLite databázy a zámky ostávajú priamo v `data/`. Reporty sa zapisujú
do `data/reports/<oblasť>/`, logy do `data/logs/`. Výstupy inštalovateľných
Claude skills sú verziované v `packages/claude/dist/`; dočasné testovacie balíky sa vytvárajú mimo projektu.
Päť najnovších dokončených kontrol projektu zostáva v aktívnom priečinku;
staršie kontroly aj ich logy sa presunú do `archive/verification/`.
Neúspešná dokončená kontrola sa tiež uchová. Rozpracované reporty sa
automaticky nearchivujú. Staršie pôvodné výstupy sú v `archive/legacy/`.
Súbory sa nemažú a ich obsah sa nemení; `data/reports/migrations/`
obsahuje záznam pôvodnej a novej cesty spolu s SHA-256. Replay klienti
vedia cez tento záznam načítať aj presunutý vstup.
```bash
.venv/bin/python scripts/manage_reports.py # náhľad presunov
.venv/bin/python scripts/manage_reports.py --apply # presun, bez mazania
```
Formálne evaluačné výsledky v `tools/*/evaluation/results/`, baseline,
GraphRAG verzie a podklady pre diplomovku sa týmto uchovávaním nemenia.
RAG merania, audity a modelové odpovede v `reports/` sa automaticky
nemažú ani nerotujú; uchovávajú sa kvôli reprodukcii. Celé `data/` zostáva
ignorované Gitom. Parametre `--output` kontrolných skriptov sú voliteľné;
staré ploché cesty `data/nazov.json` sa presmerujú do príslušnej oblasti.
### Širšie overenie kontrolovaného RAG
`complete`, `truncated` a `truncation_reasons` zostávajú technickým opisom
vyhľadávania. Overená odpoveď navyše obsahuje `answer_coverage` so scope, status,
warnings a limitations. Status `supported` znamená doložené tvrdenia/výňatky,
nie úplné prehľadanie korpusu. Bežný výber kandidátov a zdrojov preto nepridáva
automatické upozornenie k názvu, roku alebo krátkemu opisu. Obmedzený zoznam
záznamov a orezané citované zadanie dostanú konkrétne upozornenie. Pri nenájdenej
informácii sa zachová opatrné tvrdenie o dostupných podkladoch. Žiadny limit sa
kvôli tomu nezvyšuje ani sa neprepisuje na `complete=true`.
Otázka na stručný obsah pri zobrazení vyberá najviac dva krátke overené výňatky
na osobu (do 600 znakov a dvoch bodov na osobu), bez voľnej modelovej parafrázy.
Skrátenie robí renderer z už overených citátov; dlhší platný citát nie je dôvod
na odmietnutie faktov ani ďalšie modelové volanie. Pri neoddeliteľnom technickom
reťazci má zachovanie dôkazu prednosť pred limitom stručnosti. Otázka na zadanie
môže zobraziť všetky doložené body zadania. Citácie a kontroly ich doslovnosti
sa uplatňujú v oboch režimoch.
Finálne formulovanie je oddelené v `tools/answer_evaluation/presentation.py`.
Overené názvy a roky sa zobrazujú vo vetách, práce v číslovanom zozname.
Rok práce sa nevydáva za dátum jej začatia alebo dokončenia. Pri opise sa
známe rozkazovacie formulácie zadania menia na pomenovania cieľov, napríklad
„Vypracujte prehľad“ na „Vypracovanie prehľadu“. Ide o obmedzené gramatické
pravidlá po kontrole doslovného dôkazu, nie o voľné dopĺňanie faktov modelom.
Neznámy opis, podmienky, negácie a údaje o dosiahnutom stave zostávajú zachované.
Pri obsahovom porovnaní viacerých študentov sa pre každého načíta požadovaný typ
práce z rovnakého SQLite snapshotu; všetky podklady naďalej podliehajú spoločnému
tokenovému rozpočtu. Zadanie a zaznamenaný stav sa kontrolujú a zobrazujú oddelene.
Chýbajúce výsledky sa oznámia, neodvodzujú sa z cieľov. Grafová otázka na roky,
v ktorých majú práce „obaja“, používa prienik rokov a prikladá doložené typy a názvy
prác z týchto rokov; nezobrazuje namiesto odpovede všetky individuálne roky.
`rag_preparation.py` vie pomenované profily dohľadať v SQLite aj mimo prvých
výsledkov a pri viacerých možných prácach vyžiada typ alebo rok. Obsahové
porovnanie je obmedzené na štyri osoby naraz. `question_intent.py` rozlišuje
požadované problémy, metódy, experimenty, ciele a výsledky. `content_selection.py`
vyberá doslovné, typovo a osobne viazané podklady, odstraňuje opakovania a
uprednostňuje vecné výsledky pred administratívnymi poznámkami. Model dostane
len tieto kandidátske výňatky; ich skutočný JSON podlieha tokenovému rozpočtu.
Chýbajúce časti sa označia jednotlivo, aj keď sú ostatné časti zodpovedateľné.
Pri limite kontextu sa nestratí informácia, ktoré osoby používateľ požadoval.
Výňatky nesú interný `passage_id`, osobu, pôvodné označenie práce a rok. Kontrola
odmietne zamenený identifikátor a pri rovnakom texte pod rôznymi prácami vyžaduje
jednoznačné priradenie. Číslo projektu odlišuje aj dva projekty rovnakého typu
a roku. Zadanie, plánované úlohy, priebežný stav a zaznamenané výsledky sa
zobrazujú oddelene; negatívny výsledok zostáva výsledkom. Administratívna poznámka
nenahrádza technický výsledok. Porovnávané osoby si delia kontextový rozpočet,
aby dlhý prvý profil nevytlačil ostatné. Skrátenie rešpektuje celé odseky a položky
zoznamu vrátane ich výhrad; pri stručnom opise má zachovanie podmienky prednosť
pred orientačným limitom dĺžky.
Krátke porovnanie zobrazuje rovnaké oblasti vedľa seba v tabuľke; dlhé výňatky,
kód a odkazy zostávajú v samostatných sekciách bez úpravy ich obsahu. Prázdne
pomocné sekcie sa nevypisujú a chýbajúce oblasti sa konkrétne pomenujú pri danej
osobe. Spoločné vlastnosti sa uvádzajú najprv súhrnne a potom s doloženými
osobami a zdrojmi. Stručnejšie formulácie nemenia rok práce na dátum jej
začatia alebo dokončenia a ciele neprezentujú ako dosiahnuté výsledky.
Rozpoznané, ale nepodporované operátory (napríklad interval rokov, vylučovanie
alebo minimálny počet) vedú k `ambiguous` a konkrétnemu spresneniu. Rok práce
sa nepoužije ako dôkaz dátumu jej začatia či obhajoby. Chybové odpovede odlišujú
nedostupné vyhľadávanie, nedostupný model, vyťaženie a zamietnutý neoverený text;
raw výstupy odmietnutého modelu sa štandardnému klientovi neposkytujú.
Grafový filter tagu sa uplatňuje aj pri otázkach na neurčených študentov a
konkrétny typ/rok práce: všetky podmienky musí spĺňať tá istá osoba. Spojenie
„tag RAG a zároveň diplomový projekt“ nepatrí do názvu tagu. Úvodzovky umožňujú
zachovať viacslovný tag aj so spojkou „a“. Pri názve práce konkrétnej osoby sa
zachovajú aj záznamy bez názvu; odpoveď uvedie známy typ a rok a chýbajúci názov.
Spoločné tagy sa zobrazujú súhrnne, s dôkazmi pre každú požadovanú osobu.
Rozpoznané podmienky na počet vrátane slovných čísloviek („aspoň tri“) vyžadujú
spresnenie; prahové filtrovanie zatiaľ nie je implementované. Predikcia dokončenia
práce sa výslovne odlišuje od vyhľadania práce evidovanej v určitom roku.
Otázky na obsah, zadanie, ciele alebo metódy práce používajú textové výňatky
z príslušnej akademickej sekcie, nie iba metadáta o názve a roku. Výňatok musí
patriť požadovanej osobe, typu práce a prípadne roku. Názov, rok ani samotný
nadpis zadania nenahrádzajú opis. Kombinovaná otázka na názov/rok aj obsah
vyžaduje overené štruktúrované údaje aj priradené doslovné výňatky. Ak opis
v skutočne dodaných podkladoch chýba, odpoveď ho nesmie domýšľať.
`tools/rag/evaluation/json_files/verified_rag_regression.json` obsahuje
vopred vybraných 80 známych otázok z deviatich kategórií, vrátane 10
neodpovedateľných otázok a 52 otázok s typovanými očakávaniami.
Je to regresná vzorka; jej úspešnosť nie je nový nezávislý odhad presnosti.
Očakávania overuje oddelený oracle priamo z pôvodných Markdownov.
Zdrojové cesty, odtlačky a pôvod akademického roku sú uložené pri otázkach.
Ľudská anotácia sa tým nenahrádza. Dve otázky na Dária Lindvaia používajú
rok z kategórie `dp2021`; kontrolovaná cesta taký rok nepovažuje za
explicitný dôkaz roku práce. Táto neistota je uvedená v datasete.
```bash
# Len lokálne vyhľadávanie a kontrola podkladov, bez generujúceho modelu:
.venv/bin/python -m tools.answer_evaluation.evaluate_verified_rag --mode retrieve
# Úmyselné chybné odpovede na uložených skutočných kontextoch, bez LLM:
.venv/bin/python -m tools.answer_evaluation.check_verified_rag_guards --input <report.json>
# Skutočné generovanie cez /answer: vyžaduje nakonfigurovaný model a povolené
# odoslanie otázok aj wiki podkladov tomuto poskytovateľovi:
.venv/bin/python -m tools.answer_evaluation.evaluate_verified_rag --mode deliver
# Opätovné vyhodnotenie uložených odpovedí bez nového generovania:
.venv/bin/python -m tools.answer_evaluation.evaluate_verified_rag --mode replay --input <report.json>
```
Aj evaluačný runner podporuje `--no-save`; v tomto režime otázky a odpovede
spracuje v pamäti a vypíše skóre bez zápisu reportu či deklarácie.
Kontrolovaná cesta používa pri všeobecných otázkach iba doslovné výňatky
bez nežiadaných akademických tvrdení. Referenčný odkaz v citovanom texte
sa prijme iba vtedy, keď je doložený tým istým dodaným zdrojom; položky
`source_urls` naďalej označujú pôvodné wiki zdroje.
Runner posiela na backend iba otázku a parametre; očakávané odpovede model
nedostane. Vyhodnocuje finálny stav, prijatie odpovede, citácie, úplné
väzby osoba–práca–typ–rok a pokrytie očakávaných údajov vo výňatkoch.
Nesprávne „nenájdené“, blokované odpovede a technické zlyhania sú vedené
samostatne a zostávajú v menovateli. Doslovná citácia sama osebe nedokazuje
relevantnosť. Automatická kontrola pokrytia známych údajov nenahrádza
ľudské posúdenie významu všeobecných odpovedí.
Kontrola syntaxe najdôležitejších upravovaných modulov:
```bash
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:
```bash
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é:
```text
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](tools/graphrag/evaluation/results/final_v7_2026-10-26/summary.json)
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. Claude Skills a nástroje
Skills a lokálne overiteľný autentizovaný MCP most sú v `packages/claude/`.
Pre web zostáva pripraviť verejnú HTTPS adresu, token v nastaveniach konektora,
nahrať ZIPy a overiť reálne správanie Claude podľa návodu vyššie.
### 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.