Automatizované spracovanie faktúr: Prečo AI prekonáva staré OCR systémy

Od nolimeo · 7. apríla 2026
banner image

Predstavte si back-office stredne veľkej distribučnej firmy, do ktorej denne prichádzajú desiatky až stovky faktúr od rôznych dodávateľov z celého sveta. Každý dodávateľ má vlastný formát dokumentu. Niektorí posielajú čisté digitálne PDF generované priamo z ERP, iní naskenovaný papierový dokument nakrivo odfotený mobilným telefónom, na ktorom je pečiatka prekrývajúca celkovú sumu s DPH.

V tradičnom IT sa tento problém riešil nasadením systémov Optical Character Recognition (OCR). Tieto nástroje však často fungujú na báze pevných šablón (template-based OCR) alebo preddefinovaných pravidiel. Ak dodávateľ zmení rozloženie prvkov na faktúre len o pár milimetrov, napríklad posunie tabuľku s položkami nižšie alebo presunie IČO do iného rohu, šablóna môže zlyhať. Systém neprečíta dôležité pole alebo nesprávne priradí variabilný symbol k celkovej sume. Výsledkom je manuálna správa šablón, frustrácia účtovníkov, vyššie riziko chýb a softvér, ktorý stále vyžaduje ľudský dohľad.

V technologickom štúdiu nolimeo navrhujeme systémy bezšablónového vyťažovania dát postavené na moderných Vision-Language modeloch (VLM). Tieto modely nevnímajú dokument iba ako súbor pevných súradníc. Vedia analyzovať vizuálne rozloženie, text aj kontext dokumentu. Rozpoznajú názov dodávateľa, dátum splatnosti alebo tabuľku s položkami aj vtedy, keď sa na rôznych faktúrach nachádzajú na inom mieste.

V tomto technickom sprievodcovi si ukážeme, ako postaviť TypeScript pipeline na asynchrónne vyťažovanie faktúr, ako validovať dáta pomocou knižnice Zod a ako riešiť výpadky alebo nízku kvalitu dokumentov cez Dead Letter Queue (DLQ) a ľudskú kontrolu.


1. Prečo tradičné OCR zlyháva a v čom je Vision-Language AI iná

Šablónové systémy (napr. staršie verzie ABBYY, Tesseract alebo rôzne enterprise OCR nástroje) sa spoliehajú na zónovú analýzu. Programátor musí pre každého dodávateľa naklikať zóny: „Meno dodávateľa hľadaj na súradniciach X:100, Y:200.“

Tento prístup má tri zásadné limity:

  1. Rigidnosť: Stačí, ak dodávateľ pridá na faktúru jeden riadok textu navyše, celá štruktúra sa posunie nadol a zónový skener načíta nesprávne hodnoty.
  2. Slabé sémantické chápanie: Tradičné OCR iba prepisuje obrázok na text. Nedokáže posúdiť, či číslo 20251120 reprezentuje dátum splatnosti, variabilný symbol alebo číslo objednávky.
  3. Vysoké náklady na integráciu a údržbu: Ak má firma 500 dodávateľov, správa a aktualizácia 500 rôznych šablón sa stáva nočnou morou pre akékoľvek interné IT oddelenie.

Vision-Language modely (VLM), napríklad GPT-4o alebo Claude, spájajú vizuálne vnímanie s jazykovým porozumením. Dokážu analyzovať faktúru ako dokument, nielen ako sadu súradníc. Keď im povieme: „Vytiahni celkovú sumu bez DPH,“ model vie hľadať výrazy ako „Celkom bez DPH“, „Suma bez dane“, „Subtotal“ alebo „Netto“, vyhodnotiť ich vzťah k peňažným hodnotám a vrátiť relevantné číslo aj pri odlišnom rozložení faktúry.


2. Architektúra robustnej AI vyťažovacej pipeline

Pri spracovaní finančných dokumentov nesmieme nechať AI voľne generovať text. Veľké jazykové modely môžu vracať neformátované odpovede alebo si v hraničných prípadoch domýšľať chýbajúce dáta.

