تصور کنید سیستمی طراحی کردهاید که انتظار دارد سطح اهمیت یک خطا را به صورت متنی (مثلاً "high") دریافت کند، اما مدل هوش مصنوعی ناگهان عدد ۳ را میفرستد. برای یک زبان برنامهنویسی با تایپ پویا (Loosely Typed)، این یک مانع کوچک است؛ اما برای یک برنامهنویس C++ که از استخراج سختگیرانه نوع داده (Strict Type Extraction) استفاده میکند، این تغییر کوچک یک خطای مرگبار است و کل برنامه را متوقف میکند.
در ۱۶ اوت ۲۰۲۶، یک Worker در محیط C++ پس از شش روز پایداری ظاهری، با یک استثنای مدیریتنشده (Unhandled Exception) متوقف شد. طبق گزارش منتشر شده در dev.to، دلیل این اتفاق تغییر در رفتار یک مدل رایگان بود که از طریق MonkeyCode فراخوانی میشد؛ مدل شروع به ارسال انواع دادههای متناقض برای فیلدهای یکسان در قالب JSON کرده بود.
این پدیده که انحراف طرحواره (Schema Drift) نام دارد، زمانی رخ میدهد که یک مدل زبانی بزرگ (LLM) — مثل کتابخانهداری که میلیاردها صفحه را خوانده و حالا با همان لحن کتابها جواب میدهد — ساختار کلی JSON را رعایت میکند اما در نحوه نمایش مفاهیم نوسان میکند. این چالش در محیطهای مختلف تکرار شده است؛ برای مثال، پلتفرم SpaceAI360 نیز با ترکیب Zod و لایههای پاکسازی سعی در حذف توقفهای مشابه API داشت تا پایداری خروجیها را تضمین کند.
زمینه و جزئیات شکست
خط لولهی برچسبگذاری (Crash-tagging pipeline) در ابتدا کاملاً پایدار به نظر میرسید. فرآیند به این صورت بود که یک مدل رایگان، یک ردپای پشته (Stack Trace) پاکسازی شده را دریافت میکرد و در پاسخ، یک شیء JSON کوچک برمیگرداند. تستهای یکپارچگی (Integration Test) اولین پاسخها را پذیرفتند، Worker سیپلاس آن را پارس کرد و برچسب مربوطه در صف تریاژ ظاهر شد.
اما مشکل این بود که پارسر اولیه بر اساس تبدیل اجباری نوع داده (Type Coercion) زنجیرهای کار میکرد. توسعهدهندگان فرض میکردند که یک قرارداد JSON سختگیرانه وجود دارد، زیرا تا آن زمان هرگز شاهد نقض آن نبودند. کد برنامه از فراخوانیهای مستقیمی مانند j.at("severity").get<std::string>() و std::stod(j.at("confidence").get<std::string>()) استفاده میکرد. هنگامی که مدل دچار انحراف شد، این فراخوانیها خطاهای نوع (Type Errors) ایجاد کردند که Worker قادر به مدیریت آنها نبود و منجر به کرش شد.
بر اساس گزارش dev.to، تیم توسعه چندین مورد مبهم را مشاهده کرد که باعث شکست پارسر اصلی شد. فیلد 'confidence' بین یک عدد (۰.۹۳)، یک رشته ("۰.۹۳") و مقدار null نوسان میکرد. در همین حال، پرچم 'retry' بین مقدار بولی false و رشته "false" تغییر میکرد. فیلد 'severity' بین رشته "high" و عدد ۳ جابجا میشد و فیلد 'affected_function' که معمولاً یک رشته بود، گاهی به صورت یک آرایه خالی ظاهر میشد. این عدم قطعیت در پاسخها یادآور مشکلاتی است که در معماریهای جدید انتقال داده برای تضمین پایداری محیطهای عملیاتی AI از طریق بافرینگ JSON برای حل نوسانات استریمینگ بررسی شده است.
تیم توسعه در نهایت امید به مهندسی پرامپت (Prompt Engineering) — هنر سؤال درست پرسیدن برای مجبور کردن مدل به رفتار خاص — را رها کرد. آنها دریافتند که نمیتوان مدل را مجبور کرد همیشه دقیق باشد. در عوض، یک «گیت نوع» (Type Gate) اختصاصی در مقابل پارسر ساختند. این گیت مانند یک فیلتر سختگیرانه عمل میکند و هر دادهای را که دقیقاً با شکل (Shape) مورد نیاز در ساختار (Struct) سیپلاس مطابقت نداشته باشد، رد میکند.
سازوکار گیت نوع
این گیت پیش از آنکه هر دادهای استخراج شود، شیء JSON را با مجموعهای از بررسیهای صریح ارزیابی میکند. برای مدیریت خروجی، از یک Enum به نام GateDecision (شامل مقادیر Accept یا Reject) استفاده میکند تا بهجای پرتاب استثنا، یک تصمیم قطعی برگرداند:
- بررسی وجود (Existence Check): تایید میکند تمام فیلدهای ضروری شامل
severity،confidence،affected_functionوretryحضور دارند. اگر هر یک از اینها مفقود باشند، پیام "missing required field" بازگردانده میشود. - اعتبارسنجی نوع (Type Validation): با استفاده از توابعی مثل
.is_string()،.is_number()و.is_boolean()، اطمینان حاصل میکند که انواع داده با ساختار مقصد مطابقت دارند. برای مثال، اگرseverity.is_string()غلط باشد، دلیل خطا به صورت "severity is not a string" ثبت میشود. - اعتبارسنجی بازه (Range Validation): بهطور خاص بررسی میکند که امتیاز confidence حتماً بین ۰.۰ و ۱.۰ باشد. اگر مقدار خارج از این بازه باشد، خطای "confidence out of range" صادر میشود.
- منطق تصمیمگیری: تنها پس از عبور موفقیتآمیز از تمام بررسیهای فوق، گیت اجازه میدهد استخراج نهایی با متد
.get<T>()در ساختارCrashLabelانجام شود.
تست و اعتبارسنجی
بازبینی استاتیک کد (Static Code Review) نمیتوانست این انحراف را شناسایی کند، زیرا ورودیها JSONهای راه دور بودند و نه دادههای تست محلی. برای حل این مشکل، تیم یک «تست جهش» (Mutation Test) توسعه داد. آنها یک نمونه JSON سالم (Seed) را گرفتند و با استفاده از تابع mutate_seed عمداً فیلدها را به شکلهای «بد» که قبلاً مشاهده کرده بودند، تغییر دادند:
- Confidence: تبدیل به رشته از طریق
.dump()یا تبدیل به یک آرایه JSON حاوی عدد ۰.۹۳. - Severity: تغییر از رشته به عدد صحیح ۳.
- Affected Function: تنظیم روی
nullptr(مقدار null در JSON). - Retry: تغییر از مقدار بولی به رشته "false".
این رویکرد، پارسر را از «آخرین خط دفاعی» به یک «فرآیند داخلی محافظتشده» تبدیل میکند. با پیادهسازی استراتژی شکست بسته (Fail Closed)، سیستم بهجای کرش کردن، دلیل دقیق رد شدن داده را لاگ میکند. در این راستا، بررسی مکانیزم Lease در برابر حلقههای تکرار ساده نشان میدهد که تکرار سادهی درخواستها در صورت دریافت پاسخهای خالی یا نامعتبر، لزوماً منجر به پایداری API نمیشود.
برای توسعهدهندگان، این مورد یک تغییر حیاتی در نحوه یکپارچهسازی هوش مصنوعی را برجسته میکند. تکیه بر یک LLM برای رعایت قرارداد JSON یک قمار است. ایمنی واقعی زمانی حاصل میشود که با مدل زبانی به عنوان یک تولیدکنندهی غیرقابلاعتماد برخورد کنید و یک لایهی اعتبارسنجی سختگیرانه در سمت مصرفکننده پیاده کنید. تیم مذکور این گیت را از طریق دسترسی مدلهای رایگان MonkeyCode به یک مدل متصل کرد تا اعتبارساز را روی یک سرور رایگان، بدون نیاز به ماشین CI اختصاصی، اجرا کند.
با این حال، این گیت یک راهکار جامع (Silver Bullet) نیست. این ابزار مشکل ساختاری (آیا داده شکل درستی دارد؟) را حل میکند اما مشکل معنایی (Semantic) را نه. برای مثال، مدل ممکن است برای یک خطای بحرانی Null Pointer، مقدار "severity": "low" را برگرداند؛ گیت این را میپذیرد چون مقدار از نظر فنی یک رشته است. بنابراین، اعتبارسنج معنایی مجزا و بازبینی انسانی همچنان ضروری است.
علاوه بر این، تیم اشاره کرد که برای تلهمتری با فرکانس بالا، تأخیر (Latency) ناشی از یک پاس دیسریالیزاسیون اضافی و بررسیهای تکتک فیلدها ممکن است بیش از حد زیاد باشد. تیمهایی که از Protobuf با تایپ سختگیرانه در سرویسهای پایدار استفاده میکنند، احتمالاً به این سازوکار نیاز ندارند و کسانی که بودجه زمانی Real-time دارند و مسیر جایگزینی (Fallback) ندارند، ممکن است این لایه را یک پیچیدگی اضافی ببینند. همچنین، گیت نمیتواند در برابر تمام خطاهای استخراج C++، مانند اعداد بسیار بزرگ (مثلاً 1e999) یا آرایههای تودرتو در فیلدهای رشتهای، محافظت کند.
گام بعدی شما
- اگر در پروژههای C++ از
get<T>()برای JSONهای خارجی استفاده میکنید، اولین قدم این است که اشکالهای بدساختی را که قبلاً باعث کرش شدهاند، شناسایی کنید. - این اشکالها را به یک لایهی اعتبارسنجی (Type Gate) بدهید تا مطمئن شوید سیستم شما بهجای توقف فاجعهبار، بهصورت کنترلشده شکست میخورد.
- برای دادههای حساس، ترکیبی از اعتبارسنجی ساختاری و بازبینی انسانی را به کار ببرید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو