تصور کنید برنامهنویسی هستید که یک سیستم پیچیده را مدیریت میکند و هر بار که مدل هوش مصنوعی پاسخی نامعتبر میدهد، کل زنجیره عملیاتی شما با یک خطای کلی متوقف میشود. اگر هنوز از بلوکهای ساده Try/Catch برای مدیریت فراخوانهای مدل استفاده میکنید، در واقع تمام جزئیات شکست را نادیده میگیرید و سیستم خود را در برابر توهمات مدل آسیبپذیر کردهاید.
طبق بررسیهای فنی، عبارت «کار نکرد» در دنیای هوش مصنوعی یک حالت واحد نیست، بلکه مجموعهای از پیامدهای متمایز است که هر کدام استراتژی بازیابی متفاوتی میطلبد. در مراحل اولیه ادغام مدل زبانی بزرگ (LLM) — مثل کتابخانهداری که میلیاردها صفحه را خوانده و حالا با همان لحن کتابها جواب میدهد — در نرمافزارهای تجاری، اکثر توسعهدهندگان با این واقعیت مقابله نکردند و فراخوان مدل را شبیه به یک درخواست شبکه ساده دیدند که در یک بلوک Try/Catch ابتدایی پیچیده شده است. در حالی که تلاش برای فراخوان، چند بار تکرار آن و سپس پرتاب یک Exception برای یک اسکریپت مستقل کاربرد دارد، اما این الگو در یک گردش کار (Workflow) بزرگتر و پیچیده بهسرعت فرو میپاشد. این رویکرد تکراری اغلب منجر به بحرانهای عملیاتی میشود، همانطور که خطرات تکرار کورکورانه در APIها میتواند هزینههای فنی و مالی هنگفتی ایجاد کند. با تبدیل این پیامدهای متنوع به یک حالت دودویی «موفق» یا «خطا»، تابع فراخوان مجبور میشود علت شکست را از درون بلوک Catch مهندسی معکوس کند؛ نتیجهای که منجر به کدی میشود که هم شکننده است و هم مبهم.
همانطور که در تحلیلهای پیشین ما دربارهی مدیریت وضعیت در عاملهای هوشمند اشاره کردیم، راهکار مقاومتر این است که نتیجه فراخوان LLM را به عنوان یک شیء تایپشده درجه اول با چهار حالت مشخص در نظر بگیریم: موفق (OK)، کاهشیافته (DEGRADED)، خودداری (ABSTAINED) و شکست (FAILED). این مدل دیدگاه ما را از «مدیریت خطای ساده» به «رویکرد ماشین وضعیت» تغییر میدهد، جایی که در آن هر پیامد بهطور صریح وضعیت گردش کار و گامهای بعدی لازم را تعریف میکند. با رسمی کردن این حالتها، توسعهدهندگان میتوانند خط لولههایی بسازند که نه تنها تابآورتر هستند، بلکه عیبیابی و نظارت بر آنها نیز آسانتر است.
بر اساس مستندات طراحی سیستمهای تابآور، این چهار حالت را میتوان چنین باز کرد:
- موفق (OK): حالت ایدهآل است؛ فراخوان موفق بود، خروجی با Schema مورد انتظار مطابقت دارد و نتیجه کاملاً قابل استفاده است.
- کاهشیافته (DEGRADED): زمانی رخ میدهد که فراخوان شکست خورده یا نتیجهای suboptimal تولید کرده، اما یک جایگزین (Fallback) معقول وجود دارد. برای مثال، اگر یک مدل پیشرفته در خلاصهسازی یک سند شکست بخورد، سیستم ممکن است به یک مدل سادهتر و سریعتر یا یک خلاصهساز مبتنی بر اکتشافات ابتکانی (Heuristic) روی آورد. در این حالت گردش کار ادامه مییابد، اما کیفیت خروجی به عنوان سطح پایینتر پذیرفته و ثبت میشود.
- خودداری (ABSTAINED): یک بهینهسازی حیاتی است. این حالت زمانی اتفاق میافتد که سیستم، قبل یا در حین فراخوان، تشخیص دهد که هیچ کاری برای مدل وجود ندارد که انجام دهد. این رویکرد از هدر رفتن توکن (Token) — تکههای کوچکی از متن شبیه برشهای یک کیک که مدل تکهتکه میخورد — و افزایش تأخیر جلوگیری میکند.
- شکست (FAILED): خطای واقعی است؛ فراخوان شکست خورده و هیچ جایگزین معقولی وجود ندارد، به این معنی که گام فعلی گردش کار واقعاً نمیتواند پیش برود.
پیادهسازی این مدل نیازمند یک سیستم تایپینگ ساختاری است. یک مدل پیشنهادی شامل یک «پاکت» (Envelope) است که متادیتاهایی نظیر شناسه گام، تعداد تلاشها، کلاس شکست (Timeout، خطای پارس، عدم تطابق Schema یا خطاهای HTTP بالادستی)، نسخه مدل و تأخیر (Latency) را در بر میگیرد. این پاکت با یک Discriminated Union برای پیامدها جفت میشود. این ساختار تضمین میکند که اگر نتیجهای به عنوان FAILED علامتگذاری شد، توسعهدهنده به «کلاس شکست» دسترسی داشته باشد تا تصمیم بگیرد آیا باید دوباره تلاش کند یا یک انسان را باخبر سازد. در مقابل، اگر وضعیت OK یا DEGRADED باشد، تضمین میشود که مقدار خروجی در دسترس است.
یکی از چالشهای غیربدیهی در این طراحی، شکست در مرحله «پارس» (Parse) است. در بسیاری از پیادهسازیهای LLM، مدل رشتهای را برمیگرداند که باید به JSON تبدیل شود. اگر پارس شکست بخورد، وسوسهبرانگیز است که آن را در حالت کلی FAILED قرار دهیم. اما در یک خط لوله پیشرفته، اگر سیستم بتواند اطلاعات جزئی را از رشته متنی بدشکل استخراج کند، باید آن را در حالت DEGRADED قرار دهد. تفکیک بین یک Timeout شبکه (که نیاز به تلاش مجدد دارد) و یک تخطی از Schema (که ممکن است نیاز به اصلاح پرامپت داشته باشد)، سیستم را در بازیابی بسیار هوشمندتر میکند. برای جلوگیری از این نوع افت کیفیت در محیط عملیاتی، استفاده از ارکان حیاتی در لایههای Gateway میتواند مکمل این مدل تایپشده باشد.
حالت خودداری (ABSTAINED) اغلب نادیده گرفته میشود. در خط لولههای پردازش اسناد، ممکن است متونی پیدا کنید که برای خلاصهسازی بیش از حد کوتاه باشند یا هیچ موجودیت مرتبطی برای استخراج نداشته باشند. اگر این مورد را «موفق» (OK) در نظر بگیریم، تحلیلهای آماری ما دچار انحراف میشود و اگر «شکست» (FAILED) تلقی شود، هشدارهای بیمورد صادر میگردد. حالت ABSTAINED اجازه میدهد خط لوله گام مربوطه را بهطور محترمانه رد کند و یک ردپای بازرسی (Audit Trail) پاکیزه نگه دارد که نشان میدهد مدل بهطور عمدی استفاده نشده است.
این رویکرد تایپشده همچنین نحوه نظارت (Observability) را تغییر میدهد. بهجای جستوجو در لاگها برای یافتن Stack Traceها، توسعهدهندگان میتوانند توزیع نتایج را کوئری کنند. اگر یک گام خاص نرخ بالای نتایج DEGRADED را نشان دهد، این سیگنالی است که مدل اصلی در حال تقلا است اما مدل جایگزین در حال نجات وضعیت است. این موضوع یک علامت واضح است که پرامپت یا مدل اصلی نیاز به تنظیم دارد، بدون اینکه فوریت یک خرابی کامل سیستم ایجاد شود. در واقع، شناسایی این وضعیتها به ما کمک میکند تا خطاهای خاموشی را که حتی توسط داوران LLM نیز نادیده گرفته میشوند، از طریق مکانیسمهای مداخلاتی ردیابی کنیم. در مقابل، اگر نرخ FAILED افزایش یابد، این موضوع مستقیماً به مشکلات زیرساختی یا API اشاره دارد.
ادغام این مدل در لایههای ارکستراسیون مانند Inngest یا Mastra امکان مدیریت خطای Declarative را فراهم میکند. به جای تو در تو کردن بلوکهای Try/Catch، منطق گردش کار صرفاً روی پیامد خروجی سوئیچ میکند. برای مثال، یک گردش کار میتواند اینگونه پیکربندی شود: «اگر OK بود، به گام B برو؛ اگر DEGRADED بود، با یک پرچم هشدار به گام B برو؛ اگر ABSTAINED بود، مستقیماً به گام C بپر؛ و اگر FAILED بود، یک صف بازبینی انسانی را فعال کن». این جداسازی منطق اجرا از طبقهبندی پیامدها، کد را خواناتر و قابل نگهداریتر میکند.
یک ملاحظه حیاتی دیگر، «کلاس شکست» (Failure Class) است. با دستهبندی شکستها به گروههای Timeout، Parse، Schema، Upstream-5xx و Upstream-4xx، سیستم میتواند استراتژیهای بازگشت متفاوتی را پیاده کند. یک خطای 5xx از سمت ارائهدهنده LLM یک شکست گذرا است که بازگشت با تأخیر نمایی (Exponential Backoff) را توجیه میکند. اما یک خطای 4xx، اغلب نشاندهنده پرامپتی است که قوانین ایمنی را نقض کرده یا از محدودیت توکنها فراتر رفته است، جایی که تلاش مجدد بدون تغییر پرامپت، کاملاً بیفایده است. یک خطای Schema نیز نشان میدهد که مدل در حال توهم ساختاری است، که ممکن است نیازمند نسخه متفاوتی از پرامپت یا یک پارسر پذیراتر باشد.
در نتیجه، فاصله گرفتن از ذهنیت دودویی «موفق/شکست» برای مقیاسبندی برنامههای LLM ضروری است. با پذیرش یک مدل پیامد تایپشده — OK، DEGRADED، ABSTAINED و FAILED — توسعهدهندگان میتوانند تعاملات LLM را به عنوان مجموعهای از گذارهای وضعیت (State Transitions) مدیریت کنند. این کار نه تنها استحکام نرمافزار را بهبود میبخشد، بلکه تلمتریهای دقیقی را فراهم میکند که برای بهینهسازی عملکرد مدل در طول زمان لازم است. تغییر از «مدیریت خطا» به «مدیریت وضعیت»، به توسعهدهنده اجازه میدهد تا غیرقابلپیشبینی بودن ذاتی هوش مصنوعی مولد را پیشبینی کرده و سیستمهایی بسازد که بهجای فروپاشی فاجعهبار، بهطور محترمانه شکست بخورند.
گام بعدی شما
- خروجیهای فعلی LLM در پروژه خود را تحلیل کنید و ببینید چند درصد از شکستها در واقع حالتهای DEGRADED یا ABSTAINED هستند.
- یک discriminated union برای حالتهای خروجی در زبان برنامهنویسی خود (مانند TypeScript) تعریف کنید تا اجبار به بررسی هر حالت ایجاد شود.
- استراتژی Retry خود را بر اساس کلاس شکست (مثلاً فقط برای 5xxها) بازنویسی کنید.
اما این مدیریت وضعیت تنها نیمی از راه است؛ تأثیر مستقیم این مدل بر کاهش هزینههای استنتاج را در گزارش بعدی بررسی خواهیم کرد.




گفتگو