プロジェクト概要

PPT Agent — 建築提案向けAIプレゼン資料生成システム

建築家向けのAIプレゼン資料生成システムです。散在する建築企画の素材(図面、パース、調査メモ、参考事例)を、 追跡でき、再実行でき、レビューできる複数Agentのパイプラインで整理し、 トーンがそろい、話の流れが通った提案資料PDFに自動でまとめます。

1. 業務背景

利用シーン:建築家はコンペ提案や中間報告の段階で、数十から数百点のばらばらな素材を 30〜60ページほどの、統一感があり、話の流れが通ったプレゼン資料にまとめる必要があります。 設計事務所では、この作業にインターン1人が丸ごと1〜2週間かかることも珍しくありません。

主な課題

  • 素材の種類が多く、名前の付け方もばらばらで、ページ単位で整理しにくい
  • 文章作成、レイアウト、図版選びが互いに絡み、何度も手戻りが起きる
  • LLMにそのままスライドを作らせると制御しづらい。数字を作ってしまうか、レイアウトが崩れるかのどちらかで、一部分だけ直すのも難しい
設計方針

Workflow First, Agent Second。システムの本体は、状態が明確で、 中間結果を保存でき、失敗時に再実行でき、各ページの内容を元素材までたどれるワークフローです。 LLMは、決まった処理のあいだで内容を整える役に限定し、制御フローは任せません。

2. システム全体像

┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ フロントSPA  │───▶│   FastAPI    │───▶│ PostgreSQL   │
│ (フロー駆動) │    │   Routers    │    │ + pgvector   │
└──────────────┘    └──────┬───────┘    └──────────────┘
                           │
                  ┌────────┼────────┐
                  ▼        ▼        ▼
              ┌───────┐ ┌──────┐ ┌────────┐
              │ Agent │ │Celery│ │ Render │
              │  層   │ │Tasks │ │Engine  │
              └───┬───┘ └──┬───┘ └───┬────┘
                  │        │         │
                  └────────┼─────────┘
                           ▼
                    ┌──────────────┐
                    │ HTML→PNG→PDF │
                    └──────────────┘

重要な設計判断

  • 1つの Orchestrator Agent で全体を回すのではなく、FastAPI のバックグラウンドスレッド + Celery タスクを明示的につないでいます
  • 状態管理はデータベースの Project.status に直接持たせ、途中再開は「DB を読む → 該当ステージから入り直す」で実現しています
  • LLM 呼び出しは OpenRouter に統一し、自前の薄いラッパーでスキーマ検証、リトライ、同時実行数の制御を行います

3. 中核フロー:素材パッケージのパイプライン

ローカル素材フォルダ
    │
    ▼  ① ingest(ルールベースの走査 + 分類 + 重複排除)
MaterialPackage
  ├── MaterialItem    (元ファイル単位)
  ├── Asset           (派生アセット: グラフ、地図、事例カード)
  └── ProjectBrief    (素材から抽出した案件の基本情報)
    │
    ▼  ② BriefDoc Agent (LLM)
BriefDoc (ストーリーライン / 章構成)
    │
    ▼  ③ Outline Agent (Claude Opus 高性能モデル)
OutlineSpec (8〜12ページ設計: 各ページの目的と必要な素材タイプ)
    │
    ▼  ④ MaterialBinding (ルールベースの照合、LLMなし)
SlideMaterialBinding (各ページにひもづく素材 + 派生アセット + 根拠)
    │
    ▼  ⑤ VisualTheme Agent
案件全体のフォント / 配色 / 装飾ルール
    │
    ▼  ⑥ Composer Agent (Claude Haiku 高速モデル + asyncio 並列)
LayoutSpec × N (各ページの構造化レイアウト記述)
    │
    ▼  ⑦ Jinja2 + Playwright
HTML → PNG スクリーンショット
    │
    ▼  ⑧ Critic Agent (3段階レビュー + 自動修正 最大3回)
    │
    ▼  ⑨ Export
PDF

各ステージの説明

