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

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

Цели

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

Конвейер

  1. Парсинг Markdown -> IR
    • IR — это простой текст плюс диапазоны стилей (bold/italic/strike/code/spoiler) и диапазоны ссылок.
    • Смещения — это кодовые единицы UTF-16, чтобы диапазоны стилей Signal соответствовали его API.
    • Таблицы парсятся только когда канал выбирает конвертацию таблиц.
  2. Чанкинг IR (сначала формат)
    • Чанкинг происходит на тексте IR перед рендерингом.
    • Встроенное форматирование не разделяется между чанками; диапазоны нарезаются для каждого чанка.
  3. Рендеринг для каждого канала
    • Slack: токены mrkdwn (bold/italic/strike/code), ссылки как <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.
  • Блоки кода сохраняются как один блок с замыкающей новой строкой, чтобы каналы рендерили их правильно.
  • Префиксы списков и префиксы blockquote являются частью текста IR, поэтому чанкинг не разделяет в середине префикса.
  • Встроенные стили (bold/italic/strike/inline-code/spoiler) никогда не разделяются между чанками; рендерер повторно открывает стили внутри каждого чанка.

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

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

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

Спойлеры

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

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

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

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

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