Messages and delivery

การสตรีมและการแบ่งเป็นชังก์

OpenClaw มีเลเยอร์การสตรีมแยกกันสองชั้น:

  • การสตรีมแบบบล็อก (ช่องทาง): ส่ง บล็อก ที่เสร็จแล้วออกมาเมื่อผู้ช่วยเขียน ข้อความเหล่านี้เป็นข้อความช่องทางปกติ (ไม่ใช่เดลตาระดับโทเคน)
  • การสตรีมตัวอย่าง (Telegram/Discord/Slack): อัปเดต ข้อความตัวอย่าง ชั่วคราวระหว่างกำลังสร้างผลลัพธ์

ปัจจุบัน ยังไม่มีการสตรีมเดลตาระดับโทเคนจริง ไปยังข้อความช่องทาง การสตรีมตัวอย่างอิงตามข้อความ (ส่ง + แก้ไข/ต่อท้าย)

การสตรีมแบบบล็อก (ข้อความช่องทาง)

การสตรีมแบบบล็อกส่งเอาต์พุตของผู้ช่วยเป็นชิ้นขนาดหยาบเมื่อพร้อมใช้งาน

Model output
  └─ text_delta/events
       ├─ (blockStreamingBreak=text_end)
       │    └─ chunker emits blocks as buffer grows
       └─ (blockStreamingBreak=message_end)
            └─ chunker flushes at message_end
                   └─ channel send (block replies)

คำอธิบายสัญลักษณ์:

  • text_delta/events: เหตุการณ์สตรีมของโมเดล (อาจมีไม่ถี่สำหรับโมเดลที่ไม่สตรีม)
  • chunker: EmbeddedBlockChunker ที่ใช้ขอบเขตต่ำสุด/สูงสุด + ค่ากำหนดจุดตัด
  • channel send: ข้อความขาออกจริง (การตอบกลับแบบบล็อก)

การควบคุม:

  • agents.defaults.blockStreamingDefault: "on"/"off" (ค่าเริ่มต้นปิด)
  • การแทนที่ระดับช่องทาง: *.blockStreaming (และตัวแปรรายบัญชี) เพื่อบังคับ "on"/"off" ต่อช่องทาง
  • agents.defaults.blockStreamingBreak: "text_end" หรือ "message_end"
  • agents.defaults.blockStreamingChunk: { minChars, maxChars, breakPreference? }
  • agents.defaults.blockStreamingCoalesce: { minChars?, maxChars?, idleMs? } (รวมบล็อกที่สตรีมก่อนส่ง)
  • เพดานสูงสุดของช่องทาง: *.textChunkLimit (เช่น channels.whatsapp.textChunkLimit)
  • โหมดแบ่งชิ้นของช่องทาง: *.chunkMode (length เป็นค่าเริ่มต้น, newline แยกตามบรรทัดว่าง (ขอบเขตย่อหน้า) ก่อนแบ่งชิ้นตามความยาว)
  • เพดานแบบยืดหยุ่นของ Discord: channels.discord.maxLinesPerMessage (ค่าเริ่มต้น 17) แยกการตอบกลับที่สูงเพื่อหลีกเลี่ยงการถูกตัดใน UI

ความหมายของขอบเขต:

  • text_end: สตรีมบล็อกทันทีเมื่อ chunker ส่งออกมา; ล้างบัฟเฟอร์ทุกครั้งที่ text_end
  • message_end: รอจนข้อความผู้ช่วยเสร็จสิ้น แล้วค่อยล้างเอาต์พุตที่บัฟเฟอร์ไว้

message_end ยังคงใช้ chunker หากข้อความที่บัฟเฟอร์ไว้ยาวเกิน maxChars ดังนั้นจึงอาจส่งหลายชิ้นในตอนท้ายได้

การส่งสื่อพร้อมการสตรีมแบบบล็อก

