Форматирование Markdown
OpenClaw форматирует исходящий Markdown, преобразуя его в общее промежуточное представление (IR) перед рендерингом вывода, специфичного для каналов. IR сохраняет исходный текст без изменений, при этом содержит диапазоны стилей/ссылок, что позволяет поддерживать согласованность разбиения на чанки и рендеринга между каналами.Цели
- Согласованность: один шаг парсинга, несколько рендереров.
- Безопасное разбиение на чанки: разделение текста до рендеринга, чтобы встроенное форматирование никогда не разрывалось между чанками.
- Соответствие каналу: сопоставление одного и того же IR со Slack mrkdwn, Telegram HTML и диапазонами стилей Signal без повторного парсинга Markdown.
Трубопровод
- Парсинг Markdown -> IR
- IR — это обычный текст плюс диапазоны стилей (жирный/курсив/зачёркнутый/код/спойлер) и диапазоны ссылок.
- Смещения задаются в кодовых единицах UTF-16, чтобы диапазоны стилей Signal совпадали с его API.
- Таблицы парсятся только тогда, когда канал явно включает преобразование таблиц.
- Разбиение IR на чанки (сначала формат)
- Разбиение на чанки выполняется по тексту IR до рендеринга.
- Встроенное форматирование не разрывается между чанками; диапазоны нарезаются по чанкам.
- Рендеринг по каналам
- Slack: токены mrkdwn (жирный/курсив/зачёркнутый/код), ссылки как
<url|label>. - Telegram: HTML-теги (
<b>,<i>,<s>,<code>,<pre><code>,<a href>). - Signal: обычный текст + диапазоны
text-style; ссылки становятсяlabel (url), если подпись отличается.
- Slack: токены mrkdwn (жирный/курсив/зачёркнутый/код), ссылки как
Пример IR
Входной Markdown:Где используется
- Исходящие адаптеры Slack, Telegram и Signal рендерят из IR.
- Другие каналы (WhatsApp, iMessage, MS Teams, Discord) по-прежнему используют обычный текст или собственные правила форматирования; при включении преобразование таблиц Markdown применяется до разбиения на чанки.
Обработка таблиц
Таблицы Markdown поддерживаются чат-клиентами непоследовательно. Используйтеmarkdown.tables для управления преобразованием по каналам (и по аккаунтам).
code: рендерить таблицы как блоки кода (по умолчанию для большинства каналов).bullets: преобразовывать каждую строку в маркеры списка (по умолчанию для Signal + WhatsApp).off: отключить парсинг и преобразование таблиц; «сырой» текст таблицы проходит без изменений.
Правила чанков
- Лимиты чанков задаются адаптерами каналов/конфигом и применяются к тексту IR.
- Кодовые блоки (fenced) сохраняются как единый блок с завершающим переводом строки, чтобы каналы корректно их рендерили.
- Префиксы списков и цитат являются частью текста 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. Другие каналы трактуют их как обычный текст.
Как добавить или обновить форматтер канала
- Парсить один раз: используйте общий хелпер
markdownToIR(...)с параметрами, подходящими для канала (autolink, стиль заголовков, префикс цитат). - Рендерить: реализуйте рендерер с
renderMarkdownWithMarkers(...)и картой маркеров стилей (или диапазонами стилей Signal). - Разбить на чанки: вызовите
chunkMarkdownIR(...)до рендеринга; отрендерьте каждый чанк. - Подключить адаптер: обновите исходящий адаптер канала, чтобы он использовал новый чанкёр и рендерер.
- Тестировать: добавьте или обновите тесты форматирования и тест доставки, если канал использует разбиение на чанки.
Распространенные горы
- Угловые токены Slack (
<@U123>,<#C123>,<https://...>) необходимо сохранять; безопасно экранируйте «сырой» HTML. - Telegram HTML требует экранирования текста вне тегов, чтобы избежать поломки разметки.
- Диапазоны стилей Signal зависят от смещений UTF-16; не используйте смещения по кодовым точкам.
- Сохраняйте завершающие переводы строк для ограждённых блоков кода, чтобы закрывающие маркеры располагались на собственной строке.