LLM出力でMarkdownの入れ子コードブロックが崩れる問題と、インデント方式+ブックマークレットによる安定化

👉 結論だけ見たい人はこちら

概要

この記事はLLM(ChatGPT/Gemini/Claude等)が出力するMarkdownが、コードブロック使用時に表示が頻繁に“壊れる”ことに関して、仕様と実装の観点から整理し、現実的、そして比較的容易に安定させる運用方法の一つを提案する。決して「Markdownの正解」を示すものではなく、妥協できるであろうと思われる一例である。

👉 結論だけ見たい人はこちら


0. はじめに:何が壊れて、なぜ困るのか

実際にはChatGPTで検証したが、類似の問題は他の多くのLLMでもほぼ同様な問題が起きていると考えられる。「Markdownで説明して」「Markdownをコードブロックに入れて出力して」と頼むと、次のような現象が起きやすい。しかし、コピー&ペーストをするためには、コードブロックを使用したいというユーザー側の都合ももちろんある。

  • コードブロックの入れ子(ネスト)が途中で解除され、後続の文章がコードブロックの内外がしばしば逆転することがあり、表示が崩れる
  • 画面上の表示は一見よくても、他のMarkdown Viewer/Editorにコピー&ペーストした後に表示が崩れる
  • Obsidian、GitHub、VS Codeプレビュー等のMarkdown環境で、再利用しづらい

本記事のゴールは、

  • Markdown仕様(特に fenced code blocks)を根拠にしつつ
  • ChatGPT出力の癖とチャットUIの癖を踏まえ
  • 「Markdownの表示が壊れにくい運用方法」を提案すること(あくまでも現状の一つの解であり、改善の余地は大きい)

である。


1. Markdownのコードブロック:仕様の整理(最低限)

1.1 インデントコードブロック(4スペース)

Markdown原典(John Gruberの記法説明)では、コードブロックは主に

  • 各行の先頭に4スペース(または1タブ)

で表現される(いわゆる indented code block)。これが原型である。

特徴:

  • 仕様的に古く、実装差が比較的少ない
  • ただし、言語指定(syntax highlight)がしづらい(事実上できない)
  • 入れ子の表現がしやすい反面、可読性・編集性に癖がある

【図 1】Indent Code Blockの概念図

1.2 フェンスドコードブロック(``` / ~~~)

CommonMarkやGFM(GitHub Flavored Markdown)では、

  • 3連バッククォートのフェンス(```) ※4連以上も使える
  • 3連チルダのフェンス(~~~) ※4連以上も使える

で囲む fenced code block が正式に定義されている。

重要点:

  • フェンスの開始・終了は、同種のフェンス記号で行う
  • フェンス長(3以上)や、内部に同種のフェンスが現れる場合の扱いが規定される

図 2 CommonMarkの fenced code blocks の概念図(開始フェンス/終了フェンス/言語指定)


2. 「入れ子コードブロック」が壊れる理由:仕様と現実

2.1 仕様上のポイント:フェンス衝突

入れ子コードブロックで典型的に問題になるのは、

  • 外側が (```)のコードブロック
  • 内側も (```)を使う

という 同一デリミタ衝突である。

Markdownパーサは、外側の3連バッククォート(```)の内側にある3連バッククォート(```)を言語指定がなければ内側のコードブロック開始ではなく、

  • 外側コードブロック終了

と解釈し得る(実装や文脈、フェンス長により挙動差が出る)。

そして、例えば3連バッククォートに言語指定がついて(```javascript)などとなっている場合には、常にコードブロック開始強く解釈されるために、内側のコードブロック解除の3連バッククォート(```)のところで、内側と外側のコードブロックが同時に解除されてしまう。

このため、文章の表示が崩れ、以降の段落・見出しがコード扱いになったり、逆にコード扱いが解除されたりする。

図 3. 外側コードブロックの中に内側コードブロック が出現 → 外側コードブロックが閉じてしまう」模式図

2.2 実装差(Markdown方言)という現実

Markdownには単一の公式仕様がなく、

  • CommonMark
  • GFM
  • Pandoc等
  • エディタ内蔵レンダラ(Obsidian/VS Code等)

などが併存する。

コードブロック周りは比較的揃っているが、

  • 例外ケース
  • インラインコードや引用との組み合わせ
  • 入れ子の深さ

で、表示の仕様に差が出ることがある。

したがって本記事は「すべての環境で100%保証」するものではなく、

  • 主要環境で壊れにくい運用

を目的とする。


3. ChatGPTの問題:仕様外の“出力の癖”

