رفتن به محتوا

چرخهٔ عمر اعزام عامل

اعزام، یک اجرا از یک عامل، در یک گفتگو و روی یک آداپتور است. همه‌چیزِ آن روی cc_server رخ می‌دهد: کلاینت پیامی می‌فرستد و سپس جریان رویدادهایی را که بازمی‌گردند تماشا می‌کند. هیچ مسیر اعزامی در هیچ کلاینتی وجود ندارد.

عامل‌ها به شش دلیل بیدار می‌شوند و اجرا این دلایل را به‌صورت بافت بیدارشدن حمل می‌کند تا عامل بداند چرا بیدار شده است:

دلیل بیدارشدن محرک
userMessage کسی در یک فضا پیامی فرستاد، یا عامل را منشن کرد
assignment یک تیکت به عامل واگذار شد
pipelineStep یک گام پایپ‌لاین آن را اعزام کرد
childCompleted یک تیکت فرزند تمام شد
followUp یک ضربان پیگیری زمان‌بندی‌شده
recovery مسیر بازیابی پس از یک شکست دوباره اعزام کرد

بافت بیدارشدن شناسه‌هایی را که همراه دلیل‌اند حمل می‌کند — تیکت، فضا، پیام، اجرای پایپ‌لاین — و این‌ها در محیط و پرامپت عامل تزریق می‌شوند.

پرامپت لایه‌به‌لایه ساخته می‌شود و هر لایه یک بخش برچسب‌دار است:

  • هویت — شناسهٔ خودِ عامل، نام و شناسهٔ فضای کاری، همراه با دستوری صریح برای سپردن همان UUIDها به ابزارهای محدود به فضای کاری. فضای کاری ذاتیِ عامل است، پس همیشه حاضر است.
  • فهرست ابزارها و پروتکل‌های منابع، سپس چیدمان فضای کاری.
  • بازنویسی پرامپت سیستم، اگر عامل یکی داشته باشد، سپس شخصیت (پارامتری‌شده بر اساس نقش) و شرح مختصر تیم.
  • مهارت‌ها — نام‌های روی عامل.
  • قرارداد و رویهٔ اجرا برای حالت جاری.
  • بافت بیدارشدن و بافت منشن: چه کسی عامل را فراخواند و فهرست حاضران گرد آن.
  • بلوک حالت — از نمایهٔ قابلیت‌های حالت و فهرست ابزارهای مادی‌سازی‌شده‌اش تولید می‌شود، تا پرامپت هرگز ابزاری را نام نبرد که اجرا در واقع آن را ندارد.
  • بافت حافظه — واقعیت‌های بازیابی‌شده برای کار.
  • بافت گفتگو — تاریخچهٔ اخیر فضا، محدود به پنجرهٔ عین‌به‌عین 50,000 کاراکتری، بدون توجه به اندازهٔ بافت پیکربندی‌شدهٔ عامل. تاریخچهٔ قدیمی‌تر بریده نمی‌شود: عامل آن را به‌صورت درخواستی با get_messages بازیابی می‌کند و پیام‌های قدیمی‌ترِ مرتبط از نظر معنایی همچنان پدیدار می‌شوند.

دستورات میان دو جا تقسیم شده‌اند. /todo و /compact در جعبهٔ پیام رهگیری می‌شوند و عمداً هرگز به‌عنوان پیام ذخیره نمی‌شوند یا به عامل فرستاده نمی‌شوند — ذخیرهٔ /compact برش فشرده‌سازی را یک نوبت عقب می‌برد و رونوشت را آلوده می‌کند. /plan در هر دو جا رسیدگی می‌شود: جعبهٔ پیام حالت فضا را تغییر می‌دهد و همین است که سطح ابزار، پیش‌تنظیم گاردریل و پرامپت را برای این نوبت و هر نوبت پس از آن هماهنگ می‌کند. /goal، /loop و /skill:<name> درون خود اجرا و تنها روی هارنس داخلی رسیدگی می‌شوند — /goal و /loop کار خودمختار را با یک بودجهٔ هزینه آغاز می‌کنند، و نامی با پیشوند skill: به‌عنوان مهارتی جست‌وجو می‌شود که بدنهٔ SKILL.md آن به دستور این نوبت تبدیل می‌شود. همین فضای نام است که دو واژگان را از هم جدا نگه می‌دارد: تطبیق یک نام برهنه با مهارت‌ها باعث می‌شد مهارتی به نام plan یا compact برای همیشه زیر سایهٔ دستور توکار برود. یک نام برهنهٔ غیرتوکار همچنان تطبیق داده می‌شود، برای پیام‌هایی که پیش از وجود فضای نام نوشته شده‌اند. نامی که شناخته نشود صرفاً به‌عنوان متن ساده رد می‌شود.

