Messages and delivery

صف فرمان‌ها

ما اجرای پاسخ خودکار ورودی (همه کانال‌ها) را از طریق یک صف کوچک درون‌پردازشی سریال‌سازی می‌کنیم تا از برخورد چند اجرای agent جلوگیری شود، در حالی که همچنان موازی‌سازی امن میان sessionها ممکن باشد.

چرا

  • اجرای پاسخ خودکار می‌تواند پرهزینه باشد (فراخوانی‌های LLM) و وقتی چند پیام ورودی با فاصله زمانی کم برسند، ممکن است با هم برخورد کنند.
  • سریال‌سازی از رقابت بر سر منابع مشترک (فایل‌های session، لاگ‌ها، stdin مربوط به CLI) جلوگیری می‌کند و احتمال محدودیت نرخ از سمت upstream را کاهش می‌دهد.

نحوه کار

  • یک صف FIFO آگاه از lane، هر lane را با سقف هم‌زمانی قابل تنظیم تخلیه می‌کند (پیش‌فرض 1 برای laneهای پیکربندی‌نشده؛ مقدار پیش‌فرض main برابر 4 و subagent برابر 8 است).
  • runEmbeddedPiAgent بر اساس کلید session (lane session:<key>) در صف قرار می‌گیرد تا تضمین شود برای هر session فقط یک اجرای فعال وجود دارد.
  • سپس هر اجرای session در یک lane سراسری (به‌طور پیش‌فرض main) صف‌بندی می‌شود تا موازی‌سازی کلی با agents.defaults.maxConcurrent محدود شود.
  • وقتی لاگ‌گیری مفصل فعال باشد، اجراهای صف‌شده اگر پیش از شروع بیش از حدود 2s منتظر مانده باشند، یک اعلان کوتاه منتشر می‌کنند.
  • نشانگرهای تایپ همچنان بلافاصله هنگام قرار گرفتن در صف فعال می‌شوند (وقتی کانال پشتیبانی کند)، بنابراین تجربه کاربر هنگام انتظار برای نوبت خود تغییر نمی‌کند.

پیش‌فرض‌ها

وقتی تنظیم نشده باشد، همه سطح‌های کانال ورودی از این موارد استفاده می‌کنند:

  • mode: "steer"
  • debounceMs: 500
  • cap: 20
  • drop: "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 می‌کند:

  1. override درون‌خطی یا ذخیره‌شده برای هر session مربوط به /queue.
  2. messages.queue.byChannel.<channel>.
  3. messages.queue.mode.
  4. پیش‌فرض 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 می‌کنند.

مرتبط