ステージ名称LLM内容 / 成果物
Ingest ローカル素材フォルダをルールベースで走査し、ファイル種別ごとに自動分類(画像/グラフ/文書/テキスト)、重複排除して MaterialItem を生成。同時に素材から案件の基本情報を抽出して ProjectBrief を作り、グラフ、地図、事例カードなどの Asset を派生させる
BriefDoc Agent 素材パッケージ内のテキスト摘録、素材要約、ProjectBrief をもとに、LLM がストーリーラインを整理 —— 章構成、ポジショニング、物語の流れを含む。成果物 BriefDoc は後続の Outline に「何の話をするか」の意味的な起点を与える
Outline Agent✅ 高性能 ProjectBrief + BriefDoc + PPT ブループリントを入力とし、Claude Opus が 8〜12 ページの OutlineSpec を生成。各ページに目的、必要な素材タイプ、主要情報ポイントを定義する。全体の構成を決める唯一の判断ポイントなので高性能モデルを使う
MaterialBinding 純粋な Python のルールベース照合。Outline で各ページが宣言した「必要な素材タイプ + tag」に沿って MaterialPackage から一致する MaterialItem と Asset を検索し、SlideMaterialBinding(ひもづけた素材、根拠要約、カバー率、不足項目)を出力する
VisualTheme Agent ProjectBrief と参考事例の好みに基づき、案件全体のビジュアルテーマ —— フォントの組み合わせ、メイン/サブの配色、余白ルール、装飾要素 —— を LLM で生成する。VisualTheme はグローバルなスタイル制約として Composer とレンダリング層に渡される
Composer Agent✅ 高速 ページごとに OutlineSlideEntry + Binding + Theme を構造化した LayoutSpec に変換する。Haiku + asyncio.gather 8 並列で、テキスト密度、図文バランス、レイアウト骨格を制御する。LayoutSpecページレベルの中核プロトコルで、ブロック種別、内容、配置を定義する
Render Engine Jinja2 テンプレートが LayoutSpec を自己完結型 HTML(Design Token CSS インライン)にレンダリングし、asset:{id} 参照を実パスに解決する。Playwright Headless Chromium でスクリーンショットを撮り PNG を生成。カバー、概要、章区切り、地図、事例比較、グラフなど 9 種のテンプレートを用意
Critic Agent一部 3 段階レビュー(L1 ルールベース / L2 意味 LLM / L3 見た目 LLM)、詳細は下記。不合格時は Composer の局所再生成 → 再レンダリング → 再レビューを最大 3 回まで繰り返す
Export 全ページの PNG スクリーンショットをページ順に結合し、PDF ファイルを生成する

4. 設計上のポイント

4.1 素材パッケージ:幻覚を防ぐいちばん確実な方法

課題:初期版では、LLM に自由対話からそのままスライドを生成させていたため、 数字は作り物になり、引用元は追えず、1ページ直すだけでも全体を作り直す必要がありました。

対応:ユーザーの素材をまず MaterialPackage として固定し、 パイプライン全体の唯一の信頼できる情報源にします。以降の Agent は、 MaterialPackage にある内容しか使えず、勝手に補えません。

失うもの得られるもの
柔軟さ(先に素材パッケージを準備する必要がある)各ページの内容を具体的な素材ファイルまで追える
対話だけで即座に生成する使い方1ページだけ直しても他ページに影響しない
失敗しても任意の段階から再開できる

4.2 素材の対応付け:ルールベースの照合 vs LLM の判断

課題:LLM に「5ページ目にどの画像を置き、どの文章を添えるか」を決めさせると、 存在しない画像を参照したり、事例Aの画像を事例Bの文章の横に置いたりします。

対応MaterialBinding は純粋な Python のルールベース処理です。 Outline で宣言された「必要な素材タイプ + tag」に沿って MaterialPackage から一致候補を探します。 LLM はこの段階に関与しません。

効果

多少の「気の利いた組み合わせ」の余地は減りますが、画像と文章の取り違えをまとめて防げます

4.3 高性能モデルと高速モデルの使い分け