پیش از آنکه چیزی اجرا شود، گفتگو ریشهٔ کاری خودش را می‌گیرد، با یک worktree جداگانه و copy-on-write به‌ازای هر مخزن در زیر آن. به‌طور پیش‌فرض این شامل هر مخزنی است که به فضای کاری پیوند خورده؛ یک فضای PR — یا فضایی که با یک انتخاب صریح ساخته شده است — تنها مخزن‌های مجاز را تأمین می‌کند و یک گفتگوی بازبینی PR تک‌مخزنش را به head ref مربوط به pull request سنجاق می‌کند، در حالی که بقیه روی شاخهٔ پیش‌فرض خود می‌مانند. checkout منبع هرگز تغییر نمی‌کند و کل پوشهٔ گفتگو هنگام حذف فضا پس گرفته می‌شود.

copy-on-write تنها پیاده‌سازی است هر جا rift عرضه شود، که کتابخانهٔ native بسته‌بندی‌شده پشتیبان آن است. فایل‌سیستمی که نتواند reflink کند — یا پوشهٔ داده‌ای روی حجمی متفاوت از مخزن — تأمین را با شکست مواجه می‌کند، به‌جای آنکه عقب‌نشینی کند. git worktree بک‌اند روی Windows است، جایی که بک‌اند copy-on-write برای ساختن وجود ندارد، و روی میزبان‌های دیگر تنزلی خاموش نیست. نبودِ rift روی سکویی که آن را عرضه می‌کند یعنی نصبی خراب — سرور از راه‌اندازی سر باز می‌زند به‌جای آنکه بی‌صدا عقب‌نشینی کند، چون عقب‌نشینی خاموش، خرابی را برای همیشه پشت مسیری کندتر پنهان می‌کند.

دو هویت به‌ازای هر اجرا و پیش از آنکه هر فرایندی راه‌اندازی شود تعیین می‌شوند.

مؤلف‌بودن Git. کامیت‌هایی که عامل می‌سازد به نام خودِ عامل مؤلف می‌شوند — <Name> (agent) <agentId@agents.control-center.local> در هر دو GIT_AUTHOR_* و GIT_COMMITTER_* — و هرگز جعل هویت یک انسان نمی‌کنند. بخش محلی همان UUID عامل است، نه slug نامش. انسان درخواست‌کننده با یک trailer از نوع Co-Authored-By: اعتبار می‌گیرد که در محیط حمل می‌شود و در پرامپت سیستم به آن دستور داده می‌شود. هویتی که فراخوان به‌صراحت تعیین کرده باشد همیشه بر هر دو پیروز می‌شود.

هویت میزبان کد. اعتبارنامهٔ GitHub اجرا همیشه فقط از آنِ کارگزار است — یک توکن نصب App محدود به مخزن، یا fallback مربوط به PAT محیطی آن — و هرگز توکن ذخیره‌شدهٔ خودِ عضو درخواست‌کننده نیست. actingUserId بازهٔ fallback مربوط به PAT را محدود می‌کند تا عضوی که مالک سرور نیست دامنهٔ سرور را به ارث نبرد؛ اما یک توکن شخصی را روی محیط عامل نمی‌گذارد.

اعتبارنامه‌های ارائه‌دهندهٔ مدل به ترتیبی ثابت تعیین می‌شوند و این ترتیب مهم است:

  1. یک بازنویسی محیطی به‌ازای هر آداپتور،
  2. محیط فراخوان،
  3. مخزن اعتبارنامهٔ سرور (با تازه‌سازی توکن OAuth اگر نزدیک انقضا باشد)،
  4. محیط فرایند.

