تصور کنید یک عامل هوش مصنوعی در ۹۰ ثانیه، ۱۱ بار پشتسرهم سعی میکند یک ابزار تقویم را فراخوانی کند. این رفتار نشانهٔ پشتکار نیست، بلکه نتیجهٔ یک پرامپت شکسته است. طبق تحلیل فنی منتشر شده در dev.to در تاریخ ۴ سپتامبر ۲۰۲۶، کدهای خطای استاندارد HTTP — مانند خطای ۴۰۹ (Conflict) — در عمل بهعنوان دستورالعملهای ناخواسته برای مدلهای زبانی بزرگ (LLM) عمل میکنند تا زمانی که بودجهٔ توکن تمام شود، به تلاش خود ادامه دهند.
در این مورد خاص، ابزار دقیقاً طبق طراحی عمل میکرد و خطای ۴۰۹ را با متن {"error": "Conflict"} برمیگرداند. برای انسانی که ابزارهای توسعهدهنده (DevTools) مرورگر را باز کرده، این پاسخ کاملاً منطقی است؛ انسان میخواند «تداخل»، تقویم را چک میکند، میبیند رویداد شروع شده و سراغ کار دیگری میرود. اما یک عامل (Agent) — شبیه دستیاری که فقط دستورات روی کاغذ را میبیند و از فضای واقعی محیط خبر ندارد — فاقد این مدل ذهنی بیرونی است. مدل Claude کلمه «تداخل» را خواند و استنتاج کرد که مشکلی گذرا رخ داده است. چون موارد گذرا ارزش تلاش مجدد دارند، مدل دوباره امتحان کرد. کل باگ همین است: پیام خطا برای خوانندهای نوشته شده بود که در آنجا حضور نداشت.
این شکست رخ میدهد چون در سیستمهای عاملمحور (Agentic)، هر رشته متنی که یک ابزار برمیگرداند، در واقع ورودی مدل است. پیامهای خطای شما دیگر گزارشهای تشخیصی نیستند، بلکه پرامپت هستند. شما آنها را برای توسعهدهندهای نوشتید که لاگها را میخواند، اما حالا آنها را بهعنوان دستورالعمل برای گام بعدی به یک مدل زبانی دادید. انسان حافظهٔ دفعات قبلی را دارد و میتواند از همکارش بپرسد، اما مدل فقط رشته متنی شما و طرحواره (Schema) ابزار را در اختیار دارد. این چالشها در واقع بخشی از مشکلات بنیادیتر در تعامل مدلها با رابطهای برنامهنویسی هستند که پیشتر در بررسی ۴ باگ بحرانی اتصال عاملها به API به آنها پرداختیم.
همانطور که در تحلیلهای پیشین ما دربارهی امنیت و پایداری مدلهای بازمتن اشاره کردیم، تکیه بر رفتارهای پیشفرض مدلها بدون ساختاردهی دقیق، ریسک سیستمیک ایجاد میکند. وقتی ابزاری خطای مبهم برمیگرداند، مدل مجبور به حدس زدن گام بعدی است. از آنجا که اینترنت اشباع شده از منطقهای «تلاش مجدد» (Retry)، حدس پیشفرض مدل تقریباً همیشه تکرار عملیات است. این یک نقطهٔ طراحی حیاتی است که دقیقاً در حوزهٔ طراحی ابزار و پروتکل زمینهٔ مدل (MCP) قرار میگیرد و در آزمون CCAR-F (یا CCA-F) به عنوان یک مبحث کلیدی مطرح میشود.
چارچوب سه پرسشی برای خطاهای ابزار
به نقل از گزارش dev.to، برای رفع این مشکل، هر خطای ابزار باید به سه پرسش مشخص پاسخ دهد تا ابهام حذف شود. بازنویسی یک پاسخ در یک خط میتواند بلافاصله حلقه تکرار را متوقف کند. برای مثال، تغییر یک تداخل مبهم به این شکل:{ "error": "event_already_started", "message": "This event has already started and cannot be modified. Use update_event to change its end time, or create_event to schedule a new one.", "retryable": false }
- این خطا از چه نوعی است؟ بهجای وضعیت HTTP، از یک رشته پایدار و ماشینخوان استفاده کنید که مدل بتواند آن را شناسایی کند. کدی مثل
event_already_startedباrate_limitedکاملاً متفاوت است، حتی اگر هر دو در سطح کد وضعیت HTTP، تحت عنوان ۴۰۹ یکسان شوند و کد وضعیت آنها را تخت (Flatten) کند. - آیا تلاش مجدد معنا دارد؟ این را صراحتاً بگویید. یک مقدار بولی (Boolean) برای
retryableبسیار مؤثرتر از هر توصیفی است، چون مرحلهٔ استنتاج (Inference) — یعنی همان لحظهای که مدل سعی میکند جواب تولید کند، شبیه آشپزی واقعی در مقابل یادگیری دستور پخت — را بهطور کامل حذف میکند. خطاهای شبکه بایدtrueو نقض قوانین کسبوکار بایدfalseباشند. برای محدودیتهای نرخ درخواست (Rate Limits)، مقدار راtrueقرار داده و زمان انتظار مورد نیاز را ذکر کنید. - بهجای آن چه اتفاقی باید بیفتد؟ این مرحلهای است که اکثر توسعهدهندگان نادیده میگیرند. خطایی که نام ابزار جایگزین را میبرد، یک بنبست را به گامی سازنده تبدیل میکند. وقتی به مدل بگویید بهجای
create_eventازupdate_eventاستفاده کند، مانع از آن میشوید که مدل مجبور شود گام بعدی را از خودش اختراع کند.