คำสั่ง MEDIA: เป็นเมทาดาทาการส่งปกติ เมื่อการสตรีมแบบบล็อกส่งบล็อกสื่อออกไปก่อน OpenClaw จะจดจำการส่งนั้นไว้สำหรับรอบนั้น หาก payload สุดท้ายของผู้ช่วยทำซ้ำ URL สื่อเดียวกัน การส่งสุดท้ายจะตัดสื่อที่ซ้ำออกแทนการส่งไฟล์แนบอีกครั้ง

payload สุดท้ายที่ซ้ำกันทุกประการจะถูกระงับ หาก payload สุดท้ายเพิ่มข้อความที่แตกต่างรอบสื่อที่สตรีมไปแล้ว OpenClaw ยังคงส่งข้อความใหม่โดยคงสื่อให้ส่งเพียงครั้งเดียว วิธีนี้ป้องกันการส่งโน้ตเสียงหรือไฟล์ซ้ำในช่องทางอย่าง Telegram เมื่อ agent ส่ง MEDIA: ระหว่างการสตรีม และ provider รวมสิ่งเดียวกันไว้ในการตอบกลับที่เสร็จสมบูรณ์ด้วย

อัลกอริทึมการแบ่งชิ้น (ขอบเขตต่ำ/สูง)

การแบ่งชิ้นแบบบล็อกดำเนินการโดย EmbeddedBlockChunker:

  • ขอบเขตต่ำ: อย่าส่งออกจนกว่าบัฟเฟอร์ >= minChars (เว้นแต่ถูกบังคับ)
  • ขอบเขตสูง: เลือกแยกก่อน maxChars; หากถูกบังคับ ให้แยกที่ maxChars
  • ค่ากำหนดจุดตัด: paragraphnewlinesentencewhitespace → ตัดแบบบังคับ
  • โค้ดเฟนซ์: ไม่แยกภายในเฟนซ์; เมื่อถูกบังคับที่ maxChars ให้ปิด + เปิดเฟนซ์ใหม่เพื่อให้ Markdown ยังคงถูกต้อง

maxChars จะถูกจำกัดไม่ให้เกิน textChunkLimit ของช่องทาง ดังนั้นคุณจึงไม่สามารถเกินเพดานต่อช่องทางได้

การรวมชิ้น (รวมบล็อกที่สตรีม)

เมื่อเปิดใช้การสตรีมแบบบล็อก OpenClaw สามารถ รวมชิ้นบล็อกที่ต่อเนื่องกัน ก่อนส่งออกไปได้ วิธีนี้ลด "สแปมบรรทัดเดียว" ขณะยังคงให้เอาต์พุตแบบค่อยเป็นค่อยไป

  • การรวมชิ้นจะรอ ช่วงว่างที่ไม่มีการทำงาน (idleMs) ก่อนล้างบัฟเฟอร์
  • บัฟเฟอร์ถูกจำกัดด้วย maxChars และจะถูกล้างหากเกินค่านั้น
  • minChars ป้องกันไม่ให้ส่งส่วนย่อยที่เล็กเกินไปจนกว่าข้อความจะสะสมเพียงพอ (การล้างครั้งสุดท้ายจะส่งข้อความที่เหลือเสมอ)
  • ตัวเชื่อมได้มาจาก blockStreamingChunk.breakPreference (paragraph\n\n, newline\n, sentence → เว้นวรรค)
  • มีการแทนที่ระดับช่องทางผ่าน *.blockStreamingCoalesce (รวมถึงการกำหนดค่ารายบัญชี)
  • ค่าเริ่มต้นของ minChars สำหรับการรวมชิ้นจะถูกเพิ่มเป็น 1500 สำหรับ Signal/Slack/Discord เว้นแต่ถูกแทนที่

จังหวะที่เหมือนมนุษย์ระหว่างบล็อก

