Concept internals
การจัดรูปแบบ Markdown
OpenClaw จัดรูปแบบ Markdown ขาออกโดยแปลงเป็นตัวแทนระดับกลาง (IR) ที่ใช้ร่วมกันก่อนเรนเดอร์เอาต์พุตเฉพาะช่องทาง IR จะคงข้อความต้นฉบับไว้ครบถ้วนพร้อมเก็บช่วงของสไตล์/ลิงก์ เพื่อให้การแบ่งเป็นชิ้นและการเรนเดอร์สอดคล้องกันได้ในทุกช่องทาง
เป้าหมาย
- ความสอดคล้อง: ขั้นตอนการแยกวิเคราะห์เดียว เรนเดอร์ได้หลายแบบ
- การแบ่งเป็นชิ้นอย่างปลอดภัย: แยกข้อความก่อนเรนเดอร์ เพื่อไม่ให้การจัดรูปแบบแบบอินไลน์ขาดกลางระหว่างชิ้น
- เหมาะกับช่องทาง: แมป IR เดียวกันไปยัง Slack mrkdwn, Telegram HTML และช่วงสไตล์ของ Signal ได้โดยไม่ต้องแยกวิเคราะห์ Markdown ซ้ำ
ไปป์ไลน์
- แยกวิเคราะห์ Markdown -> IR
- IR คือข้อความล้วนพร้อมช่วงสไตล์ (ตัวหนา/ตัวเอียง/ขีดฆ่า/โค้ด/สปอยล์) และช่วงลิงก์
- ออฟเซ็ตเป็นหน่วยโค้ด UTF-16 เพื่อให้ช่วงสไตล์ของ Signal ตรงกับ API ของมัน
- ตารางจะถูกแยกวิเคราะห์เฉพาะเมื่อช่องทางเลือกใช้การแปลงตาราง
- แบ่ง IR เป็นชิ้น (จัดรูปแบบก่อน)
- การแบ่งเป็นชิ้นเกิดขึ้นบนข้อความ IR ก่อนเรนเดอร์
- การจัดรูปแบบแบบอินไลน์จะไม่ถูกแยกข้ามชิ้น ช่วงต่างๆ จะถูกตัดแบ่งตามแต่ละชิ้น
- เรนเดอร์ตามช่องทาง
- Slack: โทเคน mrkdwn (ตัวหนา/ตัวเอียง/ขีดฆ่า/โค้ด), ลิงก์เป็น
<url|label> - Telegram: แท็ก HTML (
<b>,<i>,<s>,<code>,<pre><code>,<a href>) - Signal: ข้อความล้วน + ช่วง
text-style; ลิงก์จะกลายเป็นlabel (url)เมื่อป้ายกำกับต่างจาก URL
- Slack: โทเคน mrkdwn (ตัวหนา/ตัวเอียง/ขีดฆ่า/โค้ด), ลิงก์เป็น
ตัวอย่าง IR
อินพุต Markdown:
Hello **world** - see [docs](https://docs.openclaw.ai).
IR (แบบแผนภาพ):
{
"text": "Hello world - see docs.",
"styles": [{ "start": 6, "end": 11, "style": "bold" }],
"links": [{ "start": 19, "end": 23, "href": "https://docs.openclaw.ai" }]
}
ใช้ที่ใด
- อะแดปเตอร์ขาออกของ Slack, Telegram และ Signal เรนเดอร์จาก IR
- ช่องทางอื่นๆ (WhatsApp, iMessage, Microsoft Teams, Discord) ยังใช้ข้อความล้วนหรือกฎการจัดรูปแบบของตนเอง โดยมีการแปลงตาราง Markdown ก่อนการแบ่งเป็นชิ้นเมื่อเปิดใช้งาน
การจัดการตาราง
ตาราง Markdown ไม่ได้รับการรองรับอย่างสม่ำเสมอในทุกไคลเอนต์แชต ใช้ markdown.tables เพื่อควบคุมการแปลงในแต่ละช่องทาง (และแต่ละบัญชี)
code: เรนเดอร์ตารางเป็นบล็อกโค้ด (ค่าเริ่มต้นสำหรับช่องทางส่วนใหญ่)bullets: แปลงแต่ละแถวเป็นหัวข้อย่อย (ค่าเริ่มต้นสำหรับ Signal + WhatsApp)off: ปิดการแยกวิเคราะห์และการแปลงตาราง ข้อความตารางดิบจะถูกส่งผ่านไป
คีย์การตั้งค่า:
channels:
discord:
markdown:
tables: code
accounts:
work:
markdown:
tables: off
กฎการแบ่งเป็นชิ้น
- ขีดจำกัดของชิ้นมาจากอะแดปเตอร์/การตั้งค่าของช่องทาง และจะถูกใช้กับข้อความ IR
- รั้วโค้ดจะถูกรักษาไว้เป็นบล็อกเดียวพร้อมขึ้นบรรทัดใหม่ท้ายบล็อก เพื่อให้ช่องทางต่างๆ เรนเดอร์ได้ถูกต้อง
- คำนำหน้ารายการและคำนำหน้า blockquote เป็นส่วนหนึ่งของข้อความ IR ดังนั้นการแบ่งเป็นชิ้นจะไม่ตัดกลางคำนำหน้า
- สไตล์แบบอินไลน์ (ตัวหนา/ตัวเอียง/ขีดฆ่า/โค้ดอินไลน์/สปอยล์) จะไม่ถูกแยกข้ามชิ้น เรนเดอร์เรอร์จะเปิดสไตล์ใหม่ภายในแต่ละชิ้น
หากต้องการรายละเอียดเพิ่มเติมเกี่ยวกับพฤติกรรมการแบ่งเป็นชิ้นข้ามช่องทาง โปรดดู การสตรีม + การแบ่งเป็นชิ้น
นโยบายลิงก์
- Slack:
[label](url)-><url|label>; URL เปล่ายังคงเป็น URL เปล่า Autolink จะถูกปิดระหว่างการแยกวิเคราะห์เพื่อหลีกเลี่ยงการลิงก์ซ้ำ - Telegram:
[label](url)-><a href="url">label</a>(โหมดแยกวิเคราะห์ HTML) - Signal:
[label](url)->label (url)เว้นแต่ว่าป้ายกำกับตรงกับ URL
สปอยล์
มาร์กเกอร์สปอยล์ (||spoiler||) จะถูกแยกวิเคราะห์เฉพาะสำหรับ Signal ซึ่งจะถูกแมปเป็นช่วงสไตล์ SPOILER ช่องทางอื่นจะถือว่าเป็นข้อความล้วน
วิธีเพิ่มหรืออัปเดตตัวจัดรูปแบบของช่องทาง
- แยกวิเคราะห์ครั้งเดียว: ใช้ตัวช่วย
markdownToIR(...)ที่ใช้ร่วมกัน พร้อมตัวเลือกที่เหมาะกับช่องทาง (autolink, สไตล์หัวเรื่อง, คำนำหน้า blockquote) - เรนเดอร์: ใช้งานเรนเดอร์เรอร์ด้วย
renderMarkdownWithMarkers(...)และแมปมาร์กเกอร์สไตล์ (หรือช่วงสไตล์ของ Signal) - แบ่งเป็นชิ้น: เรียก
chunkMarkdownIR(...)ก่อนเรนเดอร์ จากนั้นเรนเดอร์แต่ละชิ้น - เชื่อมอะแดปเตอร์: อัปเดตอะแดปเตอร์ขาออกของช่องทางให้ใช้ตัวแบ่งชิ้นและเรนเดอร์เรอร์ใหม่
- ทดสอบ: เพิ่มหรืออัปเดตการทดสอบรูปแบบ และการทดสอบการส่งมอบขาออกหากช่องทางใช้การแบ่งเป็นชิ้น
ข้อควรระวังทั่วไป
- โทเคนวงเล็บมุมของ Slack (
<@U123>,<#C123>,<https://...>) ต้องถูกรักษาไว้ และต้อง escape HTML ดิบอย่างปลอดภัย - Telegram HTML ต้อง escape ข้อความนอกแท็กเพื่อหลีกเลี่ยงมาร์กอัปที่เสีย
- ช่วงสไตล์ของ Signal ขึ้นอยู่กับออฟเซ็ต UTF-16 อย่าใช้ออฟเซ็ตแบบ code point
- รักษาการขึ้นบรรทัดใหม่ท้ายบล็อกสำหรับบล็อกโค้ดแบบมีรั้ว เพื่อให้มาร์กเกอร์ปิดอยู่บนบรรทัดของตัวเอง