Mermaid などのテキスト構文を画像生成 AI のプロンプトに渡して図解やインフォグラフィックを直接自動生成するパイプラインを運用していると、最も頭を悩ませるのが「生成の失敗によるリトライの繰り返し」です。図が複雑になるにつれて、ノードの重複や字形の破綻、意図しない接続線といった描画崩れが頻発します。

画像生成の失敗で本当に避けたいのは、リトライが重なることで発生する API 利用枠の急激な消費や、短時間のレート制限(429 Too Many Requests)による指数バックオフ待機、さらには日次クォータ枯渇によるパイプラインの完全停止です。Gemini API の仕様では、リクエスト頻度は 1 分あたりのリクエスト数(RPM)、1 分あたりの入力トークン数(TPM)、1 日あたりのリクエスト数(RPD)の 3 軸で評価され、いずれか 1 つでも上限を超えると 429 エラーが返されます1。429 RESOURCE_EXHAUSTED エラーが発生した場合は初期待機約 1 秒から最大 60 秒までの指数バックオフと再試行が推奨されますが2、クォータを使い切ってしまうと復旧まで長時間の待機を余儀なくされます。私自身、知らないうちにリトライが連続して走り、気づいたときには利用枠上限に達して作業停止に追い込まれた経験があります。

こうした事態を防ぐため、「リトライを完全に禁止する」あるいは「リトライ上限を 1 回に絞る」といった安全策を設けることも考えられます。しかし、パイプラインの本来の目的は「安全に処理を止めること」ではなく「使える画像を生成すること」です。生成を諦めてしまっては業務が進みません。また、プロンプトで「複雑にしすぎないで」「シンプルな図にして」と定性的に指示しても、生成 AI はその制約を確実に守ってはくれません。

必要なのは、事後的なリトライ制限ではなく、生成 AI に渡す手前で複雑な図を機械的に弾く決定論的な入力ガードレールです。Mermaid 要素数の事前ガードレールとは、画像生成 AI の API 呼び出し前に Mermaid 構文を静的解析し、描画破綻を招きやすいノード数やエッジ数の上限超過を機械的に検知してリトライを未然に防ぐ決定論的な検査機構です。

自社で運営する技術ブログの連載記事(全 16 図)の実績データを検証したところ、一発で生成に合格した 10 図と、失敗してやり直した 6 図の境界線は、「札 12・矢印 10・戻り 0・最大流入 2」という要素数で事前に綺麗に分かれていました。本記事では、この上限値を導出した根拠と、手集計で 2 セルの数え間違いを起こしてから自動検査スクリプトへと移行した運用の教訓をまとめます。なお、文字の潰れや字形崩れの対策については 前作の記事 で詳しく触れています。

なぜ「要素数の上限」で事前に分かれたのか:連載16図の実績と境界線

連載16図の実績データ:一発合格10図とやり直し6図の境目

自社の技術連載記事(ep01〜ep09)で作成した挿絵 16 図の実績を集計しました(レート制限の 429 エラーにより画像生成を中断して挿入自体を差し戻した ep05 の 2 図を除く 16 図が対象です)。

この 16 図について、初回の新規生成 1 回で合格し手戻りのなかった「一発合格」の 10 図と、字形崩れや余分な線、札の重複等により再生成や参照画像編集を 1 回以上行った「やり直し」の 6 図に分類して要素数を比較しました。その結果、一発で通った 10 図はすべて以下の基準を満たしていました。

  • 札(ノード)の数が 12 個以下
  • 矢印(エッジ)の数が 10 本以下
  • 戻り線(閉路を作る線)が 0 本
  • 1 つの札へ入る矢印の最大流入数が 2 本以下

一方で、やり直した 6 図のうち 5 図は、札が 12 個を超えるか、矢印が 10 本を超過していました。具体的には、一発合格した ep01-1(札 9、矢印 7)や ep04-2(札 12、矢印 10)が上限内に収まっていたのに対し、手戻りが発生した ep01-2(札 12、矢印 11)は編集 2 回、ep08-1(札 20、矢印 20)は編集 1 回を要していました。

なお、分岐や合流の数単体では、一発合格した図とやり直した図を分けることはできませんでした。たとえば ep06-1 は分岐 2・合流 2 を含んでいましたが、札 9・矢印 9 であったため一発で合格しています。合否を分けた決定的な要因は、全体の過密さを示す要素数の合計値でした。

