• Status: Accepted
  • Mode: Standard
  • Kruchten Type: Existence/Property
  • Scope: platform
  • Implementation Status: Not Started
  • 起案者: [email protected]
  • 起案日時 (JST): 2026-06-05 04:06
  • 承認日時 (JST): 2026-06-05 (PR #1483 merge = 受理・JST 19:29)
  • Deciders: [email protected] (単独)

1. コンテキスト

1.1 背景

2026-06-01 に 3 モデル突合調査を実施した結果、会社紹介 PPTX テンプレート (テーマ・スライドマスター・ロゴ・ブランドフォント) と .md 原稿から、ブランド一致かつ編集可能な提案資料 (.pptx) をブレなく生成する仕組みが存在しないことが確定した。現状は手動作成で、(1) 工数が線形に膨らむ、(2) 色・フォント・余白がブランドガイドから逸脱する、(3) 日本語が文字化けする、という 3 つの再発リスクを抱える。

1.2 現状 (As-Is)

定量・技術的事実 (3 モデル突合調査, 2026-06-01):

論点確定事実含意
実行環境Cloudflare Workers では PPTX 生成が python-pptx / LibreOffice / Pandoc / headless browser いずれも不可レンダリングは CI で server-side 実行が必須
テンプレ再利用既存 .pptx 資産を再利用できるのは Pandoc --reference-doc / python-pptx / pptx-automizer (Node) のみMarp / Slidev / reveal.js は既定でラスタ画像化・編集不可でブランド再生成に不適
CJK (日本語)OOXML は <a:latin>/<a:ea>/<a:cs> スロット分離。font.name は latin のみ設定 → 日本語は <a:ea> 未定義で fallbackHan Unification で中国語フォント (SimSun) に落ち「直/込」等が誤字形・豆腐 (▯) 化。最大の落とし穴
LLM の得手不得手LLM は HTML/要約は得意、OOXML 直生成は苦手LLM に OOXML を直接書かせる設計は破綻する

1.3 課題

提案資料の品質を人手チェックに依存させ続ける固定費を抱え、資料生成ペースが上がるほど逸脱リスク (色・フォント・余白のブランド逸脱、日本語文字化け) と工数が線形に増える。特に CJK 文字化けは Han Unification で中国語フォントへ無音 fallback するため目視で発見が遅れる。

1.4 制約・要件

  • Cloudflare Workers では PPTX 生成不可 → レンダリングは CI server-side 実行必須。
  • 既存ブランド .pptx テンプレートを所与とし再利用する (再デザインしない)。
  • 日本語 <a:ea> を確実に出力し、Microsoft 365 / LibreOffice Impress で文字化けゼロ。
  • LLM に OOXML を直接書かせない (責務分離)。
  • decision-pipeline (ADR-0019 / 0066 / 0064 / 0037) のインターフェースを再利用し新規定義しない。
  • Cloudflare Workers の 30 秒 wall-time / subrequest 50 件上限を超えない。

1.5 目標 (To-Be)

.md 原稿から Slide-IR(JSON) を中間表現として経由し、HITL 2 ゲート (Markdown 承認 + Slide-IR diff) を挟んで、CI で server-side レンダリングしたブランド一致 .pptx を生成するパイプラインを確立する。Non-Goals: DOM-to-PPTX 本線採用 (freeform_panel エスケープハッチを除く)、Google Slides 経路、テンプレ自体の再デザイン、rendered preview 上での直接編集 (round-trip 不能のため不可)。

2. 決定

明示的な Slide-IR(JSON) を中間表現として採用し、LLM と決定的処理の責務を分離する。Track 2 の 3 モデルが完全一致で支持した方針で、ブランドテンプレ再利用と CJK 安全性を両立できる唯一の構成。LLM は layout 選択・本文要約・title/notes のみを担当し、parse・validate・スロット割当・<a:ea> 付与・OOXML 出力はすべて決定的コードが担う。CI レンダラは python-pptx/Pandoc + pptx-ea-font または pptx-automizer (Node) のいずれかを段階 2 着手前に PoC で確定する。decision-pipeline の LangGraph interrupt() + Queues/DO を再利用し、Workers が同期実行で抱えるのは軽量オーケストレーション (queue 投入・HITL interrupt()・DO state 管理) のみに限定する。LLM 呼び出しと CI レンダリングは Workers 外で実行する。

パイプライン全体像:

Markdown (source of truth)
  → (決定的 segmenter) candidates
  → (LLM: layout 選択 + 本文要約, structured output) Slide-IR(JSON)
  → (決定的 validator) 構造・意味の再検証
  → HITL (Markdown 承認 + Slide-IR diff レビューの 2 ゲート)
  → (CI で python-pptx / pptx-automizer) ブランド一致 .pptx

設計の柱:

  1. 中間表現 = Slide-IR(JSON)。layout=テンプレ layout 名の enum 参照、content=placeholder 名、text=run 配列 (lang/script 付き)、per-layout 文字数 budget、provenance ブロックを持つ。
  2. CI server-side レンダリング。(A) python-pptx / Pandoc + pptx-ea-font (成熟・CJK 確実・Python CI)、(B) pptx-automizer (Node、既存ブランド .pptx を改変、TS/Node 親和) の 2 択。
  3. 責務分離。LLM は layout 選択・本文要約・title/notes のみ。OOXML は決定的コードが担う。
  4. 決定的 segmentation。H1=section header / ---・slide-level heading=新 slide / 画像・表=単独 slide。layout はテンプレ layout 名の closed enum へ dispatch する (Pandoc named-layout 契約)。
  5. LLM 制約 3 層。Schema-first (Zod/Pydantic → JSON Schema) → 構造化出力 → 意味的 post-validation。
  6. CJK 安全性 (無音破損を配布前に決定的検出)。theme に <a:ea> (Brandwares 手法) + 日本語 run で <a:ea> を必須 emit。pptx-ea-font は python-pptx の内部 XML を直接操作するため python-pptx バージョンアップで静かに無効化され <a:ea> が欠落 → 本番で SimSun フォールバックするリスクがある。これを「想定リスク」でなく配布前に決定的に検出される性質へ落とすため、(a) pip install --require-hashes で python-pptx と pptx-ea-font を同一解決でハッシュロックし Dependabot PR で目視更新、(b) CI レンダリング末尾で生成 .pptx を再読込し <a:ea> ノードの存在を XPath でアサート (欠落なら即 fail = 配布前に確実に捕捉)、(c) 直接 XML 操作による <a:ea> 付与を pptx-ea-font 非依存のフォールバックとして段階 3 以前に実装する。ブランドフォントは Google Fonts と client PowerPoint の双方に存在する版 (例 Noto Sans JP) に正規化。
  7. HITL 2 ゲート。Markdown 承認 (必須) + Slide-IR 構造 diff レビュー (推奨)。rendered preview は編集不可 (round-trip 不能)。LangGraph interrupt() + Command(resume) (approve/edit/reject/respond) と直結。
  8. オーバーフロー対策。python-pptx の autofit は開くまで未適用のため、決定的 budget + LLM split で対処する (Split > Shrink)。

3. 判断基準 (Decision Drivers)

3.1 評価軸

#重要度 (係数)案件特有の解釈
1#reliable[Must] (×2.0)日本語 <a:ea> 文字化けゼロ・ブランド一致 100%。配布前に決定的に検出される設計。
2#maintainable[Must] (×2.0)LLM/決定的処理の責務分離、Slide-IR スキーマ単一定義、decision-pipeline 再利用。
3#suitable[High] (×1.0)ブランドテンプレ再利用と編集可能 .pptx 出力 (PDF や画像化でなく) を満たす。
4#efficient[High] (×1.0)工数 7 人週・月数百円 LLM 費・CI 無料枠内に収まる。
5#operable[Medium] (×0.5)HITL 2 ゲート・CI 完了通知・撤退条件の自動算出が運用可能。

K.O. criterion: Must 軸 (#reliable / #maintainable) の score < 3 は不採用。

3.2 評価軸 × 案スコア表

各案 0-5 で評価。加重和 = (Σ score × 係数) / (満点 × Σ 係数) で正規化。

係数採択案 (Slide-IR 中間表現)案 A (LLM が OOXML 直生成)案 B (Google Slides + PDF 消費)案 C (Marp/Slidev で HTML→PPTX)
#reliable [Must]×2.05132
#maintainable [Must]×2.05143
#suitable [High]×1.05222
#efficient [High]×1.03454
#operable [Medium]×0.54243
加重和 (正規化)0.9180.3450.7000.527
K.O. 通過 (Must ≥3)

4. 検討した代替案 (Alternatives Considered)

  • 案 A (LLM が OOXML 直生成): LLM に直接 .pptx の XML を書かせる — LLM は OOXML 生成が苦手で構造破綻・CJK 欠落が頻発し reliable/maintainable とも K.O. 不通過。不採用。
  • 案 B (Google Slides 経路 + PDF 消費前提): Google Slides テンプレで生成し PDF で配布 — 編集可能 .pptx 提供という #suitable 要件を満たさず、撤退条件成立時の縮退オプションとして温存。
  • 案 C (Marp / Slidev / reveal.js で HTML→PPTX): HTML ベースのスライドツールから PPTX エクスポート — 既定でラスタ画像化・編集不可でブランド再生成に不適、<a:ea> 制御不能で Must 不通過。不採用。

5. 影響 (Consequences)

5.1 正の影響 (Good)

  • LLM/決定的処理の責務分離により Slide-IR スキーマ単一の真実源で品質が決定的に保証され、CJK 文字化けが配布前に CI で必ず検出される (XPath アサート + ハッシュロック + 直接 XML フォールバック)。
  • decision-pipeline (ADR-0019 / 0066 / 0064 / 0037) の LangGraph・Queues・DO・staging を再利用するため新規構築が中間表現スキーマ・segmenter・レンダラに限定され、追加インフラ課金 0 円。
  • ブランドテンプレを所与のまま再利用でき、編集可能 .pptx として配布可能。
  • LLM コスト月数百円〜1,000 円、CI は GitHub Actions 無料枠 (2,000 分/月) 内。

5.2 負の影響 (Bad)

  • 実工数は楽観値 7 人週に対し、renderer 選定遅延・pptx-ea-font バージョン依存対応・CJK 検証環境差異が重なると1.5〜2 倍 (約 10〜14 人週) に膨張しうる
  • 盲点 #1 (critical): テンプレ layout 名が PowerPoint UI で無警告に変更されると Slide-IR の closed enum が全件リジェクトされ .pptx ゼロ枚生成になる → 段階 2 完了条件に「テンプレ読み込み時に layout 名一覧を自動抽出して Slide-IR スキーマの enum と突合し、不一致が 1 件でもあればレンダリングを開始しない CI ゲート」を必須実装する。
  • 盲点 #2 (high): ADR-0066 で設定済みの Queues visibility timeout が CI レンダリング所要 (数分) より短いと at-least-once 配信で二重生成発生 → 段階 2 着手前に timeout 実値を確認し、CI レンダリング最大所要 (10 分 + マージン) を超える値に変更可能か検証。変更不可なら Slide-IR の job_id を冪等キーとした重複チェックロジックを段階 2 完了条件に明示。
  • 盲点 #3 (high): GAS モノレポに Python CI を同居させると既存 GAS/clasp テストと依存解決が干渉 → 「GAS 用と Python レンダラ用のワークフローファイルを別 .yml に分離し runner/environment を完全独立」させ、段階 1 完了前に既存 GAS テストへの影響ゼロを PoC で確認。
  • 盲点 #4 (high): Pandoc 経路の named layout dispatch は内部ヒューリスティクス依存で Slide-IR layout enum と非等価 → 段階 1 PoC で 1 対 1 対応を実証し、不可なら Pandoc を補助用途に限定し python-pptx を唯一の layout dispatch 実装と明記。
  • 盲点 #5 (high): 提案ラッシュ・再試行ループで GitHub Actions 無料枠 2,000 分/月を使い切ると HITL 承認済みジョブが無音で待機 → 月間消費が 1,500 分超で Slack 通知するスクリプトと、CI ジョブが一定時間内に開始されない場合の HITL 通知監視を段階 2 完了前に実装。
  • 盲点 #6 (high): linked 形式のロゴ・OLE オブジェクトは pptx-automizer / python-pptx 双方が実体コピーせず受信者環境でロゴ消失 → 段階 1 PoC でテンプレのメディア参照方式 (embedded / linked) を確認し、linked が存在すれば embed 変換手順または CI media integrity チェックを段階 2 完了条件に追加。
  • 盲点 #7 (medium): Google Slides / Keynote インポート時にフォント置換で字形・行間ずれ → パイロット 20 スライド検証条件に「Google Slides インポートおよび Keynote 変換での目視確認」を追加し、再現時の PDF 併送ポリシーを撤退条件と同セクションに記載。
  • 盲点 #8 (medium): per-layout 文字数 budget ハードコードはテンプレ改訂で陳腐化 → テンプレ .pptx から placeholder の textbox 高さ・フォントサイズを自動抽出して budget を導出するスクリプトを CI に組み込み、テンプレ更新 PR で budget が自動再計算されることを段階 2 完了条件に追加。
  • 盲点 #9 (medium): Slide-IR スキーマフィールドが要件追加のたびに増殖 → 段階 1 完了時点でフィールド数と定義理由を ADR 付録に一覧化し、段階 2・3 着手前に「前段階からの追加フィールド数と追加理由」を記録するルール追加。追加が 3 件超で撤退条件月次レビューを前倒し。

5.3 中立・トレードオフ (Neutral / Trade-offs)

  • 実装で確定する Open Question 2 点 (renderer 確定時に決定):
    • CJK 粒度: A=run 毎に script:"Jpan" 明示 (正確・冗長) / B=render 時 Unicode 検出 (単純)。推奨ハイブリッド = 既定 B + 混在・font 上書き run のみ A。日本語単一原稿なら B 寄りで開始。
    • テンプレ参照方式: pptx-automizer は shape 名キー / python-pptx は idx。renderer 確定に合わせる (いずれも座標でなく名前で参照する点は共通)。
  • 段階化 (各段で動作確認):
    1. Slide-IR スキーマ確定 + 決定的 segmenter + Markdown 承認ゲート (低リスク・スキーマと parse のみ)。
    2. CI レンダラ最小実装 (テンプレ 1 layout で .pptx を 1 枚生成し目視確認)。段階 2 着手前に renderer 確定 (PoC: 既存テンプレ全 layout 読込)。
    3. CJK <a:ea> + Slide-IR diff ゲート + オーバーフロー split を追加し、複数 layout・日本語混在で検証。
  • 影響範囲: bizlp-gas-accounting モノレポ、drp/ に相乗り。新規モジュール = Slide-IR スキーマ (Zod/Pydantic) / 決定的 segmenter / LLM layout 選択ノード / 決定的 validator / CI レンダラ / テンプレ整合性チェッカー。再利用 = LangGraph graph / Queues/consumer / DO state。
  • 過去 ADR との関係: ADR-0019 (LangGraph) / ADR-0066 (async Queues + DO, Paid plan $5/月相乗り) / ADR-0064 (web UI → CI トリガ) / ADR-0037 (serverside draft staging) に一方向依存supersede・無効化しない。decision-pipeline 側 ADR のインターフェース変更時は本 ADR 再評価。
  • Jr 引き継ぎ容易性 (テナント層審査の条件・Trade-off): LangGraph/Queues/DO/OOXML 直接操作の多層スタックは 2026-10 入社予定の Jr には引き継ぎ負荷が高い。緩和 = 習得対象スタックの最小化を renderer 確定 PoC の判断基準に含める (python-pptx 経路一本化なら Python + OOXML のみで CI 部分が完結し、pptx-automizer 不採用で Node 側学習を回避できる)。案 B (Google Slides + PDF) の初期採用は #suitable 要件 (編集可能 .pptx) を満たさず本線にしないが、1 人法人運用コストで ROI が逆転した場合の縮退先として §6 に維持する。

6. 撤退条件 (Rollback Plan)

以下の客観基準のいずれか 1 つを満たしたら、案 B (Google Slides 経路・PDF 消費前提) へ縮退する。判定はサンクコストバイアスを避けるため月次レビューで自動算出データに基づいて行い、判断記録を ADR 更新履歴に残す。

  • パイロット 20 スライド (全 layout × 混在 run × Microsoft 365 / LibreOffice Impress 開封確認 + Google Slides インポート + Keynote 変換目視確認を含む) の検証で、日本語 <a:ea> 文字化け (豆腐・誤字形) が使用する renderer いずれかで 1 件以上再現する。
  • HITL の Markdown 承認後の人手手直し率 (= PowerPoint 上で人手編集されたスライド枚数 / 総スライド枚数、CI が .pptx メタデータから自動収集) が 50% を超える案件が 3 案件連続する。
  • 段階 1 (Slide-IR スキーマ + 決定的 segmenter + Markdown 承認ゲート) が着手から 4 週を超えても、1 layout の .pptx 生成・目視確認に至らない。
  • Slide-IR スキーマフィールド追加が前段階比 3 件を超えた場合、撤退条件月次レビューを前倒し実施 (盲点 #9 対応)。
  • Google Slides / Keynote インポートで再現する字形・行間ずれが提案資料として提出不可と判断された場合、PDF 併送ポリシーへの縮退を含め判断する (盲点 #7 対応)。
  • Jr 引き継ぎ容易性チェック (テナント層審査の条件): 段階 1 完了時に「2026-10 入社予定の Jr が段階 1 コード (Slide-IR スキーマ + segmenter) を 2 週間でレビュー可能か」を判定基準として評価し、不可と判断したら習得スタックの縮小 (renderer 一本化・pptx-automizer 不採用等) を段階 2 着手の前提条件とする。

コスト試算

(パイプライン生成時に欠落した §コスト試算 H2 を起案者生テキストから復元・通過後追補。個人名は表記規約に従い職位へ置換)

実装工数 (工程別内訳、合計 約 7 人週 = 約 35 人日):

  • Slide-IR スキーマ設計 + 既存テンプレ全 layout 棚卸し (closed enum 定義): 1 人週
  • 決定的 segmenter (.md → candidates): 1 人週
  • LLM layout 選択ノード + 決定的 validator + 意味的 post-validation: 1.5 人週
  • CI レンダラ (python-pptx または pptx-automizer + <a:ea> 付与 + テンプレ整合性チェック): 1.5 人週
  • HITL 2 ゲート統合 (LangGraph interrupt 連携) + 統合テスト: 1.5 人週
  • オーバーフロー split + 予備バッファ: 0.5 人週

⚠️ この 7 人週は楽観値。renderer 選定 (python-pptx ↔ pptx-automizer) を段階 2 着手前まで先送りする設計のため確定後に segmenter/validator を一部書き直すリスク、pptx-ea-font の python-pptx バージョン依存 (直接 XML 操作フォールバックの追加実装)、CJK 検証の環境差異対応が重なると 実工数は 1.5〜2 倍 (約 10〜14 人週) に膨張しうる (§5.2 と整合)。リスク低減のため renderer は段階 1 開始前に PoC (既存テンプレ全 layout 読込) で確定させる。

人件費換算: 個人開発 (代表取締役 単独) のため外部発注費 0 円。機会費用として 7 人週 ≒ 35 人日分の自開発時間。

ランニング (月額概算):

  • LLM: 1 資料あたり要約 + layout 選択のみ (OOXML は決定的) で入出力 約 2〜5 万トークン → 数十円/本。月 10〜20 本想定で月 数百円〜1,000 円規模
  • CI レンダリング: 1 資料あたり 10 分以内 × 月 10〜20 本 ≒ 月 100〜200 分。GitHub Actions 無料枠 (2,000 分/月) 内で追加課金 0 円見込み
  • インフラ: decision-pipeline の Cloudflare Worker (ADR-0066 で Paid plan $5/月 を許容済) に相乗りで追加インフラ課金 0 円
  • 追加 SaaS: 0 円 (テンプレは既存 .pptx 再利用、フォントは Noto Sans JP 等の無料版)

現状ベースラインの記録 (審査講評対応・Review After の比較基準): 段階 1 開始前に「月間提案資料本数・1 本あたり手動作成工数・ブランド逸脱/文字化けの発生実績」を実測し本節へ追記する。未計測のまま段階 2 へ進まない。運用オーバーヘッドの実測 (テナント層審査の条件): 段階 3 完了時に月次/四半期レビュー・盲点対応・Dependabot 目視の年間総工数を実測集計し、提案資料作成の削減工数と比較した表を Review After の必須入力とする。

7. Confirmation

fitness function:

  • 検証手段:
    • パイロット 20 スライド (日本語含む・全 layout 網羅・混在 run 含む・Microsoft 365 / LibreOffice / Google Slides / Keynote 開封確認) で <a:ea> 文字化け 0 件 — CI が生成 .pptx を再読込し XPath アサート (盲点 #1 対応で layout 名 enum 突合ゲートを CI 先頭に置く)。
    • ブランド色・フォント・ロゴのテンプレ一致率 100% — 生成 .pptx とテンプレの自動 diff (linked メディア integrity チェック含む、盲点 #6 対応)。
    • HITL の Markdown 承認後の人手手直し率中央値 30% 以下 — CI が .pptx メタデータから自動収集。
    • .md 原稿 1 本から .pptx 生成完了までの CI 所要 10 分以内 (HITL 待ち時間除く) — GitHub Actions の job duration から自動算出、月間消費 1,500 分超で Slack 通知 (盲点 #5 対応)。
  • 実行頻度: CI ジョブごと (XPath / layout enum / メディア integrity / 所要時間) + 月次運用レビュー (手直し率・スキーマフィールド増加・無料枠消費)。
  • 違反時対応: XPath / layout enum / メディア integrity アサート失敗は即 fail (配布前ブロック)。手直し率 50% 超が 3 案件連続 / 文字化け再現 / 段階 1 が 4 週超で §6 撤退条件発動。スキーマフィールド +3 件超で月次レビュー前倒し。
  • Review After: 段階 3 完了から 3 ヶ月後 (本 ADR Accept を 2026-06 とした場合 2026-09〜2026-10) に運用レビュー、以後四半期ごと。負債化トリガ: (a) pptx-ea-font が 6 ヶ月以上メンテ停止または python-pptx 互換破壊で生成が壊れた場合 → 直接 XML 操作フォールバックへ移行判断、(b) ブランドテンプレ改訂時にスキーマ未同期で旧ブランド .pptx が 1 件でも生成された場合 → 同期フロー見直し、(c) Slide-IR スキーマフィールド数が当初設計比 +20% 超で「設計のための設計」退行を点検。

8. 参照 (References)

  • 関連 ADR: ADR-0019 (LangGraph 移行) / ADR-0066 (async Queues + DO アーキテクチャ, Paid plan $5/月) / ADR-0064 (web UI → CI トリガ拡張) / ADR-0037 (serverside draft staging ライフサイクル)
  • 関連 PR/Issue: -
  • 外部資料: Brandwares <a:ea> theme 手法、Pandoc --reference-doc 仕様、python-pptx / pptx-ea-font / pptx-automizer ドキュメント、Cloudflare Queues at-least-once 配信仕様、3 モデル突合調査 (2026-06-01)