تصور کنید یک برنامهنویس سیستمی را طراحی کرده که هر شب خطاهای سرور را تحلیل میکند، اما صبح روز بعد متوجه میشود دهها گزارش بحرانی بدون هیچ هشداری گم شدهاند. این اتفاق زمانی رخ میدهد که مدل هوش مصنوعی بهجای پاسخ، یک فضای خالی میفرستد اما سرور ادعا میکند که عملیات با موفقیت انجام شده است. در این سناریوی خاص، یک شغل تریاژ شبانه روی یک سرور رایگان اجرا میشود. این سیستم ابتدا اثرات زنجیرهای (Stack Traces) سه سرویس مختلف را گروهبندی کرده و یک پرامپت فشرده را به یک نقطه انتهایی (Endpoint) مدل رایگان ارسال میکند. چون لایه انتقال داده گزارش موفقیت میدهد، سیستم تماس را به عنوان «تکمیل شده» علامتگذاری میکند و در نتیجه خطاهای بحرانی مسیریابی نمیشوند و مالکان آنها بیخبر میمانند.
طبق گزارش فنی MonkeyCode، این شکست در لایه انتقال داده رخ میدهد، نه در هوش مدل. در یک مورد واقعی، این نقص باعث شد دو صفحه گزارش خطا صبح روز بعد به اشتباه به مالک اشتباهی ارسال شود، زیرا هیچکس نمیدانست مدل در واقع هیچ پاسخی برنگردانده است. این حالت خاص از شکست، که در یک بازتولید فنی توسط MonkeyCode برجسته شده، نشاندهنده یک شکست در «قرارداد» (Contract) است و ربطی به سطح هوش مدل ندارد. (افشا: این مقاله به عنوان بخشی از فعالیتهای معرفی محصول MonkeyCode تهیه شده است).
همانطور که در تحلیلهای قبلی ما دربارهی پایداری مدلهای بازمتن اشاره کردیم، تکیه بر لایههای رایگان همواره با ریسکهای پیشبینینشده همراه است. اکثر توسعهدهندگان کد HTTP 200 را به معنای موفقیت معنایی میبینند. اما در واقعیت، وضعیت ۲۰۰ فقط تایید میکند که سرور پاسخ داده است. در سرویسهای رایگان، شکاف بین «موفقیت در انتقال» و «دریافت دادهی قابل استفاده»، منبع اصلی باگهای خاموش در محیط عملیاتی است. این موضوع بهویژه در خط لولههای (Pipelines) خودکار که هیچ انسانی خروجی خام API را مانیتور نمیکند، خطرناک است و یادآور چهار نقطه کور در AWS Bedrock AgentCore است که منجر به شکستهای خاموش در استقرار میشوند. MonkeyCode دسترسی رایگان به مدل و گزینه سرور رایگان را ارائه میدهد که باعث میشود تست این دسته از باگها ارزان و ساده باشد.
تلهٔ تکرار (Retry Trap)
اولین واکنش غریزی هر توسعهدهندهای هنگام شکست یک فراخوانی API، پیادهسازی یک حلقه تکرار (Retry Loop) است. در این مورد خاص، اولین ایده تیم این بود که برای هر پاسخ ۲۰۰، یک بار تلاش مجدد اضافه کنند. اما بر اساس تحلیل MonkeyCode، تکرار درخواست در مواجهه با پاسخهای خالی ۲۰۰، اغلب وضعیت را بدتر میکند. این کار زمان مرده (Dead Time) را دوبرابر کرده و ممکن است «موارد سمی» (Poison Cases) را پنهان کند؛ جایی که فراخوانی دوم پاسخی متفاوت اما همچنان معیوب برمیگرداند. این ریسک افزایش هزینهها در محیطهای ابری نیز مشهود است، همانطور که حلقههای تکرار در پروتکل MCP توانستهاند هزینههای Bedrock را تا سه برابر افزایش دهند.
برای کارهای تکمرحلهای که خاصیت Idempotent دارند (یعنی تکرار آنها نتیجهای یکسان دارد)، یک فراخوانی واحد همراه با یک جایگزین قطعی (Deterministic Fallback)، بسیار برتر از چندین فراخوانی مبتنی بر امید است. تکرارها همچنین باعث غیرقابلپیشبینی شدن تأخیر (Latency) شده و میتوانند نقاط انتهایی رایگانِ تحت فشار را کاملاً از کار بیندازند و یک تماس بد را به سه تماس بد تبدیل کنند. موفقیت در انتقال و موفقیت معنایی دو وضعیت متفاوت هستند؛ یک HTTP 200 به این معنا نیست که پاسخ میتواند مبنای یک تصمیم مسیریابی باشد.
پیادهسازی مکانیزم «اجاره» (Lease)
برای حل این مشکل، نویسنده مفهومی به نام «اجاره» را پیشنهاد میکند؛ یک قرارداد محلی کوچک که دور فراخوانی API پیچیده شده است. این اجاره شامل چهار لایه حفاظتی بحرانی است:
- محدودیت زمانی کلی (Total Timeout): یک سقف سخت برای زمان واقعی (Wall Time) جهت جلوگیری از معلق ماندن پردازشها. در نمونه بازتولید، این مقدار روی ۹۰۰۰ میلیثانیه تنظیم شده است، در حالی که یک تایماوت مجزا برای اتصال (Connect Timeout) روی ۲۵۰۰ میلیثانیه قرار دارد.
- سقف بایتهای پاسخ (Response Byte Cap): محدودیتی برای تعداد بایتهای خوانده شده تا از نگه داشتن پاسخهای نامحدود از پروکسیها یا ریدایرکتها جلوگیری شود. در این بازتولید، سقف روی ۴۰۹۶ بایت تنظیم شده است.
- حفاظت از ساختار (Schema Guard): بررسی اینکه پاسخ نهتنها یک JSON معتبر است، بلکه حاوی فیلدهای خاص مورد انتظار است. این لایه بررسی میکند که آرایه
choicesوجود داشته باشد، آرایه خالی نباشد و مقدارchoices[0].message.contentموجود و غیرخالی باشد. این رویکرد شباهت زیادی به پیادهسازی لایههای نگهبان برای جلوگیری از خطاهای پرهزینه در عاملهای هوش مصنوعی دارد. - جایگزین قطعی (Deterministic Fallback): یک اقدام پیشفرض و امن برای زمانی که هر یک از حفاظهای بالا فعال شوند. در این مورد، جایگزین این است که مقدار
severity=unknownوowner=oncallتنظیم شود.
جزئیات فنی اجرا
در نمونه کد C++ ارائه شده، از کتابخانههای libcurl و nlohmann/json برای اجرای این مرزها استفاده شده است. یک نکته کلیدی، تابع bounded_write است که با محدود کردن اندازه پاسخ، از سرریز حافظه (Memory Overflow) جلوگیری میکند. این موضوع حیاتی است زیرا نقاط انتهایی (Endpoints) رایگان ممکن است پشت پروکسیهایی باشند که ریدایرکتها یا بدنههای بهطور غیرمنتظره بزرگی ارسال میکنند.
سیستم نتایج را در یک جدول تصمیمگیری دستهبندی میکند تا اقدام محلی را تعیین کند:
- پذیرفته شده (Accepted): شواهد نشاندهنده محتوای غیرخالی در
choices[0].message.contentاست. اقدام: ارسال به بخش مسیریابی. - خالی (Empty): شواهد نشاندهنده HTTP 200 با بدنه خالی یا محتوای خالی است. اقدام: ثبت در لاگ، استفاده از جایگزین، بدون تکرار.
- نقض ساختار (SchemaViolation): شواهد نشاندهنده JSON نامعتبر یا نبود فیلدها است. اقدام: ثبت در لاگ، استفاده از جایگزین، بدون تکرار.
- خطای انتقال (TransportError): شواهد شامل تایماوت، خطای DNS، کد غیر از 2xx یا سرریز بایت است. اقدام: ثبت در لاگ، استفاده از جایگزین، بدون تکرار.
دفاع لایهای با پوشکهای شل (Shell Wrappers)
به دلیل اینکه سرورهای رایگان اغلب محدودیتهای شدیدی در حافظه و CPU دارند، مکانیزم اجاره با یک پوشک شل (Shell Wrapper) تقویت میشود. در یک ماشین با منابع محدود، پردازش نباید یک حلقه تکرار را فورک (Fork) کند. این پوشک از دستور ulimit -v 2621424 برای محدود کردن حافظه مجازی و دستور timeout 20s استفاده میکند تا تضمین شود پردازش بدون توجه به هرگونه توقف داخلی، خارج شود.
این ساختار دو ترمز مستقل ایجاد میکند: لایه C++ قرارداد API را مدیریت میکند و لایه شل، محدودیتهای منابع سیستمعامل را. اگر کد C++ یک وضعیت معلق (Hang) را تشخیص ندهد، پوشک شل همچنان پردازش را متوقف کرده و یک فایل جایگزین امن باقی میگذارد. پوشک شل حدس نمیزند که چرا تماس شکست خورده است؛ بلکه فقط تضمین میکند که پردازش خارج شده و یک نتیجه امن بر جای میگذارد.
محدودیتها و ریسکها
این روش فقط شکل (Shape) داده را تضمین میکند، نه صحت (Correctness) آن را. مدلی که با اطمینان کامل، مالک اشتباهی را در یک شیء JSON با فرمت بینقص ارائه دهد، همچنان از لایه حفاظتی ساختار عبور خواهد کرد. علاوه بر این، تکیه بر جایگزینها میتواند شکستهای مزمن را پنهان کند اگر لاگها مانیتور نشوند. نویسنده توصیه میکند برای هر مقدار Outcome یک شمارنده اضافه کنید و زمانی که وضعیتهای Empty یا SchemaViolation یا TransportError غالب شدند، هشدار فعال کنید.
این الگو برای پیشنهادهای مسیریابی و تریاژ طراحی شده است، نه برای تولید کد یا پاسخهای کاربر نهایی. در تصمیمات پرریسک که یک برچسب اشتباه منجر به مواجهه قانونی، ایمنی یا ضرر مالی میشود، یا زمانی که تیم نمیتواند یک جایگزین امن تعریف کند، نباید از این روش استفاده کرد. یک نقطه انتهایی مدل رایگان، یک «ورودی» است، نه یک «توافقنامه سطح خدمات» (SLA).
در نهایت، باگ پاسخهای خالی ۲۰۰ با پرامپتنویسی بهتر یا مدلهای بزرگتر حل نشد؛ بلکه با این واقعبینانه کردن کلاینت حل شد که «کمتر انتظار داشته باشد و بیشتر بررسی کند». لایه رایگان همچنان مفید است، به شرطی که قرارداد محلی برای ایمنسازی آن برقرار باشد.
گام بعدی شما
- در کدهای خود، بررسی کد HTTP 200 را از تنها معیار موفقیت خارج کنید و حتماً وجود محتوا را چک کنید.
- برای هر فراخوانی API، یک مقدار جایگزین (Fallback) تعریف کنید تا در صورت شکست، سیستم بهصورت خاموش متوقف نشود.
- اگر از سرورهای محدود استفاده میکنید، پردازشهای AI را در یک پوشک
timeoutشل قرار دهید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو