باز کردن صورتحسابهای بانکی کار لذتبخشی برای هیچکس نیست. آنها به صورت PDFهای اسکنشده، خروجیهای CSV یا فایلهای XML که با مخففهای پیچیدهای مثل OFX پوشانده شدهاند، میرسند. برای حسابداران، دفترداران و سازندگان فینتک، تبدیل این اسناد به دادههای تمیز و ساختاریافته یک سردرد همیشگی است. وقتی مدلهای زبانی بزرگ (LLM) وارد صحنه شدند، به نظر میرسید که راه فراری ارائه میدهند. فقط یک PDF به ماشین بدهید و JSON بخواهید. چه مشکلی میتواند پیش بیاید؟
من دقیقاً متوجه شدم چه مشکلی میتواند پیش بیاید، زمانی که در حال ساخت StatementDecoder بودم؛ ابزاری که برای تبدیل صورتحسابهای بانکی به دادههای قابل استفاده طراحی شده است. مانند بسیاری از توسعهدهندگان، تصور میکردم بخش سخت کار، آموزش سیستم برای خواندن طرحبندیهای (layout) متنوع اسناد باشد. اما اشتباه میکردم. خواندن اسناد تقریباً ساده بود. کابوس واقعی این بود که تشخیص دهم چه زمانی ماشین بیسروصدا یک عدد را از خودش درآورده یا دو رقم را در مبلغ یک تراکنش جابهجا کرده است.
دموئی که بیش از حد خوب کار میکرد
اولین تلاش من به شکلی وسوسهانگیز ساده بود. من صورتحسابهای بانکی را مستقیماً به یک LLM فرستادم و در مقابل، درخواست JSON ساختاریافته کردم. نتایج مثل جادو بود. مدل با سهولت با طرحبندیهای مختلف کنار میآمد. PDFهای اسکنشدهای را میخواند که پارسرهای (parsers) استاندارد در برابر آنها کم میآوردند. به نظر میرسید بدون دستورالعملهای صریح، جداول، سرتیترها و صورتحسابهای چندصفحهای را درک میکند. برای چند ساعت باشکوه، فکر کردم مشکل حل شده است.
سپس آن را با دادههای واقعی مشتریان آزمایش کردم و جادو از بین رفت. بانکهای بریتانیا هر کدام از طراحیهای خاص خود برای صورتحساب استفاده میکنند و تفاوتها صرفاً ظاهری نیستند. صورتحسابهای Wise ویژگیهای فرمتبندی خاص خود را دارند. خروجیهای CSV مربوط به Revolut ساده به نظر میرسند تا زمانی که متوجه شوید چگونه تراکنشهای چندارزی و فیلدهای متادیتا را مدیریت میکنند. فایلهای قدیمی OFX، فرمتی که واقعاً به نظر میرسد متعلق به دهه ۱۹۹۰ است، ساختارهای تگ قدیمی و مشکلات کدگذاری (encoding) را به هر پارسری که انتظار مارکآپ مدرن دارد، تحمیل میکنند.
مدل همچنان دادهها را بسیار بهتر از هر سیستم قالب آمادهای استخراج میکرد. اما وقتی پای پول در میان باشد، «بسیار بهتر» کافی نیست.
وقتی دقت ۹۹ درصدی یک شکست است
مشکل اساسی استفاده از هوش مصنوعی برای استخراج دادههای مالی اینجاست: اگر یک مدل دویست ردیف تراکنش را پردازش کند و صد و نود و نه مورد را درست انجام دهد، خروجی بینقص به نظر میرسد. JSON ساختار درستی دارد. کلیدها و مقادیر با هم همخوانی دارند. یک بررسی گذرا ممکن است هیچ مورد مشکوکی نشان ندهد. با این حال، اگر آن یک خطای واحد، دو رقم را در یک مبلغ جابهجا کند، یک واریز را به برداشت تبدیل کند یا ممیز را جابهجا کند، دفترداری شما فاسد میشود. شما با نگاه کردن گذرا به انبوهی از دادههای ساختاریافته، متوجه آن نخواهید شد.
انسانی که JSON خام را بررسی میکند، به ندرت متوجه جابهجایی یک رقم در مبلغ تراکنش میشود. فرمتبندی بینقص است، که به شکلی متناقض، خطا را خطرناکتر میکند. شما نمیتوانید یک ابزار مالی عرضه کنید که «بیشتر اوقات» درست عمل میکند. ابزار باید «همیشه» درست باشد، یا باید با صدای بلند اعلام کند که مطمئن نیست.
واکنش اولیه من قابل پیشبینی بود. پرامپتهای (prompts) بهتری طراحی کردم. به مدلهای توانمندتر ارتقا دادم. با استدلال زنجیرهای (chain-of-thought) آزمایش کردم تا مدل مراحل کار خود را نشان دهد. هیچکدام از اینها مشکل اصلی را حل نکرد. من از همان سیستم احتمالی میخواستم که پاسخی تولید کند و سپس از همان سیستمِ یکسان میخواستم تأیید کند که پاسخ درست است. این تایید (verification) نیست؛ این نمایشِ «خود-سازگاری» است.
اجازه دهید ریاضی تصمیم بگیرد
صورتحسابهای بانکی ویژگیای دارند که اکثر اسناد ندارند: محدودیتهای محاسباتی داخلی. مانده ابتدایی بهعلاوه مجموع تمام تراکنشها باید با مانده پایانی برابر باشد. ماندههای جاری نیز، در صورت وجود، باید ردیف به ردیف با هم همخوانی داشته باشند. اینها ترجیحات سبکی نیستند؛ اینها قوانین سختگیرانه هستند.
من معماری را بر اساس این بینش بازسازی کردم. اکنون، هر استخراجی، بدون توجه به منبع آن، قبل از اینکه توسط کاربر دیده شود، از یک لایه اعتبارسنجی (validation layer) عبور میکند. فرقی نمیکند دادهها از یک LLM که در حال تفسیر یک PDF نامشخص است، یک موتور OCR که در حال خواندن یک صفحه اسکنشده است، یا یک پارس مستقیم CSV آمده باشد. اعتبارسنج (validator) با همه منابع به یک اندازه با سوءظن برخورد میکند.
این بررسی به طرز بیرحمانهای ساده است: هر تراکنش را با مانده ابتدایی جمع بزنید. نتیجه را با مانده پایانی اعلامشده مقایسه کنید. اگر اعداد مطابقت نداشتند، یعنی مشکلی وجود دارد. صورتحساب را برای بررسی علامتگذاری کنید. استخراج را رد کنید. اجازه ندهید داده به دست کاربر برسد.
این تغییرِ واحد، کل ماهیت محصول را تغییر داد. مدل زبانی دیگر نیازی به بینقص بودن نداشت. فقط کافی بود آنقدر خوب باشد که بتواند از یک آزمون ریاضی سربلند بیرون بیاید. فشار از «دستیابی به دقت غیرممکن در یک حوزه بدون محدودیت» به «ساخت یک حلقه بازخورد محکم بین تولید و تأیید» تغییر یافت.
اعتبارسنج همچنین الگوهایی را در خطاها آشکار کرد. انواع خاصی از اسناد بهطور مداوم در بررسی محاسباتی شکست میخوردند، که دقیقاً به من میگفت کجا باید تلاش کنم. بهجای بهبود کورکورانه مهندسی پرامپت در همه سطوح، متوجه شدم که طرحبندیهای خاص بانکی باعث بروز خطاهای سیستماتیک میشدند.
کد در جایی که متعلق به آن است، هوش مصنوعی در جایی که میدرخشد
شاید آموزندهترین درس، درک این موضوع بود که بخش بزرگی از خط لوله اصلاً به هوش مصنوعی نیاز نداشت. وقتی با فایلهای نامنظم OFX استرالیایی مواجه شدم، غریزهام این بود که توکنها را به سمت مشکل پرتاب کنم. لحظهای به این فکر کردم که XML خراب را به مدل بدهم و از آن بخواهم پیش از تجزیه، ساختار را اصلاح کند. در عوض، بیست خط کد قطعی نوشتم. این کد بلافاصله ناهنجاریهای انکودینگ و تگهای بدشکل را اصلاح کرد، بدون هیچ هزینهای برای هر فایل و با قابلیت بازتولید کامل.
آن تجربه باعث شد بفهمم خط لولههای استخراج چگونه باید سازماندهی شوند. سه وظیفه متمایز وجود دارد که نباید با هم ترکیب شوند.
- مدل اسناد نامنظم را درک میکند. فایلهای PDF اسکنشده با جداول کج و معوج، فونتهای ترکیبی و دستنویس
