Messages and delivery
صف فرمانها
ما اجرای پاسخ خودکار ورودی (همه کانالها) را از طریق یک صف کوچک درونپردازشی سریالسازی میکنیم تا از برخورد چند اجرای agent جلوگیری شود، در حالی که همچنان موازیسازی امن میان sessionها ممکن باشد.
چرا
- اجرای پاسخ خودکار میتواند پرهزینه باشد (فراخوانیهای LLM) و وقتی چند پیام ورودی با فاصله زمانی کم برسند، ممکن است با هم برخورد کنند.
- سریالسازی از رقابت بر سر منابع مشترک (فایلهای session، لاگها، stdin مربوط به CLI) جلوگیری میکند و احتمال محدودیت نرخ از سمت upstream را کاهش میدهد.
نحوه کار
- یک صف FIFO آگاه از lane، هر lane را با سقف همزمانی قابل تنظیم تخلیه میکند (پیشفرض 1 برای laneهای پیکربندینشده؛ مقدار پیشفرض main برابر 4 و subagent برابر 8 است).
runEmbeddedPiAgentبر اساس کلید session (lanesession:<key>) در صف قرار میگیرد تا تضمین شود برای هر session فقط یک اجرای فعال وجود دارد.- سپس هر اجرای session در یک lane سراسری (بهطور پیشفرض
main) صفبندی میشود تا موازیسازی کلی باagents.defaults.maxConcurrentمحدود شود. - وقتی لاگگیری مفصل فعال باشد، اجراهای صفشده اگر پیش از شروع بیش از حدود 2s منتظر مانده باشند، یک اعلان کوتاه منتشر میکنند.
- نشانگرهای تایپ همچنان بلافاصله هنگام قرار گرفتن در صف فعال میشوند (وقتی کانال پشتیبانی کند)، بنابراین تجربه کاربر هنگام انتظار برای نوبت خود تغییر نمیکند.
پیشفرضها
وقتی تنظیم نشده باشد، همه سطحهای کانال ورودی از این موارد استفاده میکنند:
mode: "steer"debounceMs: 500cap: 20drop: "summarize"
steer پیشفرض است چون نوبت model فعال را پاسخگو نگه میدارد بدون اینکه اجرای session دوم را شروع کند. همه پیامهای steering را که پیش از مرز بعدی model رسیدهاند تخلیه میکند. اگر اجرای فعلی نتواند steering را بپذیرد، OpenClaw به یک ورودی صف followup برمیگردد.
حالتهای صف
پیامهای ورودی میتوانند اجرای فعلی را steer کنند، برای نوبت followup منتظر بمانند، یا هر دو را انجام دهند:
steer: پیامهای steering را در runtime فعال صفبندی میکند. Pi همه پیامهای steering معلق را بعد از اینکه نوبت فعلی assistant اجرای tool callهای خود را تمام کرد و پیش از فراخوانی LLM بعدی تحویل میدهد؛ Codex app-server یکturn/steerدستهای دریافت میکند. اگر اجرا بهطور فعال streaming نکند یا steering در دسترس نباشد، OpenClaw به یک ورودی صف followup برمیگردد.queue(legacy): steering قدیمی یکیدرهربار. Pi در هر مرز model یک پیام steering صفشده را تحویل میدهد؛ Codex app-server درخواستهای جداگانهturn/steerدریافت میکند. مگر اینکه به رفتار سریالسازیشده قبلی نیاز داشته باشید،steerرا ترجیح دهید.followup: هر پیام را برای یک نوبت agent بعدی پس از پایان اجرای فعلی در صف قرار میدهد.collect: پیامهای صفشده را پس از پنجره سکوت در یک نوبت followup واحد ادغام میکند. اگر پیامها کانالها/threadهای متفاوتی را هدف بگیرند، برای حفظ routing بهصورت جداگانه تخلیه میشوند.steer-backlog(یاsteer+backlog): اکنون steer میکند و همان پیام را برای یک نوبت followup حفظ میکند.interrupt(legacy): اجرای فعال آن session را لغو میکند، سپس جدیدترین پیام را اجرا میکند.
Steer-backlog یعنی میتوانید پس از اجرای steerشده یک پاسخ followup دریافت کنید، بنابراین سطحهای streaming ممکن است شبیه تکرار به نظر برسند. اگر برای هر پیام ورودی یک پاسخ میخواهید، collect/steer را ترجیح دهید.
برای زمانبندی و رفتار وابستگی ویژه runtime، صف Steering را ببینید. برای فرمان صریح /steer <message>، Steer را ببینید.
بهصورت سراسری یا برای هر کانال از طریق messages.queue پیکربندی کنید:
{
messages: {
queue: {
mode: "steer",
debounceMs: 500,
cap: 20,
drop: "summarize",
byChannel: { discord: "collect" },
},
},
}
گزینههای صف
گزینهها روی followup، collect و steer-backlog اعمال میشوند (و وقتی steering به followup برمیگردد، روی steer یا queue legacy نیز اعمال میشوند):
debounceMs: پنجره سکوت پیش از تخلیه followupهای صفشده. اعداد تنها بر حسب میلیثانیه هستند؛ واحدهایms،s،m،hوdتوسط گزینههای/queueپذیرفته میشوند.cap: بیشینه پیامهای صفشده برای هر session. مقدارهای کمتر از1نادیده گرفته میشوند.drop: "summarize": پیشفرض. ورودیهای صفشده قدیمیتر را در صورت نیاز حذف میکند، خلاصههای فشرده را نگه میدارد و آنها را بهعنوان یک prompt followup ساختگی تزریق میکند.drop: "old": ورودیهای صفشده قدیمیتر را در صورت نیاز، بدون حفظ خلاصهها حذف میکند.drop: "new": وقتی صف از قبل پر است، جدیدترین پیام را رد میکند.
پیشفرضها: debounceMs: 500، cap: 20، drop: summarize.
اولویت
برای انتخاب حالت، OpenClaw این ترتیب را resolve میکند:
- override درونخطی یا ذخیرهشده برای هر session مربوط به
/queue. messages.queue.byChannel.<channel>.messages.queue.mode.- پیشفرض
steer.
برای گزینهها، گزینههای درونخطی یا ذخیرهشده /queue بر config اولویت دارند. سپس debounce ویژه کانال (messages.queue.debounceMsByChannel)، پیشفرضهای debounce مربوط به Plugin، گزینههای سراسری messages.queue و پیشفرضهای داخلی اعمال میشوند. cap و drop گزینههای سراسری/session هستند، نه کلیدهای config برای هر کانال.
overrideهای هر session
/queue <mode>را بهعنوان یک فرمان مستقل بفرستید تا حالت برای session فعلی ذخیره شود.- گزینهها را میتوان ترکیب کرد:
/queue collect debounce:0.5s cap:25 drop:summarize /queue defaultیا/queue reset، override مربوط به session را پاک میکند.
دامنه و تضمینها
- روی اجرای agent پاسخ خودکار در همه کانالهای ورودی که از خط لوله پاسخ Gateway استفاده میکنند اعمال میشود (WhatsApp web، Telegram، Slack، Discord، Signal، iMessage، webchat و غیره).
- lane پیشفرض (
main) برای ورودی + Heartbeatهای اصلی در سطح پردازش است؛ برای اجازه دادن به چند session بهصورت موازی،agents.defaults.maxConcurrentرا تنظیم کنید. - laneهای اضافی ممکن است وجود داشته باشند (مثلاً
cron،cron-nested،nested،subagent) تا jobهای پسزمینه بتوانند بدون مسدود کردن پاسخهای ورودی بهصورت موازی اجرا شوند. نوبتهای agent کرون ایزولهشده هنگام اجرای داخلی agent خود که ازcron-nestedاستفاده میکند، یک اسلاتcronرا نگه میدارند؛ هر دو ازcron.maxConcurrentRunsاستفاده میکنند. جریانهایnestedمشترک غیرکرون رفتار lane خود را حفظ میکنند. این اجراهای جداشده بهعنوان وظایف پسزمینه ردیابی میشوند. - laneهای هر session تضمین میکنند که در هر لحظه فقط یک اجرای agent به یک session مشخص دست میزند.
- بدون وابستگی خارجی یا threadهای worker پسزمینه؛ فقط TypeScript + promiseها.
عیبیابی
- اگر فرمانها گیر کرده به نظر میرسند، لاگهای مفصل را فعال کنید و دنبال خطوط "queued for ...ms" بگردید تا تأیید کنید صف در حال تخلیه است.
- اگر به عمق صف نیاز دارید، لاگهای مفصل را فعال کنید و خطوط زمانبندی صف را زیر نظر بگیرید.
- اجراهای Codex app-server که یک turn را میپذیرند و سپس انتشار progress را متوقف میکنند، توسط adapter مربوط به Codex قطع میشوند تا lane فعال session بتواند آزاد شود، بهجای اینکه منتظر timeout اجرای بیرونی بماند.
- وقتی diagnostics فعال باشد، sessionهایی که پس از
diagnostics.stuckSessionWarnMsبدون هیچ پاسخ، tool، status، block یا پیشرفت ACP مشاهدهشده درprocessingباقی میمانند، بر اساس فعالیت فعلی طبقهبندی میشوند. کار فعال بهصورتsession.long_runningلاگ میشود؛ کار فعال بدون پیشرفت اخیر بهصورتsession.stalledلاگ میشود؛session.stuckبرای bookkeeping کهنه session بدون کار فعال رزرو شده است، و فقط همین مسیر میتواند lane مربوط به session آسیبدیده را آزاد کند تا کار صفشده تخلیه شود. diagnostics تکراریsession.stuckتا وقتی session بدون تغییر بماند back off میکنند.