Внутренние концепции

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

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

Цели

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

Конвейер обработки

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

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

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

  • 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 безопасным образом.
  • HTML для Telegram требует экранирования текста вне тегов, чтобы избежать поврежденной разметки.
  • Диапазоны стилей Signal зависят от смещений UTF-16; не используйте смещения в кодовых точках.
  • Сохраняйте завершающие переводы строк для блоков кода (fenced code blocks), чтобы закрывающие маркеры оставались на отдельной строке.

TypeBoxИндикаторы набора текста