การจัดรูปแบบMarkdown¶
OpenClawจัดรูปแบบMarkdownขาออกโดยแปลงเป็นการแทนค่ากลางร่วม (intermediate representation: IR)ก่อน จากนั้นจึงเรนเดอร์เป็นเอาต์พุตเฉพาะช่องทาง IRจะคงข้อความต้นฉบับไว้ครบถ้วน พร้อมพกพาช่วงสไตล์/ลิงก์ เพื่อให้การแบ่งชิ้นและการเรนเดอร์คงความสอดคล้องกันข้ามช่องทาง The IR keeps the source text intact while carrying style/link spans so chunking and rendering can stay consistent across channels.
เป้าหมาย¶
- ความสอดคล้อง: แยกขั้นตอนการพาร์สครั้งเดียว ใช้เรนเดอร์หลายแบบ
- การแบ่งชิ้นที่ปลอดภัย: แบ่งข้อความก่อนเรนเดอร์ เพื่อให้การจัดรูปแบบแบบอินไลน์ไม่แตกข้ามชิ้น
- เหมาะกับช่องทาง: แมปIRเดียวกันไปเป็นSlack mrkdwn, Telegram HTML และช่วงสไตล์ของSignalโดยไม่ต้องพาร์สMarkdownซ้ำ
Pipeline¶
- พาร์สMarkdown -> IR - IRคือข้อความธรรมดาบวกช่วงสไตล์(bold/italic/strike/code/spoiler)และช่วงลิงก์ - ออฟเซ็ตเป็นหน่วยรหัสUTF-16เพื่อให้ช่วงสไตล์ของSignalตรงกับAPI - ตารางจะถูกพาร์สเฉพาะเมื่อช่องทางเลือกใช้การแปลงตาราง
- แบ่งชิ้นIR (format-first) - การแบ่งชิ้นทำบนข้อความIRก่อนการเรนเดอร์ - การจัดรูปแบบแบบอินไลน์จะไม่ถูกตัดข้ามชิ้น; ช่วงจะถูกตัดตามชิ้น
- เรนเดอร์ตามช่องทาง
- Slack: โทเคนmrkdwn(bold/italic/strike/code), ลิงก์เป็น
<url|label>- Telegram: แท็กHTML(<b>,<i>,<s>,<code>,<pre><code>,<a href>) - Signal: ข้อความธรรมดา+ช่วงtext-style; ลิงก์จะเป็นlabel (url)เมื่อป้ายกำกับต่างจากURL
ตัวอย่างIR¶
Input 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, MS Teams, Discord)ยังใช้ข้อความธรรมดาหรือกฎการจัดรูปแบบของตนเอง โดยจะมีการแปลงตารางMarkdownก่อนการแบ่งชิ้นเมื่อเปิดใช้งาน
การจัดการตาราง¶
ตารางMarkdownไม่ได้รับการรองรับอย่างสม่ำเสมอในไคลเอนต์แชต ใช้
markdown.tablesเพื่อควบคุมการแปลงตามช่องทาง(และต่อบัญชี) Use
markdown.tables to control conversion per channel (and per account).
code: เรนเดอร์ตารางเป็นบล็อกโค้ด(ค่าเริ่มต้นสำหรับช่องทางส่วนใหญ่)bullets: แปลงแต่ละแถวเป็นรายการหัวข้อย่อย(ค่าเริ่มต้นสำหรับSignal+WhatsApp)off: ปิดการพาร์สและการแปลงตาราง; ข้อความตารางดิบจะผ่านไปตรงๆ
คีย์คอนฟิก:
channels:
discord:
markdown:
tables: code
accounts:
work:
markdown:
tables: off
กฎการแบ่งชิ้น¶
- ขีดจำกัดการแบ่งชิ้นมาจากอะแดปเตอร์/คอนฟิกของช่องทางและถูกใช้กับข้อความIR
- โค้ดเฟนซ์จะถูกรักษาเป็นบล็อกเดียวพร้อมขึ้นบรรทัดใหม่ท้ายบล็อก เพื่อให้ช่องทางเรนเดอร์ได้ถูกต้อง
- คำนำหน้ารายการและคำนำหน้าบล็อกโควตเป็นส่วนหนึ่งของข้อความIR ดังนั้นการแบ่งชิ้นจะไม่ตัดกลางคำนำหน้า
- สไตล์อินไลน์(bold/italic/strike/inline-code/spoiler)จะไม่ถูกตัดข้ามชิ้น; ตัวเรนเดอร์จะเปิดสไตล์ซ้ำภายในแต่ละชิ้น
หากต้องการรายละเอียดเพิ่มเติมเกี่ยวกับพฤติกรรมการแบ่งชิ้นข้ามช่องทาง โปรดดู Streaming + chunking
นโยบายลิงก์¶
- Slack:
[label](url)-><url|label>; URLเปล่ายังคงเป็นเปล่า ปิดautolinkระหว่างการพาร์สเพื่อหลีกเลี่ยงการลิงก์ซ้ำ Autolink is disabled during parse to avoid double-linking. - Telegram:
[label](url)-><a href="url">label</a>(โหมดพาร์สHTML) - Signal:
[label](url)->label (url)เว้นแต่ป้ายกำกับจะตรงกับURL
Spoilers¶
Spoiler markers (||spoiler||) are parsed only for Signal, where they map to
SPOILER style ranges. Other channels treat them as plain text.
วิธีเพิ่มหรืออัปเดตตัวจัดรูปแบบช่องทาง¶
- พาร์สครั้งเดียว: ใช้ตัวช่วยร่วม
markdownToIR(...)พร้อมตัวเลือกที่เหมาะกับช่องทาง(autolink, สไตล์หัวข้อ, คำนำหน้าบล็อกโควต) - เรนเดอร์: สร้างตัวเรนเดอร์ด้วย
renderMarkdownWithMarkers(...)และแผนที่ตัวทำเครื่องหมายสไตล์(หรือช่วงสไตล์ของSignal) - แบ่งชิ้น: เรียก
chunkMarkdownIR(...)ก่อนเรนเดอร์; เรนเดอร์ทีละชิ้น - เชื่อมอะแดปเตอร์: อัปเดตอะแดปเตอร์ขาออกของช่องทางให้ใช้ตัวแบ่งชิ้นและตัวเรนเดอร์ใหม่
- ทดสอบ: เพิ่มหรืออัปเดตการทดสอบรูปแบบ และการทดสอบการส่งขาออกหากช่องทางใช้การแบ่งชิ้น
Common gotchas¶
- โทเคนมุมของSlack(
<@U123>,<#C123>,<https://...>)ต้องถูกรักษาไว้; เอสเคปHTMLดิบอย่างปลอดภัย - HTMLของTelegramต้องเอสเคปข้อความนอกแท็กเพื่อหลีกเลี่ยงมาร์กอัปเสียหาย
- ช่วงสไตล์ของSignalพึ่งพาออฟเซ็ตUTF-16; ห้ามใช้ออฟเซ็ตตามโค้ดพอยต์
- รักษาขึ้นบรรทัดใหม่ท้ายบล็อกสำหรับโค้ดเฟนซ์ เพื่อให้ตัวปิดอยู่ในบรรทัดของตัวเอง