Відкривати банківські виписки — це навряд чи те, що можна назвати розвагою. Вони приходять у вигляді відсканованих PDF, CSV-експортів або XML-файлів, прикрашених загадковими абревіатурами на кшталт OFX. Для бухгалтерів, рахівників та розробників фінтех-рішень перетворення цих документів на чисті, структуровані дані є постійним головним болем. Коли на сцену вийшли великі мовні моделі, здавалося, що вони пропонують шлях до порятунку. Просто дай машині PDF і попроси JSON. Що може піти не так?

Я дізнався, що саме може піти не так, під час розробки StatementDecoder — інструменту, призначеного для перетворення банківських виписок у придатні для використання дані. Як і багато розробників, я припустив, що найскладнішим буде навчити систему читати різноманітні макети документів. Я помилявся. Читання документів було майже тривіальним. Справжнім кошмаром було розпізнати момент, коли машина непомітно вигадала число або переставила дві цифри в сумі транзакції.

Демонстрація, яка спрацювала занадто добре

Моя перша спроба була спокусливо простою. Я направляв банківські виписки безпосередньо в LLM і запитував у відповідь структурований JSON. Результати здавалися магією. Модель легко справлялася з різними макетами. Вона читала відскановані PDF, на яких затиналися стандартні парсери. Здавалося, вона розуміє таблиці, заголовки та багатосторінкові виписки без жодних чітких інструкцій. Протягом кількох чудових годин я думав, що проблему вирішено.

Потім я протестував систему на реальних клієнтських даних, і магія зникла. Британські банки використовують власні дизайни виписок, і ці відмінності не є суто косметичними. Виписки Wise мають свої особливості форматування. CSV-експорти Revolut виглядають зрозумілими, доки ви не помітите, як вони обробляють мультивалютні транзакції та поля метаданих. Старі OFX-файли — формат, який справді виглядає так, ніби він належить до 1990-х — підкидають архаїчні структури тегів та проблеми з кодуванням будь-якому парсеру, що очікує сучасну розмітку.

Модель все одно витягувала дані набагато краще, ніж будь-яка готова шаблонна система. Але «набагато краще» — це недостатньо, коли йдеться про гроші.

Коли точність 99% — це провал

Ось фундаментальна проблема використання ШІ для витягування фінансових даних. Якщо модель обробляє двісті рядків транзакцій і правильно визначає сто дев'яносто дев'ять, результат виглядає бездоганним. JSON сформований правильно. Ключі та значення збігаються. Поверхневий перегляд не виявить нічого підозрілого. Проте, якщо ця єдина помилка переставить дві цифри в сумі, перетворить депозит на зняття коштів або змістить десятковий роздільник, ваша бухгалтерія буде зіпсована. Ви не помітите цього, просто оглядаючи стіну структурованих даних.

Людина, яка перевіряє сирий JSON, рідко помічає переставлену цифру в сумі транзакції. Форматування ідеальне, що, як не парадоксально, робить помилку ще небезпечнішою. Ви не можете випустити фінансовий інструмент, який правий більшість часу. Він має бути правим або голосно заявляти про свою невпевненість.

Моя перша реакція була передбачуваною. Я створював кращі промпти. Я переходив на потужніші моделі. Я експериментував із методом ланцюжка думок (chain-of-thought reasoning), щоб модель показувала хід своїх міркувань. Ніщо з цього не вирішило основну проблему. Я просив ту саму ймовірнісну систему згенерувати відповідь, а потім просив ту саму систему підтвердити, що відповідь правильна. Це не верифікація. Це театр самоузгодженості.

Нехай вирішує математика

Банківські виписки мають особливість, якої немає у більшості документів: вбудовані арифметичні обмеження. Початковий баланс плюс сума всіх транзакцій має дорівнювати кінцевому балансу. Поточні баланси, якщо вони є, мають збігатися рядок за рядком. Це не стилістичні вподобання. Це жорсткі правила.

Я перебудував архітектуру навколо цього інсайту. Тепер кожне витягування даних, незалежно від джерела, проходить через рівень валідації, перш ніж користувач його побачить. Неважливо, чи дані надійшли від LLM, що інтерпретує нечіткий PDF, від OCR-двигуна, що зчитує відскановану сторінку, чи через прямий парсинг CSV. Валідатор ставиться до всіх джерел однаково підозріло.

Перевірка брутально проста. Додайте кожну транзакцію до початкового балансу. Порівняйте результат із зазначеним кінцевим балансом. Якщо числа не збігаються, щось не так. Позначте виписку для перевірки. Відхиліть результат витягування. Не дозволяйте йому потрапити до користувача.

Ця єдина зміна змінила весь характер продукту. Мовна модель більше не повинна була бути ідеальною. Їй достатньо було бути достатньо хорошою, щоб видавати результат, який зможе пройти математичну перевірку. Тиск змістився з досягнення неможливої точності в неконтрольованій сфері на створення тісного циклу зворотного зв'язку між генерацією та верифікацією.

Валідатор також виявив закономірності в помилках. Певні типи документів постійно не проходили перевірку математичних розрахунків, що чітко вказало мені, куди саме варто спрямувати зусилля. Замість того, щоб сліпо вдосконалювати промпт-інжиніринг повсюдно, я побачив, що саме певні банківські макети спричиняли систематичні помилки.

Код там, де він потрібен, ШІ там, де він сяє

Мабуть, найскромнішим уроком було усвідомлення того, наскільки велика частина конвеєра взагалі не потребувала ШІ. Коли я зіткнувся з безладними австралійськими файлами OFX, моїм інстинктом було «закидати проблему токенами». Я на мить замислився над тим, щоб подати пошкоджений XML-файл моделі та попросити її виправити структуру перед парсингом. Натомість я написав двадцять рядків детермінованого коду. Він миттєво виправив дивацтва кодування та неправильно сформовані теги, з нульовою вартістю за файл і ідеальною відтворюваністю.

Цей досвід чітко окреслив, як слід організовувати конвеєри вилучення даних. Існують три окремі завдання, і їх не слід змішувати.

  • Модель розуміє безладні документи. Скановані PDF-файли з викривленими таблицями, змішаними шрифтами та рукописним