Tento článek popisuje architekturu systému, který z fotografie nebo PDF dokumentu extrahuje strukturovaná účetní data. Zaměřujeme se na konkrétní kroky pipeline — od normalizace českých formátů přes evidence-based validaci až po confidence scoring.
Co je OCR (Optical Character Recognition)
OCR je technologie optického rozpoznávání znaků. Převádí rastrový obraz textu — fotografii účtenky, sken faktury, screenshot smlouvy — na strojově čitelný text. Samotné OCR ale neprodukuje strukturovaná data. Rozpozná, že na obrázku je text „Celkem: 1 234,50 Kč", ale nerozumí, že jde o celkovou částku k úhradě.
DigiDoklad proto kombinuje OCR s multimodálním AI modelem (Google Gemini 2.0 Flash), který rozumí kontextu dokumentu — identifikuje typ dokladu, přiřadí hodnoty ke správným polím a ohodnotí spolehlivost extrakce.
Co je Structured Data Extraction
Structured Data Extraction je proces převodu nestrukturovaného textu (volný text z dokumentu) na strukturovaný datový objekt s definovanými poli a typy. V kontextu DigiDokladu to znamená transformaci surového textu dokladu na JSON objekt s více než 30 poli:
- Identifikační pole: typ dokladu, číslo faktury, variabilní symbol
- Časová pole: datum vystavení, DUZP, datum splatnosti (formát YYYY-MM-DD)
- Dodavatelská pole: název, IČO, DIČ, adresa, bankovní účet, IBAN
- Finanční pole: základ, DPH po sazbách, celkem, měna, způsob úhrady
- Metadata: reverse charge, zahraniční dodavatel, EU VAT, kategorie nákladů
Každé pole má definovaný datový typ validovaný Zod schématem. Není to CSV export surového textu — je to typově bezpečný, validovaný datový model.
Podporované typy dokumentů
Engine zpracovává pět základních typů účetních dokladů a několik typů osobních dokumentů. Mezi osobní dokumenty patří například záruční listy, smlouvy, pojistky, OP/ŘP/Pas nebo rodné listy. Všechny dokumenty jsou šifrovány end-to-end — k obsahu nemá přístup ani provozovatel služby.
Architektura pipeline: 6 kroků zpracování
Zpracování dokumentu prochází šesti sekvenčními kroky. Každý krok má jasně definovaný vstup a výstup.
Krok 1: Příjem a příprava souboru
Systém přijímá formáty JPEG, PNG, WebP, HEIC a PDF. Soubory pod 4 MB se posílají přímo jako base64. Větší soubory se uploadují do dočasného storage bucketu (ocr-temp) s TTL 300 sekund.
HEIC soubory z iOS zařízení se automaticky konvertují na JPEG. Obrázky se komprimují na maximální šířku 1600 px s kvalitou 85 % — empiricky optimální poměr mezi čitelností a velikostí payloadu.
Krok 2: AI extrakce (Gemini 2.0 Flash)
Dokument se odešle do Google Gemini 2.0 Flash. Systémový prompt obsahuje:
- Definice výstupních polí s přesnými formáty (datum jako YYYY-MM-DD, měna jako ISO 4217 kód)
- Mapování labelů na pole (kde na dokladu hledat „Faktura č.:", „VS:", „DUZP:", „Splatnost:")
- Pravidla pro klasifikaci typu dokumentu s prioritizací signálů
- Anti-hallucination instrukce (viz sekce níže)
- Self-check checklist před odesláním odpovědi
Ve firemním režimu se do promptu injektuje blok s názvem, IČO a DIČ uživatelovy firmy. AI porovná tyto údaje se sekcemi „Dodavatel" a „Odběratel" na dokladu a podle toho klasifikuje typ faktury.
AI vrací JSON s 30+ poli, evidence snippety a per-field confidence skóre.
Krok 3: Pre-Zod normalizace
Surový AI výstup prochází normalizační vrstvou PŘED Zod validací. Tato vrstva řeší nesrovnalosti v AI výstupech:
Normalizace je klíčová vrstva, protože AI modely nevracejí konzistentní formáty. Stejný model může vrátit částku jako „1234.50", „1 234,50 Kč" nebo „1234,50". Normalizace zajistí jednotný formát bez ztráty informace.
Konkrétní transformace:
Částky (normalizeAmount): Odstraní symboly měn (Kč, €, $), převede český formát s čárkou na tečku, odstraní mezery jako oddělovače tisíců, ignoruje řetězce vypadající jako data (DD.MM.YYYY). Výstup: číslo nebo null.
Data (normalizeDate): Parsuje formáty DD.MM.YYYY, DD/MM/YYYY, DD-MM-YYYY, DD. MM. YYYY (s mezerami), YYYY-MM-DD. Validuje rozsahy (měsíc 1–12, den 1–31, rok 1990–2099). Výstup: řetězec YYYY-MM-DD nebo null.
IBAN (normalizeIban): Odstraní mezery, převede na uppercase, validuje pattern (2 písmena + 2 číslice + zbytek, min. 15 znaků). Pro české IBAN (CZ + 22 číslic) derivuje český formát účtu: prefix-účet/kód banky.
IČO (supplier_ico): Extrahuje pouze číslice, validuje minimální délku 6 znaků. „IČO: 270 82 440" → „27082440".
Měna (normalizeCurrency): Mapuje textové varianty na ISO kódy: „Kč" → CZK, „€" → EUR, „koruna" → CZK. Výchozí: CZK.
Krok 4: Zod validace + Hard guardrails
Normalizovaná data projdou Zod schématem (BusinessOcrOutputSchema), které definuje:
- Enumerace pro document_type (5 hodnot), payment_method (4 hodnoty), suggested_category (9 hodnot)
- Coerce transformace pro číselné hodnoty (řetězec → číslo)
- Catch fallbacky pro chybějící pole (null místo výjimky)
- Clamp pro confidence (0.0–1.0)
Po Zod validaci se aplikují hard guardrails — poslední obranná linie proti halucinacím:
Evidence-based guardrails vynulují pole, pokud v textu dokumentu neexistuje daný label:
- invoice_number:
Musí existovat label typu „Faktura č.", „Doklad č.", „Invoice No:", „Č. účtenky:". Pokud chybí → pole = null, field_confidence = 0.
- variable_symbol:
Musí existovat label „VS:", „Var. symbol:", „Variabilní symbol:". Pokud chybí → pole = null.
- total:
Musí existovat label „Celkem", „K úhradě", „Total". Pokud chybí → field_confidence klesne na max 0.5 (pole se nenuluje, ale označí jako neověřené).
- reverse_charge:
Pokud detekován → automaticky vat_total = 0, vat_rates = [].
Krok 5: Lokální validace a confidence scoring
Po guardrailech přichází 14 nezávislých validačních kontrol. Každá produkuje strukturovaný warning s kódem, polem, lokalizovanou zprávou (CS + EN), závažností (warn/error) a volitelným quick fixem:
Aritmetické kontroly:
- subtotal + vat_total ≈ total (tolerance ±1 Kč) → severity: error
- Součet vat_rates[].amount ≈ vat_total → severity: warn
Povinná pole:
- Chybí invoice_number u faktury → severity: error
- Chybí due_date u faktury → severity: warn, quickFix: set_due_date
- Chybí payment_method u účtenky → severity: warn, quickFix: set_payment_method
Kontextové kontroly:
- Dodavatel má DIČ, ale chybí DPH rozpis (neplátce?) → severity: warn
- Cizí měna → severity: warn
- Reverse charge detekován → severity: warn
- Zahraniční / EU dodavatel → severity: warn
Evidence kontroly:
- Chybí evidence pro kritická pole (invoice_number, total, supplier_name, supplier_ico, issue_date, due_date, payment_method) → severity: warn
- Nízká per-field confidence (pod 60 %) → severity: warn
Co je Confidence Score
Confidence score je číselné hodnocení spolehlivosti celé extrakce v rozsahu 0.0–1.0 (0–100 %). Není to pravděpodobnost správnosti jednoho pole — je to agregovaná metrika ovlivněná všemi extrahovanými poli, jejich evidencí a validačními výsledky.
Princip confidence scoringu v DigiDokladu
„Raději přísný než sebejistý"
Skóre se nikdy nezvyšuje — pouze se snižuje na základě chybějících dat, chyb a varování.
Pravidla úpravy skóre (adjustConfidenceScore):
- Chybí invoice_number → cap na 0.65
- Chybí document_type → cap na 0.50
- Low document_type_confidence (pod 0.6) → cap na 0.70
- Chybí due_date u faktury → cap na 0.75
- Chybí payment_method u účtenky → cap na 0.75
- Aritmetický nesoulad (amount_math_mismatch, vat_math_mismatch) → cap na 0.60
- Více než 3 warningy → cap na 0.70
- 2+ kritická pole bez evidence → cap na 0.60
- 3+ kritická pole bez evidence → cap na 0.50
Confidence tiers:
- auto_accept (≥ 0.85): Všechna pole mají evidence, matematika sedí, žádné error warningy. Data lze přijmout bez zásahu.
- review (0.60–0.84): Některá pole mají nižší jistotu nebo chybí dílčí evidence. Uživatel vidí zvýrazněná pole k ověření.
- manual_required (pod 0.60): Příliš mnoho nejistých polí. Kompletní ruční kontrola.
Anti-hallucination pravidla
AI modely (včetně Gemini) mohou s vysokou sebejistotou tvrdit něco, co na dokladu ve skutečnosti není. Například přiřadit číslo objednávky e-shopu jako číslo faktury, nebo odvodit variabilní symbol z jiného čísla na dokladu. DigiDoklad implementuje čtyřvrstvou ochranu:
Vrstva 1 — Prompt engineering:
Systémový prompt obsahuje explicitní instrukci: „NIKDY NEVYMÝŠLEJ HODNOTY. Pokud hodnotu na dokladu nenajdeš → vrať null." Pro každé pole jsou definovány přesné labely, které musí v textu existovat. Příklad pro invoice_number: „Vyplň POUZE pokud existuje explicitní label typu Faktura č., Číslo dokladu, Daňový doklad č. NEPOUŽÍVEJ: ID terminálu, číslo objednávky e-shopu, číslo pokladny."
Vrstva 2 — Evidence snippety:
AI musí ke každému extrahovanému poli dodat evidence — krátký úryvek textu z dokumentu (max 120 znaků) včetně labelu. Evidence map je součástí JSON výstupu. Příklady:
- evidence.invoice_number: „Faktura č.: FV-2025-001"
- evidence.supplier_ico: „IČO: 27082440"
- evidence.total: „Celkem k úhradě: 3 630,00 Kč"
- evidence.variable_symbol: „VS: 2025001"
Pokud AI nemůže dodat evidence → musí nastavit field_confidence pod 0.5.
Vrstva 3 — Hard guardrails (server-side):
Tato vrstva běží na serveru nezávisle na AI. Porovnává evidence snippety s předdefinovanými seznamy labelů. Pokud evidence pro invoice_number neobsahuje žádný z 25+ definovaných labelů (faktura č, číslo faktury, doklad č, invoice no, receipt no, č. účtenky, pokladní doklad č, SIN, FV-, FA-...) → pole se vynuluje.
Analogický mechanismus platí pro variable_symbol (VS:, V.S.:, Var. symbol:...) a total (Celkem, K úhradě, Total, Amount, Částka, Suma...).
Vrstva 4 — AI self-check:
Prompt obsahuje checklist, který AI musí projít před odesláním odpovědi:
1. Je výstup validní JSON bez textu kolem?
2. subtotal + vat_total ≈ total (max ±1 Kč)?
3. Součet vat_rates[].amount ≈ vat_total?
4. Jsou čísla účtů v českém formátu (xxxxx/xxxx)?
5. Je document_type_confidence realistické?
Výsledkem je, že systém raději vrátí null než nepodloženou hodnotu.
Evidence-based parsing
Evidence-based parsing je architekturní princip, který prostupuje celým pipeline. Každá extrahovaná hodnota existuje ve dvou formách: jako datová hodnota (např. invoice_number = „FV-2025-001") a jako důkaz (evidence.invoice_number = „Faktura č.: FV-2025-001").
Systém udržuje dvě paralelní datové struktury:
- field_confidence — Record<string, number> — per-field confidence (0.0–1.0)
- evidence — Record<string, string | null> — textový úryvek z dokumentu
Kritická pole, která MUSÍ mít evidence pro důvěryhodnost: invoice_number, variable_symbol, total, vat_total, supplier_name, supplier_ico, payment_method, issue_date, due_date.
Pokud pole má hodnotu, ale nemá evidence → automaticky se přidá validační warning „missing_evidence" a celkové confidence klesá.
Krok 6: Deduplikace
Na závěr pipeline systém generuje deduplikační fingerprint — deterministický řetězec složený z klíčových polí. Formát se liší podle typu dokumentu:
- Účtenky: R|název_dodavatele|datum|čas|částka|měna|způsob_úhrady
- Faktury: I|IČO_dodavatele|číslo_faktury|datum|částka|měna
Fingerprint umožňuje detekovat duplicitní nahrání stejného dokladu bez nutnosti porovnávat obrázky.
Caching a rate limiting
Výsledky OCR se cachují přes Redis s TTL 1 hodina. Cache klíč se generuje z hashe obsahu obrázku (prvních a posledních 500 znaků base64 + délka + MIME type). Opakované nahrání stejného dokumentu vrátí výsledek okamžitě bez dalšího volání AI.
Rate limiting: max 30 požadavků za minutu na uživatele.
DigiDoklad AI OCR je dostupný v osobním i firemním režimu.
Vyzkoušejte rozpoznávání prvního dokumentu zdarma.
Začněte organizovat své dokumenty ještě dnes
Připojte se k tisícům uživatelů, kteří už nikdy neztratí důležitý dokument
Vyzkoušet AI rozpoznávání