札12・矢印10・戻り0・最大流入2の根拠と描画破綻の幾何学

なぜ「札 12・矢印 10・戻り 0・最大流入 2」という数値が境界線になったのでしょうか。その背景には、グラフ描画アルゴリズムの特性と認知上の幾何学的制約があります。

まず「戻り線 0 本」についてです。連載 16 図の中で、有向サイクルの祖先へ戻る線(戻り線)を含んでいた図は ep08-2 の 1 図のみでしたが、この図は合格までに 5 回もの生成を要しました。Mermaid の階層レイアウトエンジン(Dagre 等)では、有向グラフの閉路を解消して階層配置を決定するために、サイクルを構成するエッジを一時的に反転させる処理(acyclic 処理)を行います3。閉路が存在するとエッジが反転・迂回して配置されるためレイアウトの幾何学的複雑さが増し、グラフ描画アルゴリズムだけでなく、画像生成 AI がテキスト構文から空間配置を解釈・描画する際にも同様に破綻を招く要因になると推測されます。

次に「矢印 10 本」「最大流入 2 本」の根拠です。グラフ描画の美観に関する Purchase (1997) の実験研究では、人間の理解度(回答時間および誤答率)を最も著しく阻害する最大の要因は「エッジ交差(edge crosses)」の存在であることが実証されています4。自社の連載実績においても、矢印の数が 10 本を超えたり、1 つのノードに 3 本以上の矢印が合流したりすると、レイアウト上の過密によってエッジ交差が発生しやすくなり、画像生成 AI が線分の接続関係を取り違えて描画崩れを起こすリスクが高まりました。

実際、札(10 個)と矢印(9 本)は上限内でありながら手戻りとなった ep02-2 は、1 つのノードに 3 本の矢印が合流する構造を持っており、生成 2 回・編集 4 回を要しました。ただし、この最大流入数に関する知見は ep02-2 という 1 図の実績に基づく経験則であり、あらゆる画像生成モデルで常に失敗することを保証する絶対的な幾何学定理ではありません。自社パイプラインの安全基準としての目安です。

上限を超過した場合の対処方針:分割と文脈化

Mermaid を作成していて要素数が上限を超えてしまった場合、プロンプトで「文字を小さく描いて」と小手先の調整を試みても効果はありません。破綻を防ぐための実践的な対処法は以下の 2 つです。

  1. 図を 2 ステップに分割する: 1 枚の図に前提から結論までを詰め込まず、「前工程のアーキテクチャ」と「後工程のデータフロー」のように 2 枚の画像に分割します。
  2. 矢印を削って札の中の説明文に落とし込む: すべての関連性を矢印で繋ごうとせず、自明な関係は矢印を省き、ノード内の短いテキストや前後の本文で接続関係を説明します。

図の構造自体をシンプルに改めることが、画像生成の成功率を高める最も確実な近道です。

なぜ「手集計」ではなく「スクリプト」なのか:2セルの差異という泥臭い失敗

手集計表とスクリプト検証で発覚した2セルの誤り

要素数の上限目安を検討していた当初、エージェントは手作業と簡易な正規表現を使って 16 図の札や矢印の数を数え、集計表を作成して検討時の社内 Issue に投稿しました。その表を根拠として「札 12・矢印 10」という運用ルールを正式に決めたのです。

しかし、その直後に作成された自動検証スクリプト(mermaid-limits.py)を使って過去の図を機械的に数え直したところ、矢印の数に 2 カ所の数え間違いが見つかりました。ep01-1 の矢印が実際は 7 本なのに「6」と記録され、ep08-1 の矢印が実際は 20 本なのに「21」と記録されていたのです。

幸いにも、2 セルを訂正した後も ep01-1 は上限内(7 本 <= 10 本)、ep08-1 は超過(20 本 > 10 本)のままであり、一発合格 10 図とやり直し 6 図の合否境界そのものが狂うことはありませんでした。しかし、「対外的に報告する数字ですら、手作業や目視の集計では見落としが混入する」という泥臭い現実を突きつけられる結果となりました。