چون بازنویسی هر آداپتور اول می‌آید، بی‌صدا بر حساب ارائه‌دهنده‌ای که در برنامه متصل شده چیره می‌شود. اگر اجرایی به حساب اشتباه صورت‌حساب می‌شود، همان بازنویسی نخستین جایی است که باید نگاه کنید. اعتبارنامه‌های ارائه‌دهنده در سطح میزبان‌اند — میان همهٔ فضاهای کاری روی این نصب مشترک‌اند.

توکن GitHub اجرا از کارگزار اعتبارنامه می‌آید: یک توکن فقط‌خواندنی محدود به مخزن worktree. پوش‌ها از دروازهٔ اجرای عامل می‌گذرند، که پیش از عبور دادن از قاعدهٔ پوش به یک ریموت می‌پرسد، و هیچ کلید تیکتینگی تزریق نمی‌شود. به سندباکس و امنیت رجوع کنید.

اجرا با worktree خودش به‌عنوان پوشهٔ کاری راه‌اندازی می‌شود و — برای Claude Code — با یک پیکربندی MCP تولیدشده تا عامل بتواند به سطح ابزار Control Center برسد. ACP و bash هارنس محیطی پاک‌سازی‌شده می‌گیرند؛ Claude Code از مسیر NoSandboxAdapter محیط میزبان را به ارث می‌برد.

اجرا یک توالی رویدادِ نوع‌دار را بازمی‌گرداند؛ پردازشگر جریان آن را به‌صورت دلتاهای پیام ذخیره می‌کند، در قطعه‌های رونوشت می‌تاکند و به رویدادهای پیام‌رسانی تبدیل می‌کند:

رویداد آنچه حمل می‌کند
thinking خروجی استدلال، که درون‌خط رندر می‌شود
text خروجی دیدنی
toolCall یک فراخوانی ابزار، با شناسه و آرگومان‌های ساخت‌یافته‌اش
toolResult نتیجهٔ متناظر، علامت‌خورده وقتی که خطا داده باشد یا جریانی ناقص باشد
usage شمارش توکن‌ها و هزینه برای یک پنجرهٔ اندازه‌گیری
error یک خطا، همراه با کد ساخت‌یافتهٔ آداپتور وقتی آن را گزارش کرده باشد
sandboxViolation سیستم‌عامل یک فراخوان سیستمی را رد کرد. تنها جایی منتشر می‌شود که سندباکسی واقعاً فرایند را می‌پیچد — یادداشت بالا را ببینید
debug عیب‌یابی زمان اجرا (نشانگرهای راه‌اندازی، تصمیم‌های گاردریل، تلنگرهای قرارداد)
done اجرا تمام شد

عمداً هیچ سقف نوبتی در هیچ جا وجود ندارد. یک شمارش تکرار سخت، کار طولانی مشروع (مهاجرت‌ها، پاک‌سازی پس از انشعاب) را از پا درمی‌آورد و چیزی را نمی‌گیرد که گاردریل‌های پایین زودتر نمی‌گیرند. یک اجرا پایان می‌یابد وقتی:

  • مدل خودش متوقف می‌شود؛
  • یک انسان آن را متوقف می‌کند، یا گفتگو را تصاحب می‌کند (که هارنس داخلی را در نخستین مرز نوبتِ تمیز متوقف می‌کند — اجرای Claude Code که مرز ایمنی ندارد، به‌جای آن متوقف می‌شود)؛
  • نگهبان سکوت فعال می‌شود. تا آستانه خروجی‌ای نمی‌آید و نشست با شکست silentRun پایان داده می‌شود. پیش‌فرض 15 دقیقه است و در حالت‌های عمدتاً خواندنی (بازبینی و برنامه) به 10 کاهش می‌یابد؛ عامل می‌تواند آن را میان 1 و 240 دقیقه بازنویسی کند. نگهبان وقتی مسلح می‌شود که یک فرایند فرزند PID خود را گزارش دهد، پس CLI مربوط به Claude Code را پوشش می‌دهد — نه هارنس داخلی را، که چیزی نمی‌سازد؛
  • سقف هزینه به کار می‌افتد — اما فقط برای دستورات خودمختار.

گاردریل حلقهٔ مرگ پایان‌دهنده نیست: سه فراخوانی ابزار یکسان پشت سر هم، و حلقه عامل را هدایت می‌کند تا رویکردش را تغییر دهد.

