MENU

なぜMarkdownで**「ほげ」**は太字にならないのか?仕様の背景とLLM時代の課題

Markdownで文章を書いている際、全角のかぎ括弧の外側をアスタリスクで囲むと、太字にならずそのまま出力されてしまう現象に遭遇することがあります。一見するとパーサーの不具合のように思えますが、この挙動の根底には標準規格であるCommonMarkの仕様と、マルチバイト言語特有の構造的な乖離が存在します。さらに近年では、LLM(大規模言語モデル)のAPIを活用したアプリケーション開発においても、この仕様差がUI崩れを引き起こす要因として再浮上しています。本稿では、この問題の背景にある技術的な理由と、実践的な対策についてまとめます。

目次

CommonMarkの仕様とマルチバイト言語の構造的な乖離

Markdownの標準規格であるCommonMarkでは、アスタリスクを用いた強調構文に対して厳密な境界判定ルール(Flanking Delimiter)が定義されています。英語をはじめとするラテン文字圏では、単語がスペースで区切られるため、前後に空白があるか英数字があるかで太字の開始・終了を容易に識別できます。しかし、全角の「」や『』といった日本語の記号は、Unicodeにおいて句読点・記号(Punctuation)に分類されます。

スペースを挟まずに全角記号とアスタリスクが隣接すると、CommonMarkの判定基準では「太字の開始・終了条件を満たしていない」とみなされます。さらに、一部の古いパーサーや簡易的な処理系では、半角英数字を前提とした単語境界(\b)で正規表現マッチングを行っているため、全角記号を単語の区切りとして認識できません。つまり、規格上は修飾記号を無視してテキストとして出力するのが正しい挙動となります。

しかし、日本語の文脈でスペースを空けずに括弧を強調したいケースは日常的に発生します。そのため、QiitaやZenn、Notionといった主要なサービスや一部のレンダラーでは、CJK向けに構文解析を独自拡張したり、前処理を挟むことで意図通りの表示を実現しています。結果として、利用するパーサーや環境によって太字になる場合とならない場合が分かれるという互換性の問題が生じています。

LLMの出力傾向と「AI生成コンテンツ」の痕跡

この仕様問題は、ChatGPTやGeminiなどのLLMが普及したことで、エンドユーザーの目に触れる機会が急増しました。多くのLLMは英語の学習データをベースにしており、英語圏で一般的な引用符の強調表現を日本語生成時にもそのまま適用してしまう傾向がありました。その結果、かぎ括弧の外側をアスタリスクで囲んだMarkdownが大量に出力される事態となりました。

生成されたテキストを一般的なMarkdownパーサーに通すと、アスタリスクが解釈されずにそのまま残るため、一時期はAIが生成したテキストをそのままコピー&ペーストした痕跡として広く認知されることになりました。現在、主要なLLMのWebサービス上では、システムプロンプトの調整や後処理によって出力が自然になるよう改善が進んでいますが、モデル本来のトークン生成傾向が根本から消滅したわけではありません。

API連携とアプリケーション開発における実践的な対策

Web版のUIでは対策されていても、API経由でLLMを呼び出し、取得した生のレスポンスをフロントエンドの標準的なMarkdownライブラリ(markedreact-markdown など)へ直接流し込むシステムでは、現在でも同様のレンダリング崩れが発生します。

この問題を防ぐため、LLMを活用したアプリケーションの実装では主に以下の二通りの対策が取られます。

  • システムプロンプトによる出力制御
    モデルに対して、強調を行う場合は括弧の外側ではなく「内側」をアスタリスクで囲むよう指示を明記します。
  • パーサー手前での正規表現置換(プリプロセス)
    フロントエンドやバックエンドでMarkdownをHTMLに変換する直前に、正規表現を用いて構文を置換します。
// レンダリング直前に括弧の外側のアスタリスクを内側へ補正する例
function sanitizeMarkdown(text) {
  return text.replace(/\*\*([「『(].*?[」』)])\*\*/g, (match, p1) => {
    const openChar = p1.slice(0, 1);
    const closeChar = p1.slice(-1);
    const innerText = p1.slice(1, -1);
    return `${openChar}**${innerText}**${closeChar}`;
  });
}

仕様上は無視されるのが正しい動作であっても、日本語圏のUXを考慮すると自然な表示が求められます。LLMのAPIを利用した開発においては、モデルの出力精度だけに依存せず、パーサーへの入力手前で安全な形式へと正規化する処理を組み込んでおくことが安定運用の鍵となります。

目次