Regex → OCR → pravidlá: architektúra flowu, ktorý triedi 3 000 faktúr mesačne

Peter Kaňuk Peter Kaňuk
· · 11 min čítania
Regex → OCR → pravidlá: architektúra flowu, ktorý triedi 3 000 faktúr mesačne

Flow v Microsoft Power Automate, ktorý pre BK a.s. triedi približne 3 000 faktúr mesačne, nestojí na jednom „inteligentnom“ kroku, ale na troch vrstvách rozhodovania zoradených od najlacnejšej po najdrahšiu: porovnanie vzorov v texte e-mailu, OCR nad PDF prílohami cez Azure Document Intelligence a záložné pravidlá v SharePoint zozname. Výsledkom je 70 % faktúr doručených bez zásahu človeka a 99 % úspešnosť rozpoznávania.

V prvom diele série sme ukázali, prečo BK ušetrilo 90 hodín mesačne. Tento diel je pre tých, ktorí chcú vedieť, ako je flow postavený: aké komponenty používa, kde sú háčiky Power Automate a čo by sme dnes urobili inak.

Architektúra v skratke

VrstvaČo robíTechnológiaKedy sa spúšťa
0 – TriggerNový e-mail v zdieľanej účtovnej schránkePower Automate, Office 365 Outlook konektorkaždý e-mail
1 – TextNájde číslo objednávky v predmete alebo tele e-mailuPower Automate expressionsvždy
2 – OCRPrečíta číslo objednávky, číslo faktúry a sumu z PDFAzure Function + Azure Document Intelligenceak vrstva 1 nič nenašla
3 – PravidláSmeruje podľa odosielateľa, predmetu alebo obsahuSharePoint zoznamak vrstvy 1 a 2 nič nenašli
SpoločnéDotaz do objednávkového systému, doručenie, logHTTP konektor, Outlook, SharePoint, Power BIpo každom rozhodnutí

Prečo tri vrstvy a nie jeden AI model

Prvá myšlienka pri takejto úlohe býva „pošlime všetko do AI, nech rozhodne“. Zavrhli sme ju z troch dôvodov.

  • Náklady. Každé volanie OCR alebo jazykového modelu stojí peniaze. Pri 3 000 faktúrach mesačne je rozdiel medzi „vždy“ a „len keď treba“ výrazný. Expression v Power Automate stojí nulu.
  • Determinizmus. Účtovníctvo potrebuje vedieť, prečo faktúra skončila u konkrétneho človeka. „V predmete bolo číslo objednávky X, systém vrátil meno Y“ sa vysvetľuje ľahko. Pravdepodobnostný výstup modelu nie.
  • Správa bez IT. Podmienkou klienta bolo, aby si účtovný tím vedel meniť smerovanie sám. SharePoint zoznam vie upraviť každý; prompt alebo trénovací dataset nie.

Vrstvy sú preto zoradené od najlacnejšej a najspoľahlivejšej po najdrahšiu. Keď jedna vrstva nájde jednoznačnú odpoveď, ďalšie sa nespúšťajú. AI klasifikácia má v tomto návrhu miesto až ako štvrtá vrstva pre zvyšok, ktorý dnes rieši človek – a presne tam ju plánujeme nasadiť.

Architektúra a tok dát

Diagram toku dát: trigger e-mailu, vrstva 1 text, vrstva 2 Azure Function s Document Intelligence, vrstva 3 SharePoint pravidlá, log a Power BI

Každý e-mail prejde maximálne tromi rozhodnutiami; každé skončí buď doručením, alebo označením na ručné spracovanie, a vždy záznamom v logu.

Komponenty a ich úloha:

  • Power Automate (cloud flow) – orchestrácia. Trigger na zdieľanú schránku, vetvenie, volanie služieb, preposielanie, zápis do logu.
  • Azure Function (Python) – všetko, čo Power Automate nevie alebo robí draho: skutočný regex, spracovanie PDF, volanie Document Intelligence, spracovanie viacerých faktúr v jednom súbore.
  • Azure Document Intelligence – OCR a extrakcia polí z faktúr.
  • Interný objednávkový systém BK – zdroj pravdy pre „kto je zodpovedný za objednávku“, volaný cez HTTP.
  • SharePoint – dva zoznamy: pravidlá smerovania (spravuje biznis) a log spracovania (zapisuje flow).
  • Power BI – report nad logom.

Flow beží v tenante klienta pod servisným účtom, nie pod osobným kontom konzultanta. Zdá sa to samozrejmé, ale je to najčastejšia príčina, prečo automatizácie „záhadne“ prestanú fungovať po odchode človeka.

Vrstva 1: číslo objednávky v texte e-mailu