روی هم رفته، یک نوبت تعاملی معمولی روی هارنس داخلی نه سقف نوبت دارد، نه سقف هزینه و نه نگهبان سکوت. وقتی مدل متوقف شود یا شما متوقفش کنید پایان می‌یابد. این معامله‌ای آگاهانه است — چون گزینه‌های دیگر همه کار طولانی مشروع را از پا درمی‌آورند — اما یعنی یک اجرای تعاملی بی‌سرپرست با چیزی جز قضاوت خودِ مدل محدود نمی‌شود.

در میانهٔ اجرا، پیامی که به عامل در حال اجرا فرستاده شود به‌عنوان هدایت در صف می‌نشیند و در مرز نوبت بعدی تخلیه می‌شود، پس می‌توانید اجرا را بدون کشتنش به‌آرامی هدایت کنید. هدایت، مکث، زیرعامل‌ها و قرارداد تکمیل ویژگی‌های هارنس داخلی‌اند؛ Claude Code هیچ‌یک از آن‌ها را ندارد.

هر اعزام یک AgentRunLog می‌نویسد: زمان شروع و پایان، خلاصه، شمارش توکن‌ها و هزینهٔ تخمینی (شامل هزینهٔ تجمیع‌شده از زیرعامل‌ها)، آداپتور و مدل، شناسهٔ فرایند و مسیر گزارش، گفتگو و هر تیکت یا اجرای پایپ‌لاینی که به آن تعلق دارد، به‌علاوه دو فیلد طبقه‌بندی که بیشترِ حقیقت را حمل می‌کنند.

  • وضعیت درشت است: pending، running، completed، error.
  • زنده‌مانی خوانش ریزتر است: alive، productive، completed، blocked، empty، looping، failed، stalled، dead.
  • خانوادهٔ خطا شکست‌ها را به‌شکلی سودمند گروه‌بندی می‌کند: transientUpstream، sandboxInfrastructure، budgetExceeded، processLost، silentRun، unknown.

بستن اجرا AgentRunCompleted را منتشر می‌کند و چیزهای زیادی به آن آویزان‌اند: تجمیع هزینهٔ پایپ‌لاین، ازسرگیری گامی از پایپ‌لاین که منتظر اجرا بود، کامل‌کردن تیکت پشتیبان، نگاشت یک ارکستراسیون به یک حالت پایانی، تصمیم ادامه-یا-توقفِ سرپرست هدف، نقش بررسی‌کننده و ارزیابی بودجهٔ پس از اجرا. فهرست حضور جداگانه دنبال می‌شود، با تماشای گزارش‌های اجرای اخیر به‌جای رویداد. اعلان «عامل تمام کرد» در این فهرست نیست: رویداد وقتی شلیک می‌شود که جریان فرایند تمام شود، پیش از نوشته‌شدن پاسخ؛ پس اعلان همان MessageReceived آن نوبت است که پس از ذخیره‌شدن پیام منتشر می‌شود.

اجرای بودجه یک واکنش است، نه یک دروازه. وقتی اجرایی کامل می‌شود، ارزیاب هزینهٔ عامل را از ابتدای ماه با سقف مؤثرش مقایسه می‌کند (یک سیاست بودجهٔ پیوندخورده اگر وجود داشته باشد، وگرنه رقم ماهانهٔ خود عامل؛ صفر یعنی بی‌انتها). عبور از آستانهٔ نرم یک رخداد هشدار ثبت می‌کند؛ تمام‌کردن بودجه یک رخداد سخت ثبت می‌کند و وضعیت چرخهٔ عمر عامل را روی نمودار سازمانی به paused برمی‌گرداند. اعزام همچنان کار را از آن عامل می‌پذیرد. یک تریگر پایپ‌لاین از نوع BudgetThresholdCrossed می‌تواند کار پیگیری را از همان رویداد آغاز کند.

هر گزارش اجرا می‌تواند تبار تلاش دوباره را حمل کند — شناسهٔ اجرای والد، یک شمارندهٔ تلاش و خلاصهٔ ادامه. درخت اجرای گفتگو شمارهٔ تلاش را وقتی غیرصفر باشد رندر می‌کند. یک «دوباره تلاش کن» از گفتگو، اعزامی تازه با گزارشی نو است.