🧭 本記事は Claude Code実務運用シリーズ の STEP 6「サブエージェント化する」です。
同じ設計を開発以外(競合X分析)に横展開できることを確認します。
シリーズ全体の地図と読む順は 親記事 にまとめています。
毎週の競合X(旧Twitter)動向レポートを、Claude Code で「データ取得 → 差分分析 → 検証 → PowerPoint 生成」まで半自動で回しています。設計の下敷きは ループエンジニアリング(Addy Osmani, Loop Engineering)。「設計の対象を “1回の振る舞い” から “回り続ける仕組み全体” に上げる」という考え方です。
ただし題材が 「数値KPI」ではなく「定性的な競合分析」 だったことで、定石どおりにはいきませんでした。本記事は、この仕組みを どう設計したか を実コードで具体化したものです。結論を先に言うと——
定性レポートでは決定論チェック(A)の射程は驚くほど狭く、主役は別モデルの評価役(B)になる。
この一点を中心に、ループの骨格と各パーツの実装を順に見ていきます。
題材は実在の競合Xアカウント(公開情報)ですが、評価基準・自社アクション・実数値はすべて架空のサンプルに置き換えています。手法だけ持ち帰ってください(情報は 2026/06 時点)。
対象読者
- 定例の「調査・分析レポート」を毎週手で作っていて、AIに任せたいが品質が不安な人
- 単発のAI自動化はできたが、「回り続ける仕組み」にしたい人
- 出力が 数値ではなく文章・判断 の比率が高いレポートを自動化したい人(ここが本記事の主眼)
この記事で得られること
- 「生成役と評価役を分ける」を、再計算できない定性レポートでどう実装するか
- 本当に防ぐべき事故は「レイアウト崩れ」ではなく 「空データからの捏造」 だという視点と、その潰し方
- 社名を知らなくてもブランドカラー規則を強制する 決定論チェックの小技
- 全部入りの検証スクリプト(標準ライブラリ+python-pptx だけ、そのまま流用可)
題材:競合X週報パイプライン
やっていることはシンプルです。
- 取得:競合6社の公式Xアカウントのタイムラインを、ログイン済みブラウザ越しにスクレイプ(投稿本文・日時・views/RP)
-
分析:前週レポート(
latest.md)と差分を取り、「今週何が変わったか・戦略の意図・自社への示唆」を Markdown で生成 - 生成:固定テンプレートの PowerPoint に、テキストだけ差し替えて経営層向けスライド化
- 検証 → 保存
よくある数値集計レポート(KPI・障害件数など)との決定的な違いは、成果物の中身が「数字」ではなく「所見」だということです。件数集計なら素値から合算し直して突き合わせられますが、競合分析の中身は「ある競合は割引施策を“冠イベント化”した(観察)」「狙いは休眠ユーザーの呼び戻しだろう(推測)」のような 判断 です。引用する 82,000 views のような数字も、Xの桁丸め表示の概算で、算術的に再導出できません。
この「再計算できなさ」を前提に、ループはこう組んでいます。
全体構成:オーケストレーションプロンプトが“ループ本体”
実際の起動は、「順番」と「中止条件」だけを書いた1枚のプロンプトから始まります。これがループ本体で、実作業は2つの台本に丸ごと委譲します。Boris Cherny の "I just write loops" を定例レポートに落とすと、こうなります。
以下を順番に実行してください。
1. prompt/weekly_x_competitor_prompt.md を読み込み、指示に従って実行する
(レポート生成・保存まで)
2. latest.md の作成日が今日の日付に更新されていることを確認してから、
prompt/competitor_slides_prompt.md を読み込み、指示に従って実行する
(pptx 生成・保存まで)
ステップ1が失敗した場合(latest.md が更新されていない場合)は、
ステップ2に進まず理由を報告して終了してください。
このプロンプトは 何も生成しません。やっているのは ① step1 → step2 の順番 と、② ステップ間のゲート(latest.md の作成日が今日か) だけ。肝は、次工程に進む判断を 生成役の「できました」という自己申告ではなく、ファイルの状態(作成日) で決めていること。「完了は本人に名乗らせない」を、検証だけでなく オーケストレーション層 にも効かせています。
構成は3層です。
| 層 | 役割 | 中身(概要) |
|---|---|---|
| オーケストレーション | ループ本体=順番+中止条件 | 上記プロンプト。step1 →(作成日ゲート)→ step2 |
| 台本①:レポート | データ → Markdown | 調査対象(A/B/C枠)/保存要件(latest.md+素値アーカイブ)/分析手順(差分・異常値・戦略推測)/出力形式/データ十分性ゲート/検証2段(A・B)
|
| 台本②:スライド | レポート → PowerPoint | テンプレ方式の絶対ルール/7スライドの差し替え対応/ブランドカラーパレット/作業手順/決定論チェック(verify_deck) |
オーケストレーションが委譲している2つの台本の中身を、抜粋で示します(実名・実数値はサニタイズ済み)。これを見ると、1枚のオーケストレーションプロンプトが「何を任せているか」が具体的に掴めます。
台本①:レポート(weekly_x_competitor_prompt.md/抜粋)
# 役割
競合電子書籍アプリのXアカウントから「今週の変化」を抽出し、前回レポートとの差分を出す。
# ブラウザ制約(厳守)
- 既存のログイン済みブラウザ(127.0.0.1:9222)にのみ接続。新規/自動操作専用ブラウザは起動しない。
- 接続できなければ代替手段を使わず停止して報告。
# 調査対象
- A. 直接競合(4社)… 投稿タイプ・訴求・キャンペーン・導線まで詳細に差分を取る
- B. 市場トレンド枠(2社)… 新施策/新訴求/高反応投稿の有無だけ軽量確認
- C. 月次枠(1社)… 実行日が月の第1週のときだけ確認
# 保存要件
- latest.md に上書き + weekly_x_competitor_report_YYYY-MM-DD_wXX.md
- スクレイプ素値を raw/YYYY-MM-DD_wXX/ にアーカイブ(削除しない)
# 出力ルール(差分のみ・事実/観察/推測を分ける・推測は明記・未確認は「確認できず」)
# 出力形式
1.今週の要点(3点) / 2.直接競合の変化 / 3.市場トレンド / 4.市場全体 /
5.異常値 / 6.戦略変化の解釈(推測) / 7.自社アクション(優先度A/B/C) / 8.来週の注目 / 9.保存結果
# データ十分性ゲート(生成の前に判定)
- A枠で2社以上が取得不可なら、生成せず中止して理由を報告
# 検証(評価役・2段)
(A) verify_report.py(決定論・最大3回) → (B) 別モデルで素値と突合(最大2回)
台本②:スライド(competitor_slides_prompt.md/抜粋)
# 目的
競合X分析を、非技術者でも「何を意思決定すべきか」が分かる形にスライド化する。
# テンプレート方式の絶対ルール
- python-pptx でテンプレートを開き、テキストの差し替えのみ。
- 図形・カード・帯の追加/移動/サイズ変更/色変更/フォント変更は禁止。
- 差し替えは run 単位で元の書式を維持。テンプレ本体は変更せず別名保存。
# スライド構成(全7枚)
1.タイトル / 2.今週の結論(番号カード3) / 3.市場の変化 / 4.競合の動き(数値カード3) /
5.異常値 / 6.自社への示唆(キーワード3) / 7.アクションプラン(A-1/A-2)
# ブランドカラー(色変更禁止の“唯一の例外”)
- 各社の指定色を社名箇所に適用。明るい塗りの上の文字は必ず濃色(白文字は禁止)。
# 検証(評価役・決定論)
- verify_deck.py を実行。フッター/週/日付の一致・差し替え漏れ・文字数超過・パレット規則をチェック(最大3回修正)。
ここで効いているのは、台本の側にも「止まる条件」を書いてあることです。台本①の「接続できなければ停止」「A枠2社以上欠けたら中止」、台本②の「テキスト差し替えのみ・図形や色は触るな」。オーケストレーションが順番とゲートを、台本が現場の禁止事項と検証を持つ。この二段構えが効きます。
全体の流れを図にすると、こうです。中止条件=ゲートを菱形で明示しているのがポイントです(ここで止まれることがループの安全装置)。
「順番とゲートだけ書く」「実作業と検証は台本に委譲する」。この分離が、毎週同じ品質でループを回せる土台になっています。なお、役の受け渡し(生成役→別モデルの評価役)を主題にした2段検証は、次節でシーケンス図として見せます。
ループの骨格:6パーツの割り当て
Addy はループに必要な道具立てを6パーツに整理しています。このパイプラインでは、Claude Code と周辺の機能をこう割り当てました。
| パーツ | 役割 | このパイプラインでの実装 |
|---|---|---|
| Automations | 起動 | 週次トリガー(手動起動でも可。Xログイン状態に依存するため現状は半自動) |
| Connectors(MCP) | 外部接続 | ブラウザ操作MCPでログイン済みXのタイムラインを取得 |
| Skills / 台本 | 手順の固定 |
weekly_x_competitor_prompt.md / competitor_slides_prompt.md に検証手順まで明文化 |
| Sub-agents | 生成役と評価役の分離 | 生成は本体/検証は別モデル(例: Haiku)のサブエージェント |
| Memory | 状態の永続化 |
latest.md(前週比較の基準)+ raw/ の素値アーカイブ |
| 決定論チェック | トークン不要のゲート |
verify_report.py/verify_deck.py(後述) |
この中で、定性レポートだからこそ重く効いたのが Sub-agents(評価役の分離) と、それを 台本に固定する こと、そして暴走を止める ハード上限 です。順に実装を見ます。
設計①:生成役と評価役を分ける ― ただし(A)の射程は狭い
検証は2段。ここが本記事の肝です。Addy の主張は「コードを書いた本人は自分の作業に甘い(A model grading its own output is too generous)」。だから生成役と評価役を分け、評価役はできれば別モデルにする。
(A) 決定論チェック:トークン不要で弾けるものだけ
数値集計レポートなら「素値から合算し直して突合」が主役になります。定性レポートでは、決定論で弾けるのはこれだけ です。
- 作成日が当日か
- 比較対象(前週ファイル名)が明記されているか
- 取得状況(取得不可の有無)が明記されているか
- レポート中のカンマ区切り数値が、スクレイプ素値のどこかに存在するか(=転記ミス検出。算術的な再計算はできないが、「素値に無い数字=どこかから持ってきた疑い」は機械的に言える)
4番だけが「数字の検証」で、しかも 再計算ではなく存在チェック にまで弱まっているのがポイントです。
# scripts/verify_report.py(抜粋)
# カンマ区切り数値(4桁以上)を抽出し、素値アーカイブに存在するか確認
figs = sorted(set(re.findall(r"\b\d{1,3}(?:,\d{3})+\b", md)))
unmatched = []
for fig in figs:
plain = fig.replace(",", "")
if (fig in blob) or (plain in blob): # blob = raw/*.json を結合した素値
ok(f"数値 {fig} は素値に存在")
else:
unmatched.append(fig) # 派生値 or 転記ミスの疑い → (B)で確認
「82,000(素値の表示値)は素値にある=OK」「8.2万view(私が本文用に丸めた派生値)は素値に無い=WARN で (B) に送る」。決定論で言えるのはここまで、と割り切ります。
(B) 別モデルの評価役:ここが主役
定性レポートの妥当性——事実/観察/推測の切り分け、他週・他社からの取り違え、取得不可アカウントを推測で埋めていないか——は、人間と同じく「読んで判断」しないと分かりません。だから (A) が PASS したら、生成役とは別モデル(例: Haiku)のサブエージェント に、latest.md と素値 raw/*.json を読ませて突き合わせさせます。
台本(プロンプト)にはこう固定してあります。
## (B) 検証サブエージェント(別モデルで文章を点検)
- (A) が PASS したら、別モデル(例: Haiku)に latest.md と raw/<...>/*.json を読ませ、
- 各「変化・観察」が素値(実際の投稿)に裏付けられているか(捏造・取り違えが無いか)
- 「事実 / 観察 / 推測」の切り分けが適切か
- 「取得不可」を推測で埋めていないか
を点検させる。要修正なら直して (A) からやり直す。往復は最大2回。
この2段の検証ループを図にすると、こうです。
数値集計レポートでは (A) が主・(B) が念押しになりがちですが、定性レポートでは比重が逆転 します。これが「数字が再計算できないレポート」での最大の設計判断でした。
設計②:データ十分性ゲート ― 本当の事故は“空データからの捏造”
評価役を入れても、そもそも入力データが空なら捏造を後追いで検出するだけ です。だから生成の前に止める決定論ゲートを置きます。
# データ十分性ゲート(生成の前に必ず判定)
- A枠(直接競合4社)で直近7日の新規投稿が1件も取れていないアカウントは「取得不可」と明記。
- A枠で2社以上が取得不可なら、差分分析の土台が崩れるので レポートを生成せず中止 し理由を報告。
「取れなかったら推測で埋める」ではなく「取れなかったら止める/明記する」。当たり前ですが、AIに任せると一番崩れる規律なので、コードと台本の両方 に書き下しています。Xはログイン要求で投稿が取れないことがあり、取れていないのに前週の記憶や“それっぽい推測”で埋まったレポート が、見た目だけ綺麗に出てしまうのが一番こわい事故だからです。
設計③:素値アーカイブ ― 遅延と「後から検証」に備える
ループエンジニアリングで一番AIっぽいと言われる落とし穴が「先週の値が翌日には変わっていた(遅延集計)」です。競合Xでも views/フォロワーは時間で動きます。だから スクレイプ素値を毎週 raw/YYYY-MM-DD_wXX/ に保存し、消さない。
- 保存先: raw/YYYY-MM-DD_wXX/<account>.json(followers, 各投稿の dt/txt/aria を含む)
- このアーカイブは削除しないこと(突合可能な原本として残す)
これで「前週から継続」のような差分主張が、後から原本に当たって検証できます。(A)の数値トレーサビリティも、この原本があって初めて成立します。
設計④:スライドは固定テンプレ+テキストのみ+“社名を知らない”色チェック
スライド生成の原則はシンプルで、色・レイアウト・フォントはAIに触らせず、テキスト文言だけを差し替える(python-pptx で run 単位置換)。見た目を固定すると、毎週の差分が「数字と所見」だけになり、レビューが一瞬で終わります。
面白いのは、その決定論チェック側の工夫です。スライド4の各社ヘッダーバーは 週によって載る会社が変わる(今週はA社/B社/C社、来週は別の3社かもしれない)。なので「A社は黄色」と決め打ちで検査するとすぐ壊れます。代わりに 会社名を知らなくても成立する“規則そのもの” を検査します。
# slides/verify_deck.py(抜粋):パレット規則を会社非依存で強制する
BRAND_FILL = {"F6AB00","E60012","0D2F54","2D5FAA","FFD400","06C755"} # 6社の塗り
BRIGHT_FILL = {"F6AB00","FFD400"} # 明るい塗り → 文字は必ず #2B2924
for bar, txt, card in [("Shape 5","Text 6","S4 カード1"),
("Shape 11","Text 12","S4 カード2"),
("Shape 17","Text 18","S4 カード3")]:
bf = fill_hex(find(slide4, bar))
if bf not in BRAND_FILL: # 塗りが6社ブランド色か
fail(f"{card}: ヘッダーバー塗り={bf} がブランド色でない"); continue
want = "2B2924" if bf in BRIGHT_FILL else "FFFFFF" # 明/暗で文字色が決まる
if run_color(find(slide4, txt), 0, 0) != want:
fail(f"{card}: 社名文字色が塗り{bf}上の規則に反する")
「どの会社か」ではなく「明るい塗りの上に白文字を置いていないか」を見る。これなら毎週どの3社が来ても落ちません。差し替え漏れ(当週以外の週番号トークンの残存)や全角文字数オーバーも、ここで一緒に弾きます。soffice が無い環境でも動くので、画像化での目視に依存しません。
台本に固定する&ハード上限
肝心なのは、これらを 思いつきで毎回やらないこと。検証手順を2つのプロンプト(weekly_x_competitor_prompt.md / competitor_slides_prompt.md)に節として書き下し、毎週必ず同じ順で走るようにしています。
そして上限を切ります。Addy の言う "A loop running unattended is also a loop making mistakes unattended."(誰も見ていないループは、誰も見ていないまま間違え続ける)対策です。
- (A) の修正ループ:最大3回
- (B) の往復:最大2回
- 超えたら未解決点を残して停止(トークン暴走の防止)
ここを切らないと、ログインが切れていたり投稿が取れない週に、ループが「直そう」として延々とトークンを焼き続けます。上限は品質のためというより、財布のための安全装置です。
回してみて効いた点/注意点
| 落とし穴 | 効いた防ぎ方 |
|---|---|
| 検証の積み残し | 生成役と別の評価役((A)決定論+(B)別モデル)を台本に固定 |
| 空データからの捏造 | データ十分性ゲートで生成前に中止(後追い検出より前で止める) |
| 取り違え(先週/他社の所見の流用) | (B)に素値との突合を明示的にやらせる |
| 色・差し替え漏れ | 会社非依存のパレット規則チェック+週トークン残存スキャン |
| トークン暴走 | 修正ループ・往復の回数にハード上限 |
派手に見えるのは (B) の別モデル評価役だけで、残りは 「素値を残す」「テンプレを固定する」「決定論で弾けるものだけ弾く」 という地味な配線です。けれど定性レポートで品質を安定させたのは、この地味な配線と、“自分で自分を採点しない” という一点でした。
まとめ:器はツールが用意する、物差しは人が持つ
この設計を一言でまとめると、こうなります。
数値レポートでは「素値から再計算して突合」が物差しになる。
定性レポートでは、再計算できる数字がほとんど無い。
だから物差しは「事実と推測を分けられているか」「取れていないものを取れたことにしていないか」という 判断基準 になり、それを別モデルと台本に書き下すしかない。
ツールが用意してくれるのは器(スケジュール・テンプレ・サブエージェント・素値の置き場)まで。「良い競合週報とは何か」 という物差しは、結局やってきた人間の中にしかありませんでした。
気になった方は、手元の定例レポートを1つ選んで、まず「素値の保存 → 固定テンプレ → 決定論で弾けるものだけ弾く → 残りは別モデルに読ませる」に分解してみてください。出力が数字寄りか判断寄りかで、(A)と(B)のどちらに重心を置くべきかが、すぐ腹落ちします。
参考
- Addy Osmani, Loop Engineering(2026/6/7)― 概念の初出
このシリーズの歩き方
Claude Code実務運用シリーズ ― 暴走させない、から仕組みにするまで。

