dp-zp-agent/README.md

1023 lines
23 KiB
Markdown

# 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.
Súčasná hlavná retrieval vetva používa klasický hybridný RAG:
FTS5/BM25 + embeddingy + RRF. GraphRAG zatiaľ nie je implementovaný;
je preň pripravený samostatný evaluačný dataset a bude sa dopĺňať ako
ďalšia experimentálna vetva.
## 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ď,
- 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,
- 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
OpenWebUI / ZP Agent
jazykový model
odpoveď + source_url
```
Synchronizačná vetva:
```text
Gitea push
POST /webhook/gitea
HMAC + repository + event validácia
voliteľný git pull --ff-only
rebuild_index.py
nový SQLite index
atomická výmena databázy
```
Evaluačná vetva:
```text
evaluation/json_files/*.json
retrieval evaluator / RAG answer evaluator
runner
metriky
evaluation/results/
```
`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
├── scripts/
│ ├── common.py
│ ├── scan_zpwiki.py
│ ├── build_chunks.py
│ ├── build_sqlite_index.py
│ ├── embedding_utils.py
│ ├── rag_utils.py
│ ├── rebuild_index.py
│ ├── search_db.py
│ ├── search_core.py
│ └── search_utils.py
├── evaluation/
│ ├── evaluate_retrieval.py
│ ├── retrieval_runner.py
│ ├── metrics.py
│ ├── evaluate_rag_answers.py
│ ├── rag_runner.py
│ ├── rag_metrics.py
│ └── json_files/
│ ├── questions.json
│ ├── questions_before_ambiguity_cleanup.json
│ ├── questions_before_validation.json
│ ├── rag_answer_overrides.json
│ ├── questions_extended_2000.json
│ ├── graphrag_questions.json
│ ├── robustness_questions.json
│ └── performance_queries.json
├── data/
│ ├── documents.json
│ ├── chunks.json
│ └── zp_index.sqlite
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── requirements-dev.txt
└── README.md
```
### `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.
### `scripts/`
`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.
`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
```
### `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.
### `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_2000.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_questions.json`
Samostatný benchmark pripravený pre budúcu GraphRAG vetvu.
Obsahuje 500 otázok zameraných na:
- multi-hop vzťahy,
- spoločné témy,
- spoločné roky,
- person → title → year,
- person → topic → title,
- person → category → author,
- agregácie,
- konjunktívnu disambiguáciu,
- multi-document reasoning.
Dataset obsahuje aj GraphRAG metadata ako:
```text
graph.task
graph.hop_count
graph.start_entities
graph.expected_entities
graph.expected_relations
graph.expected_paths
```
Dôležité: existencia tohto datasetu neznamená, že je GraphRAG už
implementovaný. Dataset je pripravený na neskorší vývoj a porovnanie.
`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=KEMT/zpwiki
WEBHOOK_PULL_GIT=false
# Voliteľné
EMBEDDING_MODEL=intfloat/multilingual-e5-small
EMBEDDING_BATCH_SIZE=32
```
Tajomstvá je možné vygenerovať príkazom:
```bash
openssl rand -hex 32
```
Súbor `.env` sa nesmie commitovať.
Chunkovanie je možné konfigurovať pomocou premenných prostredia:
```dotenv
CHUNK_MAX_TOKENS=450
CHUNK_OVERLAP_TOKENS=70
CHUNK_MIN_TOKENS=80
CHUNK_TOKEN_ENCODING=cl100k_base
```
## Spustenie cez Docker
```bash
docker compose up -d --build
```
Kontrola služby:
```bash
curl http://127.0.0.1:8000/health
```
OpenAPI schéma:
```text
http://127.0.0.1:8000/openapi.json
```
Logy:
```bash
docker compose logs -f zp-agent-api
```
Zastavenie:
```bash
docker compose down
```
Hugging Face cache je uložená v persistentnom Docker volume, takže sa
embeddingový model pri bežnom reštarte kontajnera nemusí znova sťahovať.
## Reindexovanie
Celý proces načíta dokumenty, vytvorí chunky, obnoví FTS5 index a
vytvorí embedding pre každý chunk:
```bash
docker compose run --rm zp-agent-api python scripts/rebuild_index.py
```
Vzniknú súbory:
```text
data/documents.json
data/chunks.json
data/zp_index.sqlite
```
Databáza obsahuje dokumenty, chunky, FTS5 index, metadata a embeddingy.
Pri automatickom reindexovaní sa používa zámok proti súbežnému spusteniu
a atomická výmena databázy po úspešnom vytvorení nového indexu.
## Hybridné vyhľadávanie
Vyhľadávanie kombinuje:
```text
dotaz
├── FTS5 / BM25
└── embeddingové vyhľadávanie
RRF fusion
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
docker compose run --rm zp-agent-api \
python scripts/search_db.py "rag agent" --limit 5
```
Pred volaním API načítaj premenné z `.env`:
```bash
set -a
source .env
set +a
```
Vyhľadávanie cez zabezpečené API:
```bash
curl -X POST http://127.0.0.1:8000/search \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $SEARCH_API_KEY" \
-d '{
"query": "strojový preklad",
"limit": 3,
"published_only": false,
"max_per_document": 1
}'
```
API používa hybridný engine:
```text
hybrid_fts5_embeddings
```
`max_per_document=1` zabezpečí, že sa pri požiadavke na viac výsledkov
preferujú rôzne dokumenty namiesto viacerých chunkov z rovnakého
dokumentu.
## RAG
Nad hybridným retrievalom je implementovaná RAG vrstva v
`scripts/rag_utils.py`.
Endpoint `/rag` vyhľadá relevantné dokumenty a pripraví:
- textový kontext pre jazykový model,
- metadata použitých zdrojov,
- názov a autora dokumentu,
- `source_url`,
- interné retrieval informácie,
- pravidlá pre grounded odpoveď.
RAG pravidlá požadujú, aby model odpovedal iba podľa poskytnutých
zdrojov. Zároveň rozlišujú údaje, ktoré sa môžu ľahko zameniť, napríklad
autora dokumentu, osobu, o ktorej dokument pojednáva, rok začiatku
štúdia a rok záverečnej práce.
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": false,
"max_per_document": 1
}'
```
## OpenWebUI
ZP Agent je pripojený do OpenWebUI ako OpenAPI Tool Server.
OpenWebUI používa OpenAPI schému ZP Agent API a Bearer autentifikáciu
pre vyhľadávací/RAG nástroj.
Tok požiadavky:
```text
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`.
## Retrieval evaluácia
Frozen retrieval benchmark sa spúšťa napríklad:
```bash
python evaluation/evaluate_retrieval.py \
--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 evaluation/evaluate_rag_answers.py --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
evaluation/json_files/rag_answer_overrides.json
```
Pôvodný frozen `questions.json` tým ostáva nezmenený.
## Manuálna synchronizácia
Manuálne reindexovanie cez zabezpečený endpoint:
```bash
curl -X POST http://127.0.0.1:8000/sync \
-H "Content-Type: application/json" \
-H "X-API-Key: $SYNC_API_KEY" \
-d '{"pull_git": false}'
```
## Gitea webhook
Endpoint:
```text
POST /webhook/gitea
```
Webhook overuje:
- HMAC-SHA256 podpis,
- typ Gitea udalosti,
- očakávaný repozitár,
- maximálnu veľkosť payloadu,
- stav reindexovacieho zámku.
Očakávaný repozitár:
```dotenv
EXPECTED_GITEA_REPOSITORY=KEMT/zpwiki
```
Ak je povolené:
```dotenv
WEBHOOK_PULL_GIT=true
```
pred reindexovaním sa vykoná:
```bash
git pull --ff-only
```
## Bezpečnosť
Vyhľadávanie podporuje API key:
```text
X-API-Key: <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 requirements-dev.txt
```
Bežné automatizované testy:
```bash
pytest -q
```
Kontrola syntaxe najdôležitejších upravovaných modulov:
```bash
python -m py_compile \
scripts/rag_utils.py \
evaluation/evaluate_rag_answers.py \
evaluation/rag_runner.py \
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.
## Pripravené, ale zatiaľ neaktívne experimenty
Nasledujúce datasety sú pripravené, ale nemajú sa teraz používať na
priebežné ladenie aktuálneho frozen benchmarku:
```text
questions_extended_2000.json
graphrag_questions.json
robustness_questions.json
performance_queries.json
```
Odporúčané poradie ich neskoršieho použitia:
```text
stabilný klasický RAG
extended benchmark
robustness benchmark
GraphRAG implementácia
GraphRAG benchmark
performance benchmark
stress test
finálne experimenty
```
## Najbližší postup
Najbližšia vývojová etapa je zámerne menšia a má uzavrieť existujúci
klasický RAG pred otvorením ďalších experimentov.
### 1. Uzavretie retrieval/regresie
Najprv sa má potvrdiť, že aktuálne zmeny neovplyvnili frozen retrieval
baseline.
Postup:
```text
py_compile
pytest
frozen retrieval DEV strict
porovnanie s uloženým baseline
git diff --check
retrieval lock
```
Do retrieval scoringu sa potom nemá zasahovať bez nového
experimentálneho dôvodu.
### 2. Regresia section-lead RAG kontextu
Treba potvrdiť, že section-lead expanzia:
- opravuje prípady, kde primárny chunk chýba o názov/tému/rok,
- nepridáva duplicitný chunk,
- zostáva v rovnakom dokumente a sekcii,
- nemení retrieval `max_per_document=1`,
- nevytvára zbytočne veľký kontext.
### 3. `retry + backoff + resume` pre answer evaluator
Pred spustením veľkého DEV answer benchmarku sa má evaluator doplniť
tak, aby dlhý beh nebol znehodnotený jedným timeoutom alebo dočasnou
chybou API.
Plánované správanie:
```text
request
úspech ───────────────→ uložiť výsledok
└─ timeout / 429 / 5xx
retry
exponential backoff
retry limit
```
Resume mechanizmus má:
- priebežne ukladať `.partial.json`,
- pri novom spustení načítať existujúci partial výsledok,
- overiť kompatibilitu modelu/datasetu/splitu,
- preskočiť už úspešne dokončené otázky,
- pokračovať od ďalšej otázky,
- neprepisovať hotové výsledky bez explicitnej voľby,
- po úspešnom dokončení vytvoriť finálny JSON/CSV výstup.
Zároveň je vhodné:
- rozlišovať retryable a permanentné chyby,
- logovať číslo pokusu a dôvod retry,
- mať konfigurovateľný maximálny počet pokusov,
- mať konfigurovateľný počiatočný backoff,
- používať mierny delay medzi otázkami,
- validovať typy answer-level datasetových polí,
- nenačítavať osobný OpenWebUI API kľúč z fallback súboru, ak má byť
podľa bezpečnostnej politiky dostupný iba cez environment.
Až po tejto etape má zmysel spustiť väčší DEV answer experiment a robiť
systematickú error analysis.
## Čo zatiaľ nerobiť
Kým nie je uzavretá predchádzajúca etapa, netreba ešte:
- ladiť systém podľa `questions_extended_2000.json`,
- spúšťať GraphRAG benchmark,
- implementovať performance runner,
- robiť stress test,
- robiť finálny answer TEST,
- optimalizovať P95/P99 podľa neustále sa meniaceho systému.
Tieto experimenty majú zmysel až nad stabilnou konfiguráciou.