Messages and delivery
คิวคำสั่ง
เราจัดลำดับการรันการตอบกลับอัตโนมัติขาเข้า (ทุกช่องทาง) ผ่านคิวขนาดเล็กในโปรเซส เพื่อป้องกันไม่ให้การรันเอเจนต์หลายรายการชนกัน ขณะเดียวกันยังอนุญาตให้ทำงานขนานระหว่างเซสชันได้อย่างปลอดภัย
เหตุผล
- การรันการตอบกลับอัตโนมัติอาจมีค่าใช้จ่ายสูง (การเรียก LLM) และอาจชนกันเมื่อมีข้อความขาเข้าหลายข้อความเข้ามาในเวลาที่ใกล้กัน
- การจัดลำดับช่วยหลีกเลี่ยงการแข่งขันเพื่อใช้ทรัพยากรร่วมกัน (ไฟล์เซสชัน, ล็อก, CLI stdin) และลดโอกาสเกิด rate limit จาก upstream
วิธีการทำงาน
- คิว FIFO ที่รับรู้ lane จะระบายแต่ละ lane ด้วยขีดจำกัด concurrency ที่กำหนดค่าได้ (ค่าเริ่มต้น 1 สำหรับ lane ที่ไม่ได้กำหนดค่า; main มีค่าเริ่มต้นเป็น 4, subagent เป็น 8)
runEmbeddedPiAgentเข้าคิวตาม session key (lanesession:<key>) เพื่อรับประกันว่ามีการรันที่ทำงานอยู่ได้เพียงหนึ่งรายการต่อเซสชัน- จากนั้นการรันแต่ละเซสชันจะถูกเข้าคิวใน global lane (
mainโดยค่าเริ่มต้น) เพื่อให้ parallelism โดยรวมถูกจำกัดด้วยagents.defaults.maxConcurrent - เมื่อเปิดใช้การบันทึกแบบ verbose การรันที่อยู่ในคิวจะส่งประกาศสั้น ๆ หากรอนานกว่า ~2s ก่อนเริ่ม
- ตัวบ่งชี้การพิมพ์ยังทำงานทันทีเมื่อเข้าคิว (เมื่อช่องทางรองรับ) ดังนั้นประสบการณ์ผู้ใช้จึงไม่เปลี่ยนแปลงระหว่างรอคิว
ค่าเริ่มต้น
เมื่อไม่ได้ตั้งค่า พื้นผิวช่องทางขาเข้าทั้งหมดจะใช้:
mode: "steer"debounceMs: 500cap: 20drop: "summarize"
steer เป็นค่าเริ่มต้นเพราะช่วยให้ turn ของโมเดลที่ทำงานอยู่ตอบสนองได้ดีโดยไม่ต้อง
เริ่มการรันเซสชันที่สอง โดยจะระบายข้อความ steering ทั้งหมดที่มาถึง
ก่อนขอบเขตโมเดลถัดไป หากการรันปัจจุบันไม่สามารถรับ steering ได้
OpenClaw จะถอยกลับไปใช้รายการคิว followup
โหมดคิว
ข้อความขาเข้าสามารถ steer การรันปัจจุบัน รอ turn followup หรือทำทั้งสองอย่างได้:
steer: เข้าคิวข้อความ steering เข้าสู่ runtime ที่ทำงานอยู่ Pi จะส่งข้อความ steering ที่รอดำเนินการทั้งหมด หลังจาก turn ของผู้ช่วยปัจจุบันรัน tool calls เสร็จแล้ว ก่อนการเรียก LLM ครั้งถัดไป; Codex app-server ได้รับturn/steerแบบรวมชุดหนึ่งรายการ หากการรันไม่ได้ streaming อย่างทำงานอยู่ หรือ steering ไม่พร้อมใช้งาน OpenClaw จะถอยกลับไปใช้รายการคิว followupqueue(legacy): steering แบบเก่าที่ทำทีละรายการ Pi จะส่งข้อความ steering ที่เข้าคิวหนึ่งข้อความในแต่ละขอบเขตโมเดล; Codex app-server ได้รับคำขอturn/steerแยกกัน ควรใช้steerเว้นแต่คุณต้องการพฤติกรรม serialized แบบเดิมfollowup: เข้าคิวแต่ละข้อความสำหรับ turn เอเจนต์ภายหลังหลังจากการรันปัจจุบันสิ้นสุดcollect: รวมข้อความที่เข้าคิวเป็น turn followup เดียว หลังจากช่วง quiet window หากข้อความมีเป้าหมายเป็นช่องทาง/เธรดต่างกัน ข้อความเหล่านั้นจะถูกระบายทีละรายการเพื่อคง routing ไว้steer-backlog(หรือsteer+backlog): steer ตอนนี้ และ เก็บข้อความเดียวกันไว้สำหรับ turn followupinterrupt(legacy): ยกเลิกการรันที่ทำงานอยู่สำหรับเซสชันนั้น แล้วรันข้อความล่าสุด
Steer-backlog หมายความว่าคุณอาจได้รับการตอบกลับ followup หลังจากการรันที่ถูก steer ดังนั้น
พื้นผิว streaming อาจดูเหมือนซ้ำกัน ควรใช้ collect/steer หากคุณต้องการ
การตอบกลับหนึ่งรายการต่อข้อความขาเข้าหนึ่งข้อความ
สำหรับ timing และพฤติกรรม dependency เฉพาะ runtime โปรดดู
คิว Steering สำหรับคำสั่ง /steer <message>
แบบชัดเจน โปรดดู Steer
กำหนดค่าแบบ global หรือรายช่องทางผ่าน messages.queue:
{
messages: {
queue: {
mode: "steer",
debounceMs: 500,
cap: 20,
drop: "summarize",
byChannel: { discord: "collect" },
},
},
}
ตัวเลือกคิว
ตัวเลือกใช้กับ followup, collect, และ steer-backlog (และกับ steer หรือ legacy queue เมื่อ steering ถอยกลับไปเป็น followup):
debounceMs: quiet window ก่อนระบาย followup ที่เข้าคิว ตัวเลขล้วนคือมิลลิวินาที; ตัวหน่วยms,s,m,h, และdรองรับโดยตัวเลือก/queuecap: จำนวนข้อความที่เข้าคิวสูงสุดต่อเซสชัน ค่าต่ำกว่า1จะถูกละเว้นdrop: "summarize": ค่าเริ่มต้น ทิ้งรายการที่เข้าคิวเก่าที่สุดตามจำเป็น เก็บสรุปแบบกะทัดรัดไว้ และแทรกเป็น prompt followup สังเคราะห์drop: "old": ทิ้งรายการที่เข้าคิวเก่าที่สุดตามจำเป็น โดยไม่เก็บสรุปไว้drop: "new": ปฏิเสธข้อความใหม่ล่าสุดเมื่อคิวเต็มอยู่แล้ว
ค่าเริ่มต้น: debounceMs: 500, cap: 20, drop: summarize
ลำดับความสำคัญ
สำหรับการเลือกโหมด OpenClaw จะ resolve ตามลำดับ:
- override
/queueแบบ inline หรือที่จัดเก็บไว้รายเซสชัน messages.queue.byChannel.<channel>messages.queue.mode- ค่าเริ่มต้น
steer
สำหรับตัวเลือก ตัวเลือก /queue แบบ inline หรือที่จัดเก็บไว้จะชนะ config จากนั้น
จะใช้ debounce เฉพาะช่องทาง (messages.queue.debounceMsByChannel), ค่าเริ่มต้น debounce ของ Plugin,
ตัวเลือก global messages.queue, และค่าเริ่มต้นในตัว
cap และ drop เป็นตัวเลือก global/session ไม่ใช่คีย์ config รายช่องทาง
override รายเซสชัน
- ส่ง
/queue <mode>เป็นคำสั่งเดี่ยวเพื่อจัดเก็บโหมดสำหรับเซสชันปัจจุบัน - สามารถรวมตัวเลือกได้:
/queue collect debounce:0.5s cap:25 drop:summarize /queue defaultหรือ/queue resetจะล้าง override ของเซสชัน
ขอบเขตและการรับประกัน
- ใช้กับการรันเอเจนต์ตอบกลับอัตโนมัติในทุกช่องทางขาเข้าที่ใช้ gateway reply pipeline (WhatsApp web, Telegram, Slack, Discord, Signal, iMessage, webchat ฯลฯ)
- lane เริ่มต้น (
main) ครอบคลุมทั้งโปรเซสสำหรับ inbound + main heartbeats; ตั้งค่าagents.defaults.maxConcurrentเพื่ออนุญาตให้หลายเซสชันทำงานขนานกัน - อาจมี lane เพิ่มเติม (เช่น
cron,cron-nested,nested,subagent) เพื่อให้งานพื้นหลังทำงานขนานได้โดยไม่บล็อกการตอบกลับขาเข้า turn ของเอเจนต์ cron แบบ isolated จะถือ slotcronขณะที่การดำเนินการเอเจนต์ภายในใช้cron-nested; ทั้งสองใช้cron.maxConcurrentRunsโฟลว์nestedแบบ shared non-cron จะรักษาพฤติกรรม lane ของตัวเอง การรันแบบ detached เหล่านี้ถูกติดตามเป็น งานพื้นหลัง - lane รายเซสชันรับประกันว่ามีการรันเอเจนต์เพียงหนึ่งรายการเท่านั้นที่แตะเซสชันใดเซสชันหนึ่งในแต่ละครั้ง
- ไม่มี dependency ภายนอกหรือ background worker threads; ใช้ TypeScript + promises ล้วน
การแก้ไขปัญหา
- หากคำสั่งดูเหมือนค้าง ให้เปิดใช้ล็อก verbose และมองหาบรรทัด "queued for ...ms" เพื่อยืนยันว่าคิวกำลังระบาย
- หากคุณต้องการ queue depth ให้เปิดใช้ล็อก verbose และดูบรรทัด timing ของคิว
- การรัน Codex app-server ที่รับ turn แล้วหยุดส่ง progress จะถูก interrupt โดย Codex adapter เพื่อให้ lane ของเซสชันที่ทำงานอยู่ปล่อยได้ แทนที่จะรอ timeout ของการรันชั้นนอก
- เมื่อเปิดใช้ diagnostics เซสชันที่คงอยู่ใน
processingเกินdiagnostics.stuckSessionWarnMsโดยไม่มี reply, tool, status, block หรือ ACP progress ที่สังเกตเห็น จะถูกจัดประเภทตามกิจกรรมปัจจุบัน งานที่ทำงานอยู่จะล็อกเป็นsession.long_running; งานที่ทำงานอยู่แต่ไม่มี progress ล่าสุดจะล็อกเป็นsession.stalled;session.stuckสงวนไว้สำหรับการทำ bookkeeping ของเซสชันที่เก่าและไม่มีงานที่ทำงานอยู่ และมีเพียง path นั้นเท่านั้นที่ปล่อย lane ของเซสชันที่ได้รับผลกระทบได้เพื่อให้คิวงานระบาย diagnosticsession.stuckที่เกิดซ้ำจะ back off ขณะที่เซสชันยังไม่เปลี่ยนแปลง