段階モデル理由
Outline 生成Claude Opus 4.6(高性能)全体のストーリー構成は1回だけなので品質優先
Composer の単ページ生成Claude Haiku 4.5(高速)各ページごとに呼び出し、Nページ並列なので遅延とコストに敏感
Critic の意味レビューClaude Haiku 4.5(高速)軽量なチェックなので速度優先

効果:単ページ生成は asyncio.gather で8並列にし、全体の待ち時間を抑えています。 さらに Critic の意味レビューで、高速モデルが落としがちな細部を補います。

5. レビューと修正の仕組み(Critic)

LLM 出力の不安定さは、3段階のレビュー + 局所修正で収束させます。

実装LLM代表ルール
L1 ルールレビュー tool/review/layout_lint.py 純 Python 文字あふれ / 必須ブロック欠落 / 未知テンプレート / 情報量過多
L2 意味レビュー tool/review/semantic_check.py 高速モデル 数値が brief と不一致 / 根拠のない断定 / スタイル語の矛盾 / 顧客名の誤り
L3 見た目レビュー Composer のマルチモーダル LLM 高速モデル 見た目が雑然としている / 余白の無駄が多い / 画像がぼやけている

判定フロー

P0 修復不可       ──▶  ESCALATE_HUMAN  (失敗として扱い、人の対応を待つ)
P0/P1 自動修復可  ──▶  REPAIR_REQUIRED (自動修復、最大3回まで再試行)
P2 のみ           ──▶  REPAIR_REQUIRED (修復するが書き出しは止めない)
問題なし          ──▶  PASS
なぜ分けるのか

ルールで判定できるものと、意味を見ないと分からないものを分けることで、 LLM 呼び出しを70%以上減らせます。文字あふれやタイトル欠落のような問題に LLM は不要です。

6. 参考事例RAG(pgvector)

案件の雰囲気に近い参考事例を事例庫から呼び出し、Outline / VisualTheme Agent の参考にします。

データフロー

ProjectBrief (建物種別 / スタイルの好み / 規模)
    │
    ▼  build_query_text()  → フィールドをつないで検索文を作る
    ▼  get_embedding()     → 1536 次元ベクトル
pgvector のコサイン距離検索 (IVFFlat インデックス)
    │
    ▼  rerank_cases()      → 業務ルールで並べ替え
    ▼  preference_summary  → 好みの要約 → VisualTheme Agent に渡す

主要 SQL

SELECT ..., 1 - (embedding <=> CAST(:vec AS vector)) AS similarity
FROM reference_cases
WHERE building_type = :building_type
ORDER BY embedding <=> CAST(:vec AS vector)
LIMIT :top_k

実装上の工夫

01

複数の embedding provider

EMBEDDING_PROVIDERopenai / voyage / qwen / mock を切り替え、業務コードを変えずに使い分けます。

02

mock 実装

SHA256 → LCG → L2 normalize で決定論的な疑似ベクトルを作り、ローカル開発を外部APIなしで進められます。

03

フォールバック

pgvector が使えないときは、自動で building_type + style_tags のタグ絞り込みに切り替え、機能停止を避けます。

7. 技術スタック

採用技術採用理由
Web フレームワークFastAPI + Pydantic v2非同期が標準で、Schema を層をまたいで共有できる
データベースPostgreSQL 16 + pgvectorリレーショナルデータとベクトル検索を同じDBに置けるため二重書き込みを避けられる
ORM / マイグレーションSQLAlchemy 2.0 + Alembicpgvector は raw SQL で確実に扱う
タスクキューCelery 5.4 + Redis 73キュー(default / render / export)でリソースを分離できる
LLMClaude Opus 4.6 / Haiku 4.5(OpenRouter)高性能モデルと高速モデルを使い分けてコストを抑える
LLM ラッパー自前の薄いラッパーLangChain を入れず、制御性と依存サイズを保つ
レンダリングJinja2 + Playwright Headless ChromiumHTML/CSS に慣れたデザイナーが扱いやすく、画像化の再現性も高い
グラフmatplotlibデータ駆動のグラフ(bar/line/pie/radar)を出せる
デプロイdocker-compose6コンテナをそのまま起動できる: api / worker / renderer / flower / db / redis

