تصور کنید در ۲۱ اوت ۲۰۲۶ یک گزارش تولیدی دریافت میکنید که ۱۱ تراکنش را به شناسههای مشتریانی متصل کرده که اصلاً در پایگاه داده وجود ندارند. هیچ خطایی صادر نشده، هیچ وبهوکی شکست نخورده و تمام لاگها وضعیت «موفق» را نشان میدهند. این خط لوله که روی سرورهای MonkeyCode اجرا میشد، دقیقاً همان کاری را کرد که به او دستور داده شده بود: تجزیه JSON، اعتبارسنجی ساختار و ثبت رکورد.
این شکست در سیستمی رخ داد که برای استخراج جزئیات تراکنش از ایمیلهای پشتیبانی با استفاده از یک مدل رایگان از طریق درگاه MonkeyCode طراحی شده بود. وظیفه اصلی مدل این بود که خروجی را در قالب JSON ساختاریافته برگرداند. چون ظاهر خروجی بینقص بود، خطا تا زمانی که یک کوئری دستی SQL رکوردهای «یتیم» را شناس کرد، نامرئی ماند. لازم به ذکر است که این مقاله به عنوان بخشی از فعالیتهای ترویجی محصولات MonkeyCode تهیه شده است. شکست توصیف شده یک باگ محصول نیست، بلکه یک شکاف طراحی در خط لوله است که با هر مدلی که JSON برمیگرداند رخ میدهد.
این وضعیت شبیه نگهبانی است که فقط اندازه و رنگ کارت شناسایی را چک میکند، اما هرگز بررسی نمیکند که آیا نام روی کارت در لیست کارکنان هست یا خیر. سیستم «شکل» داده را چک کرد اما «حقیقت» آن را نادیده گرفت. همانطور که در تحلیلهای قبلی ما دربارهی توهمات مدلهای زبانی اشاره کردیم، خطرناکترین خطاها آنهایی هستند که شبیه موفقیت به نظر میرسند. این موضوع یادآور بررسیهای ما درباره وبهوکهای پرداخت است که در آن AI باعث پنهان شدن خطاهای بحرانی در محیط تولید شد.
کالبدشکافی یک شکست خاموش
توسعهدهنده با استفاده از یک کوئری LEFT JOIN متوجه وجود رکوردهای یتیم شد: SELECT t.transaction_id, t.customer_id, c.name FROM transactions t LEFT JOIN customers c ON c.id = t.customer_id WHERE c.id IS NULL;. ۱۱ ردیف بازگشت که همگی شناسههایی مثل "CUST-48291" داشتند؛ شناسههایی که کاملاً با فرمت مورد انتظار مطابقت داشتند.
بر اساس مستندات فنی این پروژه، توسعهدهنده پیش از یافتن علت ریشهای، سه فرضیه اشتباه را دنبال کرد:
- کوئری گزارش: اجرای مجدد کوئری روی یک تراکنش که میدانست سالم است، ثابت کرد که SQL درست است.
- تداخل زمانی (Race Conditions): برچسبهای زمانی نشان دادند که شناسههای یتیم ساعتها بعد از آخرین وارد کردن (Import) مشتریان ثبت شده بودند، که این موضوع تأخیر در همگامسازی را رد میکرد.
- بریدگی متن (Truncation): بررسی طول پاسخها تایید کرد که تمام خروجیهای مدل کامل بودهاند و هیچ بخشی از JSON قطع نشده بود.
در نهایت، چاپ خروجی خام مدل حقیقت را فاش کرد. JSON معتبر بود، انواع دادهها درست بود و فرمت صحیح بود، اما شناسه مشتری کاملاً ساختگی بود. علت ریشهای، دستوری در پرامپت بود که به مدل میگفت: «اگر مشتری بدیهی به نظر میرسد، او را استنتاج کن».
وقتی ایمیل فاقد شناسه واضح بود، مدل با اعتمادبهنفس یک شناسه اختراع کرد. این بدترین نوع شکست مدل است زیرا دقیقاً شبیه موفقیت به نظر میرسد. سیستم به دلیل سه شکاف طراحی خاص سکوت کرد:
- کوری معنایی: تجزیهکننده فقط فرمت (با استفاده از Regex
^CUST-\d{4,6}) را چک میکرد، نه وجود واقعی شناسه در دیتابیس را. - تایید زودهنگام: سیستم بلافاصله بعد از تجزیه، پیام را تایید (Acknowledge) میکرد، به این معنی که رویداد پیش از هرگونه بررسی ارجاعی از صف حذف میشد.
- سستی پایگاه داده: جدول تراکنشها فاقد محدودیت کلید خارجی (Foreign Key) روی ستون
customer_idبود و اجازه ثبت رکوردهای «شبح» را میداد.
پیادهسازی دفاع سه لایه
برای رفع این مشکل، یک سلسلهمراتب اعتبارسنجی طراحی شد تا خطاها در ارزانترین مرحله ممکن شناسایی شوند.
لایه ۱: اعتبارسنجی طرح (Schema Validation)
سیستم اکنون با استفاده از jsonschema قوانین سختگیرانهای را اجرا میکند. طرح TRANSACTION_SCHEMA اکنون نیازمند transaction_id (با الگوی ^txn_[a-z0-9]+)، customer_id (با الگوی ^CUST-[0-9]{4,6})، amount (حداقل ۰)، currency (دقیقاً ۳ کاراکتر) و یک برچسب زمانی با فرمت date-time است. برای جلوگیری از تغییرات ناگهانی در ساختار خروجی مدلها، میتوان از رویکردهایی مشابه گیتهای نوع در C++ برای مهار Drift در JSON Schema استفاده کرد.
لایه ۲: بررسی ارجاعی (Referential Check)
سیستم اکنون پیش از ثبت رکورد، تایید میکند که customer_id واقعاً در جدول مشتریان وجود دارد. اگر شناسه در مجموعه known_ids نباشد، رکورد رد میشود.
لایه ۳: تلاش مجدد با بازخورد
اگر بررسی ارجاعی شکست بخورد، سیستم خطای اعتبارسنجی خاص را به مدل برمیگرداند. متن بازخورد صراحتاً میگوید: «JSON قبلی شکست خورد: [خطا]. اگر نمیتوانی شناسه واقعی را در ایمیل پیدا کنی، همان شیء را با مقدار null برای customer_id برگردان».
سازوکار حلقه بازخورد
این منطق تلاش مجدد برای جلوگیری از «طوفان تلاش مجدد» (Retry Storms) و اتمام سهمیه نرخ (Rate Limit) در نسخههای رایگان، به یک بار محدود شده است. تجربه یک حادثه قبلی با طوفان تلاش مجدد به توسعهدهنده آموخت که حلقه را به جای سه بار، روی یک بار محدود کند. این چالش با مشکلات رایج در فراخوانیهای API مدلهای زبانی که با وجود پاسخهای خالی، وضعیت 200 برمیگردانند همسو است و نیاز به استراتژیهای پایداری دقیقتری دارد.
اگر تلاش دوم شکست بخورد، پاسخ خام به یک صف پیامهای مرده (Dead-letter Queue) در حافظه بادوام فرستاده میشود. نکته حیاتی این است که پیام اصلی تا زمانی که دادهها به عنوان «صحیح» تایید نشوند، تایید (Ack) نمیشوند. تایید زودهنگام یعنی از دست دادن رویداد؛ با تاخیر در تایید، سیستم تضمین میکند هیچ دادهای در فرآیند اعتبارسنجی گم نشود.
ماتریس مدیریت خطا
توسعهدهنده از یک جدول تصمیم برای تعیین مقصد هر شکست استفاده میکند:
- شکست در تجزیه: JSON نامعتبر یا بریده شده، یک بار تلاش مجدد میکند و سپس به صف پیامهای مرده میرود.
- شکست در طرح: فیلدهای گمشده یا انواع غلط، یک بار با بازخورد تلاش میکنند و سپس به صف پیامهای مرده میروند.
- شکست در ارجاع: شناسههای معتبر از نظر فرمت اما ناشناخته، یک بار با بازخورد تلاش میکنند و سپس به صف پیامهای مرده میروند.
- شکست در ثبت: نقض محدودیتهای دیتابیس باعث میشود پیام بدون تایید، برای بررسی دستی پارک شود.
این رویکرد، فساد خاموش را به یک شکاف مرئی تبدیل کرد. در هفته پس از اصلاح، همان شناسههای ساختگی دوباره ظاهر شدند، اما حلقه تلاش مجدد آنها را به null تبدیل کرد و رکوردها با ایمیل خام برای بررسی انسانی ذخیره شدند.
محدودیتها و موازنه ها
این سیستم یک راهکار جهانی نیست. تلاش مجدد با بازخورد، هزینه استنتاج (Inference) — لحظهای که مدل واقعاً جواب تولید میکند، شبیه خودِ آشپزی نه دوره آموزش آشپز — را افزایش میدهد و میتواند باعث برخورد با محدودیتهای نرخ در طرحهای رایگان شود. همچنین اگر مدل غیرقطعی باشد و حلقه بازخورد همگرا نشود، به جای تلاش مجدد، صف بررسی انسانی لازم است.
علاوه بر این، اگر مشتریان و تراکنشها در یک دسته (Batch) ایجاد شوند، بررسی ارجاعی باید بعد از تکمیل کل دسته اجرا شود، نه به صورت رکورد به رکورد. برای کاربردهایی که شناسههای غلط را تحمل میکنند، این لایهها زیادهروی است؛ هدف باید افزودن ارزانترین چکی باشد که شکست مشاهدهشده را میگیرد.
برای توسعهدهندگان، این یعنی «معتبر بودن فرمت» جایگزینی برای «صحیح بودن» نیست. شکاف میان یک رشته JSON معتبر و یک رکورد صادقانه در دیتابیس، جایی است که فساد خاموش زندگی میکند. خطرناکترین شکستهای مدل، آنهایی هستند که دقیقاً شبیه موفقیت به نظر میرسند.
اگر در حال حاضر پیامها را پیش از تایید معنایی دادهها تایید میکنید، احتمالاً در حال از دست دادن رویدادها یا ذخیره ارواح هستید. راه حل این است که بررسی را به همان لایهای منتقل کنید که دادهها در آن نوشته میشوند.
گام بعدی شما
- تمام خط لولههایی که از خروجی JSON مدلها استفاده میکنند را برای «صحت معنایی» (Semantic Correctness) بازبینی کنید.
- محدودیتهای کلید خارجی (Foreign Key) را در دیتابیسهای تولیدی فعال کنید تا از ورود دادههای یتیم جلوگیری شود.
- استراتژی تایید پیامها (Ack) را تغییر دهید تا تنها پس از اعتبارسنجی کامل دادهها رخ دهد.
اما داستان مدیریت خطاهای پیچیدهتر در سیستمهای عاملمحور حتی چالشبرانگیزتر است — به تحلیل ما دربارهی معماری Agentic Workflows مراجعه کنید.




گفتگو