Prvá vrstva rieši najjednoduchší a najpočetnejší prípad: dodávateľ napíše číslo objednávky do predmetu alebo tela e-mailu. Čísla objednávok BK majú pevný formát – písmenový prefix a pevný počet číslic – čo je ideálny prípad pre porovnanie vzorov.

Power Automate nemá regex

To je prvý háčik. V expressions nie je žiadna funkcia match() ani regex(). Máte indexOf, substring, contains, split a startsWith. Pri pevnom formáte to stačí:

if(
  and(
    greater(indexOf(variables('text'), 'PREFIX'), -1),
    greaterOrEquals(
      length(variables('text')),
      add(indexOf(variables('text'), 'PREFIX'), 8)
    )
  ),
  substring(variables('text'), indexOf(variables('text'), 'PREFIX'), 8),
  ''
)

Dve veci, ktoré vás v produkcii dobehnú, ak ich nevyriešite:

  • substring padá pri prekročení dĺžky. Ak je prefix na konci reťazca a za ním už nie je 6 číslic, celý flow skončí chybou. Preto kontrola greaterOrEquals(length(...)) pred každým substring.
  • Null v predmete. E-mail bez predmetu nie je prázdny reťazec, ale null, a indexOf(null, ...) zlyhá. Všetko, čo prichádza z triggera, obalte do coalesce(triggerOutputs()?['body/subject'], '').

Telo e-mailu najprv prevedieme z HTML na čistý text (akcia Html to text), inak sa číslo objednávky môže skrývať medzi tagmi a indexOf ho nájde na nesprávnom mieste alebo vôbec.

Dotaz do objednávkového systému

Nájdené číslo ide cez HTTP akciu do interného objednávkového systému BK, ktorý vráti meno zodpovedného účtovníka a názov projektu. Odpoveď spracuje Parse JSON. Schému generujeme zo vzorky, ale vždy ju ručne skontrolujeme: ak vo vzorke príde číslo ako reťazec, schéma ho zafixuje ako string a prvá odpoveď s integerom flow zhodí. Polia, ktoré môžu chýbať, sú v schéme označené ako ["string", "null"].

Ak systém číslo nepozná (preklep dodávateľa, stará objednávka), vrstva 1 končí bez výsledku a flow pokračuje vrstvou 2.

Vrstva 2: OCR nad PDF prílohami

Väčšina dodávateľov číslo objednávky do e-mailu nenapíše – je len vo faktúre. Flow preto vyfiltruje PDF prílohy a pošle ich do Azure Function.

Prečo Azure Function a nie priamo konektor

Document Intelligence má v Power Automate vlastný konektor, ale používame ho až vo funkcii. Dôvody:

  • Náklady. Veľká časť faktúr má textovú vrstvu – nie sú to skeny. Funkcia najprv skúsi vytiahnuť text knižnicou pdfplumber zadarmo a Document Intelligence volá len pre strany, kde text chýba alebo je krátky (nečitateľný sken, obrázok).
  • Skutočný regex. V Pythone sú vzory čísel objednávok jeden re.findall. V Power Automate by to bol strom podmienok.
  • Viac faktúr v jednom PDF. Dodávatelia bežne pošlú jeden súbor s piatimi faktúrami. Funkcia vráti pole výsledkov, nie jeden.
def extract_text(pdf_bytes: bytes) -> str:
    pages = []
    with pdfplumber.open(BytesIO(pdf_bytes)) as pdf:
        for page in pdf.pages:
            text = page.extract_text() or ""
            if len(text.strip()) > 50:
                pages.append(text)
            else:
                pages.append(ocr_page(page))   # Azure Document Intelligence
    return "\n".join(pages)

Funkcia vracia pre každú nájdenú faktúru číslo objednávky, číslo faktúry a sumu. Flow potom pre každé číslo objednávky zopakuje dotaz do objednávkového systému z vrstvy 1.

Háčiky pri prílohách

  • Base64 je už hotové. contentBytes z Outlook triggera je už zakódovaný obsah. Ak ho preženiete ešte raz cez base64(), funkcia dostane nezmysel a OCR „záhadne“ nenájde nič.
  • Filtrujte podľa prípony, nie content-type. Niektorí klienti posielajú PDF s application/octet-stream. Spoľahlivé je endsWith(toLower(name), '.pdf').
  • Vložené obrázky v podpise sú tiež prílohy. Bez filtra ich pošlete do OCR a platíte za logá dodávateľov.
  • Timeout. HTTP akcia v Power Automate čaká štandardne do 2 minút. Pri 20-stranovom skene to nemusí stačiť; funkcia je preto asynchrónna a flow používa Retry policy s exponenciálnym odstupom.

Úspešnosť rozpoznávania čísel z textu a PDF je v produkcii 99 %. Zvyšné percento sú prevážne fotografie faktúr z mobilu a dokumenty, kde číslo objednávky jednoducho nie je.