เมื่อเปิดใช้การสตรีมแบบบล็อก คุณสามารถเพิ่ม การหยุดแบบสุ่ม ระหว่างการตอบกลับแบบบล็อก (หลังบล็อกแรก) ได้ วิธีนี้ทำให้การตอบกลับแบบหลายบับเบิลรู้สึกเป็นธรรมชาติมากขึ้น

  • การกำหนดค่า: agents.defaults.humanDelay (แทนที่ต่อ agent ผ่าน agents.list[].humanDelay)
  • โหมด: off (ค่าเริ่มต้น), natural (800-2500ms), custom (minMs/maxMs)
  • ใช้กับ การตอบกลับแบบบล็อก เท่านั้น ไม่ใช้กับการตอบกลับสุดท้ายหรือสรุปเครื่องมือ

"สตรีมเป็นชิ้นหรือทั้งหมด"

สิ่งนี้แมปเป็น:

  • สตรีมเป็นชิ้น: blockStreamingDefault: "on" + blockStreamingBreak: "text_end" (ส่งออกระหว่างทำงาน) ช่องทางที่ไม่ใช่ Telegram ต้องมี *.blockStreaming: true ด้วย
  • สตรีมทั้งหมดตอนท้าย: blockStreamingBreak: "message_end" (ล้างครั้งเดียว อาจเป็นหลายชิ้นหากยาวมาก)
  • ไม่มีการสตรีมแบบบล็อก: blockStreamingDefault: "off" (ตอบกลับสุดท้ายเท่านั้น)

หมายเหตุช่องทาง: การสตรีมแบบบล็อกจะ ปิดอยู่ เว้นแต่ ตั้งค่า *.blockStreaming เป็น true อย่างชัดเจน ช่องทางสามารถสตรีมตัวอย่างสด (channels.<channel>.streaming) ได้โดยไม่มีการตอบกลับแบบบล็อก

เตือนตำแหน่งการกำหนดค่า: ค่าเริ่มต้น blockStreaming* อยู่ใต้ agents.defaults ไม่ใช่ config ราก

โหมดการสตรีมตัวอย่าง

คีย์มาตรฐาน: channels.<channel>.streaming

โหมด:

  • off: ปิดใช้การสตรีมตัวอย่าง
  • partial: ตัวอย่างเดียวที่ถูกแทนที่ด้วยข้อความล่าสุด
  • block: ตัวอย่างอัปเดตเป็นขั้นแบบแบ่งชิ้น/ต่อท้าย
  • progress: ตัวอย่างความคืบหน้า/สถานะระหว่างการสร้าง และคำตอบสุดท้ายเมื่อเสร็จสิ้น

streaming.mode: "block" เป็นโหมดการสตรีมตัวอย่างสำหรับช่องทางที่แก้ไขได้ เช่น Discord และ Telegram โหมดนี้ไม่ได้เปิดใช้การส่งบล็อกผ่านช่องทางนั้น ใช้ streaming.block.enabled หรือคีย์ช่องทางแบบเดิม blockStreaming เมื่อต้องการการตอบกลับแบบบล็อกปกติ Microsoft Teams เป็นข้อยกเว้น: ไม่มีการขนส่งบล็อกแบบร่างตัวอย่าง ดังนั้น streaming.mode: "block" จึงแมปเป็นการส่งบล็อกของ Teams แทนการสตรีมแบบบางส่วน/ความคืบหน้าแบบเนทีฟ

การแมปช่องทาง

ช่องทาง off partial block progress
Telegram ร่างความคืบหน้าที่แก้ไขได้
Discord ร่างความคืบหน้าที่แก้ไขได้
Slack
Mattermost
MS Teams สตรีมความคืบหน้าแบบเนทีฟ

เฉพาะ Slack:

  • channels.slack.streaming.nativeTransport สลับการเรียก Slack native streaming API เมื่อ channels.slack.streaming.mode="partial" (ค่าเริ่มต้น: true)
  • Slack native streaming และสถานะเธรดผู้ช่วยของ Slack ต้องมีเป้าหมายเป็นเธรดตอบกลับ DM ระดับบนสุดไม่แสดงตัวอย่างรูปแบบเธรดนั้น แต่ยังสามารถใช้โพสต์ตัวอย่างแบบร่างและการแก้ไขของ Slack ได้

