چرا اجرای ایجنت مدل درخواست-پاسخ را میشکند
بیشتر فریمورکهای ایجنت حول یک حلقه درخواست-پاسخ ساخته شدهاند: ارسال یک prompt، دریافت یک completion، افزودن به یک بافر، تکرار. این مدل برای تعاملات کوتاه کار میکند. برای اجراهایی که ساعتها طول میکشند، سیستمهای خارجی را فراخوانی میکنند، منتظر ورودی انسانی میمانند و پیش از وقفه بهطور جزئی کامل میشوند، شکست میخورد.
یک ایجنت طولانیمدت یک inference واحد نیست. یک workflow است: دنبالهای از تصمیمها، tool callها، انتظارها و نوشتن در سیستمهای خارجی. هر گام میتواند مستقلاً شکست بخورد. فرایند میتواند با یک deploy rollout دوباره راهاندازی شود. یک provider میتواند در میانه فراخوانی timeout بخورد. یک worker میتواند evict شود. وقتی هر یک از اینها رخ دهد، اجرا در وضعیت ناسازگار رها میشود: برخی اثرات جانبی اعمال شدهاند، برخی نه، و بافر مکالمهای که طرح را نگه میداشت از بین رفته است.
بدون مکانیزم بازپخش، تنها استراتژی بازیابی شروع از نو است. شروع از نو هر نوشتن خارجی که قبلاً موفق شده را تکرار میکند. این یک نگرانی نظری نیست. رفتار پیشفرض بیشتر استکهای ایجنت امروز است و دلیل اینکه تیمها در نهایت اسکریپتهای آشتی ad-hoc مینویسند یا وضعیت را پس از crash بهصورت دستی ترمیم میکنند همین است.
دو خاصیتی که اهمیت دارند
اجرای بادوام برای ایجنتها بر دو خاصیت استوار است. هر دو در سیستمهای توزیعشده بهخوبی شناخته شدهاند. هیچکدام در فریمورکهای ایجنت استاندارد نیستند.
بازپخش قطعی
با داشتن همان تصمیمها و نتایج ابزار ثبتشده، اجرا باید از همان نقطه از سر گرفته شود. این یعنی هر ورودی غیرقطعی — خروجی مدل، پاسخ ابزار، خواندن ساعت، seed تصادفی — در یک log فقط-افزودنی ثبت میشود. در راهاندازی مجدد، runtime لاگ را بازپخش میکند، بخشهای قطعی workflow را دوباره اجرا میکند و اثرات جانبی ثبتشده را رد میکند. اجرا از همان گامی که فرایند در حال انجام آن مرده بود ادامه مییابد.
این با checkpoint گرفتن از یک مکالمه یکسان نیست. بافر مکالمه یک خلاصه با اتلاف اطلاعات است. log بازپخش یک رکورد دقیق از آنچه اجرا تصمیم گرفت و آنچه مشاهده کرد است. تفاوت زمانی مهم میشود که یک گام به نتیجه ابزاری وابسته باشد که دیگر قابل بازیابی نیست، یا وقتی خروجی مدل در گام هفتم تعیین میکند گام هشتم کدام شاخه را میرود.
اثرات خودتوان
هر نوشتن خارجی باید کلیدی حمل کند که تلاش تکراری را به یک no-op تبدیل کند. اگر اجرا یک payment API را فراخوانی میکند، فراخوانی شامل یک idempotency key مشتقشده از run ID و step ID است. اگر API همان کلید را دو بار دریافت کند، نتیجه اصلی را برمیگرداند بهجای اینکه دوباره شارژ کند. اگر اجرا ایمیلی میفرستد، ارسال کلیدگذاری میشود تا retry نسخه تکراری تولید نکند. اگر اجرا در پایگاه دادهای مینویسد، نوشتن یک upsert کلیدگذاریشده بر run و step است.
خودتوانی خاصیت ایجنت نیست. خاصیت قرارداد میان ایجنت و هر سیستم خارجی است که لمس میکند. آن قرارداد باید طراحی، مستند و تست شود. نمیتوان آن را از یک prompt استنتاج کرد.