Pre bezpečnú integráciu s účtovným systémom, napríklad KROS, Pohoda alebo SAP, staviame architektúru na troch pilieroch:

  1. Vizuálna analýza dokumentu (OpenAI Vision API): Dokument vo formáte PDF alebo obrázku (PNG/JPEG) premeníme na base64 a odošleme do modelu spolu s inštrukciami pre presnú sémantickú analýzu.
  2. Štruktúrovaný JSON formát (Structured Outputs): Využívame funkciu response_format s JSON schémou, aby model vracal dáta v definovanej štruktúre.
  3. Type-safety a runtime validácia (Zod): Prijatý JSON zvalidujeme pomocou TypeScript knižnice Zod. Zod overí typy dát, znormalizuje dátumy do ISO formátu, prepočíta sumy na kontrolu matematickej konzistencie a zachytí akékoľvek chyby pred zápisom do databázy.
 [ Naskenovaná faktúra ] ──► [ Prevod PDF na Base64 ] ──► [ OpenAI GPT-4o Vision ]
                                                                  │
                                                      (Structured JSON Output)
                                                                  ▼
 [ Zápis do ERP / DB ] ◄── [ Kontrola DPH & Sumy ] ◄── [ Zod Schema Validation ]

3. Technická implementácia v TypeScript a Node.js

Nižšie uvádzame zjednodušenú backendovú implementáciu v jazyku TypeScript. Kód pokrýva definíciu dátovej schémy pomocou Zod, odoslanie dokumentu do OpenAI a následnú validáciu s matematickým overením, či súčet položiek súhlasí s celkovou sumou faktúry.

// src/services/invoice-processor.ts
import { OpenAI } from "openai";
import { z } from "zod";

// 1. Definícia prísnej Zod schémy pre položku faktúry
const InvoiceItemSchema = z.object({
  description: z.string().describe("Názov tovaru alebo služby"),
  quantity: z.number().positive().describe("Množstvo"),
  unitPrice: z.number().nonnegative().describe("Jednotková cena bez DPH v eurách"),
  vatRate: z.number().nonnegative().describe("Sadzba DPH v percentách (napr. 20)"),
  totalPriceWithoutVat: z.number().nonnegative().describe("Celková cena riadku bez DPH"),
});

// 2. Definícia schémy pre celú faktúru
const InvoiceDataSchema = z.object({
  invoiceNumber: z.string().describe("Číslo faktúry / daňového dokladu"),
  variableSymbol: z.string().nullable().describe("Variabilný symbol platby"),
  issueDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Dátum vystavenia vo formáte YYYY-MM-DD"),
  dueDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Dátum splatnosti vo formáte YYYY-MM-DD"),
  
  supplierName: z.string().describe("Obchodné meno dodávateľa"),
  supplierIco: z.string().regex(/^\d{8}$/).describe("IČO dodávateľa (presne 8 číslic)"),
  supplierDic: z.string().describe("DIČ dodávateľa"),
  supplierIban: z.string().describe("IBAN účet dodávateľa pre platbu"),

  customerName: z.string().describe("Meno odberateľa"),
  customerIco: z.string().regex(/^\d{8}$/).describe("IČO odberateľa"),

  items: z.array(InvoiceItemSchema).describe("Zoznam fakturovaných položiek"),
  
  totalAmountWithoutVat: z.number().nonnegative().describe("Celková suma faktúry bez DPH"),
  totalVatAmount: z.number().nonnegative().describe("Celková suma DPH v eurách"),
  totalAmountWithVat: z.number().nonnegative().describe("Celková suma na úhradu s DPH"),
  currency: z.string().default("EUR").describe("Mena faktúry (trojpísmenný kód, napr. EUR)"),
});

export type InvoiceData = z.infer<typeof InvoiceDataSchema>;

export class AIInvoiceProcessor {
  private openai: OpenAI;

  constructor() {
    const apiKey = process.env.OPENAI_API_KEY;
    if (!apiKey) {
      throw new Error("Chýba kľúč OPENAI_API_KEY v premenných prostredia.");
    }
    this.openai = new OpenAI({ apiKey });
  }

