Messages and delivery
صف فرمان
ما اجراهای پاسخ خودکار ورودی (همه کانالها) را از طریق یک صف کوچک درونفرایندی سریالسازی میکنیم تا از برخورد چند اجرای عامل جلوگیری شود، در حالی که همچنان همزمانی امن در سراسر نشستها مجاز است.
چرا
- اجراهای پاسخ خودکار میتوانند پرهزینه باشند (فراخوانیهای LLM) و وقتی چند پیام ورودی با فاصله کم میرسند، ممکن است با هم برخورد کنند.
- سریالسازی از رقابت بر سر منابع مشترک (فایلهای نشست، لاگها، stdin مربوط به CLI) جلوگیری میکند و احتمال رسیدن به محدودیتهای نرخ بالادستی را کاهش میدهد.
نحوه کارکرد
- یک صف FIFO آگاه از lane هر lane را با سقف همزمانی قابل پیکربندی تخلیه میکند (پیشفرض 1 برای laneهای پیکربندینشده؛ مقدار پیشفرض main برابر 4 و subagent برابر 8 است).
runEmbeddedAgentبر اساس کلید نشست در صف قرار میدهد (lanesession:<key>) تا تضمین کند برای هر نشست فقط یک اجرای فعال وجود دارد.- سپس هر اجرای نشست در یک lane سراسری (
mainبهطور پیشفرض) صفبندی میشود تا همزمانی کلی توسطagents.defaults.maxConcurrentمحدود شود. - وقتی لاگبرداری پرجزئیات فعال باشد، اجراهای صفشده اگر پیش از شروع بیش از حدود 2 ثانیه منتظر مانده باشند، یک اعلان کوتاه صادر میکنند.
- نشانگرهای تایپ همچنان بلافاصله هنگام قرار گرفتن در صف فعال میشوند (وقتی کانال پشتیبانی کند)، بنابراین تجربه کاربر در مدت انتظار برای نوبت تغییری نمیکند.
پیشفرضها
وقتی تنظیم نشده باشد، همه سطحهای کانال ورودی از این موارد استفاده میکنند:
mode: "steer"debounceMs: 500cap: 20drop: "summarize"
هدایت در همان نوبت پیشفرض است. پرامپتی که میان اجرای فعال میرسد، وقتی اجرا بتواند هدایت را بپذیرد، به runtime فعال تزریق میشود؛ بنابراین اجرای نشست دومی شروع نمیشود. اگر اجرای فعال نتواند هدایت را بپذیرد، OpenClaw منتظر میماند تا اجرای فعال تمام شود و سپس پرامپت را شروع میکند.
حالتهای صف
/queue کنترل میکند وقتی یک نشست از قبل اجرای فعال دارد، پیامهای ورودی عادی چه کنند:
steer: پیامها را به runtime فعال تزریق میکند. OpenClaw همه پیامهای هدایت معلق را پس از پایان اجرای فراخوانیهای ابزار در نوبت فعلی دستیار و پیش از فراخوانی بعدی LLM تحویل میدهد؛ app-server مربوط به Codex یکturn/steerدستهای دریافت میکند. اگر اجرا فعالانه در حال streaming نباشد یا هدایت در دسترس نباشد، OpenClaw تا پایان اجرای فعال منتظر میماند و سپس پرامپت را شروع میکند.followup: هدایت نکن. هر پیام را برای یک نوبت بعدی عامل پس از پایان اجرای فعلی در صف قرار بده.collect: هدایت نکن. پیامهای صفشده را پس از پنجره سکوت در یک نوبت پیگیری واحد ادغام کن. اگر پیامها کانالها/رشتههای متفاوتی را هدف بگیرند، برای حفظ مسیریابی جداگانه تخلیه میشوند.interrupt: اجرای فعال آن نشست را متوقف کن، سپس جدیدترین پیام را اجرا کن.
برای زمانبندی ویژه runtime و رفتار وابستگی، صف هدایت را ببینید. برای فرمان صریح /steer <message>، هدایت را ببینید.
از طریق messages.queue بهصورت سراسری یا برای هر کانال پیکربندی کنید:
{ messages: { queue: { mode: "steer", debounceMs: 500, cap: 20, drop: "summarize", byChannel: { discord: "collect" }, }, },}گزینههای صف
گزینهها روی تحویل صفشده اعمال میشوند. debounceMs همچنین پنجره سکوت هدایت
Codex را در حالت steer تنظیم میکند:
debounceMs: پنجره سکوت پیش از تخلیه پیگیریهای صفشده یا دستههای collect؛ در حالتsteerمربوط به Codex، پنجره سکوت پیش از ارسالturn/steerدستهای. اعداد بدون واحد میلیثانیه هستند؛ واحدهایms،s،m،hوdتوسط گزینههای/queueپذیرفته میشوند.cap: بیشینه پیامهای صفشده برای هر نشست. مقادیر کمتر از1نادیده گرفته میشوند.drop: "summarize": پیشفرض. ورودیهای قدیمیتر صف را در صورت نیاز حذف کن، خلاصههای فشرده را نگه دار و آنها را بهعنوان یک پرامپت پیگیری مصنوعی تزریق کن.drop: "old": ورودیهای قدیمیتر صف را در صورت نیاز حذف کن، بدون حفظ خلاصهها.drop: "new": وقتی صف از قبل پر است، جدیدترین پیام را رد کن.
پیشفرضها: debounceMs: 500، cap: 20، drop: summarize.
هدایت و streaming
وقتی streaming کانال partial یا block باشد، هدایت میتواند شبیه چند پاسخ
کوتاه قابل مشاهده به نظر برسد، در حالی که اجرای فعال به مرزهای runtime میرسد:
partial: پیشنمایش ممکن است زود نهایی شود، سپس پس از پذیرفته شدن هدایت، پیشنمایش جدیدی شروع میشود.block: بلوکهایی در اندازه پیشنویس میتوانند همان ظاهر ترتیبی را ایجاد کنند.- بدون streaming، وقتی runtime نتواند هدایت در همان نوبت را بپذیرد، هدایت پس از اجرای فعال به یک پیگیری تبدیل میشود.
steer ابزارهای در حال اجرا را لغو نمیکند. وقتی جدیدترین پیام باید اجرای
فعلی را لغو کند، از /queue interrupt استفاده کنید.
تقدم
برای انتخاب حالت، OpenClaw به این ترتیب حل میکند:
- بازنویسی درونخطی یا ذخیرهشده
/queueبرای هر نشست. messages.queue.byChannel.<channel>.messages.queue.mode.- مقدار پیشفرض
steer.
برای گزینهها، گزینههای درونخطی یا ذخیرهشده /queue بر پیکربندی مقدماند. سپس
debounce ویژه کانال (messages.queue.debounceMsByChannel)، پیشفرضهای debounce
مربوط به Plugin، گزینههای سراسری messages.queue و پیشفرضهای داخلی اعمال
میشوند. cap و drop گزینههای سراسری/نشست هستند، نه کلیدهای پیکربندی
برای هر کانال.
بازنویسیهای هر نشست
/queue <steer|followup|collect|interrupt>را بهعنوان یک فرمان مستقل بفرستید تا حالت صف برای نشست فعلی ذخیره شود.- گزینهها را میتوان ترکیب کرد:
/queue collect debounce:0.5s cap:25 drop:summarize /queue defaultیا/queue resetبازنویسی نشست را پاک میکند.
دامنه و تضمینها
- برای اجراهای عامل پاسخ خودکار در همه کانالهای ورودی که از خط لوله پاسخ Gateway استفاده میکنند اعمال میشود (وب WhatsApp، Telegram، Slack، Discord، Signal، iMessage، webchat و غیره).
- lane پیشفرض (
main) برای ورودی + Heartbeatهای اصلی در کل فرایند مشترک است؛ برای اجازه دادن به چند نشست بهصورت موازی،agents.defaults.maxConcurrentرا تنظیم کنید. - laneهای اضافی ممکن است وجود داشته باشند (برای مثال
cron،cron-nested،nested،subagent) تا کارهای پسزمینه بتوانند بدون مسدود کردن پاسخهای ورودی بهصورت موازی اجرا شوند. نوبتهای عامل cron ایزوله در حالی که اجرای عامل داخلی آنها ازcron-nestedاستفاده میکند، یک جایگاهcronرا نگه میدارند؛ هر دو ازcron.maxConcurrentRunsاستفاده میکنند. جریانهای مشترک غیر cron مربوط بهnestedرفتار lane خودشان را حفظ میکنند. این اجراهای جداشده بهعنوان وظایف پسزمینه ردیابی میشوند. - laneهای هر نشست تضمین میکنند که در هر زمان فقط یک اجرای عامل به یک نشست مشخص دست میزند.
- بدون وابستگی خارجی یا threadهای worker پسزمینه؛ فقط TypeScript + promiseها.
عیبیابی
- اگر فرمانها گیر کرده به نظر میرسند، لاگهای پرجزئیات را فعال کنید و برای تأیید اینکه صف در حال تخلیه است، دنبال خطوط "queued for ...ms" بگردید.
- اگر به عمق صف نیاز دارید، لاگهای پرجزئیات را فعال کنید و خطوط زمانبندی صف را دنبال کنید.
- اجراهای app-server مربوط به Codex که یک نوبت را میپذیرند و سپس دیگر پیشرفت منتشر نمیکنند، توسط adapter مربوط به Codex متوقف میشوند تا lane نشست فعال بهجای انتظار برای timeout اجرای بیرونی آزاد شود.
- وقتی diagnostics فعال باشد، نشستهایی که پس از
diagnostics.stuckSessionWarnMsهمچنان درprocessingمیمانند و هیچ پیشرفت مشاهدهشدهای در پاسخ، ابزار، وضعیت، بلوک یا ACP ندارند، بر اساس فعالیت فعلی طبقهبندی میشوند. کار فعال با عنوانsession.long_runningلاگ میشود؛ فراخوانیهای مدل بیصدای دارای مالک نیز تاdiagnostics.stuckSessionAbortMsدر وضعیتsession.long_runningمیمانند تا providerهای کند یا غیر streaming خیلی زود متوقفشده گزارش نشوند. کار فعال بدون پیشرفت اخیر با عنوانsession.stalledلاگ میشود؛ فراخوانیهای مدل دارای مالک در آستانه لغو یا پس از آن بهsession.stalledتغییر میکنند، و فعالیت کهنه مدل/ابزار بدون مالک بهعنوان long-running پنهان نمیشود.session.stuckبرای حسابداری نشست کهنه اما قابل بازیابی رزرو شده است، از جمله نشستهای صفشده بیکار با فعالیت کهنه مدل/ابزار بدون مالک، و فقط همان مسیر میتواند lane نشست تحت تأثیر را آزاد کند تا کارهای صفشده تخلیه شوند. diagnosticsهای تکراریsession.stuckتا وقتی نشست بدون تغییر بماند، عقبنشینی میکنند.