この問題はMarkdown仕様だけでは説明しきれない。

ChatGPT(および他のLLM)出力においては、

  • 学習データ由来で(```)がデフォルトになりやすい
  • 途中で最内部のコードブロック解除時に表示が崩れる
  • “外側のコードブロックを維持する”という状態管理が苦手

といった理由で、

  • 仕様的にギリギリ成立する書き方であっても、

を高頻度で誤表示となる。

さらに、チャットUIのレンダリングと、コピー結果のテキストが一致しない(または期待通りでない)ケースもあり、

  • 画面で見えているものがそのまま正しくコピーして使えるとは限らない

という問題がある。

LLMの出力
   ↓
チャットUIでのレンダリング
(この時点ですでに構造が壊れている/曖昧)
   ↓
画面を直接コピー
(UI固有の構造や要素が混入)
   ↓
貼り付け
   ↓
Markdownパーサ
→ 表示崩壊が確定・拡大
LLM output
   ↓
Chat UI rendering
   (already broken or ambiguous)
   ↓ user tries to copy
Chat UI–specific structure mixed in
   ↓ paste
Markdown parser
   → further breakage

図 4.【概念図】チャットUI表示・コピー・再レンダリングの3段階で発生するズレ

※多くの場合、Markdownはコピー前のチャットUI段階ですでに壊れている
 その状態で画面を直接コピーすると、UI固有の構造が混入し、問題が拡大する。

別の問題としてもう一つ注意することがある。ChatGPTのWebChat UI の画面は一見してMarkdownのように見えるけれど、実際にはMarkdownと似てはいても、仕様に多少の違いがあるため、コピー&ペーストすると、一部Markdownとは違う仕様のテキストが混じる。例えば、コードブロックがある部分はMarkdownの仕様通りのテキストでコピーされないので、貼り付け先で修正が必要になる。


4. 実験(概要のみ):どの方法が“現実的に”安定したか

入れ子コードブロックを複数パターンで試した結果、実務上は次の傾向になった。

  • 4スペースインデント(indented code block)は強いが、言語指定や可読性に多少の難あり
  • 外側と内側で同じ(```)を使う場合は、3連と4連を混ぜても同一にしか扱われなかった
  • 外側と内側のデリミタを分離するのが望ましいが、最低でもCustomGPTの初期指示&高頻度のルール(指示)の再注入が必要なので、一般向きではない

本記事では実験の詳細は以下のアコーディオン部に畳んである。実験に使ったテキストや結果のスクリーンショットなどが多いので、興味のある人だけ開いて読んでほしい。それ以外の人は、飛ばして結論を読んでほしい

  • 4.1 6種類のテストパターン
    Six Patterns Texts for the Tests.
  • 4.2 ChatGPT WebChatのスクリーンショット
    Screenshots from ChatGPT

ここで、①~③は不合格で、④~⑥のみ貼付けテストに進んだ。
Cases 1–3 did not produce usable results. Only Cases 4–6 were selected for the copy-and-paste tests.

  • 4.3 Obsidianに貼り付け
    Obsidian paste results

④と⑤が最終候補として残る。
Among them, Cases 4 and 5 remained as the final practical candidates.

👉 結論までスキップする人はこちら



① 外に3連`markdown&中に3連`javascript

---
ChatGPT, GeminiなどでMarkdownを出力する際にはコードブロックにしてもらうとコピーしやすいのですが、その文中にコードがネストして出てくる場合は、意外と正しい表現が難しいのです。

```markdown
JavaScriptで簡単な計算結果を表示するには、以下のコードを使用します。


```javascript
let sum = 10 + 20;
console.log("合計は " + sum + " です。");
```

上記のコードは、```sum```という変数に10と20の合計を代入し、その結果をコンソールに出力します。
```
というのは、正しく表示されていますでしょうか?また、例えばObsidianにコピー&ペーストしてみたら、どうなりますか?


---

② 外は3連`markdown、中は4連`javascript

---
ChatGPT, GeminiなどでMarkdownを出力する際にはコードブロックにしてもらうとコピーしやすいのですが、その文中にコードがネストして出てくる場合は、意外と正しい表現が難しいのです。

```markdown
JavaScriptで簡単な計算結果を表示するには、以下のコードを使用します。


````javascript
let sum = 10 + 20;
console.log("合計は " + sum + " です。");
````

上記のコードは、```sum```という変数に10と20の合計を代入し、その結果をコンソールに出力します。
```
というのは、正しく表示されていますでしょうか?また、例えばObsidianにコピー&ペーストしてみたら、どうなりますか?


---

③ 外は3連`bash、中は3連`javascript

---
ChatGPT, GeminiなどでMarkdownを出力する際にはコードブロックにしてもらうとコピーしやすいのですが、その文中にコードがネストして出てくる場合は、意外と正しい表現が難しいのです。

```bash
JavaScriptで簡単な計算結果を表示するには、以下のコードを使用します。


```javascript
let sum = 10 + 20;
console.log("合計は " + sum + " です。");
```

上記のコードは、```sum```という変数に10と20の合計を代入し、その結果をコンソールに出力します。
```
というのは、正しく表示されていますでしょうか?また、例えばObsidianにコピー&ペーストしてみたら、どうなりますか?


---

④ 外は3連`markdown、中は3連~javascript

---
ChatGPT, GeminiなどでMarkdownを出力する際にはコードブロックにしてもらうとコピーしやすいのですが、その文中にコードがネストして出てくる場合は、意外と正しい表現が難しいのです。

```markdown
JavaScriptで簡単な計算結果を表示するには、以下のコードを使用します。


~~~javascript
let sum = 10 + 20;
console.log("合計は " + sum + " です。");
~~~

上記のコードは、```sum```という変数に10と20の合計を代入し、その結果をコンソールに出力します。
```
というのは、正しく表示されていますでしょうか?また、例えばObsidianにコピー&ペーストしてみたら、どうなりますか?


---

⑤ 外は3連`markdown、中は4連スペース(仕様上言語名が書けないが、これがオリジナルのコードブロックの記法)

---
ChatGPT, GeminiなどでMarkdownを出力する際にはコードブロックにしてもらうとコピーしやすいのですが、その文中にコードがネストして出てくる場合は、意外と正しい表現が難しいのです。

```markdown
JavaScriptで簡単な計算結果を表示するには、以下のコードを使用します。


    
    let sum = 10 + 20;
    console.log("合計は " + sum + " です。");
    

上記のコードは、```sum```という変数に10と20の合計を代入し、その結果をコンソールに出力します。
```
というのは、正しく表示されていますでしょうか?また、例えばObsidianにコピー&ペーストしてみたら、どうなりますか?

---


⑥ 外3連`markdown、中4連スペース兼3連`javascript(ハイブリッド型)

---
ChatGPT, GeminiなどでMarkdownを出力する際にはコードブロックにしてもらうとコピーしやすいのですが、その文中にコードがネストして出てくる場合は、意外と正しい表現が難しいのです。
    
```markdown
JavaScriptで簡単な計算結果を表示するには、以下のコードを使用します。

    
    ```javascript
    let sum = 10 + 20;
    console.log("合計は " + sum + " です。");
    ```
    
上記のコードは、```sum```という変数に10と20の合計を代入し、その結果をコンソールに出力します。
```
というのは、正しく表示されていますでしょうか?また、例えばObsidianにコピー&ペーストしてみたら、どうなりますか?


---

Subject: Markdown nested code block rendering test (English version)

---


①  Outer: triple backticks `markdown`, Inner: triple backticks `javascript`

---

When asking ChatGPT, Gemini, or similar tools to output Markdown, it is easier to copy when the content is placed inside a code block. However, when code appears nested inside that text, achieving correct representation can be surprisingly difficult.

````markdown
To display a simple calculation result in JavaScript, use the following code.


```javascript
let sum = 10 + 20;
console.log("The total is " + sum + ".");
````

The code above assigns the sum of 10 and 20 to the variable `sum` and outputs the result to the console.

`````
Is this displayed correctly? Also, what happens if you copy and paste this into Obsidian, for example?


---

②  Outer: triple backticks `markdown`, Inner: quadruple backticks `javascript`

---
When asking ChatGPT, Gemini, or similar tools to output Markdown, it is easier to copy when the content is placed inside a code block. However, when code appears nested inside that text, achieving correct representation can be surprisingly difficult.

```markdown
To display a simple calculation result in JavaScript, use the following code.


````javascript
let sum = 10 + 20;
console.log("The total is " + sum + ".");
`````

The code above assigns the sum of 10 and 20 to the variable `sum` and outputs the result to the console.

````
Is this displayed correctly? Also, what happens if you copy and paste this into Obsidian, for example?


---

③  Outer: triple backticks `bash`, Inner: triple backticks `javascript`

---
When asking ChatGPT, Gemini, or similar tools to output Markdown, it is easier to copy when the content is placed inside a code block. However, when code appears nested inside that text, achieving correct representation can be surprisingly difficult.