Vrstva 3: pravidlá, ktoré si spravuje biznis

Tretia vrstva zachytáva e-maily bez čísla objednávky: pravidelné faktúry za energie, nájmy, služby, alebo dodávateľov, ktorí číslo zo zásady neuvádzajú. Tu nie je čo rozpoznávať; je čo vedieť. A to vie účtovný tím, nie my.

Pravidlá žijú v SharePoint zozname s týmito stĺpcami:

StĺpecTypPríklad
Odosielateľ obsahujetext@dodavatel-energie.sk
Predmet obsahujetextVyúčtovanie
Obsah PDF obsahujetextOdberné miesto 123
Príjemcaosobaúčtovník
Projekttextnázov strediska
Prioritačíslo10
Aktívneáno/nieáno

Flow načíta aktívne pravidlá zoradené podľa priority a vyhodnotí ich postupne; prvá zhoda vyhráva. Prázdna podmienka sa ignoruje, takže pravidlo môže byť len „odosielateľ“ alebo kombinácia všetkých troch.

Tri detaily, ktoré rozhodli o tom, či to tím bude používať

  • Stĺpec Priorita. Bez neho sa pri dvoch zhodách rozhoduje náhodne (presnejšie: podľa poradia ID), čo tím rýchlo prestane dôverovať.
  • Stĺpec Aktívne namiesto mazania. Pravidlo sa dá vypnúť a vrátiť. Nič sa nestratí.
  • Príjemca ako stĺpec typu Osoba, nie text. E-mail vyberá flow z profilu, takže preklep v adrese nie je možný. Pozor pri čítaní: lookup a person stĺpce vracajú objekt, nie hodnotu – e-mail je v items('Apply_to_each')?['Prijemca']?['Email'].

Zmena pravidla sa prejaví pri ďalšom e-maile, bez zásahu do flow a bez nasadzovania. Od spustenia v marci 2026 pridal účtovný tím desiatky pravidiel sám.

Logovanie: každý e-mail má záznam

Posledný krok každej vetvy je zápis do SharePoint zoznamu Log spracovania. Záznam obsahuje:

  • identifikátor e-mailu (Internet Message ID) a čas prijatia,
  • odosielateľa a predmet,
  • metódu rozhodnutia: text, ocr, pravidlo, rucne,
  • nájdené číslo objednávky, číslo faktúry a sumu (ak sú),
  • príjemcu a projekt,
  • stav (dorucene, chyba, rucne) a prípadnú chybovú správu,
  • odkaz na beh flowu.

Posledný bod šetrí najviac času pri podpore. Odkaz na konkrétny beh sa dá zložiť priamo vo flowe:

concat(
  'https://make.powerautomate.com/manage/environments/',
  workflow()?['tags']?['environmentName'],
  '/flows/', workflow()?['name'],
  '/runs/', workflow()?['run']?['name']
)

Keď účtovník napíše „faktúra od XY mi neprišla“, nájdeme záznam podľa odosielateľa a jedným klikom sme v behu, ktorý ju spracoval.

Chyby sa logujú, nie strácajú

Celé spracovanie je v Scope bloku; za ním nasleduje druhý Scope nastavený na Run after: has failed, has timed out. Ten zapíše do logu stav chyba s textom z result('Scope_spracovanie') a e-mail označí kategóriou na ručné spracovanie. Bez tejto konštrukcie by neúspešný beh skončil červenou ikonou v histórii flowu, ktorú nikto nesleduje, a faktúra by zostala v schránke.

Power BI nad logom

Report číta zoznam priamo cez SharePoint konektor a ukazuje:

  • podiel metód rozhodnutia v čase (koľko % išlo cez text, OCR, pravidlá, ručne),
  • úspešnosť OCR a priemerný čas spracovania,
  • dodávateľov s najvyšším podielom ručného spracovania – presne tam sa oplatí pridať pravidlo alebo požiadať dodávateľa, aby uvádzal číslo objednávky.

Práve tento posledný pohľad posunul podiel automatizácie z počiatočných hodnôt na dnešných 70 % – nie zmenou kódu, ale pridaním pravidiel a dohodou s pár dodávateľmi.

Päť poučení z produkcie

  1. Premenné inicializujte len na koreni flowu. Initialize variable nefunguje vo vetve podmienky, v Apply to each ani v Scope. Všetky premenné vytvárame hneď za triggerom s prázdnou hodnotou a vo vetvách používame len Set variable. Znie to triviálne, ale je to najčastejší dôvod, prečo sa flow nedá uložiť po refaktoringu.
  2. Vypnite súbežnosť tam, kde píšete do premenných. Apply to each beží štandardne paralelne. Ak v ňom nastavujete premennú (napr. „nájdené číslo“), dve iterácie sa prepíšu. Buď Concurrency control na 1, alebo zbierajte výsledky cez Append to array.
  3. Správne poradie vrstiev sa ukáže až na dátach. Pred nasadením sme flow pustili nad mesiacom historických e-mailov v režime „len loguj, neposielaj“. Ukázalo to, ktoré vzory čísel objednávok chýbajú, a pridalo pár percent k automatizácii ešte pred spustením.
  4. Každý externý dotaz musí mať plán B. Objednávkový systém má výpadky. Ak HTTP akcia zlyhá, flow nepadá, ale pokračuje vrstvou 3 a do logu zapíše, že systém nebol dostupný. Report potom ukáže, koľko faktúr to postihlo.
  5. Migrácia medzi tenantmi nie je export/import. Flow vznikol v našom tenante a sťahoval sa do tenantu klienta. Export ako balíček zoberie logiku, ale nie pripojenia: každý SharePoint URL, schránku a endpoint funkcie treba prenastaviť a všetky konektory znova overiť. Použite environment variables od začiatku, ušetrí to hodiny.

Čo by sme dnes spravili inak

Vrstvu 1 by sme od začiatku presunuli tiež do Azure Function. Porovnanie vzorov cez indexOf/substring funguje, ale pri každom novom formáte čísla objednávky rastie strom podmienok vo flowe. Jedno volanie funkcie s regexom je čitateľnejšie, testovateľné a lacné – stojí zlomok centu, zatiaľ čo správa vetveného flowu stojí čas.

Časté otázky

Vie Power Automate pracovať s regulárnymi výrazmi?

Natívne nie. V expressions sú len indexOf, substring, contains, split a podobné funkcie. Pre skutočný regex treba Azure Function, Office Script alebo konektor tretej strany. Pri pevnom formáte čísel stačia expressions, pri viacerých vzoroch sa oplatí funkcia.

Ako sa v Power Automate robí OCR faktúr?

Cez Azure Document Intelligence – buď priamo konektorom, alebo cez Azure Function. V BK používame funkciu, ktorá najprv skúsi textovú vrstvu PDF knižnicou pdfplumber a OCR volá len pre strany bez textu. Šetrí to väčšinu nákladov na OCR.

Prečo nepoužiť AI klasifikáciu na všetko?

Pre náklady, determinizmus a správu bez IT. Pravidlá a vzory sú lacné, vysvetliteľné a upraví ich účtovný tím. AI má zmysel ako ďalšia vrstva pre zvyšok, ktorý nič iné nezachytí.

Koľko stojí prevádzka takéhoto flowu?

Nízke jednotky eur mesačne za Azure Function a Document Intelligence pri objeme ~3 000 faktúr; Power Automate je súčasťou licencií Microsoft 365, ktoré firma už má. Presná suma závisí od podielu skenovaných dokumentov.

Ako sa rieši e-mail s viacerými faktúrami v jednom PDF?

Azure Function vráti pole výsledkov, jeden na faktúru. Flow pre každú položku zopakuje dotaz do objednávkového systému. Ak patria rôznym účtovníkom, e-mail sa prepošle každému z nich s poznámkou, ktorá faktúra je jeho.

Čo sa stane, keď flow zlyhá?

Chybu zachytí Scope s nastavením Run after: has failed. Zapíše sa do logu so stavom chyba a odkazom na beh, e-mail sa označí na ručné spracovanie. Žiadna faktúra sa nestratí bez záznamu.

Záver: lacné vrstvy prvé, drahé posledné

Najdôležitejšie rozhodnutie v tomto riešení nebolo technologické, ale poradie: lacné a deterministické vrstvy prvé, drahé a pravdepodobnostné posledné, a medzi nimi vrstva, ktorú si biznis spravuje sám. Rovnaký vzor funguje na objednávky, dodacie listy, reklamácie – na čokoľvek, čo prichádza do zdieľanej schránky a niekto to musí rozhodnúť.

V ďalšom diele série ukážeme, ako sme účtovný tím presvedčili, aby si pravidlá písal sám – a prečo automatizácia, ktorú si ľudia nevedia upraviť, časom umrie.

Ak riešite podobnú úlohu a chcete si návrh overiť skôr, než začnete stavať, Discovery sprint je na to: za dva týždne máte audit dát, návrh architektúry a ukážku nad vašimi reálnymi e-mailami.

Séria „Z účtárne do automatu“: 1. diel – 90 hodín mesačne · 2. diel – tento článok · Prípadová štúdia: BK a.s.

Pokračujte v čítaní

Súvisiace články

Páčil sa vám článok?

Krátke poznatky z reálnych dátových projektov. Bez spamu.