نشانه و تشخیص نادرست
یک حادثه ازدحام بهندرت با برچسب مسئله زمانبندی میرسد. بهشکل یک مسئله استدلال میرسد. یک ایجنت حلقه میزند، دو ایجنت با هم تناقض پیدا میکنند، سه ایجنت همان artefact را از promptهای کمی متفاوت تولید میکنند، و postmortem بهسمت مدل میرود: planner بیش از حد مبهم بود، sub-agent دستوراتش را بد فهمید، critic بهقدر کافی انتقادی نبود.
بررسی trace معمولاً داستان دیگری میگوید. حلقه در پاییندست یک صف قرار دارد. یک tool call — یک جستوجوی کند، یک API با rate limit، یک دیتابیسی که تصمیم گرفته فلسفی باشد — بیشتر از timeout فراخوان طول میکشد. فراخوان retry میکند. چون retry یک task تازه است و نه re-claim همان واحد کار، دوباره fan-out میشود. ایجنتهای خواهری که منتظر همان نتیجه بالادستی بودند حالا خودشان هم timeout میخورند و retry میکنند. هر retry همان context را حمل میکند، پس هزینه توکن حادثه بهصورت فوقخطی رشد میکند در حالی که کار واقعی اصلاً رشد نمیکند.
کیفیت استدلال در این traceها غالباً کافی است. ایجنتها خروجی میانی معقولی تولید میکنند؛ فقط آن را چند بار، به ترتیب اشتباه، و بر پایه فرضهای کهنه تولید میکنند. آنچه غایب است یک planner بهتر نیست. مکانیزمی برای shed یا تعویق بار است.
این تمایز مهم است چون دو تشخیص، راهحلهای متضاد را ایجاب میکنند. اگر مسئله استدلال باشد، راهحل کار روی prompt و انتخاب مدل است. اگر مسئله صفبندی باشد، راهحل معماری است: admission control، backpressure، idempotency، و یک scheduler که محدودیتهای خودش را میشناسد.
admission control بهعنوان نخستین تصمیم طراحی
هر یال fan-out در یک task graph به دو عدد اعلامشده نیاز دارد: یک عرض بیشینه و یک عمق بیشینه. عرض یعنی چند فرزند میتوانند همزمان در جریان باشند. عمق یعنی چند نسل تفویض مجاز است پیش از آنکه graph باید نتیجه برگرداند. هر دو ثابتهای زمان طراحی هستند، در مشخصات graph اعلام میشوند، نه اینکه در production با تماشای صورتحساب کشف شوند.
یک قاعده admission مفید، عطفی است: یک task تنها زمانی پذیرفته میشود که هم یک worker slot و هم یک budget slice در دسترس باشد. worker slotها همزمانی را محدود میکنند. budget sliceها هزینه و مواجهه با زمان دیواری را محدود میکنند. اگر هرکدام تمام شده باشد، task شروع نمیشود. با یک deadline در صف قرار میگیرد، یا با دلیل ثبتشده حذف میشود.

حذف یک نتیجه مشروع است. دیسپچری که نمیتواند کار را رد کند، دیسپچر نیست؛ یک قیف است. گسترش بیکران و بیصدا حالت شکست است، و دقیقاً به این دلیل بیصداست که هیچچیز در سیستم اجازه ندارد نه بگوید.
یک مکانیزم مشخص: وضعیت ارکستراتور را بهجای یک گفتوگو، بهصورت یک دفتر کل بازنمایی کنید. هر task پذیرفتهشده یک lease نگه میدارد — (task_id, worker_slot, budget_slice, deadline). lease در زمان تکمیل، در شکست صریح، یا در انقضا آزاد میشود. کار scheduler در هر tick تطبیق taskهای در صف با leaseهای آزاد است. این کار بیزرقوبرق است و تمام بازی همین است.
backpressure میان ایجنتها
sub-agentها باید بتوانند «الان نه» را علامت بدهند، بهجای اینکه کار را جذب کنند و دیر شکست بخورند. شکست دیرهنگام گران است: کار پیشتر context، توکن و زمان مصرف کرده، و شکست در نقطهای ظاهر میشود که والد اطلاعات لازم برای بازیابی ارزان را از دست داده است.
دو مکانیزم، رد کردن زودهنگام را ایمن میکنند.
نخست، یک صف مشترک با visibility timeout. یک task دقیقاً توسط یک worker برای یک بازه محدود claim میشود. اگر worker در آن بازه تکمیل نکند یا تمدید نکند، claim منقضی میشود و task دوباره قابل مشاهده میشود. این جلوگیری میکند از اینکه دو ایجنت همان واحد کار را claim کنند، که شایعترین منبع خروجی تکراری در ازدحامهایی است که بهنظر میرسد با هم تناقض دارند. آنها اختلاف نظر ندارند؛ هر دو یک کار را انجام میدهند.
دوم، idempotency key روی هر عملیات دارای side effect. وقتی یک retry همان کلید تلاش اصلی را حمل میکند، سیستم پاییندستی میتواند deduplicate کند بهجای اجرای دوباره. این همان چیزی است که اجازه میدهد backpressure تهاجمی باشد. بدون idempotency، رد کردن و retry تهاجمی خطرناک است، چون یک retry ممکن است دوباره بنویسد. با idempotency، رد کردن کار ارزان و ایمن است، و سیستم میتواند طوری تنظیم شود که بار را زود shed کند بهجای جذب و فروپاشی.
این trade-off واقعی است: idempotency نیازمند همکاری از هر tool و هر سرویس خارجی در graph است، و بعضی از آنها این را فراهم نمیکنند. جایی که فراهم نمیکنند، دیسپچر باید یا دسترسی به آن tool را سریال کند یا semantics حداکثر-یکبار را بپذیرد و شکافها را صریحاً مدیریت کند. تظاهر به اینکه مسئله وجود ندارد، روشی است که یک ازدحام دستهای از باگ پیدا میکند که فقط زیر
چهار اندازهگیری بهازای هر نوع task بیشتر زمین تشخیصی را پوشش میدهد:

