Přeskočit na hlavní obsah

Vytěžování dokladů a dalších dat v ESO9 Start pomocí AI

Vytěžování dokladů a dalších dat v ESO9 Start pomocí AI

V ESO9 Start je k dispozici řešení pro vytěžování libovolných dat z PDF nebo obrázků bez integrace vytěžovacích nástrojů 3.stran (např.wFlow). Typicky jej lze využít pro vytěžování a zakládání dokladů libovolných typů, ale i pro vytěžování dat z obrázků (např. fotografií produktů).

Základní mechanismus vytěžení dat

Vytěžování je v ESO9 Start zapojeno jako běžná akce na tlačítko. Základním vstupem je vytěžovaný dokument v DMS; může se jednat o PDF nebo obrázek. Akce na tlačítko provede vytěžení zadaného dokumentu, jehož výstupem jsou získaná data ve formátu JSON. Tato data jsou následně poslána na vstup uživatelské stored procedury, která je zpracuje - např. z nich založí doklad.
Celé řešení sestává z akce na tlačítko, která volá webovou službu pro spolupráci s AI.

Webová služba ESO9AIsvc

Vytěžování v ESO9 Start je založeno na enginu Open AI a je součástí stejné webové služby, jako AI asistent a online Podpora. Instalace a konfigurace této služby je proto stejná, jako v případě AI asistenta (tj. může běžet jako web v Internet Information Services pod Windows nebo v Linuxovém Docker kontejneru.

Uživatelská akce pro vytěžení dat

Komunikaci ESO9 s webovou službou zprostředkovává knihovna ExtractDataAI.dll, která je součástí instalace aplikačního serveru. Knihovna se volá z libovolného formuláře ESO9 jako běžná akce na tlačítko se syntaxí:

<input type="button" name="Actx_ExtractDataAI.ExtractData" value="Vytěž PDF dokument" BrwAction="10">

Akce pro své spuštění potřebuje tyto parametry z datového zdroje stránky:

  • DOKUMENTFILEGUID - FileGUID (identifikátor) vytěžovaného dokumentu z DMS ESO9
  • MF_EXTRACT_STORED_PROCEDURE - jméno stored procedury použité pro zpracování vytěžených dat (např. založení dokladu)
  • MF_EXTRACT_TYPE - typ vytěžovaných dat: 1 = PDF dokument, 2 = obrázek Výstupní parametry akce jsou:
  • MF_TABLENAME_CREATED a MF_IDTABLE_CREATED - jméno tabulky a ID záznamu, který vznikl jako výsledek zpracování vytěžených dat. Položky lze ve formuláři použít k sestavení odkazu na nově založenou entitu (např. doklad).
  • CIS_DOK - v případě, že se jedná o vytěžování a zakládání dokladů, akce vrací číslo nově založeného dokladu.
  • SLOZKA_IDENT - v případě, že se jedná o vytěžování a zakládání složek dokladů, akce vrací identifikátor zkoží nebo služby na nově vzniklé složce dokladu.

Stored procedura pro zpracování vytěžených dat

Vytěžená data ve formátu JSON jsou zaslána na vstup stored procedury předané do akce v parametru MF_EXTRACT_STORED_PROCEDURE.
Procedura očekává tyto vstupní parametry:

  • @ExtractedJson NVARCHAR(MAX) - vytěžená data ve formátu JSON. Konkrétní struktura závisí na zadaném promptu, viz níže.
  • @IdDokument INT - ID vytěžovaného dokumentu. Slouží pro propojení vytěžených dat (která se také ukládají do DMS jako soubor) ze zdrojovým dokumentem (PDF nebo obrázek).
  • @IDLOGUSER INT - ID uživatele, pod nímž se zakládají záznamy z vytěžených dat (např. doklady).
  • @TABLENAME VARCHAR(128) - výstupní parametr s názvem tabulky, ve které se založil nový záznam. V případě vytěžování dokladů se jedná o tabulku "HDOK".
  • @IDTABLE INT - výstupní parametr, ve kterém je k dispozici ID nově založeného záznamu. Položky @TABLENAME a @IDTABLE jsou zároveň ve výstupním recordsetu, který je výsledkem volání procedury.

Prompt pro vytěžení dat

Při vytěžování dat je klíčový správně zadaný prompt, který se používá na vstupu požadavku na AI. Pro každý typ vytěžovaných dat se hodí jiné zadání. V rámci ESO9 Start se pro získání promptu používá položka // POPIS_TYPDOC// z číselníku typů dokumentů. Každý vytěžovaný dokument tedy musí mít předem zadán svůj typ a podle něj se při vytěžování použije správný prompt. V ESO9 Start je jako ukázka zadaný prompt pro vytěžení objednávek:

Jsi expert na analýzu obchodních dokumentů. 
Budeš zpracovávat více dokumentů v sérii. Pro každý dokument:
- Extrahuj VŠECHNA strukturovaná data a vrať je ve formátu JSON
- Zachyť kompletní informace včetně všech subjektů, adres, položek, částek
- Použij popisné názvy polí
- NEVYMÝŠLEJ žádná data - pouze to, co je v dokumentu
- JE POVOLENO přiřazovat texty k položkám podle jejich vizuální blízkosti a pořadí
- Vrať pouze validní JSON bez markdown formátování
- MUSÍŠ vrátit ÚPLNÝ JSON se VŠEMI položkami z dokumentu!
- NEzkracuj seznam položek
- Vždy vypíš kompletní pole "items" se všemi řádky z dokumentu

Vrať přesně tento JSON tvar:

{
"ExtractedJson": {
"document_type": null,
"order_number": null,
"document_series": null,
"document_code": null,
"document_number": null,
"issue_date": null,
"requested_delivery_date": null,
"payment_terms_days": null,
"payment_method": null,
"incoterms": null,
"purpose_of_delivery": null,
"contract_number": null,
"project_code": null,
"buyer": {
"name": null,
"company_id": null,
"vat_id": null,
"address": {
"street": null,
"postal_code": null,
"city": null,
"country": null
}
},
"supplier": {
"name": null,
"company_id": null,
"vat_id": null,
"address": {
"street": null,
"postal_code": null,
"city": null,
"country": null
}
},
"delivery_address": {
"name": null,
"street": null,
"postal_code": null,
"city": null,
"country": null
},
"items": [
{
"line_number": null,
"item_code": null,
"description": null,
"order_reference": null,
"project_code": null,
"quantity": null,
"unit": null,
"unit_price": null,
"vat_rate": null,
"line_total_ex_vat": null,
"line_total_vat": null,
"line_total_inc_vat": null
}
],
"totals": {
"currency": null,
"total_ex_vat": null,
"total_vat": null,
"total_inc_vat": null
},
"prepared_by": null,
"notes": []
}
}

Podmínky pro extrakci:
- Pokud pole v dokumentu není, vrať null nebo [] podle typu
- company_id a vat_id vrať vždy jako string
- unit ponech v původním tvaru z dokumentu
- payment_method ponech jako text z dokumentu, nenormalizuj na ERP hodnoty
- Pokud je položka víceřádková, spoj popis do jednoho stringu
- Texty umístěné bezprostředně POD řádkem položky (např. na dalším řádku)
považuj za součást této položky
- Pokud položka nemá description, použij nejbližší text pod ní
- Pokud je více řádků textu mezi dvěma položkami, patří k první z nich
- U položek bez popisu použij nejbližší textový blok pod nimi
- Text mezi dvěma položkami patří k předchozí položce, pokud není zjevně jinak
- buyer = objednavatel / odběratel / kupující
- supplier = dodavatel / prodávající
- delivery_address vyplň jen pokud je z dokumentu zjevná
- notes musí být pole čistých textových řetězců bez markdownu, bez odkazů a bez formátování
- Emaily, telefony a kontakty vracej jako plain text
- Pokud je dokument obchodně purchase order / objednávka, nastav document_type na "objednavka"
- order_number musí obsahovat pouze hlavní číslo objednávky, ne doprovodný text, ne účel dodávky, ne název projektu
- Pokud je v jednom řádku například "Objednávka: ESCO 26/33 Nábytok", pak:
- order_number = "ESCO 26/33"
- "Nábytok" nepatří do order_number
- purpose_of_delivery vyplň jen pokud je skutečně z dokumentu zřejmý účel dodávky jako samostatná informace
- notes neplň běžnými identifikačními údaji firem, pokud nejde o skutečné poznámky nebo instrukce
- Do notes nezařazuj obsah, který už je správně uložen v jiných polích, například:
- buyer
- supplier
- delivery_address
- payment_method
- totals
- prepared_by
- Pokud je poznámka zjevně navázaná na konkrétní položku, preferuj ji v položce jako description, order_reference nebo project_code, ne v notes
- Pokud je měna uvedena symbolem, převeď:
- Kč / KC -> CZK
- € -> EUR
- $ -> USD
- Pokud je v dokumentu více možných čísel, do order_number dej hlavní číslo objednávky
- document_series vyplň jen pokud je zjevně oddělená od čísla objednávky
- document_number vyplň jen pokud je zjevně oddělené od řady nebo kódu
- payment_terms_days vyplň jen pokud je počet dnů přímo uveden nebo jednoznačně vyplývá z dokumentu

Zcela jiné zadání bude např. v případě vytěžování obrázků s označením pneumatik (příklad z praxe):

Jsi expert na rozpoznávání označení pneumatik. 
Budeš zpracovávat fotografie pneumatik. Pro každou z nich:
- Extrahuj VŠECHNA strukturovaná data a vrať je ve formátu JSON
- NEVYMÝŠLEJ žádná data - pouze to, co je na fotografii

Vrať přesně tento JSON tvar:
{
"ExtractedJson": {
"mark": null
}

Příklad vytěžování objednávek

Na základě předchozích bodů lze sestavit jednoduchý příklad z praxe, v němž budeme vytěžovat (prodejní) objednávky, které nám posílají odběratelé v PDF s různými formáty. Protože je posílají na jeden sběrný mail, využijeme automat pro Vyčítání e-mailů a jejich ukládání do ESO9. Při zakládání příloh z došlých e-mailů jim rovnou přidělíme typ dokumentu POBJAI, na který máme v číselníku typů dokumentů navázaný prompt z předchozí kapitoly. Zároveň můžeme do stejného číselníku založit typ dokumentu JSON, který se standardně používá pro označení souboru JSON s vytěženými daty.
Do formuláře s přílohou (tj. PDF dokumentem uloženým v DMS) si zapojíme tlačítko pro volání akce

<input type="button" name="Actx_ExtractDataAI.ExtractData" value="Vytěž PDF dokument" BrwAction="10">

Do datového zdroje formuláře DMS doplníme povinné položky pro volání této akce:

select *
, 0 as MF_IDTABLE_CREATED
, '' as MF_TABLENAME_CREATED
, 1 as MF_EXTRACT_TYPE
, 'spAIPDF_ExtractedOrder2ESO9' as MF_EXTRACT_STORED_PROCEDURE
from QDOKUMENT

Do formuláře zároveň přidáme odkaz na nově založený doklad:

<a href="esoform.asp?TPage=Prodej/PObj.htm&HDOKSTYLE=&SDOKSTYLE=&IDTYPDOK_STAVHDOK=52&Cinnost=2. 2. 4.10&TModule=Prodej&VZORTEXT=Prodejní objednávka&RelType=PARAMETERS&StartFilter=0&WHERE=where IDHDOK=%MF_IDTABLE_CREATED%&FormType=Editor">Založená prodejní objednávka</a>

Z došlého e-mailu se nám do DMS založí objednávka v PDF formátu s typem dokumentu POBJAI:
Objednávka zboží
Stiskem tlačítka "Vytěž PDF dokument" dojde k vytěžení PDF dokumentu a založení dokladu, jehož přílohou je PDF dokument, z něhož vznikla:
Založený doklad
Zároveň v DMS vznikne dokument typu JSON, který obsahuje (syrová) vytěžená data pro snazší kontrolu správnosti vytěžení:

{
"ExtractedJson": {
"document_type": "objednavka",
"order_number": "000260538",
"document_series": "000",
"document_code": null,
"document_number": null,
"issue_date": "13. 4. 2026",
"requested_delivery_date": "16. 4. 2026",
"payment_terms_days": "30",
"payment_method": "Platebním příkazem",
"incoterms": "FCA",
"purpose_of_delivery": null,
"contract_number": null,
"project_code": null,
"buyer": {
"name": "xxx",
"company_id": "xxx",
"vat_id": "xxx",
"address": {
"street": "xxx",
"postal_code": "xxx",
"city": "xxx",
"country": null
}
},
"supplier": {
"name": "xxx",
"company_id": "xxx",
"vat_id": "xxx",
"address": {
"street": "xxx",
"postal_code": "763 02",
"city": "Zlín 4",
"country": null
}
},
"delivery_address": {
"name": null,
"street": null,
"postal_code": null,
"city": null,
"country": null
},
"items": [
{
"line_number": 1,
"item_code": "01/962O",
"description": "LÁTKA VELVETY 10",
"order_reference": "26K0139",
"project_code": null,
"quantity": 18.30,
"unit": "bm",
"unit_price": 515.00,
"vat_rate": null,
"line_total_ex_vat": 9424.50,
"line_total_vat": null,
"line_total_inc_vat": 11403.65
},
{
"line_number": 2,
"item_code": "01/962Q",
"description": "LÁTKA VELVETY 17",
"order_reference": "26K0182",
"project_code": null,
"quantity": 15.10,
"unit": "bm",
"unit_price": 515.00,
"vat_rate": null,
"line_total_ex_vat": 7776.50,
"line_total_vat": null,
"line_total_inc_vat": 9409.57
}
],
"totals": {
"currency": "CZK",
"total_ex_vat": 17201.00,
"total_vat": null,
"total_inc_vat": 20813.22
},
"prepared_by": "ruzickova",
"notes": [
"Dodavatel byl seznámen s účelem použití objednaného materiálu a služby.",
"Spokojený uživatel informačního systému Helios Orange"
]
}
}
Alternativní převod PDF na obrázek

V případě, že je struktura vytěžovaného PDF složitější, je možné, že výsledky nebudou uspokojivé. V takovém případě může pomoci převod PDF na obrázek na následné OCR obrázku, které v některých případech poskytuje lepší výsledky. Pro tuto variantu postačí nastavit hodnotu řídícího parametru MF_EXTRACT_TYPE na hodnotu 2. Na pozadí dojde k automatickému převodu PDF na obrázek ve vysokém rozlišení a následně je jiným způsobem provedeno OCR.

Příklad vytěžování označení pneumatik

Dalším příkladem z praxe je vytěžování označení pneumatik. Fotografie pneumatik vznikají v mobilní aplikaci, pro naše účely je podstatný pouze fakt, že fotografie pneumatiky se objeví v DMS ESO9.
Pro ukázku použijeme stejný formulář DMS, pouze v položce "Vytěžování dat" zvolíme "Obrázek" (tj.MF_EXTRACT_TYPE=2):
Fotografie pneumatiky

Praktické poznatky

Jak sestavit správný prompt ?

Pro správné vytěžení je klíčový vhodný prompt. K jeho sestavení může opět pomoci AI:

  • Do běžného webového promptu https://chatgpt.com/ vložíme vzorek PDF specifický např. pro jednoho odběratele / jednu tiskovou šablonu.
  • Prompt doplníme ukázkou promptu z ESO9 Start s tím, že je třeba jej přizpůsobit formátu konkrétní šablony (např. specifické požadavky na umístění kódu zboží nebo jiné klíčové položky).
  • Navržený prompt vyzkoušíme na vytěžení několika PDF dokumentů daného typu.
  • Ověříme doklad vzniklý vytěžením a v případě nesrovnalostí opakujeme kroky výše.
Kolik stojí vytěžení jednoho dokladu ?

OpenAI API je zpoplatněno dle využitých tokenů s tím, že jsou zvlášť zpoplatněny vstupní a výstupní tokeny a cena samozřejmě závisí na použitém jazykovém modelu. Z dosavadní praxe vyplývá, že cena vytěžení jednostránkového dokladu se pohybuje mezi 0,2 - 0,3 Kč, v případě vícestránkového dokladu s desítkami položek/řádků se může cena vyšplhat na 1,0 -1,2 Kč. Ve druhém uvedeném případě, kde se AI používá k vytěžení označení pneumatiky, se cena vytěžení jedné fotky pohybuje okolo 0,05 Kč.