スプレッドシートや人手によるデータ作業のエラーを研究した Panko (2008) でも、人間が行うデータ入力や集計作業においてエラーの発生は一般的であり決して無視できないことが示されています5。どんなに注意深くチェックしたつもりでも、目視確認に頼る運用には限界があります。

構造化テキストは人間が数えるな:決定論的検査の必然性

構文や構造が厳密に決まっているテキストの集計を、人間による目視や LLM の曖昧なカウントに委ねてはなりません。LLM もまた、複雑なコンテキストの解釈や正規表現の網羅において見落としを起こします。

テキストのパースや要素数のカウントといった決定論的なタスクには、決定論的なツール(スクリプト)を使うのが鉄則です。生成されたテキストの誤字脱字など、後から修正が効く可逆な成果物であれば事後の検収で構いません。しかし、画像生成 AI の API 枠消費やレート制限待機のような「取り戻せない不可逆な損失」に対しては、API を叩く前の段階に機械的な予防ゲートを配置し、決定論的に遮断する必要があります。

手法別の比較表

画像生成における失敗防止には「生成後リトライ」「手作業の事前目視」「スクリプト静的検査」の 3 つのアプローチが存在しますが、API 利用枠の保護と検査の再現性を両立するにはスクリプトによる事前検査が最も適しています。

手法検査タイミング検出の確実性API利用枠・待機時間の保護自動化・運用負荷
生成後リトライ画像生成後破綻してから気づく(遅延)❌ 枠を浪費し 429 待機のリスク低(失敗時に人間や AI が指示)
手作業の事前目視画像生成前△ 見落としリスク(集計表作成で手作業による計数ミスが発生)⭕ 未然に防げる❌ 人間の認知負荷が高くスケール不能
スクリプト静的検査画像生成前⭕ 決定論的(100% の再現性)⭕ 要素数超過による枠浪費を阻止(超過時は exit 1)⭕ CI/パイプラインに組み込み完全自動化

人間による手集計では集計表の作成時に 2 カ所の数え間違いが生じたように、目視確認には常にミスが伴います。一方でスクリプトによる静的検査は、過去 16 図の合否判定(超過なし 10 pass / 超過あり 6 fail)を 100% の再現性で機械的に判定でき、要素数超過による不可逆な API 呼び出しを確実に防ぐことができます。

mermaid-limits.py による自動判定と過去16図の完全再現

筆者が運営する技術ブログのリポジトリ(非公開。以下、リポジトリA)に実装した自動検査スクリプト mermaid-limits.py は、Markdown ファイル内の Mermaid ブロック(flowchart および graph)を静的解析し、札数・矢印数・戻り線・最大流入数を抽出します。

Mermaid の flowchart 構文仕様に準拠し6、通常矢印(-->)だけでなく点線矢印(-.->)や太線矢印(==>)、ラベル付き矢印などを網羅的に認識してエッジ数をカウントします。そして、定められた上限値を 1 つでも超えた場合は exit 1 で異常終了し、画像生成プロセスの起動を未然にブロックします。

以下のコードは、スクリプトのコアとなる要素数抽出・判定ロジックです。

ファイル: mermaid-limits.py 引用元: リポジトリAの mermaid-limits.py

LIMITS = {'札': 12, '矢印': 10, '戻り': 0, '最大流入': 2}

SKIP = re.compile(r'^(%%|classDef\b|class\b|style\b|linkStyle\b|direction\b|click\b|end$)')
SHAPE = re.compile(r'([A-Za-z_][\w-]*)\s*(\[\[.*?\]\]|\(\(.*?\)\)|\{\{.*?\}\}|\[.*?\]|\(.*?\)|\{.*?\}|>.*?\])(?::::\w+)?')
TEXT_LABEL = [
    (re.compile(r'--\s+(?:"[^"]*"|[^-"]+?)\s+(-->|---|--x|--o)'), r'\1'),
    (re.compile(r'-\.\s+(?:"[^"]*"|.+?)\s+\.(->|-)'), r'-.\1'),
    (re.compile(r'==\s+(?:"[^"]*"|[^=]+?)\s+(==>|===)'), r'\1'),
]
LINK = re.compile(r'(<-->|-\.->|-\.-|-->|--x|--o|---|==>|===|~~~)')
HEADED = {'<-->', '-.->', '-->', '--x', '--o', '==>'}