خطر ابزارهای غیر-Idempotent
برچسب زدن یک خطا بهعنوان «قابل تکرار»، ریسک جدیدی ایجاد میکند: ماشین تکثیر. وقتی به مدل میگویید کدام خطاها قابل تکرار هستند، باید مطمئن شوید که تکرار واقعاً امن است. این گزارش موردی را برجسته میکند که در آن ابزار create_event خاصیت Idempotency (یکسانساز بودن) نداشت. وقتی یک Timeout رخ داد و عامل دوباره تلاش کرد، دو ورودی یکسان در تقویم ایجاد شد.
این حالت شکست اغلب در زمان تست نامرئی میماند چون تستها بهندرت Timeout را شبیهسازی میکنند. در محیط عملیاتی، این موارد تکراری معمولاً بهعنوان خطای کاربر رد میشوند، چون دو ورودی یکسان شبیه اشتباه انسانی به نظر میرسد.
برای جلوگیری از این اتفاق، توسعهدهندگان باید راهکار «خستهکننده» اما درست را اجرا کنند: پذیرفتن یک کلید Idempotency از سمت کلاینت، ذخیره آن در برابر منبع ایجاد شده و بازگرداندن نتیجهٔ اصلی در صورت تکرار. طراحی ابزارها بهگونهای که فراخوانی مجدد اثرات جانبی تکراری ایجاد نکند، پیشنیازِ اعلامِ «امن بودن تکرار» است، نه یک بهینهسازی برای مراحل بعد. اگر چیزی را retryable علامتگذاری کنید در حالی که Idempotent نیست، در واقع کلیدهای یک ماشین تکثیر را به مدل دادهاید.
ضدالگوی «نتیجهٔ خالی»
شکست بحرانی دیگر زمانی رخ میدهد که زیر-عاملها (Subagents) هنگام مواجهه با یک Exception، آرایههای خالی [] برمیگردانند. این مورد، نسخهٔ بدترِ خطاهای غیرمفید است. در این سناریو، زیر-عامل شکست میخورد، لایهٔ پوشاننده (Wrapper) خطا را میگیرد، آن را لاگ میکند و یک آرایه خالی برمیگرداند. ارکستراتور [] را دریافت کرده و آن را بهعنوان یک حقیقت پذیرفت: «هیچ نتیجهای وجود ندارد».
این وضعیت یک شکاف اعتماد خطرناک ایجاد میکند. عامل با اطمینان گزارش میدهد که چیزی پیدا نشد، در حالی که جستوجو اصلاً اجرا نشده است. این یک ضدالگوی رایج است چون کد «تدافعی» به نظر میرسد — چون خطا را میگیرد — اما فراخواننده راهی ندارد تا بین «جستوجو شد و چیزی یافت نشد» و «جستوجو هرگز اجرا نشد» تفاوت قائل شود.
این یک تصمیم طراحی در سطح معماری عاملمحور و ارکستراسیون است، نه صرفاً وظیفه نویسنده زیر-عامل. نتیجهٔ خالی و نتیجهٔ شکستخورده باید در پاسخ متمایز باشند. قرارداد بین ارکستراتور و زیر-عاملها به یک سیگنال خطای صریح نیاز دارد، نه فقط یک کانال موفقیت ضمنی. بسیار راحتتر است که این موضوع را در ابتدا درست پیاده کنید تا اینکه بعداً بخواهید آن را در چهار جای مختلف که [] را بهعنوان پاسخ قطعی میپذیرند، اصلاح کنید.
چرا طرحوارهها (Schemas) کافی نیستند؟
بسیاری از توسعهدهندگان باور دارند که یک JSON Schema سختگیرانه این مشکلات را حل میکند. اما طرحواره فقط شکل پاسخ را تحمیل میکند، نه صحت محتوا را. شما میتوانید کاملاً با قرارداد خطا مطابقت داشته باشید و همچنان در یک شکست دائمیِ قوانین کسبوکار، پاسخ {"code": "ERROR", "message": "Something went wrong", "retryable": true} را برگردانید.
این تلهٔ خروجیهای ساختاریافته است: اعتبارسنجی به شما میگوید پاکت نامه درست است، اما چیزی دربارهٔ نامهٔ داخل آن نمیگوید. شما فقط همان حلقه تکرار قبلی را ساختهاید، اما این بار با اعتبارسنجی. مدل همچنان یک پرامپت مبهم دریافت میکند و احتمالاً به همان حلقه تکرار بازمیگردد.
پیادهسازی عملی و MCP
برای کسانی که پروتکل زمینهٔ مدل (MCP) را پیاده میکنند، این تصمیمات بسیار اثرگذار هستند. اشتباهات رایج در سرورهای MCP عمدتاً از همین جنساند؛ تصمیماتی که برای مصرفکننده انسانی درست بودند اما لحظهای که مدل شروع به خواندن آنها کرد، غلط شدند. علاوه بر این، دانهبندی (Granularity) ابزارها با این موضوع در تداخل است: ابزارهای کمتر و گستردهتر به معنای تجمع حالتهای شکست بیشتر در یک سطح خطای واحد است.
برای اصلاح رفتار سیستم، این پنج تغییر را به ترتیب اجرا کنید:
۱. برای هر شکست، یک کد پایدار تعریف کنید که کد وضعیت HTTP نباشد.
۲. بهجای واداشتن مدل به استنتاج، یک پرچم صریح retryable اضافه کنید.
۳. اقدام جایگزین را در متن پیام نام ببرید.
۴. هر چیزی که retryable علامتگذاری شده را واقعاً Idempotent کنید.
۵. در هر شکل از پاسخ، شکست را از «خالی بودن» متمایز کنید.
این تغییر دیدگاه، هزینهٔ شکست را تغییر میدهد. از دست دادن ۳۰ ثانیه توسط یک انسان به دلیل خطای بد، یک مزاحمت است؛ اما سوزاندن پنجرهٔ زمینه (Context Window) — یعنی همان میز کاری که مدل برای پردازش متن در اختیار دارد — یا انجام یک اقدام بهظاهر درست اما غلط توسط یک عامل که تا مدتها کسی متوجه آن نمیشود، یک ریسک سیستمیک است.
سطح ابزارهای فعلی خود را بررسی کنید. اگر خطاهای شما برای توسعهدهندهای نوشته شده که لاگ میخواند، در واقع دارید به عامل هوش مصنوعی خود نقشهای با برچسبهای غلط میدهید.
گام بعدی شما
- تمام پاسخهای خطای APIهای خود را بازبینی کنید و هر کجا که از کدهای HTTP بهتنهایی استفاده شده، یک کد خطای متنی (String) اختصاصی اضافه کنید.
- فیلد
retryableرا به ساختار پاسخهای ابزارهای خود اضافه کنید تا مدل را از حدس زدن رها کنید. - در پیامهای خطا، نام ابزار جایگزین را ذکر کنید تا مدل را به جای بنبست، به مسیر درست هدایت کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو