Concept internals
Форматирование Markdown
OpenClaw преобразует исходящий Markdown в общее промежуточное представление (IR) перед формированием вывода для конкретного канала. IR хранит обычный текст вместе с диапазонами стилей и ссылок, поэтому один этап синтаксического анализа обслуживает все каналы, а разбиение на фрагменты никогда не разделяет форматирование внутри диапазона.
Конвейер
- Преобразование Markdown в IR (
markdownToIR) — обычный текст и диапазоны стилей (полужирный, курсив, зачёркнутый, код, блок кода, спойлер, цитата, заголовок 1-6), а также диапазоны ссылок. Смещения измеряются в кодовых единицах UTF-16, поэтому диапазоны стилей Signal напрямую соответствуют его API. Таблицы анализируются только тогда, когда канал включает один из режимов таблиц. - Разбиение IR на фрагменты (
chunkMarkdownIR/renderMarkdownIRChunksWithinLimit)- разбиение выполняется по тексту IR перед формированием вывода, поэтому встроенные стили и ссылки разделяются по фрагментам, а не обрываются на границе.
- Формирование вывода для каждого канала (
renderMarkdownWithMarkers) — карта маркеров стилей преобразует диапазоны во встроенную разметку канала.
| Канал | Средство формирования вывода | Примечания |
|---|---|---|
| Slack | токены mrkdwn (*bold*, _italic_, `code`, ограждения кода) |
Ссылки преобразуются в <url|label>; автоматическое распознавание ссылок отключено при анализе во избежание дублирования ссылок |
| Telegram | HTML-теги (<b>, <i>, <s>, <code>, <pre><code>, <a href>, <tg-spoiler>) |
Также поддерживает таблицы и заголовки форматированных сообщений (<h1>-<h6>), когда включён richMessages |
| Signal | обычный текст + диапазоны text-style |
Ссылки формируются как label (url), когда подпись отличается от URL |
| Discord, WhatsApp, iMessage, Microsoft Teams и другие каналы | обычный текст | Стилизация на основе IR отсутствует; преобразование таблиц Markdown по-прежнему выполняется через convertMarkdownTables |
Пример IR
Входной Markdown:
Привет, **мир**! См. [документацию](https://docs.openclaw.ai).IR (схематично):
{ "text": "Привет, мир! См. документацию.", "styles": [{ "start": 6, "end": 11, "style": "bold" }], "links": [{ "start": 19, "end": 23, "href": "https://docs.openclaw.ai" }]}Обработка таблиц
markdown.tables определяет, как канал преобразует таблицы Markdown, отдельно для
каждого канала и при необходимости для каждой учётной записи:
| Режим | Поведение |
|---|---|
code |
Формировать выровненную ASCII-таблицу внутри блока кода (режим по умолчанию при откате) |
bullets |
Преобразовывать каждую строку в пункты маркированного списка label: value |
block |
Сохранять нативные таблицы, если транспорт их поддерживает; иначе переходить к code |
off |
Отключить анализ таблиц; исходный текст таблицы передаётся без изменений |
Значения плагинов по умолчанию для каналов: Signal, WhatsApp и Matrix по умолчанию используют
bullets; Mattermost — off; Telegram — block (который
разрешается в code, если для учётной записи не включён richMessages). Любой
канал без явно заданного значения плагина по умолчанию переходит к code.
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)
и Telegram (преобразуются в <tg-spoiler>). Другие каналы обрабатывают
||...|| как обычный текст.
Добавление или обновление средства форматирования канала
- Выполните анализ один раз с помощью
markdownToIR(...), передав подходящие для канала параметры (autolink,headingStyle,blockquotePrefix,tableMode). - Сформируйте вывод с помощью
renderMarkdownWithMarkers(...)и карты маркеров стилей (либо пользовательской логики диапазонов стилей для таких транспортов, как Signal). - Разбейте на фрагменты с помощью
chunkMarkdownIR(...)илиrenderMarkdownIRChunksWithinLimit(...)перед формированием каждого фрагмента. - Подключите адаптер, чтобы новый механизм разбиения и средство формирования вывода вызывались из пути отправки исходящих сообщений.
- Протестируйте с помощью тестов форматирования и теста исходящей доставки, если канал разбивает сообщения на фрагменты.
Распространённые ошибки
- Токены Slack в угловых скобках (
<@U123>,<#C123>,<https://...>) должны сохраняться при экранировании; необработанный HTML по-прежнему необходимо безопасно экранировать. - Для HTML в Telegram необходимо экранировать текст за пределами тегов, чтобы избежать повреждения разметки.
- Диапазоны стилей Signal используют смещения UTF-16, а не смещения по кодовым точкам.
- Сохраняйте завершающие переводы строк в ограждённых блоках кода, чтобы закрывающий маркер располагался на отдельной строке.