การย้ายคีย์เดิม:

  • Telegram: ค่าเดิม streamMode และค่า streaming แบบสเกลาร์/บูลีนจะถูกตรวจพบและย้ายโดยเส้นทาง doctor/ความเข้ากันได้ของ config ไปยัง streaming.mode
  • Discord: streamMode + streaming แบบบูลีนยังคงเป็น alias ขณะรันไทม์สำหรับ enum streaming; รัน openclaw doctor --fix เพื่อเขียน config ที่บันทึกไว้ใหม่
  • Slack: streamMode ยังคงเป็น alias ขณะรันไทม์สำหรับ streaming.mode; streaming แบบบูลีนยังคงเป็น alias ขณะรันไทม์สำหรับ streaming.mode รวมถึง streaming.nativeTransport; nativeStreaming แบบเดิมยังคงเป็น alias ขณะรันไทม์สำหรับ streaming.nativeTransport รัน openclaw doctor --fix เพื่อเขียน config ที่บันทึกไว้ใหม่

พฤติกรรมขณะรันไทม์

Telegram:

  • ใช้ sendMessage + การอัปเดตตัวอย่างด้วย editMessageText ใน DM และกลุ่ม/หัวข้อ
  • ข้อความสุดท้ายจะแก้ไขตัวอย่างที่ใช้งานอยู่ในตำแหน่งเดิม; ข้อความสุดท้ายที่ยาวจะใช้ข้อความนั้นซ้ำสำหรับชิ้นแรกและส่งเฉพาะชิ้นที่เหลือ
  • โหมด progress เก็บความคืบหน้าของเครื่องมือไว้ในร่างสถานะที่แก้ไขได้ ล้างร่างนั้นเมื่อเสร็จสิ้น และส่งคำตอบสุดท้ายผ่านการส่งปกติ
  • หากการแก้ไขสุดท้ายล้มเหลวก่อนยืนยันข้อความที่เสร็จสมบูรณ์ OpenClaw จะใช้การส่งสุดท้ายแบบปกติและล้างตัวอย่างเก่าที่ค้างอยู่
  • การสตรีมตัวอย่างจะถูกข้ามเมื่อเปิดใช้การสตรีมแบบบล็อกของ Telegram อย่างชัดเจน (เพื่อหลีกเลี่ยงการสตรีมซ้ำซ้อน)
  • /reasoning stream สามารถเขียนเหตุผลลงในตัวอย่างชั่วคราวที่จะถูกลบหลังการส่งสุดท้าย

Discord:

  • ใช้ข้อความตัวอย่างแบบส่ง + แก้ไข
  • โหมด block ใช้การแบ่งชิ้นแบบร่าง (draftChunk)
  • การสตรีมตัวอย่างจะถูกข้ามเมื่อเปิดใช้การสตรีมแบบบล็อกของ Discord อย่างชัดเจน
  • payload สุดท้ายที่เป็นสื่อ ข้อผิดพลาด และการตอบกลับแบบระบุชัดเจนจะยกเลิกตัวอย่างที่รออยู่โดยไม่ล้างเป็นร่างใหม่ แล้วใช้การส่งปกติ