def count(block: str) -> dict:
    nodes, subgraphs, edges = [], set(), []
    for raw in block.splitlines():
        line = raw.strip()
        if not line or SKIP.match(line):
            continue
        m = re.match(r'subgraph\s+([\w-]+)', line)
        if m:
            subgraphs.add(m.group(1))
            continue
        for nid in SHAPE.findall(line):
            if nid[0] not in nodes:
                nodes.append(nid[0])
        line = SHAPE.sub(r'\1', line)
        line = re.sub(r'\|[^|]*\|', '', line)
        line = re.sub(r':::\w+', '', line)
        for pat, rep in TEXT_LABEL:
            line = pat.sub(rep, line)
        parts = LINK.split(line)
        segs = [[t.strip() for t in p.split('&') if t.strip()] for p in parts[0::2]]
        for nid in (n for seg in segs for n in seg):
            if nid not in nodes and nid not in subgraphs:
                nodes.append(nid)
        for i, op in enumerate(parts[1::2]):
            if op == '~~~':
                continue
            edges += [(a, b, op in HEADED) for a in segs[i] for b in segs[i + 1]]
    real = [(a, b) for a, b, _ in edges if a in nodes and b in nodes]
    adj = {n: [b for a, b in real if a == n] for n in nodes}
    state, back = {}, 0

    def dfs(n):
        nonlocal back
        state[n] = 1
        for b in adj[n]:
            if state.get(b) == 1:
                back += 1
            elif b not in state:
                dfs(b)
        state[n] = 2

    for n in nodes:
        if n not in state:
            dfs(n)
    inflow = [sum(1 for a, b, h in edges if h and b == n) for n in nodes]
    return {'札': len(nodes), '矢印': len(edges), '戻り': back, '最大流入': max(inflow, default=0)}

この実装では、正規表現によってノード形状や矢印のラベルを除去した上で接続関係を抽出し、隣接リストを作成しています。さらに深さ優先探索(DFS)を用いて訪問状態を管理することで、有向グラフ内の祖先へ戻るエッジ(戻り線)の数を正確に計数しています。

過去の連載記事 Markdown に対してこのスクリプトを一括実行した際のコマンドと、当リポジトリの一次計測ログに記録された実行結果ログは以下の通りです。

