はじめに
iOS / Android の Crashlytics を見て、定例会議向けの週次レポート(PowerPoint)を毎週作る——という定型作業を、Claude Code / Claude Cowork で自動化しました。
この手の「AI でレポート自動生成」は記事も多いのですが、本記事で伝えたいのはツールの使い方ではなく 役割分担の設計 です。結論を先に書くと、こうです。
AI にスライドを丸ごと作らせない。レイアウトはテンプレート(コード)に固定し、AI はデータ取得と JSON 作成に専念させる。
サニタイズしたサンプル一式を GitHub で公開しています。架空の電子書籍アプリ「BookShelf」を題材にしているので、そのまま動かせます。
1. なぜ「AI に丸ごと作らせる」とうまくいかないのか
最初は素直に「Crashlytics MCP でデータを取って、いい感じのスライドを作って」と毎回 LLM に任せていました。これは動くのですが、運用すると問題が出ます。
- 回によって表がはみ出す・列がずれる
- 配色やフォントが毎回微妙に違う
- 「先週と同じ見た目」を維持できない
原因はシンプルで、毎回ゼロからレイアウトを生成しているから です。レイアウトは本来「毎週変わらないもの」なのに、変わる前提で生成すると、揺れがそのまま品質のブレになります。
2. 責務を2つに分ける
そこで、レポート生成を 2 つの責務に分けました。
| 担当 | 役割 | 変わるもの |
|---|---|---|
| プロンプト(AI) | データ取得・検証・JSON 作成 | 数字・文章(毎週変わる) |
| テンプレート(Python) | レイアウト・配色・グラフ描画 | 構造(固定。崩れない) |
AI の出力は JSON だけ にします。スライドの見た目は python-pptx で書いた1本のテンプレートスクリプトが、その JSON を受け取って描画します。
python build_report.py --data report_data.json --out weekly_report.pptx
この分け方の効果は、運用してみると効いてきます。
- 修正範囲が分離する: 「見た目を直したい」ならスクリプトだけ、「取得データを変えたい」ならプロンプトだけ。お互いに影響しない
- 崩れが構造的に起きない: レイアウトはコードで固定なので、毎回同じ
- iOS / Android で共通: テンプレートは1本。プラットフォーム差は JSON のキー(アプリ名・フッター等)で吸収する
3つ目は地味ですが重要で、テンプレートが1本だと 改善が両 OS に同時反映 され、フォーマットの差分やメンテ漏れが起きません。
3. プロンプトは「小さな組織の設計図」として書く
プロンプト側(AI の担当)は、ただ「データを取って」ではなく、責務を明文化した指示書にしています。役割を分けて書くと、AI の判断が安定します。
- 何を取得するか: topIssues(今週・前週・前々週の3期間)、OS 別、サンプルイベント
- どう検証するか: 取得値が不完全なら推定で補完せず「データなし」にする
- どう JSON にまとめるか: スキーマと、各フィールドの意味・制約
特に効いたのが データ検証ルールの明文化 です。たとえば「バージョン別集計が FATAL 実数に対して極端に少ないときは、サンプルから推定で埋めず、理由を添えて『データなし』にする」と書いておくと、AI が勝手に数字を作りません。レポートの信頼性は、こうした「やらないことの定義」で決まります。
4. テンプレートに「判断」を持たせる
テンプレート側は単なる描画だけでなく、レポートの一貫性を保つロジックも引き受けます。ここが、ただの pptx 出力スクリプトとの違いです。
4-1. 傾向の矢印は符号から自動決定する
前週比の矢印(↑ ↓ →)を JSON で受け取るのではなく、数値の符号からテンプレートが決定 します。
def trend_text_and_color(pct):
if pct is None:
return "—", GRAY
if pct > 5:
arrow, color = "↑", RED # 増加=悪化
elif pct < -5:
arrow, color = "↓", TEAL # 減少=改善
else:
arrow, color = "→", GRAY # ±5%未満は横ばい
pct_txt = "±0%" if pct == 0 else f"{pct:+d}%"
return f"{arrow} {pct_txt}", color
こうすると「矢印は↑なのに数値はマイナス」のような不一致が原理的に起きません。判断基準(±5%)も1か所に集約されます。
4-2. 取得できないものは「データなし」と明示する
null を受け取ったら、推定で埋めずに「データなし」「—」と表示します。グラフでも、前週ランク外で実数が取れない週は高さ0+「—」にして、0件と「計測外」を区別 します。0 と表示すると「先週は0件だった」と誤読されるためです。
4-3. 新規 Issue を自動でマークする
今週初めて上位に入った Issue は、グラフに new!! バッジ、テーブルに「新規」と自動表示します。直近3週を並べているので、初出が「前週」か「今週」かもバッジの位置で分かります。
4-4. 行の高さを内容に合わせる
技術詳細テーブルの行高さは、セルの行数を見積もって決めます。最初は「全文字を全角幅」で計算していたら、クラス名やスタックトレースのような半角英数主体のセルで行数を倍近く過大見積もりし、行が間延びしました。
そこで 実際のワードラップを再現 する方式にしました。半角英数の連続(クラス名・識別子)は折り返し不可の1語として扱い、全角は1文字ずつ折り返す、という貪欲法です。
def estimate_lines(text, width_in, font_pt):
cap = max(2, int(width_in / (font_pt * 0.0075))) # 1行に入る半角換算文字数
total = 0
for seg in str(text).split("\n"):
tokens = re.findall(r"[!-~]+|.", seg) # 英数の連続 or 全角1文字
lines, line = 1, 0
for tk in tokens:
w = sum(1 if ord(c) < 0x80 else 2 for c in tk)
if line + w <= cap:
line += w
elif w > cap: # 行幅を超える長語は強制分割
rest = w - (cap - line)
lines += math.ceil(rest / cap)
line = rest % cap or cap
else:
lines += 1
line = w
total += lines
return total
地味ですが、SecureKeyStore_GenerateKey のような長い識別子を含むセルの行高さが、文字数ベースの概算では合いません。実物で初めて分かる類の問題です。
5. 「視覚的な差異は情報の差異に対応させる」
レイアウトを固定すると、今度は「色やマークに意味を持たせる」ことに集中できます。私が一貫して守っているのは、
視覚的な差異は、情報の差異に対応させる。意味のない装飾は欠陥である。
という原則です。今回の調整は、ほぼすべてこの原則の適用例でした。
- KPI カードの強調色は「最重要だから」赤、ではなく 増加(悪化)だから赤・減少(改善)だからティール と意味に固定
- グラフの色は「今週=クリムゾン」で全シート統一。同じ色=同じ意味(今週の FATAL 件数)
- 3週推移は時間順に淡グレー→スレート→クリムゾンと濃くして、左から右への時間の流れを直感的に
-
new!!バッジや「新規」表示は、新規という情報を持つものにだけ付く
色を足したくなったら「これは情報の差異を表しているか?」と自問する。表していないなら足さない。これだけで、見た目の説得力が変わります。
6. 実データでしか見つからない問題を、計測で潰す
サンプルデータでは出ず、本番データで初めて顕在化する問題がありました。代表的なものが「実行タイミングのブレ」です。
対象期間を「実行日の前日基準」で決めていたため、定期実行が月曜か水曜かで7日間の窓ごとズレ、前週比や新規判定がブレていました。これは対象期間を 固定の ISO 週(月〜日) に紐づけて解決。実行が何曜でも、対象は常に「直近の完了した1週間」になります。
ポイントは、想像ではなく 実測して原因を突き止めてから直す ことでした。スライドを PDF→画像に変換してピクセル値や行の高さを確認する、という地道な検証が効きました。
soffice --headless --convert-to pdf weekly_report.pptx
pdftoppm -jpeg -r 100 weekly_report.pdf slide
7. 運用:定期実行+ワンクリック再生成
定期実行は Claude Cowork のスケジュール済みタスクとして登録しています。加えて、定例前に最新データで作り直したいときは、タスク画面の再生ボタン(今すぐ実行)を押すだけで任意のタイミングでも生成できます。「自動で毎週」と「必要なときに手動で」の両方が、同じ仕組みで賄えます。
まとめ
- AI にスライドを丸ごと作らせない。レイアウトはテンプレートに固定し、AI はデータ取得と JSON 作成に専念させる
- プロンプトは「小さな組織の設計図」。何を取り、どう検証し、どう JSON にまとめるかを責務として明文化する
- テンプレートには描画だけでなく 判断(矢印・データなし・新規判定・行高さ)を持たせる
- 色やマークは装飾ではなく 情報の差異 に対応させる
- 本番データでしか出ない問題は、想像ではなく 計測 で潰す
サンプル一式は GitHub に置いてあります。report_data.sample.json を差し替えれば同じレイアウトで中身だけ変わるので、自分のプロダクト向けに作り替える出発点に使ってください。
