Перейти к основному содержанию

Форматирование 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:
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 для управления преобразованием по каналам (и по аккаунтам).
  • code: рендерить таблицы как блоки кода (по умолчанию для большинства каналов).
  • bullets: преобразовывать каждую строку в маркеры списка (по умолчанию для Signal + WhatsApp).
  • off: отключить парсинг и преобразование таблиц; «сырой» текст таблицы проходит без изменений.
Ключи конфига:
channels:
  discord:
    markdown:
      tables: code
    accounts:
      work:
        markdown:
          tables: off

Правила чанков

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

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

  • 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(...) с параметрами, подходящими для канала (autolink, стиль заголовков, префикс цитат).
  2. Рендерить: реализуйте рендерер с renderMarkdownWithMarkers(...) и картой маркеров стилей (или диапазонами стилей Signal).
  3. Разбить на чанки: вызовите chunkMarkdownIR(...) до рендеринга; отрендерьте каждый чанк.
  4. Подключить адаптер: обновите исходящий адаптер канала, чтобы он использовал новый чанкёр и рендерер.
  5. Тестировать: добавьте или обновите тесты форматирования и тест доставки, если канал использует разбиение на чанки.

Распространенные горы

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