  /**
   * Prijme faktúru ako base64 reťazec (podporuje PNG, JPEG, WEBP) a vyťaží z nej štruktúrované dáta
   */
  public async processInvoiceImage(base64Image: string, mimeType: string = "image/jpeg"): Promise<InvoiceData> {
    try {
      const prompt = `Zanalyzujte priložený obrázok faktúry a vytiahnite z neho všetky relevantné informácie.
Uistite sa, že:
1. Všetky peňažné hodnoty sú prevedené na desatinné čísla.
2. Dátumy sú striktne naformátované ako YYYY-MM-DD.
3. Ak variabilný symbol nie je explicitne uvedený, použite číslo faktúry.
4. IČO musí byť očistené o medzery a obsahovať presne 8 číslic.`;

      // Volanie OpenAI API s využitím Structured Outputs
      const response = await this.openai.chat.completions.create({
        model: "gpt-4o",
        temperature: 0.0, // Nízka teplota obmedzuje kreatívne dopĺňanie mimo dokumentu
        response_format: { type: "json_object" }, // Vynútenie JSON formátu
        messages: [
          {
            role: "user",
            content: [
              { type: "text", text: prompt },
              {
                type: "image_url",
                image_url: {
                  url: `data:${mimeType};base64,${base64Image}`,
                },
              },
            ],
          },
        ],
      });

      const rawJson = response.choices[0]?.message?.content;
      if (!rawJson) {
        throw new Error("Model nevrátil žiadny obsah.");
      }

      const parsedData = JSON.parse(rawJson);

      // 3. Spustenie runtime validácie cez Zod schema
      const validatedData = InvoiceDataSchema.parse(parsedData);

      // 4. Matematická seba-kontrola (Cross-validation)
      this.validateCalculations(validatedData);

      return validatedData;

    } catch (error) {
      if (error instanceof z.ZodError) {
        console.error("Zod schéma zlyhala pri validácii dát faktúry:", error.errors);
        throw new Error(`Chyba štruktúry vyťažených dát: ${JSON.stringify(error.errors)}`);
      }
      console.error("Systémová chyba počas vyťažovania faktúry:", error);
      throw error;
    }
  }

  /**
   * Vykoná matematické overenie vyťažených cien na elimináciu halucinácií LLM
   */
  private validateCalculations(data: InvoiceData): void {
    const TOLERANCE = 0.05; // 5 centov tolerancia na zaokrúhľovanie položiek

    // Súčet jednotlivých položiek bez DPH
    const calculatedSubtotal = data.items.reduce((sum, item) => sum + item.totalPriceWithoutVat, 0);
    const subtotalDiff = Math.abs(calculatedSubtotal - data.totalAmountWithoutVat);

    if (subtotalDiff > TOLERANCE) {
      throw new Error(
        `Matematická nekonzistencia: Súčet položiek (${calculatedSubtotal.toFixed(2)} EUR) ` +
        `nesúhlasí s celkovou sumou bez DPH (${data.totalAmountWithoutVat.toFixed(2)} EUR).`
      );
    }

    // Overenie celkovej sumy s DPH
    const expectedTotalWithVat = data.totalAmountWithoutVat + data.totalVatAmount;
    const totalDiff = Math.abs(expectedTotalWithVat - data.totalAmountWithVat);

    if (totalDiff > TOLERANCE) {
      throw new Error(
        `Matematická nekonzistencia: Súčet základu dane a DPH (${expectedTotalWithVat.toFixed(2)} EUR) ` +
        `nesúhlasí s vyťaženou celkovou sumou s DPH (${data.totalAmountWithVat.toFixed(2)} EUR).`
      );
    }

    console.log(`Matematické overenie faktúry ${data.invoiceNumber} prebehlo úspešne.`);
  }
}

4. Riešenie hraničných stavov a zlyhaní v produkcii

Pri spracovaní finančných dát treba počítať s chybami a navrhnúť systém tak, aby žiadna faktúra nezmizla a aby sa do účtovníctva nezapisovali neoverené dáta.

4.1. Čo ak je naskenovaný dokument rozmazaný alebo nečitateľný?

Ak je kvalita obrazu mizerná, LLM vráti neúplné dáta. Vtedy Zod validácia zlyhá (napríklad nenájde IČO alebo zlyhá regulárny výraz pre IBAN).

  • Riešenie: Systém nesmie vyhodiť neošetrenú výnimku 500. Namiesto toho zachytíme ZodError, označíme stav spracovania faktúry ako REQUIRES_HUMAN_REVIEW a uložíme surové dáta do databázy spolu s chybovým logom. Používateľovi v back-office sa zobrazí rozhranie, kde vidí pôvodný dokument vedľa čiastočne vyťažených polí a chýbajúce informácie doplní ručne.

4.2. Koncept Dead Letter Queue (DLQ) pre neúspešné spracovanie