این کجا طراحی ایجنت را تغییر میدهد
وقتی بازپخش قطعی و اثرات خودتوان را بهعنوان الزامات میپذیرید، چندین تصمیم طراحی تغییر میکند.
طبقهبندی ابزار
Tool callها باید به سه سبد طبقهبندی شوند:
- خالص: بدون اثر خارجی، تکرار ایمن. خواندن یک فایل، محاسبه یک hash، قالببندی یک رشته.
- خودتوان: اثر خارجی، تکرار ایمن با همان کلید. Upsertها، فراخوانیهای API کلیدگذاریشده، ارسال پیام خودتوان.
- غیرقابل تکرار: اثر خارجی که نمیتوان با خیال راحت تکرار کرد. ارسال یک اعلان یکباره، اجرای یک معامله، حذف یک منبع بدون مسیر soft-delete.
کلاس سوم در بازپخش به تأیید انسانی نیاز دارد. اگر اجرا پس از تلاش برای یک اثر غیرقابل تکرار اما پیش از تأیید آن مرده باشد، runtime نمیتواند خودش تصمیم بگیرد که آیا retry کند. باید ابهام را آشکار کند و منتظر بماند. این یک هزینه واقعی است: یک گام human-in-the-loop به بازیابی اضافه میکند و یعنی برخی اجراها بدون مداخله نمیتوانند همگرا شوند. جایگزین — retry بیصدا — بدتر است.
وضعیت در یک مخزن بادوام
وضعیت ایجنت به یک مخزن بادوام با schema صریح تعلق دارد، نه به یک بافر مکالمه. Schema باید اجرا را توصیف کند: ورودیهایش، گام فعلیاش، نتایج گامهای کاملشده، گام در انتظار و بودجه مصرفشده تا این لحظه. مکالمه یک نما روی آن وضعیت است، نه خود وضعیت.
این یک پیامد عملی دارد: مدل میتواند تعویض شود، prompt میتواند بازنویسی شود و اجرا همچنان میتواند از سر گرفته شود. وضعیت به context window مدل گره نخورده است.
بودجه متصل به اجرا
اجرای بودجه به اجرای بادوام متصل میشود، نه به یک فرایند یا یک session. یک اجرای از سر گرفتهشده همچنان در برابر همان سقف میشمارد. اگر اجرا پیش از crash ۶۰ درصد بودجه token خود را مصرف کرده باشد، با ۴۰ درصد باقیمانده از سر گرفته میشود. این از حالت شکستی جلوگیری میکند که در آن حلقه crash-and-restart هزینه را بیصدا چند برا
مکانیزم ساده است: بودجه یک فیلد در رکورد اجرای بادوام است که در هر گام کاهش مییابد و پیش از تلاش برای اثر جانبی آن گام پایدار میشود. معاوضه این است که بررسیهای بودجه به هر گام یک نوشتن اضافه میکنند که latency و هزینه ذخیرهسازی را افزایش میدهد. برای اجراهایی که گامهای کوچک زیادی دارند، این سربار قابل اندازهگیری است. جایگزین — از دست دادن وضعیت بودجه در crash — بدتر است.

پیامدهای ارزیابی
اجرای بادوام نحوه تست سیستمهای ایجنت را تغییر میدهد.
تزریق crash بهعنوان تست استاندارد
مفیدترین تست تزریق crash است: worker را در هر گام بکشید و تأیید کنید که اجرا به همان وضعیت پایانی همگرا میشود. این یک unit test نیست. یک property test روی state machine اجرا است. باگهایی که اهمیت دارند را میگیرد: گامهایی که خودتوان نیستند، وضعیتی که پیش از یک اثر جانبی پایدار نشده، مسیرهای بازپخشی که از اجرای اصلی واگرا میشوند.
یک harness تزریق crash به راهی برای کشتن worker در یک گام مشخص نیاز دارد. این یعنی runtime باید مرزهای گام را بهعنوان hook آشکار کند. ساختن آن hookها کار است. نادیده گرفتنشان یعنی حالتهای شکست فقط در production ظاهر میشوند.
logهای بازپخش بهعنوان fixtureهای ارزیابی
یک log بازپخش رکورد یک مسیر واقعی است: ورودیها، تصمیمها، نتایج ابزار، اثرات جانبی. آن رکورد میتواند بهعنوان یک fixture ارزیابی استفاده شود. یک تیم میتواند یک مسیر را در برابر یک مدل جدید دوباره اجرا کند بدون دوباره اجرا کردن اثرات جانبی. تصمیمهای مدل با تصمیمهای ثبتشده مقایسه میشوند و تفاوتها تحلیل میشوند.
این یک تغییر قابل توجه است. امروز، ارزیابی یک مدل جدید روی وظایف ایجنت معمولاً یعنی دوباره اجرا کردن وظیفه بهصورت end-to-end، که یعنی دوباره اجرا کردن اثرات جانبی، که یعنی یا استفاده از sandbox یا پذیرش هزینه. logهای بازپخش به تیمها اجازه میدهند کیفیت تصمیم را از اجرای اثر جدا کنند. معاوضه این است که logهای بازپخش تنها بهخوبی ضبط هستند: اگر اجرای اصلی به روشهایی غیرقطعی بود که log ثبت نکرده، بازپخش به دلایلی واگرا میشود که هیچ ربطی به مدل ندارند.
اول چه چیزی بسازیم
ترتیب مهم است. با log بازپخش شروع کنید: یک رکورد فقط-افزودنی از تصمیمها، نتایج ابزار و مرزهای گام. سپس هر نوشتن خارجی را با کلیدی مشتقشده از run و step خودتوان کنید. سپس ابزارها را طبقهبندی کنید و برای کلاس غیرقابل تکرار تأیید انسانی اضافه کنید. سپس بودجه را به رکورد اجرا متصل کنید. سپس harness تزریق crash را بسازید.
هر گام مستقلاً مفید است. log بازپخش بهتنهایی debugging را ممکن میکند. نوشتنهای خودتوان بهتنهایی از اثرات جانبی تکراری جلوگیری میکنند. سیستم کامل، ایجنتهای طولانیمدت را قابل بهرهبرداری میکند.
حلقه prompt بخش دشوار نیست. بخش دشوار زیرساختی است که به یک اجرا اجازه میدهد از فرایندی که آن را شروع کرد جان سالم به در ببرد.