```bash
To display a simple calculation result in JavaScript, use the following code.


```javascript
let sum = 10 + 20;
console.log("The total is " + sum + ".");
````

The code above assigns the sum of 10 and 20 to the variable `sum` and outputs the result to the console.

````
Is this displayed correctly? Also, what happens if you copy and paste this into Obsidian, for example?


---

④  Outer: triple backticks `markdown`, Inner: triple tildes ~~~`javascript`

---
When asking ChatGPT, Gemini, or similar tools to output Markdown, it is easier to copy when the content is placed inside a code block. However, when code appears nested inside that text, achieving correct representation can be surprisingly difficult.

```markdown
To display a simple calculation result in JavaScript, use the following code.


~~~javascript
let sum = 10 + 20;
console.log("The total is " + sum + ".");
~~~

The code above assigns the sum of 10 and 20 to the variable ```sum``` and outputs the result to the console.
````

Is this displayed correctly? Also, what happens if you copy and paste this into Obsidian, for example?

---

⑤  Outer: triple backticks `markdown`, Inner: four-space indentation
(Note: language name cannot be specified by design; this is the original code block syntax)

---

When asking ChatGPT, Gemini, or similar tools to output Markdown, it is easier to copy when the content is placed inside a code block. However, when code appears nested inside that text, achieving correct representation can be surprisingly difficult.

````markdown
To display a simple calculation result in JavaScript, use the following code.


    
    let sum = 10 + 20;
    console.log("The total is " + sum + ".");
    

The code above assigns the sum of 10 and 20 to the variable ```sum``` and outputs the result to the console.
````

Is this displayed correctly? Also, what happens if you copy and paste this into Obsidian, for example?

---

⑥  Outer: triple backticks `markdown`, Inner: four-space indentation + triple backticks `javascript` (hybrid)

---

When asking ChatGPT, Gemini, or similar tools to output Markdown, it is easier to copy when the content is placed inside a code block. However, when code appears nested inside that text, achieving correct representation can be surprisingly difficult.

````markdown
To display a simple calculation result in JavaScript, use the following code.

    
    ```javascript
    let sum = 10 + 20;
    console.log("The total is " + sum + ".");
    ```
    
The code above assigns the sum of 10 and 20 to the variable ```sum``` and outputs the result to the console.
````

Is this displayed correctly? Also, what happens if you copy and paste this into Obsidian, for example?

---


5. 結論:インデント式運用ルール

5.1 ルール(日本語のプロンプト)

## 入れ子になったコードブロックに関する規則
フェンスドコードブロック内では、インデント式でコードを表示すること。

これをbookmarkletで容易に使用する方法は後述する。

5.2 Rule(English prompt)

## Rules for nested code blocks
Inside a fenced code block, display any inner code using the indented style (4 spaces).
Do not use fenced code blocks inside another fenced code block.

I provide a bookmarklet to insert this rule easily when needed.

You might need some tuning.

This prompt may require small adjustments depending on your environment.


6. なぜインデント方式を選んだか(仕様+運用)

本章では、4章の実験結果を踏まえ、最終的な運用ルールとして「インデント方式」を採用した理由を整理する。

  • 3連チルダ
    • 言語を記載できるので可読性が良い
    • 外側と内側でデリミタを分離でき、フェンス衝突を避けられる
    • 通常のChatGPTでは、一発のプロンプトでルールを守らせるのが困難
    • フェンスドコードブロックとなった途端に、LLMは3連バッククォート(```)を使うと、強く学習している。
    • LLMの後工程で、ポストプロセッサーが介入して3連バッククォート(```)に置換している虞もある。
  • インデント方式
    • ほぼずべてのMarkdown環境で安定して使える
    • フェンスドコードブロックとインデントコードブロックと呼び分けるとプロンプトが書きやすい
    • ポストプロセッサーによる介入が少ない

要するに、

  • 仕様に反しない
  • 必要なときにだけBookmarkletで指示を追加することが可能
  • LLMに守らせやすい

という3点のバランスが良い。
しかしながら、フェンスドコードブロックで3連チルダを使うのは魅力的である。筆者も、個人的にはCustomGPTで3連チルダ方式を使用していることが多いので、今後もこの方向の探索は続けたい。


7. Bookmarkletと注意点(限界と例外)

  • Bookmarkletは、下記のリンクをパソコンのブラウザのブックマークバーまでドラッグしてください。タブレットやスマートフォンなどで、ドラッグでBooklarkletが作成できない場合は、ダミーで作ったBookmarkの編集でアドレスにコード(こちらのページを参照)を登録してください。ChatGPTのWeb UIの変更などもあり得ますので、その場合はご容赦ください。
    ChatGPTでコードブロックを出す前にBookmarkをクリックすると、入力中のプロンプトの「前に」インデント式のコードブロックを使うように追加指示を挿入します。必要なときにだけ使用するということが可能です。別ページに、Bookmarkletの説明記事を書きますので、そちらも参照ください。
  • LLMの性質上、どうしても完全な再現性はありませんし、プロンプトを気をつけて作成しても100%の成果は原理的に保証できません。
    • 回答が単純な場合は、全体をMarkdownのコードブロックに入れないことも多い。
    • Bookmarkからルールを読み込ませても、数ターンの内には効果が減衰するので、必要なときは毎回Bookmarkletでルールを読み込ませたほうが良い。
  • Bookmarkletを使わなくても、ブラウザ拡張機能などでも同様な対応は可能なはずです。
  • 個人個人のカスタマイズもあるでしょうし、例外は排除できないと思います。そこで、Bookmarkletはご自由に改変して各自の使いやすいようにご利用ください。

8. まとめ

  • 入れ子コードブロックの崩壊は、Markdown仕様とLLM出力の相互作用で起きる
  • 実務上は 内側をインデント方式に統一するルールが、主要環境で安定しやすい
  • 3連チルダも、CustomGPTなどで安定して使える可能性は、まだ追求の余地があると考える。

参考文献

  1. CommonMark Project, The CommonMark Specification(CommonMark Spec Version 0.31.2 (2024-01-28))https://spec.commonmark.org/0.31.2/
  2. GitHub, GitHub Flavored Markdown Spec(GFM Spec Version 0.29-gfm (2019-04-06))https://github.github.com/gfm/
  3. John Gruber, Markdown: Syntax(Daring Fireball)https://daringfireball.net/projects/markdown/syntax

本サイトについて

Note
This site is currently maintained primarily in Japanese.
Some articles may be partially translated into English over time.

(本サイトは、現在は日本語のページが主体です。)

本サイト(NoaRecord)では、体験を通して生成AIを実務で使いこなすための知見を整理・公開しています。
特に、GPTs(Custom GPT)やBookmarkletなど、比較的手軽に導入できる手法を中心に扱っています。

  • 「ChatGPTと話してみた」はnoteで連載中使ってて「あれ?」と思うような事例を簡単に紹介、解説しています。noteの方は基本的に無料で読めますが、ときどきXでシェアのお願いをしている回もありますので、ご協力いただければ幸いです。既に50話を超えて、100話以上になるネタは既に準備してありますので、順次作成して公開していきます。読んでるだけで、ChatGPTとの間のお互いの誤解が解消されていってほしいと思って書いています。近い内に本サイトの方では、もっと深堀りした内容を「増補版」として追加したいと思っていますが、公開条件等は未定です。話題によって内容の多寡はありますが、ツッコんでる解説のあるところは、かなり推測も混じえた内容も書いています。
  • GPTs(CustomGPT)を公開する人向けの「プロンプトインジェクション対策」シリーズ。無対策だと、せっかくのあなたのノウハウが全部盗まれてしまうかもしれません。だからと言ってプロ並みの対策も簡単にはできません。noteで初級コースは全部無料で見れますので、GPTsで副収入などを考えている方などは、ぜひご覧ください。中級以上は有料としていますが、販売するGPTsの価格などに応じて、バランスのとれた対策を考える切っ掛けにしていただければと思います。無料のものでも最低限は使えるものにはなっています。
  • 「プロンプトインジェクション対策実践ケーススタディ」は、下書きが終わった段階なので、公開時期未定です。具体的なGPTsを作ることを想定しながら、プロンプトにどういう対策をしていくべきか、noteでは出せないレベルの防御テクニックの情報をこちらのサイトで出していく予定です。
  • GPTsのActions講座も予定しています。プロンプトだけではなく、外部のツールを使ってネットからAPIで情報を取ってきたりするものや、情報を取ってくる以外の使い方の教材も考えています。
  • その他、Bookmarkletを使ったものや、小回りの利くツールも開発したり、教材にしたりしていく予定です。
  • Discordも始めたいと思っています。

 

よろしければ、Xアカウントのフォローやシェアなどの応援、ご協力をお願い致します。

 公式:@NoaRecord   筆者個人雑談用:@Thinking_reed
※ 個人アカウントは雑談が主なので、情報の入手のためのフォローは公式をお勧めします。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール