Architektura
Pět rozhodnutí, ze kterých plyne všechno ostatní. Popisují zamýšlený stav; co z toho už existuje v kódu, je na roadmapě.
Pět rozhodnutí
Event sourcing, protože zdroj je event stream
Systém je konzumentem oficiálního proudu událostí. Z něj staví lokální append-only repliku a z ní přehrává projekci aktuálního stavu. Oprava chyby v interpretaci znamená přehrát log, ne znovu stahovat rejstřík.
Detail
Dokumentová pipeline je oddělená od příjmu
Příjem je levný, rychlý a musí stíhat rejstřík. Extrakce je drahá, pomalá a občas potřebuje člověka. Spojit je znamená zpomalit příjem na rychlost nejpomalejšího skenu, proto jsou to dvě fronty s vlastními limity.
Detail
Výsledky extrakce jsou content-addressed a cachované
Dokument je identifikovaný SHA-256 svých bajtů a výsledek extrakce je uložený proti tomuto hashi. Přehrání projekce znovu použije cache a nespustí OCR ani LLM. Bez toho by každá oprava projektoru stála celý backfill znovu.
Detail
Confidence je plnohodnotné pole, ne řádek v logu
Každá vytěžená hodnota nese skóre spolehlivosti, použitou metodu a stránku dokumentu. Pod prahem nejde do projekce, ale do fronty lidské kontroly — u vysokých nominálů je práh přísnější.
Detail
Jeden zapisovatel, mnoho čtenářů
Feed vlastní vždy právě jeden poller, držený leasem v Redisu s TTL; lease po mrtvém držiteli vyprší a je převzatý. Všechno ostatní čte projekci, takže záruky uspořádání zůstávají triviální a škálování je otázkou jen na straně čtenářů.
Detail
%% title: Cesta údaje od rejstříku k analytikovi
%% caption: Příjem metadat a zpracování dokumentů běží ve dvou oddělených frontách. Do projekce se zapíše jen hodnota, která prošla validací a prahem spolehlivosti; ostatní jde k člověku. Schéma popisuje návrh, stav po oblastech drží roadmapa.
flowchart LR
subgraph SRC["Zdroj"]
WS[("WS ISIR<br/>proud akcí")]
end
subgraph ING["Příjem"]
POL["Poller<br/>jeden zapisovatel"] --> REP[("Replika<br/>append-only log")]
end
subgraph DOC["Dokumentová pipeline"]
DL["Stažení PDF"] --> EX["OCR + LLM<br/>extrakce do schématu"] --> VAL{"Validace<br/>a práh"}
end
subgraph OUT["Čtení"]
PRJ[("Projekce<br/>odvozená")] --> API["Čtecí API<br/>a aplikace"]
end
WS --> POL
REP -- "akce s dokumentem" --> DL
REP --> PRJ
VAL -- "jisté" --> PRJ
VAL -- "nejisté" --> Q["Fronta<br/>lidské kontroly"]
Q -- "rozhodnutí je událost" --> PRJ
click WS href "/docs/semantika-zdroje" "Sémantika zdroje ISIR"
click POL href "/architecture/jeden-zapisovatel-mnoho-ctenaru" "Jeden zapisovatel, mnoho čtenářů"
click REP href "/architecture/event-sourcing-protoze-zdroj-je-event-stream" "Event sourcing"
click DL href "/architecture/oddelena-dokumentova-pipeline" "Oddělená dokumentová pipeline"
click EX href "/architecture/ocr-a-llm-extrakce-do-schematu" "OCR + LLM extrakce do schématu"
click VAL href "/architecture/confidence-je-plnohodnotne-pole" "Confidence je plnohodnotné pole"
click Q href "/roadmap/fronta-lidske-kontroly" "Fronta lidské kontroly"
click PRJ href "/about/log-je-pravda-projekce-je-odvozena" "Log je pravda, projekce je odvozená"
click API href "/api" "Přehled API"
class Q st-in_progress
Barva uzlu je stav položky na roadmapě: Rozpracováno
Z dokumentu na údaj, a co když to selže
OCR + LLM extrakce do schématu
Klasické OCR na formulářích přihlášek selhává. Pipeline kombinuje OCR a multimodální extrakci do deklarovaného JSON schématu, výsledek validuje a teprve pak zapisuje.
Detail
Chování při selhání
Selhání zdroje, pád uprostřed dávky ani nejistá extrakce nesmí skončit zápisem nesprávného stavu. Každý z těchto případů má předem určené chování a žádné z nich se nepřevádí na negativní výsledek.
Detail
%% title: Stádia dokumentové pipeline
%% caption: Dokument je adresovaný hashem obsahu a výsledek extrakce se cachuje podle hashe a verze extraktoru. Neprošlá kontrola citace nebo částky pošle pohledávku ke kontrole bez ohledu na ostatní. Dnes jen pro přihlášky; obecná klasifikace typu dokumentu, čtení rozvržení stránky a skóre po jednotlivých polích zatím postavené nejsou.
flowchart LR
A["Stažení<br/>adresováno obsahem"] --> H{"Hash<br/>už známý?"}
H -- "ano" --> C[("Cache<br/>SHA-256 + verze extraktoru")]
H -- "ne" --> B["Výběr dokumentu<br/>typ události a oddíl"]
B --> O["Text stránky<br/>vrstva nebo OCR"]
O --> E["Extrakce modelem<br/>do JSON schématu"]
E --> V{"Validace<br/>citace, částky,<br/>součty, IČO"}
V -- "selhala" --> Q["Fronta kontroly"]
V -- "prošla" --> S{"Skóre<br/>po dílčí pohledávce"}
S -- "pod prahem" --> Q
S -- "nad prahem" --> W[("Zápis s proveniencí<br/>dokument, stránka, skóre")]
E -.-> C
click A href "/roadmap/dokumentova-pipeline" "Dokumentová pipeline"
click C href "/architecture/vysledky-extrakce-jsou-cachovane" "Výsledky extrakce jsou cachované"
click E href "/architecture/ocr-a-llm-extrakce-do-schematu" "OCR + LLM extrakce do schématu"
click O href "/architecture/ocr-a-llm-extrakce-do-schematu" "OCR + LLM extrakce do schématu"
click V href "/docs/extrakce" "Extrakce"
click S href "/architecture/confidence-je-plnohodnotne-pole" "Confidence je plnohodnotné pole"
click Q href "/roadmap/fronta-lidske-kontroly" "Fronta lidské kontroly"
click W href "/about/cislo-bez-zdroje-neni-udaj" "Číslo bez zdroje není údaj"
class A st-in_progress
class Q st-in_progress
Barva uzlu je stav položky na roadmapě: Rozpracováno
Co musí přežít incident
Replika a úložiště dokumentů. Všechno ostatní je odvozené a znovu postavitelné. Zálohovací politika neplyne z ničeho jiného než z téhle věty.
%% title: Oprava je přehrání, ne ruční UPDATE
%% caption: Replika se nikdy needituje. Chyba v interpretaci se opraví v projektoru, zvýší se jeho verze a projekce se přehraje z logu; extrakce se nevolá znovu, protože je v cache.
flowchart LR
LOG[("Replika<br/>neměnné akce")] --> P1["Projektor vN"] --> V1[("Projekce vN")]
V1 -. "chyba v interpretaci" .-> FIX["Oprava projektoru<br/>verze vN+1"]
LOG --> P2["Projektor vN+1"]
FIX --> P2
CACHE[("Cache extrakcí")] --> P2
P2 --> V2[("Projekce vN+1")]
BAD["Ruční UPDATE"] -. "zakázáno" .-x V1
click LOG href "/architecture/event-sourcing-protoze-zdroj-je-event-stream" "Event sourcing"
click CACHE href "/architecture/vysledky-extrakce-jsou-cachovane" "Výsledky extrakce jsou cachované"
click V1 href "/roadmap/projekce" "Projekce"
click V2 href "/roadmap/projekce" "Projekce"
click BAD href "/about/log-je-pravda-projekce-je-odvozena" "Log je pravda, projekce je odvozená"
class V1 st-in_progress
class V2 st-in_progress
Barva uzlu je stav položky na roadmapě: Rozpracováno