- عمق صف — چه مقدار کار در انتظار است، نمونهبرداریشده در طول زمان.
- زمان انتظار — تأخیر admission، از enqueue تا اعطای lease.
- زمان claim تا تکمیل — تأخیر اجرا، جدا از زمان انتظار تا بتوان این دو را به هم نسبت داد.
- ضریب تقویت retry — کل تلاشها تقسیم بر واحدهای متمایز کار.
ضریب تقویت، تکسیگنال مفیدتر است. وقتی نزدیک یک است، سیستم هر تکه کار را تقریباً یک بار انجام میدهد. وقتی بالا میرود، ازدحام ظرفیتش را صرف بازتولید نتایجی میکند که از قبل داشته، و هیچ مقدار پالایش prompt آن را پایین نمیآورد.
یک اجرا باید بهصورت یک timeline از admissionها بازسازیپذیر باشد — task در صف قرار گرفت، lease اعطا شد، lease تمدید شد، lease منقضی شد، task تکمیل شد، task به dead-letter رفت — نه بهصورت فهرستی از پیامها. logهای پیام میگویند ایجنتها چه گفتند. timelineهای admission میگویند سیستم چه کرد، و فقط دومی یک حادثه را توضیح میدهد.
روی ضریب تقویت alert بگذارید، نه روی تعداد ایجنت. تعداد ایجنت یک معیار نمایشی است که هم در یک انفجار سالم و هم در یک شکست آبشاری بهنرمی بالا میرود. ضریب تقویت این دو را از هم متمایز میکند.
یک قاعده طراحی
با ارکستراتور مثل یک دیسپچر با سه جزء رفتار کنید: یک scheduler، یک دفتر کل بودجه، و یک مسیر dead-letter. scheduler مالک admission است. دفتر کل مالک حسابداری هزینه و همزمانی است. مسیر dead-letter مالک هر چیزی است که retryهایش تمام شده، تا شکستها قابل مشاهده و قابل بررسی باشند بهجای اینکه بیصدا تا ابد دوباره در صف قرار بگیرند.
خروجی planner باید یک task graph با عرضها و عمقهای اعلامشده باشد، نه تفویض آزاد. plannerی که نثری مثل «بازار را بررسی کن و یافتهها را ترکیب کن» تولید میکند، یک آرزو تولید کرده، نه یک graph. plannerی که nodeهایی با fan-out کراندار، وابستگیهای صریح، و سقف بودجه بهازای هر node تولید میکند، چیزی تولید کرده که یک دیسپچر میتواند اجرا کند و یک دفتر کل بودجه میتواند اعمال کند.
ازدحام را روی throughput و تکمیل زیر بار اندازه بگیرید، نه روی ظرافت گفتوگویش. کیفیت گفتوگو یک proxy است که در demoها با موفقیت همبستگی دارد و در production از آن جدا میشود. throughput زیر یک بودجه همزمانی ثابت، نرخ تکمیل در یک deadline ثابت، و ضریب تقویت اعدادی هستند که پیشبینی میکنند سیستم با ترافیک واقعی دوام میآورد یا نه.
پیامد ناخوشایند این است که ازدحامی با استدلال متوسط و زمانبندی عالی، از ازدحامی با استدلال عالی و بدون admission control بهتر عمل میکند، چون دومی ظرفیتش را صرف دوباره انجام دادن کاری میکند که قبلاً انجام داده. زمانبندی بخش غیرجذاب سیستم نیست. بخشی است که تعیین میکند آیا بخش جذاب اصلاً فرصت اجرا پیدا میکند یا نه.