Všetky prichádzajúce faktúry spracovávame asynchrónne na pozadí pomocou frontu správ (Message Queue, napr. BullMQ v Node.js alebo PostgreSQL-based queue). Ak spracovanie zlyhá z dôvodu dočasného výpadku OpenAI API, napríklad Rate Limit 429, implementujeme Exponential Backoff Retry stratégiu: automatické opakovanie s narastajúcim časovým odstupom, napríklad po 5 s, 30 s a 5 minútach.

Ak spracovanie zlyhá 5-krát po sebe z dôvodu poškodeného súboru, faktúra sa presunie do Dead Letter Queue (DLQ). Tým zabránime zablokovaniu hlavného spracovateľského kanálu pre ostatné dokumenty a administrátor dostane notifikáciu o zaseknutom dokumente.

 [ Nová Faktúra ] ──► [ Spracovanie v BullMQ ]
                             │
                  (Chyba: Rate Limit API) ──► Automatický Retry (x5)
                             │
                  (Chyba: Poškodený súbor)
                             ▼
                [ Dead Letter Queue (DLQ) ] ──► Slack Alert / Manuálny zásah

4.3. Viacstranové a objemné PDF dokumenty

Veľké PDF súbory obsahujúce desiatky strán (napríklad mesačné zúčtovania za telekomunikačné služby) by zbytočne spotrebovali obrovské množstvo tokenov (context window) a predražili spracovanie.

  • Riešenie: Systém najprv analyzuje veľkosť súboru. Ak ide o viacstranové PDF, pomocou knižnice ako pdf-lib alebo microservisu na báze graphicsmagick extrahujeme iba relevantné strany, často prvú a poslednú. Práve tam sa zvyčajne nachádzajú identifikačné údaje, rekapitulácia a celkové sumy. Tým vieme znížiť spracovateľské náklady a zrýchliť odozvu API.

5. Integrácia vyťažených dát do slovenských ERP systémov

Keď prejdeme fázou úspešnej extrakcie a validácie, dáta musíme bezpečne zapísať do účtovného softvéru. Mnohé slovenské a české ERP systémy (KROS, Pohoda, Money S3) nemajú moderné REST API pre priamy zápis faktúr na pozadí alebo používajú integračné mechanizmy, ktoré treba riešiť individuálne.

Túto bariéru prekonávame dvoma spoľahlivými spôsobmi:

  1. Generovanie štandardizovaného XML/ISDOC: Naša backendová služba transformuje zvalidovaný JSON na XML štruktúru presne podľa špecifikácie daného ERP (napr. importný formát pre Pohodu alebo KROS). Tieto XML súbory sa ukladajú na zabezpečené úložisko (napr. SFTP alebo Amazon S3), odkiaľ si ich účtovný systém v pravidelných intervaloch automaticky sťahuje a importuje.
  2. API Middleware: Pre moderné cloudové ERP systémy vyvíjame dedikovaný Node.js middleware, ktorý transformuje vyťažené JSON payloady na API dopyty a bezpečne ich zapisuje v reálnom čase, pričom detailne loguje stav každej transakcie.

Záver: Premeňte rutinnú prácu na automatizovaný proces

Nasadenie Vision-Language AI na automatizované spracovanie faktúr odstraňuje monotónnu prácu, ktorá zbytočne uberá čas účtovníkom a finančným manažérom. Správne navrhnutá pipeline vie rýchlo vyťažiť dáta, skontrolovať ich štruktúru, porovnať sumy a problémové dokumenty poslať na ľudskú validáciu.

Pri implementácii firemných AI riešení je však kľúčové vedieť, kde sa finančné dokumenty spracúvajú, kto k nim má prístup a ako sa validujú pred zápisom do účtovníctva. Finančné dáta patria medzi najcitlivejšie informácie vo firme a ich spracovanie potrebuje kontrolovanú, auditovateľnú a typovo bezpečnú infraštruktúru.

Sme technologické štúdio nolimeo. Navrhujeme AI pipeline, ERP integrácie a backend systémy pre firmy, ktoré chcú automatizovať spracovanie dokumentov bez toho, aby stratili kontrolu nad dátami a účtovnými procesmi.

Chcete automatizovať spracovanie faktúr a prepojiť vyťažovanie dát s vaším účtovným systémom? Napíšte nám a prejdeme si dokumenty, ERP integráciu aj bezpečný technický návrh.

Máte záujem posunúť váš projekt vpred?