Concept internals

Форматирование Markdown

OpenClaw форматирует исходящий Markdown, преобразуя его в общее промежуточное представление (IR) перед рендерингом вывода для конкретного канала. IR сохраняет исходный текст без изменений, одновременно передавая диапазоны стилей/ссылок, чтобы разбиение на фрагменты и рендеринг оставались согласованными между каналами.

Цели

  • Согласованность: один этап разбора, несколько рендереров.
  • Безопасное разбиение: разделение текста перед рендерингом, чтобы встроенное форматирование никогда не разрывалось между фрагментами.
  • Соответствие каналу: сопоставление одного и того же IR с Slack mrkdwn, Telegram HTML и диапазонами стилей Signal без повторного разбора Markdown.

Конвейер

  1. Разбор Markdown -> IR
    • IR — это обычный текст плюс диапазоны стилей (жирный/курсив/зачеркнутый/код/спойлер) и диапазоны ссылок.
    • Смещения указаны в кодовых единицах UTF-16, чтобы диапазоны стилей Signal совпадали с его API.
    • Таблицы разбираются только тогда, когда канал включает преобразование таблиц.
  2. Разбиение IR (сначала форматирование)
    • Разбиение выполняется по тексту IR перед рендерингом.
    • Встроенное форматирование не разделяется между фрагментами; диапазоны нарезаются для каждого фрагмента.
  3. Рендеринг для каждого канала
    • Slack: токены mrkdwn (жирный/курсив/зачеркнутый/код), ссылки как <url|label>.
    • Telegram: HTML-теги (<b>, <i>, <s>, <code>, <pre><code>, <a href>).
    • Signal: обычный текст + диапазоны text-style; ссылки становятся label (url), когда метка отличается.

Пример IR

Входной Markdown:

markdown
Hello **world** - see [docs](https://docs.openclaw.ai).

IR (схематично):

json
{  "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: преобразовывать каждую строку в пункты маркированного списка (по умолчанию для Matrix, Signal и WhatsApp).
  • off: отключить разбор и преобразование таблиц; исходный текст таблицы передается дальше.

Ключи конфигурации:

yaml
channels:  discord:    markdown:      tables: code    accounts:      work:        markdown:          tables: off

Правила разбиения

  • Лимиты фрагментов берутся из адаптеров/конфигурации каналов и применяются к тексту IR.
  • Блоки кода сохраняются как единый блок с завершающим переводом строки, чтобы каналы корректно их рендерили.
  • Префиксы списков и цитат являются частью текста IR, поэтому разбиение не происходит внутри префикса.
  • Встроенные стили (жирный/курсив/зачеркнутый/встроенный код/спойлер) никогда не разделяются между фрагментами; рендерер заново открывает стили внутри каждого фрагмента.

Если вам нужно больше информации о поведении разбиения между каналами, см. Потоковая передача + разбиение.

Политика ссылок

  • Slack: [label](url) -> <url|label>; голые URL остаются голыми. Автоссылки отключены во время разбора, чтобы избежать двойного связывания.
  • Telegram: [label](url) -> <a href="url">label</a> (режим разбора HTML).
  • Signal: [label](url) -> label (url), если метка не совпадает с URL.

Спойлеры

Маркеры спойлеров (||spoiler||) разбираются только для Signal, где они сопоставляются с диапазонами стиля SPOILER. Другие каналы обрабатывают их как обычный текст.

Как добавить или обновить форматтер канала

  1. Разобрать один раз: используйте общий помощник markdownToIR(...) с подходящими для канала параметрами (автоссылки, стиль заголовков, префикс цитаты).
  2. Отрендерить: реализуйте рендерер с renderMarkdownWithMarkers(...) и картой маркеров стилей (или диапазонами стилей Signal).
  3. Разбить: вызовите chunkMarkdownIR(...) перед рендерингом; отрендерьте каждый фрагмент.
  4. Подключить адаптер: обновите исходящий адаптер канала, чтобы он использовал новый разделитель и рендерер.
  5. Протестировать: добавьте или обновите тесты форматирования и тест исходящей доставки, если канал использует разбиение.

Частые подводные камни

  • Токены Slack в угловых скобках (<@U123>, <#C123>, <https://...>) должны сохраняться; безопасно экранируйте необработанный HTML.
  • Telegram HTML требует экранирования текста вне тегов, чтобы избежать поврежденной разметки.
  • Диапазоны стилей Signal зависят от смещений UTF-16; не используйте смещения по кодовым точкам.
  • Сохраняйте завершающие переводы строк для огражденных блоков кода, чтобы закрывающие маркеры попадали на отдельную строку.

Связанные материалы

Was this useful?
On this page

On this page