Concept internals

Markdown 서식

OpenClaw은 발신 Markdown을 채널별 출력으로 렌더링하기 전에 공유 중간 표현 (IR)으로 변환합니다. IR은 일반 텍스트와 스타일/링크 범위를 유지하므로 한 번의 파싱 단계로 모든 채널을 처리할 수 있으며, 청크 분할 시 범위 중간에서 서식이 나뉘지 않습니다.

파이프라인

  1. Markdown을 IR로 파싱 (markdownToIR) - 일반 텍스트와 스타일 범위 (굵게, 기울임꼴, 취소선, 코드, 코드 블록, 스포일러, 인용문, 제목 1~6) 및 링크 범위입니다. 오프셋은 UTF-16 코드 단위이므로 Signal 스타일 범위가 해당 API와 직접 정렬됩니다. 표는 채널이 표 모드를 사용하도록 설정된 경우에만 파싱됩니다.
  2. IR 청크 분할 (chunkMarkdownIR / renderMarkdownIRChunksWithinLimit)
    • 렌더링 전에 IR 텍스트를 분할하므로 인라인 스타일과 링크가 경계를 가로질러 끊어지는 대신 청크별로 분할됩니다.
  3. 채널별 렌더링 (renderMarkdownWithMarkers) - 스타일 마커 맵이 범위를 채널의 네이티브 마크업으로 변환합니다.
채널 렌더러 참고
Slack mrkdwn 토큰 (*bold*, _italic_, `code`, 코드 펜스) 링크는 <url|label>이 되며, 중복 링크를 방지하기 위해 파싱 중 자동 링크를 비활성화합니다
Telegram HTML 태그 (<b>, <i>, <s>, <code>, <pre><code>, <a href>, <tg-spoiler>) richMessages가 켜져 있으면 리치 메시지 표와 제목(<h1>-<h6>)도 지원합니다
Signal 일반 텍스트 + text-style 범위 레이블이 URL과 다르면 링크를 label (url)로 렌더링합니다
Discord, WhatsApp, iMessage, Microsoft Teams 및 기타 채널 일반 텍스트 IR 기반 스타일링은 없으며, Markdown 표 변환은 여전히 convertMarkdownTables를 통해 실행됩니다

IR 예시

입력 Markdown: OC_I18N_900000 IR(개략도): OC_I18N_900001

표 처리

markdown.tables는 채널별로, 선택적으로 계정별로 채널이 Markdown 표를 변환하는 방식을 제어합니다.

모드 동작
code 코드 블록 안에 정렬된 ASCII 표로 렌더링합니다(대체 기본값)
bullets 각 행을 label: value 글머리 기호 항목으로 변환합니다
block 전송 수단에서 지원하는 경우 네이티브 표를 유지하고, 그렇지 않으면 code로 대체합니다
off 표 파싱을 비활성화하며, 원시 표 텍스트를 변경 없이 전달합니다

채널별 Plugin 기본값: Signal, WhatsApp, Matrix의 기본값은 bullets이고, Mattermost의 기본값은 off이며, Telegram의 기본값은 block입니다 (계정에서 richMessages가 활성화되어 있지 않으면 code로 해석됨). 명시적인 Plugin 기본값이 없는 채널은 code를 대체 기본값으로 사용합니다. OC_I18N_900002

청크 분할 규칙

  • 청크 제한은 채널 어댑터/구성에서 가져오며 렌더링된 출력이 아니라 IR 텍스트에 적용됩니다.
  • 펜스로 둘러싸인 코드 블록은 채널이 닫는 펜스를 올바르게 렌더링할 수 있도록 후행 줄 바꿈이 포함된 하나의 블록으로 유지됩니다.
  • 목록 및 인용문 접두사는 IR 텍스트의 일부이므로 청크 분할 시 접두사 중간에서 나뉘지 않습니다.
  • 인라인 스타일은 청크 사이에서 절대 분할되지 않으며, 렌더러는 다음 청크의 시작 부분에서 열린 스타일을 다시 엽니다.

채널 간 청크 경계 및 전달 동작은 스트리밍 및 청크 분할을 참조하세요.

링크 정책

  • Slack: [label](url) -> <url|label>이며, 단독 URL은 그대로 유지됩니다.
  • Telegram: [label](url) -> <a href="url">label</a>입니다(HTML 파싱 모드).
  • Signal: 레이블이 이미 URL과 일치하지 않는 한 [label](url) -> label (url)입니다.

스포일러

스포일러 마커(||spoiler||)는 Signal(SPOILER 스타일 범위에 매핑)과 Telegram(<tg-spoiler>에 매핑)에서 파싱됩니다. 다른 채널은 ||...||을 일반 텍스트로 처리합니다.

채널 포매터 추가 또는 업데이트

  1. 채널에 적합한 옵션(autolink, headingStyle, blockquotePrefix, tableMode)을 전달하여 markdownToIR(...)한 번만 파싱합니다.
  2. 스타일 마커 맵과 함께 renderMarkdownWithMarkers(...)렌더링합니다 (또는 Signal과 같은 전송 수단에는 사용자 지정 스타일 범위 로직을 사용합니다).
  3. 각 청크를 렌더링하기 전에 chunkMarkdownIR(...) 또는 renderMarkdownIRChunksWithinLimit(...)청크를 분할합니다.
  4. 발신 전송 경로에서 새 청크 분할기와 렌더러를 호출하도록 어댑터를 연결합니다.
  5. 채널에서 청크를 분할하는 경우 형식 테스트와 발신 전달 테스트로 테스트합니다.

흔히 발생하는 문제

  • Slack 꺾쇠괄호 토큰(<@U123>, <#C123>, <https://...>)은 이스케이프 처리 후에도 유지되어야 하며, 원시 HTML은 여전히 안전하게 이스케이프해야 합니다.
  • Telegram HTML에서는 마크업이 손상되지 않도록 태그 외부의 텍스트를 이스케이프해야 합니다.
  • Signal 스타일 범위는 코드 포인트 오프셋이 아닌 UTF-16 오프셋을 사용합니다.
  • 닫는 마커가 독립된 줄에 위치하도록 펜스로 둘러싸인 코드 블록의 후행 줄 바꿈을 유지하세요.

관련 항목

Was this useful?
On this page

On this page