Otwieranie wyciągów bankowych nikogo nie bawi. Przychodzą one w formie skanów PDF, eksportów CSV lub plików XML obudowanych tajemniczymi akronimami, takimi jak OFX. Dla księgowych, rachunkowych i twórców fintechów przekształcanie tych dokumentów w czyste, ustrukturyzowane dane to nieustanna udręka. Kiedy na scenę wkroczyły duże modele językowe, wydawały się one być rozwiązaniem problemu. Po prostu nakarm maszynę plikiem PDF i poproś o JSON. Co może pójść nie tak?

Dowiedziałem się dokładnie, co może pójść nie tak, budując StatementDecoder – narzędzie zaprojektowane do konwersji wyciągów bankowych na użyteczne dane. Jak wielu programistów, założyłem, że najtrudniejszym zadaniem będzie nauczenie systemu czytania różnorodnych układów dokumentów. Myliłem się. Czytanie dokumentów było niemal trywialne. Prawdziwym koszmarem było rozpoznanie momentu, w którym maszyna po cichu wymyśliła liczbę lub zamieniła dwie cyfry w kwocie transakcji.

Demo, które zadziałało aż za dobrze

Moja pierwsza próba była kusząco prosta. Przekazywałem wyciągi bankowe bezpośrednio do LLM i prosiłem o ustrukturyzowany JSON w odpowiedzi. Wyniki wydawały się magiczne. Model z łatwością radził sobie z różnymi układami. Czytał skany PDF, na których standardowe parsery się wykładały. Wydawało się, że rozumie tabele, nagłówki i wielostronicowe wyciągi bez żadnych dodatkowych instrukcji. Przez kilka radosnych godzin myślałem, że problem został rozwiązany.

Potem przetestowałem to na rzeczywistych danych klientów i magia wyparowała. Brytyjskie banki mają własne wzory wyciągów i różnice te nie są jedynie kosmetyczne. Wyciągi Wise mają swoje specyficzne formatowanie. Eksporty CSV z Revolut wyglądają na proste, dopóki nie zauważysz, jak radzą sobie z transakcjami wielowalutowymi i polami metadanych. Stare pliki OFX – format, który wygląda, jakby pochodził z lat 90. – rzucają archaiczne struktury tagów i problemy z kodowaniem w stronę każdego parsera, który oczekuje nowoczesnych znaczników.

Model wciąż wyodrębniał dane znacznie lepiej niż jakikolwiek gotowy system szablonów. Jednak „znacznie lepiej” to za mało, gdy w grę wchodzą pieniądze.

Kiedy 99% dokładności to porażka

Oto fundamentalny problem z wykorzystywaniem AI do ekstrakcji danych finansowych. Jeśli model przetworzy dwieście wierszy transakcji i dwieście jeden na sto dziewięćdziesiąt będzie poprawnych, wynik wygląda nienagannie. JSON jest poprawny pod względem składni. Klucze i wartości się zgadzają. Powierzchowna kontrola może nie wykazać niczego podejrzanego. Jednak jeśli ten pojedynczy błąd zamieni miejscami dwie cyfry w kwocie, zmieni depozyt w wypłatę lub przesunie przecinek dziesiętny, Twoja księgowość zostanie skażona. Nie wyłapiesz tego, przeglądając wzrokiem ścianę ustrukturyzowanych danych.

Człowiek przeglądający surowy JSON rzadko dostrzega zamienioną cyfrę w kwocie transakcji. Formatowanie jest idealne, co paradoksalnie czyni ten błąd bardziej niebezpiecznym. Nie można wypuścić narzędzia finansowego, które ma rację przez większość czasu. Musi mieć rację albo głośno ogłosić, że nie jest pewne wyniku.

Moja początkowa reakcja była przewidywalna. Przygotowałem lepsze prompty. Przeszedłem na bardziej zaawansowane modele. Eksperymentowałem z rozumowaniem typu chain-of-thought, aby model pokazywał swój tok rozumowania. Żadne z tych rozwiązań nie naprawiło sedna problemu. Prosiłem ten sam probabilistyczny system o wygenerowanie odpowiedzi, a następnie prosiłem ten sam system o potwierdzenie, że odpowiedź jest poprawna. To nie jest weryfikacja. To teatr spójności wewnętrznej.

Niech matematyka zdecyduje

Wyciągi bankowe mają cechę, której nie posiada większość dokumentów: wbudowane ograniczenia arytmetyczne. Saldo początkowe plus suma wszystkich transakcji musi równać się saldu końcowemu. Salda bieżące, jeśli występują, muszą zgadzać się w każdym wierszu. To nie są preferencje stylistyczne. To twarde reguły.

Przebudowałem architekturę w oparciu o to spostrzeżenie. Teraz każda ekstrakcja, niezależnie od źródła, przechodzi przez warstwę walidacji, zanim jakikolwiek użytkownik ją zobaczy. Nie ma znaczenia, czy dane pochodzą z LLM interpretującego nieczytelny PDF, silnika OCR czytającego zeskanowaną stronę, czy bezpośredniego parsowania CSV. Walidator traktuje wszystkie źródła jako równie podejrzane.

Sprawdzenie jest brutalnie proste. Dodaj każdą transakcję do salda początkowego. Porównaj wynik z podanym saldem końcowym. Jeśli liczby się nie zgadzają, coś jest nie tak. Oznacz wyciąg do sprawdzenia. Odrzuć ekstrakcję. Nie pozwól, aby trafiła do użytkownika.

Ta jedna zmiana zmieniła cały charakter produktu. Model językowy nie musiał już być idealny. Musiał być tylko na tyle dobry, aby wygenerować wynik, który przetrwa matematyczny test. Nacisk przesunął się z osiągania niemożliwej dokładności w nieograniczonym obszarze na budowanie ścisłej pętli zwrotnej między generowaniem a weryfikacją.

Walidator ujawnił również wzorce w błędach. Niektóre typy dokumentów konsekwentnie nie przechodziły weryfikacji matematycznej, co dokładnie wskazało mi, gdzie warto zainwestować wysiłek. Zamiast ślepo ulepszać prompt engineering we wszystkich obszarach, mogłem zauważyć, że konkretne układy bankowe powodowały systematyczne błędy.

Kod tam, gdzie powinien być kod, AI tam, gdzie błyszczy

Być może najbardziej pouczającą lekcją pokory było uświadomienie sobie, jak duża część potoku w ogóle nie wymagała AI. Kiedy napotkałem niechlujne australijskie pliki OFX, moim instynktem było „rzucenie tokenów” w problem. Krótko rozważałem podanie uszkodzonego pliku XML modelowi i poproszenie go o naprawę struktury przed parsowaniem. Zamiast tego napisałem dwadzieścia linii deterministycznego kodu. Natychmiast naprawił on nietypowe kodowanie i błędne znaczniki, nie generując żadnych kosztów za plik i zapewniając idealną powtarzalność.

To doświadczenie uświadomiło mi, jak powinny być organizowane potoki ekstrakcji. Istnieją trzy odrębne zadania i nie powinny być one ze sobą mieszane.

  • Model rozumie niechlujne dokumenty. Skanowane pliki PDF z wykrzywionymi tabelami, mieszanymi czcionkami i pismem ręcznym