python3 tools/checks/mermaid-limits.py content/post/*.md
ep01.md:345: 札9 矢印7 戻り0 最大流入2
ep01.md:470: 札12 矢印11 戻り0 最大流入2  超過: 矢印11>10
ep02.md:527: 札10 矢印10 戻り0 最大流入2
ep02.md:695: 札10 矢印9 戻り0 最大流入3  超過: 最大流入3>2
ep03.md:687: 札8 矢印7 戻り0 最大流入2
ep03.md:753: 札15 矢印8 戻り0 最大流入1  超過: 札15>12
ep04.md:363: 札10 矢印8 戻り0 最大流入2
ep04.md:822: 札12 矢印10 戻り0 最大流入1
ep06.md:702: 札9 矢印9 戻り0 最大流入2
ep06.md:882: 札8 矢印7 戻り0 最大流入2
ep07.md:678: 札9 矢印7 戻り0 最大流入1
ep07.md:824: 札8 矢印8 戻り0 最大流入2
ep08.md:533: 札20 矢印20 戻り0 最大流入2  超過: 札20>12 矢印20>10
ep08.md:732: 札9 矢印11 戻り1 最大流入2  超過: 矢印11>10 戻り1>0
ep09.md:473: 札8 矢印6 戻り0 最大流入1
ep09.md:643: 札14 矢印13 戻り0 最大流入2  超過: 札14>12 矢印13>10

出力結果の通り、一発合格した 10 図はすべて超過なし(pass)として通過し、やり直した 6 図はすべて「超過:」が付与されて exit 1(fail)となりました。過去 16 図の実績境界がスクリプトによって 100% 完全再現されたことが確認できます。

メリットの裏にあるトレードオフと適用限界

リトライ制限ではなく「手前で複雑な図を避ける」理由

API 利用枠の枯渇や長時間の作業停止を防ぐ最も簡単な手段は、リトライ処理を禁止することです。しかし、画像生成パイプラインの真の目的は、安全に処理を停止させることではなく、業務で使える画像を確実に生み出すことにあります。

もし事後のリトライ回数を単に絞るだけの対策を採った場合、複雑な図を渡された AI は描画崩れを起こし、リトライもできずに目的未達のまま終了してしまいます。だからこそ、事後リトライの制限だけに頼るのではなく、入力側の Mermaid の段階で複雑な図を回避し、初回の新規生成で一発合格する確率を手前で最大限に高めておくアプローチが必要なのです。

「要素数を絞ると情報量が減る」という懸念への回答

「札 12・矢印 10 という制限は厳しすぎて、複雑な業務フローやシステムの全体像を表現できなくなるのではないか?」という懸念を抱く方もいるかもしれません。

しかし、1 枚の画像に要素を過密に詰め込むことは、生成 AI の描画破綻を誘発するだけでなく、人間の読者にとっても認知負荷を著しく増大させます。グラフ描画の美観に関する研究でも明らかなように、エッジ交差が増えるほど読者の理解速度と正確性は低下します4。

1 枚ですべてを語ろうとせず、2 枚のステップ図に分けるか、自明な矢印を省いてノード内の文章で補足した方が、結果として読者にとっても視認性と理解度が格段に向上します。

静的検査スクリプトの限界:意味的欠陥・字形崩れの境界

要素数検査スクリプト(mermaid-limits.py)は、構造の過密による描画破綻を未然に防ぐための強力なガードレールですが、画像生成の失敗をすべて解決できるわけではありません。静的構文チェックでは防げない領域も存在します。

  1. 字形崩れ: 複雑な漢字や専門用語が画像化された際に文字の形が崩れる問題。これは構文ではなく、平易な表現への言い換えによって対処する必要があります(詳細は 前作の記事 を参照)。
  2. ラベル混同: 同じ形状のノードが並んだ際に、AI がラベルの割り当てを取り違える問題。これはノードの形状を差別化するなどの対策が必要です(詳細は 同型要素のラベル混同を防ぐ過去記事 を参照)。

構文の過密を防ぐ「静的検査」と、語彙や配置を工夫する「プロンプト・構文設計」を組み合わせた多層防御の視点が不可欠です。

前提が変われば結論はどう変わるか

画像生成 AI の技術進化によって、本記事で示した前提が将来どう変わり得るのか、そして変わらない核は何なのかを整理しておきます。

今後、Diffusion モデルやマルチモーダル基盤モデルの空間認識能力・文字レンダリング精度が飛躍的に向上すれば、「札 12・矢印 10」という具体的な閾値自体は上限値自体が緩和される可能性があります。

しかし、モデルがどれほど進化しようとも、以下の 2 つの前提は変わりません。

  1. API レート制限とクォータによる不可逆な損失: クラウド API を利用する限り、RPM/TPM/RPD のレート制限や利用枠上限は存在し続けます。無駄なリクエスト消費の代償は常に不可逆です。
  2. 人間の視覚認知の限界: エッジが過度に交差した過密な図が読者に与える認知過負荷は、モデル性能の進化とは無関係に一定です。

したがって、「構文が決まったテキストを決定論的なスクリプトで検査し、不可逆な API 呼び出しの前に予防ゲートを置く」という設計思想そのものは、モデルが変わっても陳腐化しません。

なお、今回の運用改善の副産物として、もう 1 つの教訓が得られました。当初、過去図の生成回数ややり直しの履歴を集計する際、記録が散逸していたためセッションログの探索に約 20 万トークンを消費してしまいました。この反省から、以後は画像生成パイプラインが完了した際、コミットメッセージ本文に infographic_N: 新規X・編集Y(理由) の形式で生成回数と手戻り理由を 1 行ずつ記録する運用へと改めました。将来の閾値見直しのコストを下げるためにも、生成実績をバージョン管理のコミットログに構造化して残す工夫が重要になります。

よくある質問(FAQ)

Q. Mermaid の要素数上限(札12・矢印10)は他の画像生成 AI や図種にもそのまま適用できますか?

そのまま無条件で適用することは推奨しません。本記事で提示した「札 12・矢印 10・戻り 0・最大流入 2」という数値は、当時のパイプラインにおいて flowchart(フローチャート)を用いたインフォグラフィック 16 図の実測から導出した自社固有の経験則です。使用するモデルの描画特性や解像度、対象とする図種(シーケンス図やクラス図など)によって適切な閾値は異なります。大切なのは固定の数値をそのまま鵜呑みにすることではなく、自社の生成実績ログを集計し、合否の境界線をスクリプトに落とし込むアプローチそのものです。

Q. Mermaid の要素数が上限を超えた場合、どのように修正すればよいですか?

図を 2 枚のステップ図に分割するか、矢印を削ってノード内の説明文に落とし込むのが効果的です。1 枚の図にすべての情報と関係性を詰め込もうとすると、エッジ交差が増加して描画崩れを招くだけでなく、読者の理解も妨げてしまいます。「全体像と詳細」「前工程と後工程」のように分割するか、自明な接続線を省いてテキストで文脈を伝えることで、上限内に収めながら図解の品質を高めることができます。

まとめ

  • 不可逆な API 損失は事前予防ゲートで防ぐ: 画像生成のリトライによる利用枠消費や 429 エラー待機時間は取り戻せないため、生成後の対処ではなく、生成前の入力構文に対する静的検査で弾く必要があります。
  • リトライ制限ではなく入力の複雑さ抑制で成立確率を高める: パイプラインの目的は安全に止めることではなく使える画像を生成することです。事後リトライの禁止で諦めるのではなく、手前で複雑な図を避けることで一発合格の成立確率を高めます。
  • 作り直しの境界は要素数で分かれる: 自社連載 16 図の実績において、一発合格(10 図)とやり直し(6 図)の境界は「札 12・矢印 10・戻り 0・最大流入 2」という要素数で事前に綺麗に分かれました。
  • 構文テキストの集計はスクリプトに任せる: 人間や LLM の目視集計は必ず見落としを生みます(実測でも 2 セルの誤りが発生)。構造が決まっている構文は決定論的なスクリプトで機械的に判定すべきです。
  • 過密な図は分割か文脈化で解決する: 要素数上限を超過した際は、プロンプトでの調整を試みるのではなく、図の分割または矢印の削減によって構造そのものをシンプルにします。
  • 生成記録をコミットに残し将来の再検証に備える: 試行回数や失敗理由をセッションログに散逸させず、コミットメッセージ等に構造化して残すことで、将来の上限見直しコストを大幅に下げることができます。

Mermaid を活用した図解自動生成パイプラインの構築や、AI エージェントにおける決定論的ガードレールの設計・運用について、ぜひ コンタクトフォーム よりお気軽にご相談ください。

Footnotes

  1. Rate limits | Gemini API Google, 2026年10月31日閲覧。API レート制限の3つの軸(RPM, TPM, RPD)および超過時の 429 エラー仕様は節「Rate limits」、日次クォータ(RPD)のリセット仕様は節「Requests per day」。 ↩

  2. Troubleshooting guide | Gemini API Google, 2026年10月31日閲覧。429 RESOURCE_EXHAUSTED エラーに対する指数バックオフ戦略(初期待機約1秒から最大60秒)および jitter 推奨は節「Resource exhausted (429)」。 ↩

  3. Dagre lib/acyclic.ts dagrejs, GitHub ソースコード, 2026年10月31日閲覧。有向グラフの閉路解消における greedyFAS の呼び出しとエッジ反転処理(reversed フラグの付与と復元)の実装仕様。 ↩

  4. Which aesthetic has the greatest effect on human understanding? Purchase, H. C., Graph Drawing (GD 1997), Lecture Notes in Computer Science, vol 1353, Springer, 1997, pp. 248-261。エッジ交差の最小化(minimise edge crosses)が人間の認知理解度(回答時間および誤答率)に最大の負の影響を与える実験的知見は pp. 248-251。 ↩ ↩2

  5. Spreadsheet Errors: What We Know. What We Think We Can Do Panko, R. R., arXiv:0802.3457 [cs.CY], 2008。人手によるデータ作成・集計におけるエラー発生の一般性と重大性は pp. 1-3。 ↩

  6. Flowcharts Syntax | Mermaid Mermaid.js 公式, 2026年10月31日閲覧。flowchart におけるノード形状および各種矢印記法(通常 -->、点線 -.->、太線 ==> 等)の構文仕様は節「Links between nodes」。 ↩