Slack:

  • partial สามารถใช้ Slack native streaming (chat.startStream/append/stop) เมื่อพร้อมใช้งาน
  • block ใช้ตัวอย่างแบบร่างสไตล์ต่อท้าย
  • progress ใช้ข้อความตัวอย่างสถานะ แล้วตามด้วยคำตอบสุดท้าย
  • DM ระดับบนสุดที่ไม่มีเธรดตอบกลับจะใช้โพสต์ตัวอย่างแบบร่างและการแก้ไขแทน Slack native streaming
  • การสตรีมตัวอย่างทั้งแบบเนทีฟและแบบร่างจะระงับการตอบกลับแบบบล็อกสำหรับรอบนั้น ดังนั้นการตอบกลับของ Slack จะถูกสตรีมผ่านเส้นทางการส่งเพียงเส้นทางเดียว
  • payload สุดท้ายที่เป็นสื่อ/ข้อผิดพลาดและผลสุดท้ายของความคืบหน้าจะไม่สร้างข้อความร่างชั่วคราว; เฉพาะผลสุดท้ายแบบข้อความ/บล็อกที่แก้ไขตัวอย่างได้เท่านั้นที่จะล้างข้อความร่างที่รออยู่

Mattermost:

  • สตรีมการคิด กิจกรรมเครื่องมือ และข้อความตอบกลับบางส่วนลงในโพสต์ตัวอย่างแบบร่างเดียว ซึ่งจะถูกทำให้เป็นผลสุดท้ายในตำแหน่งเดิมเมื่อคำตอบสุดท้ายปลอดภัยที่จะส่ง
  • ถอยกลับไปส่งโพสต์สุดท้ายใหม่หากโพสต์ตัวอย่างถูกลบหรือไม่พร้อมใช้งานด้วยเหตุอื่นในเวลาทำผลสุดท้าย
  • payload สุดท้ายที่เป็นสื่อ/ข้อผิดพลาดจะยกเลิกการอัปเดตตัวอย่างที่รออยู่ก่อนการส่งปกติ แทนการล้างเป็นโพสต์ตัวอย่างชั่วคราว

Matrix:

  • ตัวอย่างแบบร่างจะถูกทำให้เป็นผลสุดท้ายในตำแหน่งเดิมเมื่อข้อความสุดท้ายสามารถใช้เหตุการณ์ตัวอย่างซ้ำได้
  • ผลสุดท้ายที่เป็นสื่อเท่านั้น ข้อผิดพลาด และเป้าหมายการตอบกลับไม่ตรงกันจะยกเลิกการอัปเดตตัวอย่างที่รออยู่ก่อนการส่งปกติ; ตัวอย่างเก่าที่มองเห็นอยู่แล้วจะถูกลบแบบ redaction

การอัปเดตตัวอย่างความคืบหน้าของเครื่องมือ

การสตรีมตัวอย่างยังสามารถรวมการอัปเดต ความคืบหน้าของเครื่องมือ ได้ด้วย - บรรทัดสถานะสั้น ๆ เช่น "กำลังค้นหาเว็บ", "กำลังอ่านไฟล์" หรือ "กำลังเรียกใช้เครื่องมือ" - ซึ่งปรากฏในข้อความตัวอย่างเดียวกันระหว่างที่เครื่องมือกำลังทำงาน ก่อนการตอบกลับสุดท้าย วิธีนี้ทำให้รอบการใช้เครื่องมือหลายขั้นตอนยังคงมีความเคลื่อนไหวให้เห็น แทนที่จะเงียบระหว่างตัวอย่างการคิดแรกกับคำตอบสุดท้าย

พื้นผิวที่รองรับ:

  • Discord, Slack, Telegram และ Matrix จะสตรีมความคืบหน้าของเครื่องมือเข้าไปในการแก้ไขตัวอย่างแบบสดตามค่าเริ่มต้นเมื่อการสตรีมตัวอย่างทำงานอยู่ Microsoft Teams ใช้สตรีมความคืบหน้าแบบเนทีฟในแชตส่วนตัว
  • Telegram จัดส่งพร้อมการอัปเดตตัวอย่างความคืบหน้าของเครื่องมือที่เปิดใช้มาตั้งแต่ v2026.4.22; การเปิดใช้ต่อไปจะรักษาพฤติกรรมที่เผยแพร่ไปแล้วนั้นไว้
  • Mattermost รวมกิจกรรมของเครื่องมือเข้าไว้ในโพสต์ตัวอย่างฉบับร่างเดียวของตัวเองอยู่แล้ว (ดูด้านบน)
  • การแก้ไขความคืบหน้าของเครื่องมือจะทำตามโหมดการสตรีมตัวอย่างที่ทำงานอยู่; ระบบจะข้ามการแก้ไขเหล่านี้เมื่อการสตรีมตัวอย่างเป็น off หรือเมื่อการสตรีมบล็อกรับช่วงข้อความไปแล้ว บน Telegram, streaming.mode: "off" เป็นแบบผลลัพธ์สุดท้ายเท่านั้น: ข้อความความคืบหน้าทั่วไปจะถูกระงับด้วยแทนที่จะส่งเป็นข้อความสถานะแยกต่างหาก ขณะที่พรอมป์การอนุมัติ เพย์โหลดสื่อ และข้อผิดพลาดยังคงถูกกำหนดเส้นทางตามปกติ
  • หากต้องการคงการสตรีมตัวอย่างไว้แต่ซ่อนบรรทัดความคืบหน้าของเครื่องมือ ให้ตั้งค่า streaming.preview.toolProgress เป็น false สำหรับช่องทางนั้น หากต้องการให้บรรทัดความคืบหน้าของเครื่องมือยังมองเห็นได้ขณะซ่อนข้อความคำสั่ง/การรัน ให้ตั้งค่า streaming.preview.commandText เป็น "status" หรือ streaming.progress.commandText เป็น "status"; ค่าเริ่มต้นคือ "raw" เพื่อรักษาพฤติกรรมที่เผยแพร่ไปแล้ว นโยบายนี้ใช้ร่วมกันโดยช่องทางฉบับร่าง/ความคืบหน้าที่ใช้ตัวเรนเดอร์ความคืบหน้าแบบกะทัดรัดของ OpenClaw ซึ่งรวมถึง Discord, Matrix, Microsoft Teams, Mattermost, ตัวอย่างฉบับร่างของ Slack และ Telegram หากต้องการปิดการแก้ไขตัวอย่างทั้งหมด ให้ตั้งค่า streaming.mode เป็น off
  • การตอบกลับด้วยข้อความอ้างอิงที่เลือกบน Telegram เป็นข้อยกเว้น: เมื่อ replyToMode ไม่ใช่ "off" และมีข้อความอ้างอิงที่เลือกอยู่ OpenClaw จะข้ามสตรีมตัวอย่างคำตอบสำหรับรอบนั้น ทำให้บรรทัดตัวอย่างความคืบหน้าของเครื่องมือไม่สามารถเรนเดอร์ได้ การตอบกลับข้อความปัจจุบันที่ไม่มีข้อความอ้างอิงที่เลือกยังคงใช้การสตรีมตัวอย่าง ดูรายละเอียดได้ที่ เอกสารช่องทาง Telegram

คงบรรทัดความคืบหน้าให้มองเห็นได้ แต่ซ่อนข้อความคำสั่ง/การรันแบบดิบ:

{
  "channels": {
    "telegram": {
      "streaming": {
        "mode": "partial",
        "preview": {
          "toolProgress": true,
          "commandText": "status"
        }
      }
    }
  }
}

ใช้รูปแบบเดียวกันภายใต้คีย์ช่องทางความคืบหน้าแบบกะทัดรัดอื่น เช่น channels.discord, channels.matrix, channels.msteams, channels.mattermost หรือตัวอย่างฉบับร่างของ Slack สำหรับโหมดฉบับร่างความคืบหน้า ให้วางนโยบายเดียวกันไว้ภายใต้ streaming.progress:

{
  "channels": {
    "telegram": {
      "streaming": {
        "mode": "progress",
        "progress": {
          "toolProgress": true,
          "commandText": "status"
        }
      }
    }
  }
}

ที่เกี่ยวข้อง