8. 主な設計判断

Q1:なぜ LangChain / LangGraph を使わないのか

依存関係にはありますが、コードからは参照されていません。初期に試した LangGraph StateGraph は削除済みで、理由は次の通りです。

  • この業務は決まったパイプラインであり、Agent の自律判断ではない。状態機械はすでに DB 内 (ProjectStatus 列挙)にあり、Celery がリトライと永続化も担っています。ここに LangGraph Checkpointer を重ねると二重管理になります
  • プロセスをまたぐ非同期境界が多い。FastAPI のバックグラウンドスレッド、Celery worker、render worker を 1つの StateGraph 実行基盤に収めにくいです
  • 自前ラッパーで、スキーマ検証、JSON 解析の再試行、Semaphore による同時実行数制御、エラー分類までカバーできており、 LangChain より制御しやすい状態です

Q2:なぜ PDF-first で PPTX ではないのか

  • レイアウト精度、フォント表現、図文の混在は HTML/CSS のほうが python-pptx より明らかに強いです
  • 最終納品物がもともと PDF なので、その場で再編集する要件がありません
  • python-pptx は依存に入っているため、必要になれば後から足せますが、現時点の主線ではありません

Q3:なぜレビューを L1 / L2 / L3 に分けるのか

  • コスト:L1 はゼロコストで 70% ほどの問題を止められるため、無意味な LLM 呼び出しを減らせます
  • 説明しやすさ:ルールレビューのエラーコード(R001 / R002...)は、そのままユーザーへの説明に使えます
  • 直しやすさ:ルール層のエラーは repair_plan.py が直接修正でき、LLM を通す必要がありません

Q4:単一 Agent 編成ではなく分散編成なのはなぜか

実際の編成ロジックは次に分かれています。

  • api/routers/material_packages.py:取り込みの開始
  • api/routers/outlines.py:バックグラウンドスレッドで生成チェーンを接続
  • tasks/*.py:Celery で重い非同期処理を担当

理由:各ステージで使うリソースの性質が大きく違うからです。Outline は LLM bound、 Render は CPU/IO bound、Export は IO bound なので、Celery のキューで分離したほうが、 1つの Agent プロセスに詰め込むより素直です。

9. 実装上の難所

難所対応
LLM の出力形式が安定しない 自前の call_llm_structured を用意し、Pydantic JSON Schema を system prompt に埋め込み、失敗時はリトライし、エラー内容でモデルに修正を促す
Playwright の Celery worker でのコールドスタートが遅い 独立した renderer キューを切り出し、ブラウザインスタンスを専用で維持する
Reference Agent の検索精度 クエリ文には建物種別、スタイル、規模など、見分けに効く項目だけを使い、空欄は除外する
ローカル開発が外部APIに依存する mock embedding として SHA256 → LCG → L2 normalize を使い、同じテキストなら同じベクトルを返すようにしてエンドツーエンドテストを回せるようにする

10. 今後の拡張

  • PPTX 出力:python-pptx の経路を補い、デザイナーが二次編集できるようにする
  • 見た目レビューの閉ループ化:L3 の見た目レビューは今は問題を付けるだけで自動修正しないため、 Critic が修正指示を出し、Composer に戻す形にしたい
  • 素材パッケージ単位の RAG:今は参考事例庫だけを対象にしているが、将来は案件自身の素材も索引化し、 「意味で素材を探す」を可能にしたい
  • Workflow から Agent へ:現状は決まったパイプラインだが、「結果が気に入らない → どのページを直すかを自律判断 → 複数回再生成」のような要件が出たら、LangGraph で部分的な Agent 化を検討する

11. 覚えてほしい3つのポイント

01

Workflow First, Agent Second

LLM に制御フローを渡さず、状態を明確にし、再実行と追跡を可能にします。

02

素材パッケージが唯一の信頼できる情報源

幻覚を防ぐには、LLM に勝手に補う余地を与えないことが重要です。

03

高性能モデルと高速モデルの併用 + 3段階レビュー

納得できる品質を最小コストで得て、高価なモデルは本当に必要なところだけに使います。