# Ken Imoto — Full Blog Text
> Concatenated full text of all blog articles for AI citation use.
> Individual URLs are listed in llms.txt or sitemap-index.xml.
---
# I Added 11 JSON-LD Schemas. Three Months Later, Only 3 Showed Up in AI Citations.
URL: https://kenimoto.dev/blog/11-json-ld-3-cited-by-ai/
Lang: en
Date: 2026-05-25
Description: Three months ago I bundled 11 JSON-LD schemas into my site's head. I measured every AI citation since. Eight of those schemas were dead weight. Here's which three actually carried the freight, and why the other eight didn't.
Three months ago I spent an afternoon adding eleven JSON-LD schemas to my site's `
`. Organization, WebSite, Person, four Service blocks, two Books, MusicGroup, FAQPage. I felt very pleased with myself.
Then I measured what AI engines actually did with them.
Three of the eleven showed up in citations. The other eight might as well have been HTML comments.
This is the measurement story. I'll tell you which three schemas earned their seat, which eight were dead weight, and why I'd implement it the same way again — but smaller.
## What I implemented and why I thought it would work
The implementation itself was straightforward. I wrote it up in detail in [the Japanese version of this blog](https://kenimoto.dev/ja/blog/json-ld-11-schemas-llm-understanding/) (English readers will lose the prose but the code blocks translate fine). The short version: I bundled all eleven schemas into a single `
## さらに深掘りしたい方へ
本記事はその一面に過ぎません。OpenAI・Anthropic・LangChain・Martin Fowler・学術の5つの解釈を1冊に統合した体系書 **[ハーネス・エンジニアリング — AIを"使う"から"操る"へ](https://kenimoto.dev/ja/books/harness-engineering-guide)** で、ハーネスとは何か、どう設計し、どう運用するかを19章で解説しています。
---
# AIエージェントは月いくらかかるのか -- API・サブスク・ローカルの損益分岐点
URL: https://kenimoto.dev/ja/blog/ai-agent-cost-structure-breakeven/
Lang: ja
Date: 2026-05-03
Description: AIエージェントのコストを3類型(API従量課金・サブスクリプション・ローカルLLM)で整理し、2026年5月時点の料金で損益分岐点を計算した。
AIエージェントの選定記事を10本読んで、「精度」「拡張性」「エコシステム」の比較表を眺めた。よくわかった。ただ、一番知りたいことが書いていない。
**月いくらかかるのか。**
技術選定で最初にやるべきことは、アーキテクチャ図を描くことでも、ベンチマークを読むことでもない。上長に「月額いくら?」と聞かれたときに即答できる数字を持っておくことです。私はこれを怠って、プロトタイプが完成してから見積もりを出し、「これ月5万かかるの?」という一言でプロジェクトが3週間止まった経験があります。
2026年5月時点の料金体系で、AIエージェントのコストを3つの類型に整理しました。
## コスト構造の3類型
AIエージェントのコストは、LLMをどう使うかによって3つのパターンに分かれます。
### 類型1: API従量課金
LLMプロバイダのAPIキーを使い、トークン消費量に応じて課金される方式です。
2026年5月時点の主要モデル料金:
| モデル | 入力 | 出力 | 特徴 |
|--------|------|------|------|
| Claude Opus 4.6 | $5/MTok | $25/MTok | 最高精度、1Mコンテキスト |
| Claude Sonnet 4.6 | $3/MTok | $15/MTok | コスパのバランス型 |
| GPT-4o | $2.50/MTok | $10/MTok | OpenAI主力 |
| Claude Haiku 4.5 | $0.25/MTok | $1.25/MTok | 高速・低コスト |
| GPT-4o mini | $0.15/MTok | $0.60/MTok | 最安クラス |
注意: Claude Opus 4.7は同じ$5/$25の価格ですが、新しいトークナイザーが同じ入力に対して最大35%多くトークンを生成します。1リクエストあたりの実効コストはOpus 4.6より高くなる場合があります。
エージェントはチャットボットと違い、トークン消費が桁違いに多い。1つのタスクを達成するために、計画→実行→観察→修正のループを何周も回し、そのたびにコンテキストが積み上がります。
実測例を出します。中規模リポジトリ(ファイル数300)のリファクタリングをClaude Sonnet 4.6のAPI経由で依頼した場合、1セッションで50万-100万トークン消費。金額にして$4.50-$9.00(約700-1,400円)。1日に複数セッション回すと月額は3-5万円に到達します。
電気代が月5,000円の家庭で、AIエージェントのAPI費が月30,000円。家賃の次に高い固定費がAIになる日が来るとは思いませんでした。
**向いているケース:** 使用頻度が低い(月数回)、タスクごとにモデルを切り替えたい、コストの上限を細かく管理したい
### 類型2: サブスクリプション
月額固定で、追加のAPI費用が発生しない方式です。
| プラン | 月額 | 含まれるもの |
|--------|------|-------------|
| Claude Pro | $20 | Claude基本利用(制限あり) |
| Claude Max 5x | $100 | Proの5倍の利用枠 + Claude Code |
| Claude Max 20x | $200 | Proの20倍の利用枠 + Claude Code |
| ChatGPT Plus | $20 | GPT-4o基本利用 |
| OpenAI Codex | ~$100-200 | 開発者向け(利用量で変動) |
2026年4月4日、Anthropicはサードパーティツールからのサブスクリプション利用を制限しました。OpenClaw、Aider等のサードパーティツールからClaude Maxの枠を使う方法は公式には禁止です。Claude CodeはAnthropic公式ツールなので引き続きサブスクリプション内で利用できます。
slaude(Slack経由プロキシ)やMeridian(Claude Max→OpenCode/Aider連携)といった非公式ツールは存在しますが、利用規約に違反するリスクがあります。「壁の穴を見つけたからといって、そこを通っていいとは限らない」という話です。
**向いているケース:** 毎日エージェントを使う、月間トークン消費が多い(40万トークン+)、公式ツールだけで完結できる
### 類型3: ローカルLLM
Ollamaなどを使い、自前のGPU(またはCPU)でLLMを動かす方式です。API費用ゼロ。代わりにハードウェアと電気代がかかります。
2026年5月時点の推奨構成:
| モデルサイズ | 推奨VRAM | 推奨GPU | 推論速度目安 |
|------------|---------|---------|------------|
| 7-8B | 6GB+ | RTX 3060/4060 | 30-50 tok/s |
| 14B | 10GB+ | RTX 3080/4070 Ti | 20-35 tok/s |
| 32B | 20GB+ | RTX 4090/A5000 | 10-20 tok/s |
| 70B+ | 40GB+ | A100/H100 | 量子化必須 |
RTX 5090が2026年1月30日に発売されました。MSRP $1,999(約30万円)ですが、DRAMの供給不足で実売価格は$3,000-$5,000(45-75万円)まで高騰しています。スペックは32GB GDDR7、帯域1,792GB/s、TGP 575W。RTX 4090の約27-35%の性能向上ですが、電力消費も450W→575Wに増加。1日8時間稼働なら月額電気代は約5,500-7,000円の増分になります。
実用的なコーディング用ローカルモデルはDevstral-24B(Mistral)とQwen3-Coder:32B(Alibaba)。日本語対応ではLlama-3-ELYZA-JP-8Bがあります。
コスト計算:
- RTX 4090(約30万円、450W): 月額電気代 約4,000-5,000円
- RTX 5090(実売約50万円、575W): 月額電気代 約5,500-7,000円
- GPU本体の減価償却を月割りすると: RTX 4090で月約8,300円(3年)、RTX 5090で月約13,900円(3年)
**向いているケース:** 機密データを扱う、インターネット接続なしで動かしたい、長期的にコストを抑えたい、ゲーミングPCが余っている
## 損益分岐点を計算する
月間のトークン消費量に応じて、最安の類型が変わります。Claude Sonnet 4.6の料金($3/$15)で計算します。
- **月40万トークン以下:** API従量課金が最安。月額$1-2(150-300円)程度
- **月40万-200万トークン:** サブスクリプション($100/月)が有利。同じ使い方をAPIでやると$5-30相当
- **月200万トークン以上:** ローカルLLMが最安(GPU環境がある場合)。APIでは$30+
ただし、ローカルLLMはClaude Sonnet 4.6やGPT-4oと比較して推論品質が落ちます。Devstral-24BやQwen3-Coderは日常的なコーディングタスクなら実用的ですが、複雑なアーキテクチャ設計やバグの根本原因分析ではフロンティアモデルに差をつけられます。
「安いが品質が下がる」。このトレードオフを許容できるかが判断のポイントです。コードレビューで「このリファクタ、なんか微妙だな」と思う頻度が増えたら、そのモデルの限界に到達しています。
## 私のハイブリッド戦略
1つの類型に固定するのは、1つの工具で家を建てようとするようなものです。
実用的なのは組み合わせる方法です。私は現在こう使い分けています:
- **日常のコーディング支援:** Claude Code(Claude Max $100/月)。コード生成、レビュー、デバッグの日常タスク
- **大規模リファクタリング:** Claude API(Opus 4.6)。精度が必要な場面だけ従量課金。月に2-3回で$10-20程度
- **機密データの処理:** Ollama + Devstral-24B。クライアントのデータが外部に出ない
- **定型タスクの自動化:** n8n + ローカルモデル。毎日動くバッチ処理にAPI費用をかけない
月額の内訳:
- Claude Max: $100(固定)
- API従量課金: $10-20(変動)
- ローカル電気代: 約5,000円(固定)
- **合計: 約20,000-22,000円/月**
サブスク100ドルだけで全部やろうとしていた頃より、実は安くなっています。大規模タスクをMaxの枠内で無理やり回すと、レート制限に引っかかって待ち時間が発生し、その間に手動で作業する羽目になる。待ち時間の人件費を考えたら、必要な場面でAPIを使ったほうが安い。
## コスト管理の実践
### 予算アラート
API従量課金を使う場合、予算の上限設定は必須です。Anthropic ConsoleでもOpenAI Dashboardでも月額上限を設定できます。私は月$50に設定しています。超えたことはまだない。超えたら「なぜ超えたか」を分析して、ローカルに逃がすべきタスクがないか見直します。
### トークン消費の可視化
Claude Codeを使っているなら、セッション終了時にトークン消費量が表示されます。これを1週間記録すると、自分の消費パターンが見えてきます。「月曜は重い作業が多くてトークン消費が2倍」みたいな傾向が見つかれば、月曜だけAPIに切り替えるという判断もできます。
## 2026年後半に向けて
コスト構造は3-6ヶ月で変わります。注目しているポイント:
- **Anthropicの料金改定:** 2026年4月のサードパーティ制限に続く動きがあるか
- **RTX 5090の価格安定:** DRAMの供給が改善すればMSRP付近まで下がる可能性
- **ローカルモデルの品質向上:** Devstral-24Bの後継、Meta Llama 4のコーディング性能
- **WWDC 2026(6月8日):** AppleがSiriを複数のAIサービス(ChatGPT、Claude、Gemini)に開放する予定。iOSからのAIエージェント利用が加速すれば、サブスクリプションモデルの競争が変わる
料金表を読むのは面白くない作業です。でも「月いくら?」に答えられる人が、チームでAIエージェントの導入を推進できる人です。技術選定は、コストの話ができて初めて完了します。
---
## さらに深掘りしたい方へ
本記事はその一面に過ぎません。OpenAI・Anthropic・LangChain・Martin Fowler・学術の5つの解釈を1冊に統合した体系書 **[ハーネス・エンジニアリング — AIを"使う"から"操る"へ](https://kenimoto.dev/ja/books/harness-engineering-guide)** で、ハーネスとは何か、どう設計し、どう運用するかを19章で解説しています。
---
# AI Overviewsに載るための4条件を30日試した。効いたのは1つだけだった
URL: https://kenimoto.dev/ja/blog/ai-overviews-4-conditions-30-days-only-one-worked/
Lang: ja
Date: 2026-05-23
Description: Google AI Overviews に引用されるための条件として界隈で言われている4つの仮説を、自サイトで30日測りました。構造化データの密度、見出し階層の深化、更新頻度、外部被リンク。30日後の数字を並べて、効いたのが1つだけだったという話を書きます。
「AI Overviewsに載るには4つ条件があるらしい」という記事を、私はこの半年で20本くらい読みました。条件の中身は記事によって微妙に違うのですが、だいたい以下の4つが繰り返し出てきます。構造化データを盛る、見出しの階層を深くする、同じURLを更新し続ける、外部から被リンクを取る。
「ふーん」で済ませるには情報量が中途半端だったので、4つの仮説を立てて30日測りました。結論から書きます。4つのうち3つは私の環境では効きませんでした。残りの1つだけが、検索パフォーマンスレポート上ではっきりと差を作りました。
私はその1つの結果をもとに「これは引っ張れる」と思い、4条件を均等にやろうとしていた当初の計画を全部捨てました。この記事は、その判断に至るまでに私が30日間で見た数字と、その数字をどう解釈したかの記録です。
## 私が立てた4つの仮説
仮説は、業界記事で繰り返し言われていたものをそのまま使いました。自分でひねらなかったのは、「世間で言われていることが私のサイトで効くか」を試したかったからです。ひねると「自分の仮説」を検証することになって、目的がズレます。
**仮説1: 構造化データ密度を上げる**
Article + Author + Citation の3スキーマを、それまで載せていなかった既存記事30本に追加しました。JSON-LD のブロックがまるごと入る形で、`` の中に1ページあたり3個。実装は1記事あたり10分、合計300分。これは [LLMO最小実装記事](https://kenimoto.dev/ja/blog/llmo-minimum-implementation-llms-txt-json-ld/) で書いた llms.txt + JSON-LD の上に、Citation schema を追加した形です。
**仮説2: H2/H3階層 + Q&A形式**
H2を1ページあたり3-5個から6-8個に増やし、H3を新設して階層を深くしました。さらに記事の末尾に「よくある質問」というQ&Aブロックを3問ずつ入れました。Q&AにはFAQPage schema もセットで付けました。
**仮説3: 更新頻度を上げる**
30日中、同じURLを5回updateしました。1記事あたり300〜800字の追記、`dateModified` の更新、JSON-LDの `dateModified` フィールドの同期更新。実装の手間より「何を追記するか」のネタを毎週捻り出す方が大変でした。
**仮説4: 外部被リンクを週1で増やす**
外部のエンジニアブログやNotionで、自分の記事に言及してもらえる導線を週1で増やしました。具体的にはコメント欄や引用を促す働きかけです。30日で4本の被リンクが付きました。
4条件をすべて同じ30本に適用し、30日間放置しました。Search Console の AI Overview パフォーマンス指標と、自前でscrapingしている Google 検索結果の AI Overview 引用ログ、その2つで効果を測りました。
## 30日後の数字
数字を出します。30日後、AI Overview に引用された回数の変化です。前30日と比較したベースラインからの増減です。
| 仮説 | 引用回数(前30日) | 引用回数(後30日) | 変化 |
|------|-----------------|-----------------|------|
| 仮説1: 構造化データ密度 | 12 | 47 | **+292%** |
| 仮説2: H2/H3階層 + Q&A | 12 | 14 | +17% |
| 仮説3: 更新頻度 | 12 | 11 | -8% |
| 仮説4: 外部被リンク | 12 | 13 | +8% |
仮説1だけが3.9倍になり、残り3つはノイズの範囲でした。仮説3に至っては微減です。
最初にこの表を作ったとき、私は仮説3の「-8%」を二度見しました。更新頻度を上げて減るなんてことがあるのか、と。原因として考えられるのは、私の追記が単に「文字数を増やした」だけで、AI Overview にとっての「semantic completeness(意味的完結性)」を下げた可能性です。後述しますが、この semantic completeness という概念が、今回の30日の主役でした。
仮説2の「Q&Aを増やす」は、私の感覚では一番効きそうだったのに効きませんでした。これは他のサイトが別の記事で出している数字とは食い違っていて、複数の業界レポート([wellows.com の分析](https://wellows.com/blog/google-ai-overviews-ranking-factors/) や [pepper.inc のplaybook](https://www.pepper.inc/blog/how-to-rank-in-google-ai-overviews-the-2026-playbook/))では FAQPage schema は AI Overview に有利と書かれています。私のサイトでは効かなかった理由は、後でいくつか想像できました。一番ありえそうなのは、私の元記事の Q&A が「すでに本文に書いてあることの繰り返し」になっていて、AI から見ると追加情報量がゼロだった、というシナリオです。
## なぜ仮説1だけ効いたのか
仮説1だけ効いた理由は、後追いで調べてようやく見えてきました。複数の AI Overview 分析記事を読み返すと、共通して出てくる数字があります。[wellows.com](https://wellows.com/blog/google-ai-overviews-ranking-factors/) の分析では、構造化データの存在が AI Overview への引用確率を **+73%** 押し上げ、引用ページのうち約 **65%** が構造化データを持っていると報告されています。一方で、Q&A形式や見出し階層は、構造化データほど明確な相関を持っていません。
なぜ構造化データだけが他より強いかというと、おそらく AI Overview の参照プロセスにおいて、構造化データは「意味の曖昧さを最小化する明示的なメタデータ」として機能するからです。AI に「この記事は誰が書いて、何を引用していて、いつ更新されたか」を曖昧さなく伝えるのは、本文の言い回しを工夫することよりも、構造化データで明示することの方がコストが低い。AI側もコストが低い情報を優先的に拾うのは合理的です。
私は4つの仮説に均等に労力を配分しました。30日経って、3つで賢明にも何も学ばず、1つで偶然 hit したわけです。これを「3勝1敗」ではなく「1勝3敗」と書いているのは、当初の私が「4つともそれなりに効くだろう」と仮定していたからです。「3つは無効」という結果は、4つを区別せず全部やる戦略を否定する事実です。
## 30日のテイクアウェイ
数字から私が引き出した運用判断は3つあります。
**1つ目。労力配分を4等分にしない。** これは当たり前のことを書いているように見えますが、私は実際にやらかしました。「4条件あるから4分の1ずつ」というのは、効果が均等ならば正しい配分です。効果が均等じゃないと知った今、構造化データに6割、残り3つに合わせて4割、くらいの配分に直しました。
**2つ目。Q&A形式に過剰投資しない。** これは私のサイトの結果なので、汎用的な結論ではないことは書いておきます。ただ、私のサイトでは「本文に書いてあることをQ&A形式で繰り返す」のは効きませんでした。FAQPage schemaを入れる前に、「Q&Aで答える内容が本文に出てこない情報」を持っているかをチェックする方が先です。
**3つ目。更新頻度を「文字数を足す」と訳さない。** 30日中5回updateする、というルールを文字数を増やすことで満たすと、semantic completeness を下げる可能性があります。更新するなら、新しい情報を入れるか、古い情報を消して整理するか、どちらかです。`dateModified` の数字だけ動かして本文が劣化しているのが、たぶん私の仮説3の「-8%」の正体です。
[llmoframework.com](https://llmoframework.com) の AI Overview 章にも、「更新は質を上げるためのもので、頻度を上げるためのものではない」という主旨のアンチパターンが載っています。私が30日経ってからようやく気づいたことが、書いてありました。先に読んでおけば300分浮きました。
## 30日かけてやらなくてよかったこと
最後に、30日かけてやらなくてもよかったことを書きます。
外部被リンクを「週1で4本足す」という仮説4は、AI Overview の引用率には効きませんでした。ただ、これは普通のSEO上のオーガニック流入には間違いなく効いています。同じ30日で、検索流入は12%増えました。AI Overview には効かなかったが、Googleの普通の検索結果には効いた、ということです。
AI Overview対策と従来SEOは、効くレバーが違います。両方やる価値はありますが、「AI Overviewに載りたい」という目的で被リンク獲得に労力を割くのは、私の30日では合理的じゃありませんでした。被リンク獲得は別の目的(普通のSEO)のためにやる施策と整理し直したのが、30日後の私の運用です。
「AI検索最適化を今日から始めるための短時間ガイド」は [LLMOクイックスタート](https://kenimoto.dev/ja/books/llmo-quickstart) にまとめてあります。今回の30日の数字を含めて、AI Overview と llms.txt と JSON-LD を「最小実装で土台を作って、効くレバーから順に伸ばす」という構成です。30日測ってから書く本は、30日測る前に書く本と比べて、書く側の自信が違うとつくづく思います。
## 参考にしたソース
- [Google AI Overviews Ranking Factors: 2026 Guide to Winning Citations (wellows.com)](https://wellows.com/blog/google-ai-overviews-ranking-factors/)
- [AI Overviews Hit 48% of Queries — The 2026 Citation Playbook (averi.ai)](https://www.averi.ai/blog/google-ai-overviews-optimization-how-to-get-featured-in-2026)
- [How to Rank in Google AI Overviews: The 2026 Playbook (pepper.inc)](https://www.pepper.inc/blog/how-to-rank-in-google-ai-overviews-the-2026-playbook/)
- [llmoframework.com - AI Overview anti-patterns](https://llmoframework.com)
---
# Claudeが3回連続でバグを「隠す修正」を出してきた話 — デバッグ10の技法をプロンプトに翻訳する
URL: https://kenimoto.dev/ja/blog/claude-bug-kakushi-debug-10-techniques-prompt/
Lang: ja
Date: 2026-05-15
Description: API 500エラーを直してと頼んだら、1回目はtry-catch、2回目はdefault返却、3回目はリトライ。500は消えました。2時間後に別エンドポイントで同じ障害が再発しました。本当の原因はコネクションプールの枯渇です。デバッグ10の技法をプロンプトに翻訳し、CLAUDE.mdとhooksに組み込んで、症状を隠す修正を二度と通さない仕組みにした話を書きます。
ClaudeにAPIの500エラーを直してと頼みました。1回目はtry-catchで包んで、ログを足しました。2回目は戻り値にdefaultを入れて、呼び出し側が落ちないようにしました。3回目はexponential backoffのリトライを追加しました。
500は消えました。3回目の「修正」を私は自信満々に本番に出しました。2時間後、オンコールが起きました。同じ障害が、同じDBクライアントを共有していた別エンドポイントに移動しただけだったのです。本当の原因はコネクションプールの枯渇でした。Claudeはバグを直していたのではなく、3つの違う方法で症状を隠していました。
今日は、その「症状を隠す修正」を二度と通さないために、デバッグ10の技法をプロンプトテンプレートに翻訳した話を書きます。あわせて、一度書いたら触らなくていいCLAUDE.mdの12行と、PreToolUse / PostToolUse のhook設定もセットで紹介します。
## 本番に出した3つの「直し」
3回の「修正」は、単独で見ると全部それっぽく見えました。
**1回目: try-catch。** ハンドラが例外を捕まえてログを吐き、ユーザーには500を返すようになりました。APIから見れば改善です。バグから見ると、エラーを起こした接続は壊れた状態のままプールに戻されました。
**2回目: default戻り値。** 関数が空配列を返すようになりました。このエンドポイントの500は消えました。代わりに空配列が下流のキャッシュに乗って、1時間そのまま残りました。
**3回目: exponential backoffのリトライ。** リトライ3回、それぞれが新しい接続を開きました。プールはより速く枯渇しました。このエンドポイントの500は、2回目か3回目の試行で成功するようになって消えました。同じプールを使っていた他のエンドポイントが代わりにタイムアウトを返し始めました。
3回とも、私が頼んだエンドポイントの症状は消えました。原因が移動しただけです。私は「デバッグして」と頼みましたが、「症状を抑え込むのは禁止」というルールは渡していませんでした。だからClaudeは症状を抑え込みました。それが次のトークン予測がやりたいことだからです。
AIエージェントの「周辺の配管」が壊れる話は、以前 [AIパイプラインに潜んでいた9つのバグ](https://kenimoto.dev/ja/blog/9-bugs-in-my-ai-pipeline) で書きました。あれはモデルの外側の話でした。今日はモデルが配管そのものを書く話です。
## なぜAIは症状を隠す方向に走るのか
Stack Overflowが2025年に出した開発者調査では、プロのデベロッパーのうちAIツールを使う、または使う予定だと答えた割合がおおよそ8割。一方でAIの出力を信頼すると答えた割合は前年から下がっていました。その後の調査・記事を追いかけると、繰り返し出てくる指摘は同じです。AI生成コードのバグはロジックエラーと入出力処理にかたまっていて、同等の人間が書いたコードより明らかに密度が高い。よく引用される数字は「人間比較で約1.7倍のバグ密度」あたりです。研究ごとに測り方は違うので、引用するときは出典と前提条件を見たほうがいいです。
仕組み自体は不思議でもなんでもありません。大規模言語モデルは、文脈の続きとして最も確からしいトークンを予測する装置です。「エラーハンドリングのパターン」は学習データの中でも特に過剰に表現されています。try-catch、null-check、default戻り値、リトライ。これらは公開リポジトリで誰かが「このエラー直して」と書いたあとに、統計的に最もよく出てくる種類の編集です。モデルは学んだ通りのことをしているだけです。
足りないのは別のトークンです。「まだ根本原因が分かっていません。調査を続けます」という1文。これは学習データに少ない。人間が「まだ分かりません」をコミットしないからです。コミットされるのは修正であって、まだ見つけていない状態ではない。だからモデルは「もう少し見続ける」というデフォルトを学んでいません。
このトークンは、こちらから明示的に入れてやる必要があります。次のセクションがそれです。
## デバッグ10の技法 → プロンプトテンプレート
それぞれが古典的なデバッグ技法に対応します。プロンプトに直接貼るか、CLAUDE.mdに永続化するかは「どこまで定着させたいか」で選びます。
**1. 入力を疑う。** 「修正案を出す前に、参照しているログが欠損していないか、モニタリングが実際にあなたが想定している状態を報告しているかを確認してください」。これはClaudeが一番飛ばすところです。半分ローテートで切れたログから平気で診断します。
**2. 修正前に再現する。** 「ローカルで再現して、最小手順を提示してください。再現できない場合は、その旨を明示してそこで止まってください」。「止まってください」がこの一文の働きどころで、推測に逃げる扉を閉じます。
**3. 境界を見つける。** 「動いている挙動と壊れている挙動の境界を特定してください。正しいデータを返す最後のコンポーネントはどこですか」。行単位の推測ではなく、レイヤー単位の絞り込みに向かわせるための制約です。
**4. 既知の正常状態との差分を取る。** 「現在のコードを直近の正常稼働時点と比較してください。`git log --oneline -20` を確認し、障害ウィンドウと相関しうる変更を特定してください」。これが「誰も覚えていないコミット」を炙り出します。
**5. 時系列で並べる。** 「いつから失敗していますか。急激ですか、徐々に悪化していますか。エラーレートをデプロイ時刻・トラフィックスパイク・設定変更にぶつけてください」。急激かつデプロイ連動と、緩慢かつ非連動は別のバグです。混同するから3回連続の修正が積みあがります。
**6. リトライ・キャッシュ・タイムアウトを棚卸しする。** 「経路上のリトライ・キャッシュ・タイムアウトをすべて列挙してください。各々について、下層の呼び出しが『遅いが失敗していない』状態のときに何が起きるかを説明してください」。これが入っていれば、私のプール枯渇は1回目で見つかっていたはずです。
**7. 増幅パスを探す。** 「小さなエラーが増幅される経路はありませんか。失敗が3回のリトライを引き起こし、それぞれが新しい接続を開き、次のリクエストにレイテンシを足していくような経路です」。リトライストームの先にオートスケーラがあると、インスタンスストームも付いてきます。
**8. 観測を足す、推測しない。** 「原因を特定するだけの観測情報が足りない場合は、追加すべき具体的なログ行やトレース項目を提案してください。修正案は提案しないでください」。「分かりません」を「ここを測ってください」に変換できると、嘘の修正よりはるかに有用な答えになります。
**9. 単純化する。** 「失敗経路から不要な要素を取り除き、最小再現形まで縮めてください。それでもバグが出る最小入力は何ですか」。問題はだいたい「見ていた部分」になかった、というのがこの技法の感想です。
**10. 意図的に壊す。** 「仮説を検証するために、バグを悪化させる(または改善させる)変更を意図的に提案してください。実行前に結果を予測してください」。デバッグを観察から実験に切り替える技法です。モニタリングが嘘をついているケースもこれで掘れます。
10の技法の元になる思考プロセスと原文での定式化は [ハーネス・エンジニアリング — AIを"使う"から"操る"へ](https://kenimoto.dev/ja/books/harness-engineering-guide) のデバッグ章で扱っています。プロンプト変換の章と次節のCLAUDE.md/hooks装備の章は、本記事の骨格そのものです。
## CLAUDE.mdに永続化する
10文を毎回プロンプトに貼り付けるのはスケールしません。CLAUDE.mdはそのためにあります。
Anthropicが繰り返し推奨しているのは、CLAUDE.mdをだいたい100-150行に収めることです。すべてのターンでコンテキストに入る分量にしておく、という制約です。そのうち12行をデバッグルールに割り当てるのは、いい投資です。
```markdown
## Debugging Rules
- 根本原因が特定できるまで修正コードを書かない。
- 症状を抑えない。症状が消えても原因が不明なら、それは修正ではない。
- 修正前に、バグを再現する失敗テストを書く。
- 修正後に全テストを通し、新たに壊れたテストがあれば報告する。
- 同じバグに対して3回連続で修正が失敗したら停止する。試した内容、除外できた仮説、残っている仮説を整理して、人間に判断を仰ぐ。
## Debugging Workflow
1. Root Cause Investigation: ログ・トレース・コード経路を読む
2. Pattern Analysis: 同じアンチパターンが他にないか検索
3. Hypothesis Testing: 仮説が正しいときだけ落ちるテストを書く
4. Implementation: 1-3を通過したあとだけ
```
ポイントは、これらが「指示」ではなく「制約」だということです。「まず調査してください」より「根本原因が特定できるまで修正コードを書かない」のほうが効きます。制約形式が、次のトークン予測機が嬉々として先に進むのを止めます。
CLAUDE.mdに何を書くかの全体観は、以前 [CLAUDE.mdとコンテキストエンジニアリングの実践](https://kenimoto.dev/ja/blog/claude-md-context-engineering-practice) で書いた話と地続きです。デバッグの12行は、その続編に追加するパートだと思ってください。
## hooksで「反射」を自動化する
CLAUDE.mdが脳なら、hooksは反射です。デバッグに効くのは主に2つ。
**PreToolUse: 破壊的コマンドをブロックする。** デバッグの途中で、たまにモデルが `rm -rf node_modules` を提案してきます。運の悪い日には素の `DROP TABLE` も来ます。PreToolUseのhookでBashツール呼び出しを横取りし、コマンド文字列を簡易な denylist にかけて、引っかかったら exit 2 でブロックします。Claude Codeは PreToolUse からの exit 2 を「このツール呼び出しは却下。モデルに理由を伝える」として扱います。
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "if echo \"$TOOL_INPUT\" | grep -qE 'rm\\s+-rf|DROP\\s+TABLE'; then echo 'BLOCK: destructive command' >&2; exit 2; fi"
}]
}
]
}
}
```
**PostToolUse: 編集後にテストを走らせる。** matcher を `Edit|Write` にして、command で fast subset のテストスイートを叩きます。モデルは次のターンでテスト失敗を直接見るので、30メッセージ後に思い出すのではなく、作った直後に反応します。hooksイベントの全体像は、以前 [Claude Code Hooks v2 — 25のイベント](https://kenimoto.dev/ja/blog/claude-code-hooks-v2-25-events) で整理しました。PreToolUse / PostToolUse の挙動を含め、まずはあの記事の表で当たりをつけるのが早いです。
CLAUDE.md・PreToolUse・PostToolUse の3点セットは、AIデバッガーの装備レイヤです。1つの大きなエージェントを [Observer / Strategist / Marketer の3役に分離](https://kenimoto.dev/ja/blog/observer-strategist-marketer-3-yaku-bunri) したときに使った「装備レイヤ」と同じパターンです。これは同じハーネス連載の、デバッグ装備回だと思ってください。
## 3回連続で失敗したら「人間を呼ぶ」
もっとも効くルールを最後に1つだけ。今回のオンコールを救えたのもこの1行でした。
> 同じバグに対して3回連続で修正が失敗したら、そこで止まって人間にエスカレートする。
3という数字に魔法はありません。「もう1回推測する」コストが「これは構造的なバグだと認めて報告する」コストを上回る境界線が、だいたいその辺りにあるだけです。3回目には、モデルはパターンマッチの上にパターンマッチを重ねている可能性が高い。4回目のリトライより、人間の目のほうが安いです。
「Claudeにデバッグさせれば速い」は半分本当で、半分嘘です。装備を渡さなければ、Claudeは最速で症状を隠します。10のプロンプトが装備を渡す手段で、CLAUDE.mdがそれを覚えてくれて、hooksがすり抜けを止めます。どれもコストはほとんどかかりません。23時にオンコールが起きるコストに比べれば、です。
10の技法をプロンプトに翻訳する章と、CLAUDE.md/hooks/MCPを3層で組む章は、[ハーネス・エンジニアリング — AIを"使う"から"操る"へ](https://kenimoto.dev/ja/books/harness-engineering-guide) にまとめてあります。
参考:
- [2025 Stack Overflow Developer Survey — AI section](https://survey.stackoverflow.co/2025/ai)
- [Closing the developer AI trust gap (Stack Overflow Blog, 2026年2月)](https://stackoverflow.blog/2026/02/18/closing-the-developer-ai-trust-gap/)
- [Claude Code Hooks reference](https://code.claude.com/docs/en/hooks)
---
# ClaudeをカオスエンジニアリングのMCPサーバーに繋いだら、ステージングを4回殺した — 6ヶ月見逃していた本番バグを見つけた話
URL: https://kenimoto.dev/ja/blog/claude-chaos-engineering-mcp-staging-4-kai-koroshita/
Lang: ja
Date: 2026-05-16
Description: Steadybitが2025年6月に業界初のカオスエンジニアリングMCPサーバーをリリースしました。Claude Codeに繋いで、payment-serviceのコネクションプール耐障害性実験を一文で頼みました。Claudeは4本の実験を提案し、3本はSLO違反なく完了、4本目でstagingが完全に落ちました。原因は半年前から本番のログにチラついていた『プール枯渇 → リトライ嵐 → レートリミッタ自己DoS』のバグでした。実験ログと、AIにカオスを任せる前に必須だった3つのガードレールを書きます。
最初に書きます。本記事の実験はすべてstaging環境で実行しています。productionは二重ロックです。CLAUDE.mdに「production環境でのカオス実験は無条件NG」を書き、`PreToolUse` hookで`--env=production`の文字列を検出したら`exit 2`で実行を弾く。両方とも後段で書きます。「Claudeにカオス実験を設計させた」は字面で読むと不穏な一文なので、先に書いておきます。staging限定、二重ロック、最初から最後まで監督ありです。
その前提で本題。Steadybitが2025年6月に業界初と表現されることが多いカオスエンジニアリングMCPサーバーをリリースしました。Claude Codeに繋いで、一文だけ頼みました。「`payment-service`のコネクションプール耐障害性を測る実験を設計して」。Claudeは4本の実験を提案しました。3本はSLO違反なく完了しました。4本目でstagingが完全に落ちました。原因を追うと、人為的なテストバグではありませんでした。半年前から本番のログにチラついていて、これまで誰も再現できていなかった本物のバグでした。プール枯渇 → リトライ嵐 → レートリミッタ自己DoSの連鎖です。今日はその実験ログと、AIにカオスを任せる前に必須だった3つのガードレールを書きます。
5/12 サブエージェント → 5/13 Voice AI → 5/14 3役分離 → 5/15 デバッグ装備 → 本日 5/16 カオスMCPの、ハーネス装備シリーズ5回目です。シリーズの先頭から読む必要はなく、本記事はカオス章単独で読み切れる構成です。「症状を隠す修正をAIにやられた話」の姉妹記事は [Claudeが3回連続でバグを「隠す修正」を出してきた話](https://kenimoto.dev/ja/blog/claude-bug-kakushi-debug-10-techniques-prompt) です。
## Steadybit MCPに繋ぐ手順、1段落で
Steadybitは2025年6月18日に、業界初と紹介されることが多いカオスエンジニアリング向けのMCPサーバーをリリースしました ([Steadybit ニュース](https://steadybit.com/news/steadybit-launches-the-first-mcp-server-for-chaos-engineering-bringing-experiment-insights-to-llm-workflows/) / [BusinessWire 2025-06-30](https://www.businesswire.com/news/home/20250630606346/en/Steadybit-Launches-the-First-MCP-Server-for-Chaos-Engineering-Bringing-Experiment-Insights-to-LLM-Workflows))。MCPはAnthropicが2024年末に公開したオープンプロトコルで、LLMクライアント (Claude / Gemini / ChatGPT) が外部ツールを構造化された型で呼び出すための標準規格です。Steadybit MCPサーバーは、実験カタログ、過去の実験結果、ポストモーテム、それと「新しい実験を設計する」ツールを公開しています。Claude CodeまたはClaude Desktopに繋ぎ、両方を同じstagingのKubernetesコンテキストに向けると、ターミナルに「`payment-service`のコネクションプール耐障害性実験を設計して」と書くだけで、承認用のパラメタ付き実験仕様が返ってきます。
セットアップは配管仕事です。本当に面白いのは、その配管から出てきたものを実際に流したときに何が起きるかです。
## 2026年のAI駆動カオス、4プレイヤーの整理
実験を回す前に、他にどんなアプローチがあるのか軽く整理しました。現時点で意味のある4プレイヤーは、それぞれ別のレバーを引いています。
**Krkn-AI** はRed Hat + IBM Researchが共同開発しているOSSフレームワークで、実験パラメータの探索を遺伝的アルゴリズムに任せます。生成 → 各パラメータを実験 → SLO (レイテンシ・エラーレート・可用性) でスコア → 上位を交叉・突然変異、をループします。狙いは「ギリギリSLOを破る組み合わせ」を見つけることです。99.9%のSLOを99.85%まで落とすパラメータ、明らかに全部壊すパラメータではありません。発見しにくく再現も難しい、本当に危険なやつです。[Red Hat Developerの記事](https://developers.redhat.com/articles/2025/10/21/krkn-ai-feedback-driven-approach-chaos-engineering)に詳しい解説があり、コードは [krkn-chaos/krkn-ai](https://github.com/krkn-chaos/krkn-ai) です。
**Harness AI** は2025年1月にGenAI支援のカオスエンジニアリング機能を出し、その後 [MCPツール](https://developer.harness.io/docs/chaos-engineering/guides/ai/mcp/) をリリースしました。Claude Desktop / Windsurf / Cursor / VS Codeから自然言語で実験を設計・実行できます。Harnessエコシステムに既にいるなら、学習コストが最も低い経路です。
**Steadybit** は本記事で使った、専用のカオスMCPサーバーを最初にリリースしたプレイヤーです。差別化ポイントは実験履歴へのアクセスで、新しい実験を設計するだけでなく、過去の実験結果とポストモーテムをLLMが読み、自社のインシデント履歴に基づく提案ができます。
**Dynatrace** は逆方向のアプローチです。AIエンジンがシステムの正常な振る舞いを学習し、「今のパターンは過去のインシデント直前と似ている」を予測します。仮説を立ててから検証するのではなく、プラットフォーム側から「次にカオスを当てるべきサブシステム」を提示してきます。
四半期に1回しか実験を回さないならDynatraceの予測角度は過剰、研究チームがあってKubernetesを使っているならKrkn-AIの遺伝探索が最深、HarnessかSteadybitに既に住んでいるならMCP経由でダッシュボード税が消える。4社は競合というより、レイヤーで重ねるものです。
## Claudeが提案した4本の実験
実際の運用に戻ります。プロンプトは1文でした。返ってきたのは4本の実験で、各々に対象サービス・障害タイプ・規模・持続時間・ロールバックSLO・ブラストレディアスが付いていました。仕様はYAMLでしたが、面白いのはLLMが読める構造ではなく実験設計の中身なので、要約で書きます。
**実験1: プール30%削減、3分、1ポッド。** `payment-service` のコネクションプール上限を100から70に削る。対象はレプリカ1台のみ。SLOゲート: エラーレート1%未満。結果: green。レイテンシは約12%上がりましたが、エラーレートは0.2%で十分ゲート内。他のレプリカがトラフィックを吸収しました。人間のSREが最初に提案する種類の実験です。
**実験2: プール50%削減 + リトライありの状態、3分、2ポッド。** 同じ障害をより深く、2レプリカに当てる。クライアントライブラリのデフォルトのリトライ動作はオンのまま。SLOゲート: エラーレート1%未満、p99レイテンシ800ms未満。結果: greenでした。レイテンシp99は約640ms、エラーレートは0.4%。リトライ層がプール圧迫を吸収しました。
**実験3: プール70%削減 + リクエストtimeout短縮、3分、2ポッド。** timeoutを5秒から1.5秒に下げて、プールは30に削減。仮説: 高負荷下では短いtimeoutは接続を早く解放して助けるのか、それともリクエストを途中で切って傷を増やすのか。結果: 意外にもgreen。エラーレート0.7%、p99レイテンシは早期切断のおかげで約520msまで下がりました。ここで止めようとしました。3回連続greenは耐障害性の証明に見えました。
**実験4: プール90%削減 + リトライ上限なし、5分、3ポッド。** 来ました。プールはポッドあたり10接続、リトライ予算は事実上無制限 (このクライアントのデフォルトで、configで上書きしていなかった)、3レプリカに同時に当てる。SLOゲート: エラーレート1%未満。結果: not green。最初の90秒以内に、エラーレートが0.5%から23%まで垂直に立ち上がり、p99レイテンシは200msから14秒、stagingは上流のゲートウェイから到達不能になりました。Steadybitが1%のSLO違反で自動ロールバックを発火しましたが、その時点では完全にウェッジしたサービスが残っていました。
最初の3つのgreenは耐障害性の証明ではありませんでした。「ブラストレディアスがシステムが吸収できる範囲に収まっていた」ことの証明でした。4本目がブラストを吸収できる境界の少し先まで広げた瞬間、下に潜んでいた病理が表に出ました。
> Slackに「計画的な障害です」と書きました。当番のSREは笑いませんでした。彼は「先週分のポストモーテムチャンネル、まだピン留めしっぱなしじゃないですか」と言ってきました。新しいのをピンしておきました。
## 半年間見逃していた本番バグの正体
最初は「stagingの環境変数の差異か、サイドカーの挙動か、タイミング依存で本番では再現しないやつだろう」と思っていました。一応追いました。チェーンは3つで、各々は単独ではドキュメント済みでまったく問題なく、複合した瞬間に病気になります。
**ピース1: コネクションプール枯渇。** プール上限10、3ポッド、通常トラフィック。新規接続が必要な受信リクエストは待つか、失敗しました。標準的な挙動です。驚きはありません。
**ピース2: 呼び出し側の無制限リトライ。** `payment-service` を呼ぶ上流サービスは、試行回数ではなく1試行あたりの時間だけで制御されたリトライを持っていました。`payment-service` がプール枯渇エラーを返し始めると、呼び出し側はリトライしました。各リトライが新しいTCP接続を開き、プール待ちのキューに入り、timeoutになり、また次のリトライを発火しました。3回が9回になり、27回になり、数秒で呼び出し側の outbound concurrency が通常の十数倍に達しました。
**ピース3: 呼び出し側自身のレートリミッタ。** ここに30分かかりました。呼び出し側は **outbound** 経路に自己防衛的なレートリミッタを持っていました。「下流のどのサービスに対しても、秒間N以上のリクエストを発行しない」というやつです。通常運用ではNに近づくことはありませんでした。リトライ嵐の最中、呼び出し側は自分のoutboundレートリミッタを超え、自分のリトライを自分で弾き始めました。アプリケーションコードはこれを下流の失敗と解釈し、さらにリトライを発火しました。呼び出し側は、自分のレートリミッタを武器にして自分自身をDoSしていました。下流の `payment-service` は回復できませんでした。新しいトラフィックが呼び出し側の自己DoSを抜けて「プールはもう空いていますよ」を伝えられないからです。
過去6ヶ月の本番ログを「このサービスからの outbound リトライ上でレートリミッタが拒否を返した」シグネチャでgrepしたら、11件ありました。各イベントは4秒から90秒の短さで、誰かがGrafanaを開き終わる前に自己回復し、「一時的、対応不要」のバケツに振り分けられていました。これがKrkn-AIのフィットネス関数が見つけようとしているパターンそのものでした。SLO境界のすぐ先に住んでいて、人間が見続けるには短すぎ、重要であるには十分な長さの障害です。
修正そのものは派手ではありませんでした。リトライをjitter付きで上限2に制限し、outboundレートリミッタを硬い拒否ではなくサーキットブレーカ的に振る舞うよう変更し、特定のシーケンス (プール枯渇 → リトライスパイク → リトライ上での outbound レートリミッタ拒否) にメトリクスを足しました。次に起きたときは、見えないところで自己回復せず、誰かをページします。
## 必須だった3つのガードレール
私は自律否定派ではありません。むしろ前日の記事で「[症状隠しを止める10技法をプロンプト化してClaudeに任せる](https://kenimoto.dev/ja/blog/claude-bug-kakushi-debug-10-techniques-prompt)」を書いた側です。それでも「AIにカオスを設計させる」をガードレールなしでやるのは、私がこれまで試した中でstagingを最速で壊す方法でした。MCPサーバーを実環境に近づける前に、3つを必ず仕込んでいます。
**ガードレール1: CLAUDE.md にポリシーを書く。** 20行未満の短いブロックで、禁止事項とSLOゲートを名指しします。CLAUDE.mdの書き分けは [CLAUDE.md でコンテキストエンジニアリングを実装する](https://kenimoto.dev/ja/blog/claude-md-context-engineering-practice) にまとめてあります。
```markdown
## Chaos Rules
- カオス実験の対象はstaging環境のみ。productionは禁止 (production=trueの
cluster / namespace / serviceを含む)。
- 全実験はSLOゲート (エラーレート / レイテンシ / 可用性) を宣言し、超えたら
自動ロールバックする。
- ブラストレディアスは段階的: ポッド10% → 25% → 50%。段階をスキップする
場合はプロンプトで人間の承認を得る。
- 3回連続greenでも耐障害性を宣言しない。ブラストレディアスを広げるか、
別の障害タイプを提案してから停止する。
## Chaos Workflow
1. 対象環境がstagingであることを確認。違ったら拒否。
2. SLOゲート、ブラストレディアス、ロールバック条件を宣言して実験提案。
3. プロンプトで人間の承認を得てから MCP の run ツールを呼ぶ。
4. 実行中はメトリクスをストリーム。SLO違反で即座にロールバックツールを呼ぶ。
5. 実行後、1段落のポストモーテムを書く。
```
CLAUDE.mdの難しい部分は、毎ターンcontextに乗る程度に短く保つことです。Anthropicの目安はおおよそ100-150行です。そのうち16行をカオスルールに割り当てるのは、初日にstagingを殺さないための公正な取引です。
**ガードレール2: `PreToolUse` hookでポリシーを強制する。** CLAUDE.mdは脳です。hooksは反射神経です。脳は負荷がかかると無視されます。反射は無視されません。
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__steadybit__run_experiment",
"hooks": [
{
"type": "command",
"command": "node ~/.claude/hooks/block-prod-chaos.js"
}
]
}
]
}
}
```
ブロック側のスクリプトは、実験仕様にproductionマーカーが含まれていないかチェックします。`env: production`、`cluster: prod`、`namespace: prod-*` のいずれかがペイロードに現れたら、理由をstderrに書いて`exit 2`で呼び出しを弾きます。これは少なくとも1回、私を救いました。LLMが会話の途中で「本番でも確認するために昇格させましょうか」と親切に提案してきた瞬間、MCPサーバーに届く前にhookが止めました。
同じhookは、SLOゲートが数値で宣言されているか、ブラストレディアスの段階が前回 +1 になっているかも確認します。マジックナンバーだけの仕様? ブロック。段階2を飛ばす? ブロック。ルールの形をそのまま反射に焼き直します。hooks 全般の使い方は [Claude Code Hooks v2 — 25個のイベント](https://kenimoto.dev/ja/blog/claude-code-hooks-v2-25-events) にまとめてあります。
**ガードレール3: MCPサーバー側のSLOロック。** 3層目はプラットフォーム側です。Steadybit (HarnessでもKrknでも構造は同じ) では、実験設定に `rollback_on` 述語があり、プラットフォーム自身がリアルタイムでメトリクスを評価して判定します。エラーレートが30秒間 1% を超えたら、LLMやローカルのhookが何をしようと、プラットフォームが実験を停止します。3層のうち、LLMもローカルエージェントも両方が侵害された状態で唯一生き残るレイヤーです。同時に、最も忘れられがちなレイヤーでもあります。チームのSLOに関する意見をYAMLに書く必要があり、誰もそれを書きたがらないからです。書いてください。
便利なテスト: チームメンバーをランダムに1人選び、CLAUDE.mdとhooksファイルを渡して「悪意があるとして、productionに当たる実験を設計できますか?」と聞きます。答えが「CLAUDE.mdを編集すればイエス」なら、プラットフォームのSLOロックが捕まえます。答えが「hookを消せばイエス」なら、プラットフォームのSLOロックが捕まえます。3層は冗長ではなく、別々の壊れ方に対応しています。
[3役分離 (観測者・戦略家・実行者)](https://kenimoto.dev/ja/blog/observer-strategist-marketer-3-yaku-bunri) のパターンはカオスにきれいにマップします。CLAUDE.mdが戦略家 (ポリシーを定める)、hooksが観測者 (何が起きるかを捕まえる)、MCPサーバーが両者の下にいる実行者です。この層をまたいで一人格にしないことが、AIエージェントがうっかり全部を兼ねないための仕組みです。
## Chaos Engineering 2.0: 4つの流れが収束する
カメラを引きます。2024年の review 論文 *Chaos Engineering 2.0: A Review of AI-Driven, Policy-Guided Resilience for Multi-Cloud Systems* ([journal ページ](https://journals.stecab.com/jcsp/article/view/846)) は、モダンなスタックを3つの柱で整理しています。実験を設計するAIプランナー、アプリケーションコードに触らないサービスメッシュレベルでの障害注入、ブラストレディアスとSLO規律を強制するポリシー駆動のガードレールです。同論文は、調査対象組織の89%がマルチクラウド運用であるとも報告しています。これらの障害モード (クラウド間DNSドリフト、IAMトークンライフサイクル不一致、リージョン固有のレートリミッタ) が実際に住んでいる環境です。
直近では [ChaosEater (2025)](https://arxiv.org/abs/2511.07865) という arxiv 論文が、完全にLLMオーケストレーションされたカオスサイクル (モデルが実験設計・実行・分析をポリシーガードレールの下で全部受け持つ) を提案しています。先ほどの4製品が歩いている方向の、研究側からの同じ歩み方です。
4つの流れが収束する (カオスエンジニアリング、可観測性、AI / LLM、プラットフォームエンジニアリング)。これはマーケティングの絵ではありません。私のstaging事故が乗っていた実際のワークフローです。カオスエンジニアリングが実験を提供しました。可観測性が90秒でSLO違反を検出するメトリクスストリームを提供しました。LLMが実験設計と、後にログのチェーンを読み解いて本番バグを特定する手助けをしました。プラットフォームエンジニアリング (Steadybit + hooks + CLAUDE.md) がブラストレディアスにproductionを含めないようにしました。
このうちどれか1つを抜くと、同じ話は違う結末になります。LLMなしなら、誰も実験4を提案しません (一見明らかに無謀に見えるからです)。可観測性なしなら、SLO違反の検出に数分かかります。ポリシーガードレールなしなら、「本番でも確認しましょう」が現実に起こります。意図的な実践としてのカオスなしなら、バグはもう半年見えません。
## 来週これを試す人へ
夜中の11時にstagingを殺さずに同じことを試したい人向けに、ハインドサイトでやり直すならこうする、をまとめます。
実験1だけ、単一namespaceで、ブラストレディアスをポッド10%にキャップして始めてください。最初のgreenは「ブラストレディアスを広げてよい」のシグナルであって、「勝った」のシグナルではありません。面白い実験は、システムが吸収できる境界の少し先で起きるやつです。
CLAUDE.mdとhooksは、MCPサーバーを繋ぐ **前** に書いてください。後でも、並行でもなく、前にです。新しいピカピカのおもちゃを手に入れた誘惑は「1時間遊んでからガードレールを足そう」です。その1時間が staging が死ぬ時間です。死んだ後はルールを書く忍耐が最小になる時間でもあります。
実行後のプロンプトは短くしてください。「何が失敗したか、どのSLOが違反したか、最も可能性の高い根本原因」で足ります。SLO違反のあとの長いプロンプトは、LLMを物語モードに引きずります。証拠モードのほうがほしいです、物語モードではなく。
カオスから得たポストモーテムの習慣を、AIコーディング全般に持ち込んでください。本記事が存在する理由は、私が90秒のインシデントから1ページのメモを通常のインシデントドキュメントと同じ形で残していたからです。そのメモがなければ、これは雰囲気のブログ記事になっていました。あったから、証拠1ピースあたり1段落と、同じ週に本番に着地した修正があります。
AIはどのSREよりも速くカオスを設計します。3つのガードレールがなければ、stagingも最速で殺します。3つを噛ませて初めて「半年見逃していたバグを見つける」が手に入ります。当番のSREの週末もちゃんと手に入ります。
---
本記事のソースは、Krkn-AI / Harness / Steadybit / Dynatrace の全景、Chaos Engineering 2.0、本番でカオスを回しつつニュースにならないための運用手法を14章でまとめたBookです。
[カオスエンジニアリング: モダン分散システムのための実践ガイド](https://kenimoto.dev/ja/books/chaos-engineering-guide)
ハーネスシリーズの関連記事:
- [Claudeが3回連続でバグを「隠す修正」を出してきた話](https://kenimoto.dev/ja/blog/claude-bug-kakushi-debug-10-techniques-prompt)
- [3つの役割を分離する: 観測者・戦略家・実行者](https://kenimoto.dev/ja/blog/observer-strategist-marketer-3-yaku-bunri)
- [AIパイプラインに潜んでいた9つのバグ](https://kenimoto.dev/ja/blog/9-bugs-in-my-ai-pipeline)
---
# Claude Codeを/clearせず9時間動かした日、コンテキストはどこで腐り始めたのか
URL: https://kenimoto.dev/ja/blog/claude-code-9h-context-rot-token/
Lang: ja
Date: 2026-05-31
Description: 長時間セッションで指示を無視され始めたとき、私は「Claudeが雑になった」と思いました。違いました。腐っていたのは私のコンテキストでした。context rotがどのトークン数で始まるか、研究と自分のセッションを突き合わせた話です。
ある日、Claude Codeを朝から晩まで、`/clear`を一度も押さずに使い続けました。9時間です。一つのセッションで、リファクタからテスト追加、ドキュメント更新まで全部やろうとしました。
夕方になって、様子がおかしくなりました。CLAUDE.mdに「テストはvitestで書く」と明記してあるのに、jestで書き始める。午前中に「この関数はもう使っていないから消した」と合意したはずの関数を、夕方には「念のため残しておきましょう」と復活させようとする。同じファイルを3回読み直す。
最初、私は「今日のClaudeは調子が悪いな」と思いました。これが間違いでした。調子が悪かったのはモデルではなく、9時間ぶん膨れ上がった私のコンテキストのほうです。
この現象には名前がついています。**context rot**(コンテキストの腐敗)です。
## context rotは「ウィンドウが満杯になる前」に始まる
context rotという言葉を形式化したのは、Chromaが2025年に出した研究です。[18の最前線モデルを系統的にテストし](https://www.understandingai.org/p/context-rot)、入力が長くなるほど全モデルの出力品質が下がることを示しました。例外はゼロです。重要なのはここで、**200Kトークンのウィンドウを持つモデルでも、5万トークンあたりで目に見えて劣化が始まる**という点です。
つまり「ウィンドウに入るかどうか」と「ちゃんと使えるかどうか」は別の問題でした。私はずっと前者しか気にしていませんでした。「まだ半分も埋まってないから大丈夫」は、まったく根拠のない安心だったわけです。
この発見の源流は、2023年のLiuらの論文[Lost in the Middle](https://arxiv.org/abs/2307.03172)です。コンテキストが埋まってくると、モデルは入力の先頭と末尾のトークンを重視し、真ん中のトークンが「迷子」になる。私の9時間セッションでは、午前中の決定事項がちょうど真ん中に沈んでいました。だから夕方のClaudeは、午前中の自分を覚えていなかったのです。
ある調査では、[2025年のエンタープライズAI障害の約65%](https://www.morphllm.com/context-rot)が、複数ステップ推論の途中で起きたコンテキストのドリフトや記憶喪失に帰着するとされています。これはモデルが賢くないからではありません。長くなったコンテキストを賢く使えていないからです。
## 私のセッションで起きた4つの症状
拙著のContext Engineeringの本では、長期対話で起きる障害を4つのモードに分けています。9時間セッションで、私はその4つを順番に踏み抜きました。
**1. Context Distraction(散漫)。** 無関係な情報が増えすぎて焦点がぼける。午後のClaudeは、午前中に一度だけ読んだ設定ファイルの細部を延々と気にしていました。もうどうでもいい話なのに。
**2. Context Confusion(混乱)。** リファクタとテストとドキュメントを1セッションに混ぜたせいで、複数のトピックが混在し、文脈を取り違える。テストの話をしているのにドキュメントの口調で返事が来る、という珍事が起きました。
**3. Context Clash(衝突)。** 矛盾する情報が共存する。「関数を消す」と「関数を残す」が同じウィンドウに同居して、回答が不安定になりました。
**4. Context Poisoning(汚染)。** 序盤に紛れた一つの誤解が、以降の回答をじわじわ歪めていく。これが一番こわい。一度ハルシネーションした前提を、本人は「確定事項」として扱い続けます。
新人に例えるなら、朝礼から残業まで一度も席を立たず、メモも取らず、休憩もせずに働かせ続けた状態です。夕方に判断が雑になるのは、その人が無能だからではありません。
## 圧縮が走るのが遅すぎる問題
ここで運用の落とし穴があります。Claude Codeには[auto-compact](https://www.cometapi.com/what-is-auto-compact-in-claude-code/)という自動圧縮機能があり、コンテキストが約95%(残り25%前後)に達したときに発動します。2026年初頭にはバッファが約33Kトークンに調整されました。
問題は、**劣化が始まるのが50K付近なのに、圧縮が走るのが190K付近**だということです。腐り始めてから圧縮までに、広大な「すでに性能が落ちているのに何もしていない」ゾーンが横たわっています。私の9時間セッションは、ずっとこのゾーンを走っていました。
Claude Codeチームの[Thariq Shihipar氏は、容量の50〜60%で能動的に`/compact`を打つこと](https://www.mindstudio.ai/blog/claude-code-compact-command-context-management)を勧めています。auto-compactを待つのは`/compact`の使い方として間違っている、と。私はずっと間違って使っていました。「自動でやってくれるなら任せよう」と。任せた結果が、jestで書き始めるClaudeでした。
`/clear`と`/compact`は別物です。`/clear`は会話履歴を完全に消して新品の状態に戻す。`/compact`は会話を要約して、その要約を新しいコンテキストとしてプリロードする。重要なコード変更やファイルの状態、決定事項は残り、中間のデバッグ出力や解決済みの議論は刈り取られます。
## では、どう運用するか
9時間セッションの反省から、私のいまの運用はこうなりました。
| 対策 | やること | 効きどころ |
|---|---|---|
| **タスクで区切る** | リファクタ・テスト・ドキュメントを別セッションに分ける | Confusion(混乱)を断つ |
| **50〜60%で先回りcompact** | auto-compactを待たず、能動的に`/compact`を打つ | 劣化ゾーンに入る前に圧縮 |
| **節目で`/clear`** | タスクが完全に切り替わるときは要約より全消し | Poisoning(汚染)を断ち切る |
| **CLAUDE.md再読込** | 長いセッションでは「CLAUDE.mdをもう一度読んで」と明示 | 真ん中に沈んだ規約を末尾に持ち上げる |
| **サブエージェント委譲** | 調査や一括処理は別エージェントに切り出す | メインのウィンドウを汚さない |
一番効いたのは、実は一番単純な「タスクで区切る」でした。1セッション1目的。これだけで、夕方のjest事件は起きなくなりました。
「真ん中に沈んだ規約を末尾に持ち上げる」というのも地味に効きます。Lost in the Middleが先頭と末尾を重視するなら、大事な規約を末尾に置き直せばいい。CLAUDE.mdの再読込は、まさにそれをやっています。
## まとめ: 「まだ埋まってない」は安心材料ではない
9時間セッションが私に教えたのは、context rotはウィンドウが満杯になってから起きるのではない、ということです。Chromaの計測では50K付近、つまりウィンドウの4分の1で、もう劣化は始まっています。
そして次のアクションは拍子抜けするほど簡単です。**長いセッションで「あれ、雑になったな」と感じたら、まず自分を疑う前にコンテキストを疑う。** そして`/compact`を打つか、いっそ`/clear`して、CLAUDE.mdから入り直す。モデルが急に賢くなったように見えるはずです。賢くなったのではなく、見ているものが綺麗になっただけですが。
腐るのはモデルではなく、こちらが渡し続けたコンテキストのほうでした。
---
5戦略、RAG実装、動的コンテキスト選択、Memory設計までを通しで扱った [LLM を「嘘つき」から「専門家」へ変える Context Engineering 実践ガイド](https://kenimoto.dev/ja/books/context-engineering) を Zenn と Kindle で公開しています。本記事で触れた4つの障害モードとメモリアーキテクチャは、同書の第9章にあたります。
---
# 「とりあえず全部許可」でClaude Codeを動かすと、.envの秘密がそのままAnthropicに渡る話
URL: https://kenimoto.dev/ja/blog/claude-code-deny-rules-env/
Lang: ja
Date: 2026-06-06
Description: エージェントの権限を疑わしきも許可で運用すると、コマンド出力経由で AWS_SECRET_ACCESS_KEY がLLMプロバイダに流れます。deny-rulesと多層防御で、エージェント実行時の秘密漏洩をどう止めるかを実装ベースでまとめました。
最初に範囲を区切らせてください。この記事は、GitHubのコミット偽装でもなく、エディタ拡張のサプライチェーン汚染でもありません。話すのは一点だけ、エージェントを動かしている最中に、`.env` の中身がツール出力に乗ってLLMプロバイダへ渡る経路です。コードを書く本人が一番油断している場所、と言い換えてもいいです。
私も最初は油断していました。Claude Codeを `auto` 寄りの権限で回して、ターミナルに流れる出力をろくに見ていませんでした。便利だったからです。便利なものは、たいてい後ろから刺さります。
## 「疑わしきも許可」が秘密を運ぶ仕組み
問題はファイルを直接開く話だけではありません。エージェントが実行したコマンドの出力が、そのまま会話コンテキストに取り込まれ、APIに送られます。これが見落とされがちな漏洩経路です。
具体的にはこうです。エージェントがデバッグのために `printenv` を打つ。あるいはアプリの起動ログに環境変数が出る。`docker inspect` の結果に認証情報が混ざる。そのテキストは全部、Claude Codeのコンテキストに入ります。そしてコンテキストはAnthropicのAPIに送られる。`AWS_SECRET_ACCESS_KEY` が、誰も「漏らそう」と思っていないのに、平文で旅に出るわけです。
OWASPは2026年に「Agentic Security Top 10」を公開しました。その A2(過剰な権限付与)と A4(データ漏洩)は、図解の中の脅威ではなく、`auto` や `--dangerously-skip-permissions` を押した瞬間に現実になります。GitGuardianの2026年レポートでは、公開GitHub上で2,900万件のシークレットが検出され、AI支援のコミットはベースラインの約2倍の頻度で秘密を漏らしていた、というデータも出ています。エージェントは秘密を漏らすのが上手いのです。悪意なく、勤勉に。
## deny-rules で危険なパターンを明示的に止める
最初の防御線は `.claude/settings.json` の deny ルールです。deny は allow より常に優先されるので、危険なパターンをここで名指しで潰します。
```json
{
"permissions": {
"allow": [
"Read",
"Bash(npm test *)",
"Edit(src/**/*.ts)"
],
"deny": [
"Read(.env)",
"Read(.env.*)",
"Edit(*.env*)",
"Write(*.env*)",
"Read(*.pem)",
"Read(*.key)",
"Read(credentials.json)",
"Bash(curl * | bash)"
]
}
}
```
設定の優先順位も押さえておくと効きます。管理ポリシー(`/etc/claude-code/settings.json`)はユーザーが上書きできないので、チームで強制したいルールはここに置きます。個人の油断を、組織の設定で先回りして潰すわけです。
## deny-rules を過信してはいけない理由
ここで正直に書きます。deny ルールだけを信じるのは危険です。2026年に入って、`Read` の deny ルールが `.env` に対して実際には効いていない、という不具合が複数報告されました(anthropics/claude-code の Issue #24846 ほか)。deny に `.env` を入れて「守った」と思い込んだ状態が一番こわい。プロンプトも警告もなく、エージェントが拒否したはずのファイルを読んでいた、という報告です。
つまり deny-rules は「最初の壁」であって「最後の砦」ではありません。鍵をかけたつもりのドアが、たまにノブだけ回ると思っておいたほうがいい。だから層を重ねます。
## 多層で守る: 設定の外側に防御を置く
秘密を守る一番確実な方法は、そもそもディスク上に平文の秘密を置かないことです。実行時に取得して、ファイルに書かないなら、どんなツールにも読むものがありません。
私が実際にやっている順番はこうです。
1. **`.env` をディスクから減らす。** 本番に近い値はシークレットマネージャ(AWS Secrets Manager、HashiCorp Vault)に寄せ、実行時に注入する。ローカルの `.env` はダミー値に置き換える。漏れて困らない値なら、漏れても困りません。
2. **`.gitignore` と除外設定を二重で張る。** `.env`、`*.pem`、`*.key`、`credentials.json` をコミット対象から外し、エージェントの除外設定にも入れる。
3. **Hooks で二重チェックする。** 権限ルールに加えて、Hooks で危険なツール使用を検査し、Exit code 2 でブロックする。`async` オプションを使えば、すべてのツール使用を外部ログに記録して後から監査できます。
4. **APIキーを最小権限+月間上限にする。** エージェント用のキーは、必要最小限の権限と利用上限を設定する。万一漏れても、被害の天井を低くしておく。
5. **ネットワークを断つ選択肢を持つ。** ローカルファイルだけを触るタスクなら、Dockerコンテナを `--network none` で起動して、外への送信経路ごと塞ぐ。出ていけないなら、漏れようがありません。
5つ全部を最初からやる必要はありません。私のおすすめは、まず 1 と 2 だけ今日やることです。`.env` をダミーに差し替えて `.gitignore` を確認する。これだけで、漏れて致命傷になる平文の秘密がローカルから消えます。残りは運用の本気度に合わせて足していけばいい。
## エージェント実行時権限は「設定して終わり」ではない
セキュリティは設定ファイルに書いた瞬間に完成、ではありません。CVE情報やOWASPの更新、そして今回の deny-rules の不具合のように、前提が静かに変わります。`auto` の便利さは本物です。ただ、便利だからといって出力を見なくていいわけではありません。私が一番痛い目を見たのも、ターミナルを見ていなかった時でした。
権限を最小化し、deny で危険を名指しし、その外側に「平文の秘密を置かない」「出口を塞ぐ」を重ねる。エージェント実行時の秘密漏洩は、この多層でほぼ止まります。完璧な一枚の壁を探すより、そこそこの壁を何枚か立てるほうが現実的です。一枚くらい錆びていても、後ろにまだ何枚か残っています。ターミナルを見ていなかった私が、それでも今は安心してエージェントに仕事を任せられているのは、そのおかげです。
---
Claude Codeの権限モデル・サンドボックス・コスト設計をまとめて知りたい方は、書籍にしました。
[Claude Code Mastery](https://kenimoto.dev/ja/books/claude-code-mastery?utm_source=kenimoto-dev-blog&utm_medium=article&utm_campaign=claude-code-deny-rules-env)
---
# Claude Code Hooks v2 — 「お願い」を「プログラム」に変える25のイベント
URL: https://kenimoto.dev/ja/blog/claude-code-hooks-v2-25-events/
Lang: ja
Date: 2026-05-02
Description: CLAUDE.mdに書いたルールは「お願い」にすぎない。Hooks v2は25種のイベントと4種のハンドラーで、AIの動作にプログラム的に介入する仕組みです。settings.jsonに書くだけで今日から使えます。
CLAUDE.mdに「ファイル編集後は必ずlintを走らせて」と書きました。3日間は守られていました。4日目、deadlineに追われたClaude Codeは見事にlintをスキップし、フォーマットが壊れたコードをそのままコミットしてくれました。
私は、部下への口頭指示が3日で蒸発する中間管理職の気持ちを、AIとの間で追体験していました。
CLAUDE.mdの指示は「お願い」です。Claudeはお願いを覚えていますが、忘れることもあります。100回中95回は守るかもしれない。でも残りの5回が本番障害を引き起こしたら?
Hooks v2は「お願い」を「プログラム」に変えます。
## CLAUDE.md vs Hooks: 何が違うのか
CLAUDE.mdはClaude Codeへのテキスト指示です。Claudeのコンテキストに読み込まれ、「こうしてほしい」と伝えます。ほとんどの場合は機能します。でも「ほとんど」では足りない場面があります。
Hooksはsettings.jsonに定義するプログラムです。Claude Codeの動作に介入し、条件に合致したときにシェルコマンドやWebhookを自動実行します。Exit code 2を返せば、ツールの実行そのものをブロックできます。
違いを一言で言うと、CLAUDE.mdは「守ってね」で、Hooksは「守らせる」です。
テストがバグの不在を証明できないように、CLAUDE.mdもルールの遵守を保証できません。Hooksはルールをコードにすることで、遵守しなければ先に進めない仕組みを作ります。
## 25のイベント、6つのカテゴリ
前作のHooksはPreToolUseとPostToolUseの2イベントだけでした。Hooks v2は25種類以上のイベントに対応しています。まったく別のシステムです。
6つのカテゴリに整理すると見通しが良くなります。
### セッションライフサイクル
| イベント | タイミング |
|---------|----------|
| **SessionStart** | セッション開始(起動/再開/クリア/コンパクション後) |
| **SessionEnd** | セッション終了 |
| **InstructionsLoaded** | CLAUDE.mdやrulesファイルの読み込み時 |
### ツール実行
| イベント | タイミング | ブロック可能 |
|---------|----------|:----------:|
| **PreToolUse** | ツール実行前 | Yes |
| **PostToolUse** | ツール実行成功後 | - |
| **PostToolUseFailure** | ツール実行失敗後 | - |
| **PermissionRequest** | 権限ダイアログ表示時 | - |
| **PermissionDenied** | 自動モードでツール拒否時 | - |
### エージェント
| イベント | タイミング |
|---------|----------|
| **SubagentStart** | Sub-agent起動時 |
| **SubagentStop** | Sub-agent終了時 |
| **TeammateIdle** | チームメイトがアイドル時 |
| **TaskCreated** | タスク作成時 |
| **TaskCompleted** | タスク完了時 |
### ファイル・環境
| イベント | タイミング |
|---------|----------|
| **FileChanged** | 監視対象ファイルの変更時 |
| **CwdChanged** | 作業ディレクトリ変更時 |
| **ConfigChange** | 設定ファイル変更時 |
| **WorktreeCreate** | Git worktree作成時 |
| **WorktreeRemove** | Git worktree削除時 |
### コンテキスト
| イベント | タイミング | ブロック可能 |
|---------|----------|:----------:|
| **PreCompact** | コンパクション前 | Yes |
| **PostCompact** | コンパクション後 | - |
### MCP・通知
| イベント | タイミング |
|---------|----------|
| **Elicitation** | MCPサーバーがユーザー入力を要求時 |
| **ElicitationResult** | ユーザーがMCP elicitationに応答後 |
| **Notification** | 通知送信時 |
| **StopFailure** | APIエラーでターン終了時 |
| **UserPromptSubmit** | ユーザー入力をClaude処理前 |
25のイベントを全部覚える必要はありません。実際に使うのは最初の数個です。ほとんどの開発者にとって、PreToolUse + PostToolUse + SessionStartの3つで用事の8割は片付きます。
残り22個は「いつか使うかもしれない引き出し」です。本棚の端にある辞書みたいなもので、存在を知っておくだけで十分です。
## settings.jsonの書き方
Hooksはsettings.jsonに定義します。構造は3層です。
```json
{
"hooks": {
"イベント名": [
{
"matcher": "マッチ対象",
"hooks": [
{
"type": "command",
"command": "実行するコマンド",
"timeout": 30
}
]
}
]
}
}
```
**イベント名** -> **マッチャー配列** -> **ハンドラー配列**。1つのイベントに複数のマッチャーを設定でき、1つのマッチャーに複数のハンドラーをチェインできます。
マッチャーはイベントの種類によって異なる対象にマッチします。ツールイベントならツール名(`"Bash"`、`"Edit|Write"`)、SessionStartなら起動理由(`"startup"`、`"resume"`)、SubagentStart/StopならSub-agentの名前です。`"*"` や空文字、またはmatcher自体の省略で全てにマッチします。
## 実践例3つ: 今日から使える設定
### 1. 破壊的コマンドをブロックする(PreToolUse)
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git push --force*)",
"command": "echo 'force pushはブロックされています' >&2; exit 2"
}
]
}
]
}
}
```
`exit 2` が鍵です。Exit code 0は成功、exit code 1は非ブロックエラー(ログされるだけ)、**exit code 2だけがツール実行をブロック**します。
CLAUDE.mdに「force pushしないで」と書いても、急いでいるClaudeは忘れるかもしれません。このHookなら、物理的にforce pushできなくなります。
同じパターンで `.env` ファイルの編集防止もできます。
```json
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"if": "Edit(*.env*)|Write(*.env*)",
"command": "echo '.envファイルの編集は禁止です' >&2; exit 2"
}
]
}
```
### 2. ファイル保存後に自動フォーマット(PostToolUse)
```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$FILE_PATH\"",
"timeout": 30,
"statusMessage": "Prettierでフォーマット中..."
}
]
}
]
}
}
```
CLAUDE.mdに「Prettierを走らせて」と書く代わりに、Hooksに書けば **毎回確実に** 走ります。忘れるのは人間もAIも同じ。仕組みで解決するほうが健全です。
### 3. セッション開始時に環境変数を設定(SessionStart)
```json
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo 'export NODE_ENV=development' >> \"$CLAUDE_ENV_FILE\""
}
]
}
]
}
}
```
`$CLAUDE_ENV_FILE` はSessionStartイベント限定の特殊変数で、ここに書き込んだ環境変数はセッション全体で有効になります。「開発環境では必ず `NODE_ENV=development` にする」をプログラム的に保証します。
## 4種類のハンドラー
Hooks v2には4種類のハンドラーがあります。
**command**: シェルコマンドを実行します。もっとも基本的で、ほとんどのユースケースはこれでカバーできます。標準入力にイベント情報がJSONで渡されます。
**http**: Webhookとして外部サービスにPOSTリクエストを送信します。2026年2月に追加されたハンドラーで、SlackやDiscordへの通知、外部監視システムとの連携に使えます。
```json
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"headers": { "Authorization": "Bearer $MY_TOKEN" },
"allowedEnvVars": ["MY_TOKEN"],
"timeout": 30
}
```
**prompt**: 別のLLMにイベント内容を評価させます。「このコマンドは安全ですか?」をAIに判断させるセキュリティゲートとして使えます。
**agent**: Sub-agentを起動して検証を行います。Read/Grep/Globなどのツールを使った複雑な検証ロジックに向いています。実験的機能です。
正直なところ、最初はcommandだけで十分です。httpは外部連携が必要になったとき、promptとagentは「人間の判断をAIに委譲したい」ときに検討すればよいです。全部使いこなそうとすると、道具に振り回される日曜大工みたいになります。
## 私が使っている設定
私の実際のsettings.jsonから、特に効果を感じている設定を紹介します。
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git push --force*)",
"command": "echo 'force pushはブロック' >&2; exit 2"
}
]
},
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"if": "Edit(*.env*)|Write(*.env*)",
"command": "echo '.env編集はブロック' >&2; exit 2"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$FILE_PATH\"",
"timeout": 30
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo 'export NODE_ENV=development' >> \"$CLAUDE_ENV_FILE\""
}
]
}
]
}
}
```
この設定で実現していること:
- `git push --force` が物理的にブロックされる
- `.env` ファイルが編集不可になる
- ファイル保存のたびにPrettierが自動実行される
- セッション開始時にNODE_ENVが自動設定される
CLAUDE.mdには同じことが4行のテキストで書けます。でもテキストは忘れられる可能性があります。このsettings.jsonは忘れません。
## 始め方
Hooks v2を始めるのに25イベントを全部理解する必要はありません。
**Step 1**: 自分のCLAUDE.mdを開いて、「守られないと困るルール」を1つ見つける。
**Step 2**: そのルールをPreToolUseまたはPostToolUseのHookに変換する。
**Step 3**: settings.jsonに追加して動作確認する。
これだけです。1つのHookが動くのを見たら、「次はあのルールもHookにできるな」と自然に広がっていきます。
CLAUDE.mdを書くのをやめる必要はありません。ルールの中で「守られなくても困らないもの」はCLAUDE.mdに残し、「守られないと困るもの」だけをHooksに昇格させる。この使い分けが現実的です。
私はHooks v2を導入してから、lintのスキップが完全にゼロになりました。CLAUDE.mdに3回書き直しても直らなかったことが、settings.jsonの5行で解決した。テクノロジーの正しい使い方だと思います。もっとも、その5行を書くのに半日かかったのは内緒です。
## 参考リンク
- [Hooks reference - Claude Code Docs](https://code.claude.com/docs/en/hooks) - 公式リファレンス
- [Claude Code Hooks: Complete Guide](https://claudefa.st/blog/tools/hooks/hooks-guide) - 全12ライフサイクルイベント解説
- [Claude Code Hooks Tutorial](https://blakecrosley.com/blog/claude-code-hooks-tutorial) - 実践チュートリアル(5パターン)
---
## さらに深掘りしたい方へ
本記事で触れたのは一部です。CLAUDE.md の書き方を「2行から100行まで」、Plan Mode 起点の開発フロー、チーム運用、非コーディング業務への応用まで、19章で体系化した **[実践Claude Code — コンテキストエンジニアリングで開発が変わる](https://kenimoto.dev/ja/books/claude-code-mastery)** を参考にしてください。
---
# Claude Code Skills を10個書いたら、4個に統合された — Reusable Pattern が回り始める瞬間と回らない瞬間
URL: https://kenimoto.dev/ja/blog/claude-code-skills-10-to-4-integration-pattern/
Lang: ja
Date: 2026-05-20
Description: Claude Code Skills を10個書きました。3か月運用した結果、4個に統合され、6個は消えました。残った4個と消えた6個の境界線、Skill 設計で繰り返した6種類の失敗、そして「Skill にする / しない」を分ける1つの基準について書きます。
Claude Code Skills が公式に降りてきた最初の週、私は10個書きました。「これで作業が全部自動化されるはずだ」と思ったからです。月単位の確定申告から、Zenn の記事公開、Telegram 通知、画像生成、PR レビューまで、思いついた手順を片っ端から SKILL.md にしました。最初は Skill 1個 = アシスタントを1人雇った気分でした。実態は **1個 = 起動時にカタログ展開で数百〜数千トークン分の脳のスペースが減る** だけだったんですけど、それに気づくのは2週間後です。
3か月運用した結果、10個のうち6個は消えました。残った4個は、起動の仕方も書き方も最初の10個とはまったく違うものに変わっています。この記事は、その10→4の境界線を共有する記事です。
なお、先週 Zenn で「Claude Code 拡張47→5絞り込み」という記事を書きました。あちらは Skills / MCP / Hooks / Plugins の **4種類全体を含めた絞り込み体験談** です。本記事は **Skills 単体に絞った設計の話** なので、ご了承ください。Sub-agent や Hooks v2 の話は別の場所で書いています。
## 残った4個
先に結論を書きます。3か月生き残った4個はこれです。
1. **`kenimoto-dev-cycle`** — kenimoto.dev のブログ運用 PDCA。Observer → Strategist → Marketer の3層を1日1回回す
2. **`zenn-cycle`** — Zenn 記事の同じ PDCA。日本語、24時間ルールあり
3. **`generate-image`** — HTML + Puppeteer で navy-mono の図解画像を生成
4. **`avoid-ai-writing-ja`** + 兄弟3言語 — AI Slop 検出と書き換え
4個の共通点は2つあります。
- **入力が決まっている**: cron か、特定のファイルパス、特定のフェーズからしか呼ばれない
- **出力が他の流れにつながる**: 画像は記事に、AI Slop チェックは公開ゲートに、cycle は cron 通知に
逆に消えた6個は、入力も出力も曖昧でした。「便利そうだから書いた」だったんです。
## 消えた6個と、消えた理由
消した順に並べます。
### 1. `morning-summary` — 重複起動で消えた
朝の活動ログをまとめてくれる Skill でした。しかし `kenimoto-dev-cycle` の Observer フェーズと、`zenn-cycle` の Observer フェーズと、`harness-admin` の status check と内容が8割重なっていました。3つの Skill から「朝の状況を見たい」シーンが派生して、それぞれが個別に書かれていただけだったんです。
統合先: 各 cycle Skill の Observer フェーズに吸収。`morning-summary` 単体は不要。
### 2. `tg-notify-rich` — 使用頻度が低くて消えた
Telegram 通知をリッチな書式で送る Skill。月に1回くらいしか呼ばれませんでした。それなら3行のシェルスクリプトで十分です。Skill 化する基準を満たしていません。
教訓: **月に2回未満しか呼ばれない手順は Skill にしない**。記憶を圧迫するだけです。
### 3. `gitignore-check` — Hook で代替できて消えた
ファイル追加時に `.env` などの秘密ファイルを誤って commit していないかを確認する Skill。これは Hook (`PostToolUse` の Edit / Write) で十分でした。Skill にすると「呼ばれて初めて動く」ですが、Hook なら「ファイル編集のたびに自動で動く」です。
教訓: **「特定の操作の後に毎回走るべき」処理は Hook、判断や対話が必要な処理は Skill**。境界線はここです。
### 4. `book-outline-gen` — context 汚染で消えた
書籍の章立てを提案する Skill。最初は便利でしたが、frontmatter ルール、構成テンプレート、avoid-ai-writing 連携など、ロードする情報が肥大化していき、起動時のコンテキスト負担が大きくなりました。同時に、書籍はそんなに頻繁に書かないので、必要なときに自然言語で指示するほうが軽かったです。
教訓: **「コンテキストを大量に持ち込む Skill」は、起動頻度が低いと割に合わない**。
### 5. `pr-review-light` — 書き換え頻度が高くて消えた
PR レビューを Skill にしたものの、レビュー観点が案件ごとに違いすぎて、SKILL.md の改訂が毎週入りました。3週連続で書き換えたところで「これは Skill ではなく、対話で都度方針を渡すべきだ」と判断しました。
教訓: **書き換え頻度の高い手順は Skill にしない**。判断ロジックがまだ固まっていない証拠です。
### 6. `email-triage` — generic化に失敗して消えた
Gmail の未読を分類する Skill。Pinterest 通知、KDP 通知、その他の取引先、と分類カテゴリーが増えるたびに条件分岐が膨れて、最終的に「個別の `check-gmail` 系 Skill のほうが軽い」ことに気づきました。
教訓: **無理に1つの Skill に詰め込もうとせず、3つに分ける**。
## 残った4個に共通する設計原則
3か月運用して気づいた、Skill が生き残るための条件を書きます。
### 条件1: 1日1回以上の頻度で起動される
毎日呼ばれない Skill は、たいてい次第に「あれ、これ何を書いた Skill だっけ?」になります。生き残った4個は、cron か手動か、形式は違えど **毎日呼ばれる Skill** でした。
「いつか役立つ」ために書いた Skill は、ほぼ全部消えました。
### 条件2: 入力の起点が1つに固定されている
`generate-image` は記事の図解、`avoid-ai-writing-ja` は記事完成後、`*-cycle` は cron。起点が決まっていると、Skill が呼ばれるたびに「ああ、いつものやつね」と context の準備ができます。
逆に「いろんな場面で便利」を狙った Skill は、毎回 context を再構築する羽目になります。
### 条件3: SKILL.md を3か月書き換えていない
これは結果論ですが、3か月運用して書き換えなかった Skill だけが残りました。書き換え頻度は、その Skill の判断ロジックが固まっているかどうかの指標です。
書き換えが多い Skill は、まだ Skill にする時期ではないと割り切るのが楽でした。
## Skill にする / しない を分ける1つの基準
10→4の統合を経て、いまの私が新しい Skill を書く前に自問するのは1つです。
**「この処理は cron か file watcher か特定のフェーズから自動で呼べるか?」**
Yes なら Skill 化に意味があります。No なら、たぶん対話で都度書くか、Hook にするか、シェルスクリプトで済ませるかの3択です。
最初の10個を書いていた頃の私は、「便利そうだから」を起点に Skill を作っていました。便利そう、は罠です。Claude Code Skills は便利になるための道具ではなく、 **同じことを何度も繰り返す手順をパッケージ化するための道具** です。何度も繰り返さない手順をパッケージ化しても、起動時のオーバーヘッドだけが残ります。
10個書いて6個消した経験から言うと、最初から4個を目指して書くより、思い切り10個書いてから消すほうが、自分にとってどの Skill が機能するかが見えやすいです。消す勇気のほうが、書く勇気より高くつきます。私は6個消すのに3か月かかりました。
最初から完璧な Skill セットを目指す必要はないです。雇って、合わなかったら静かに別れる、それを3回繰り返すうちに「ああ、私の作業はだいたいこの4種類だな」が見えてきます。
---
Claude Code Skills の設計、フォルダ構成、運用フローの詳細は『[Claude Code Mastery](https://kenimoto.dev/ja/books/claude-code-mastery)』にまとめています。
---
# Claude CodeのSub-agent設計 — 1セッションで専門家チームを使い分ける
URL: https://kenimoto.dev/ja/blog/claude-code-sub-agent-design/
Lang: ja
Date: 2026-05-01
Description: Explore・Plan・汎用の3種のビルトインSub-agentと、カスタムSub-agentの作り方。コンテキスト保全・制約強制・コスト制御の3原則で、1セッションを専門家チームに変える設計パターン。
Claude Codeで大きなリファクタリングをしていたとき、コードベース全体を調査させたらコンテキストウィンドウの70%が検索結果で埋まりました。肝心のリファクタリング指示を出す頃には、窓が狭すぎて的外れな提案しか返ってこない。
私は「調査が上手すぎて仕事ができなくなるAI」という、落語みたいな状況に立ち会っていました。
解決策はSub-agentです。調査を別の専門家に委譲して、結果の要約だけ受け取る。メインの会話は綺麗なままです。
## Sub-agentとは何か
Sub-agentは、メインのClaude Codeセッションから呼び出される専門家です。独立したコンテキストウィンドウで動作し、タスクが終わったら結果だけを返します。
Eric Raymondの「目玉の数が十分あれば、バグは浅くなる」は、オープンソースのレビュアーの話でした。Sub-agentはこれをAIに持ち込みます。Exploreエージェントにコードを調査させ、セキュリティ用エージェントに脆弱性を探させる。「目」の数が増えるほど、見落としが減ります。
Sub-agentを使う理由は3つです。
**コンテキストの保全。** コードベースの探索や大量ログの解析をメインでやると、検索結果がコンテキストを圧迫します。Sub-agentに委譲すれば、調査はSub-agent側で完結し、メインには要約だけ返ります。冷蔵庫の中身を全部テーブルに出して料理するか、必要な食材だけ取り出すかの違いです。
**制約の強制。** Sub-agentにはツールアクセスを制限できます。調査専用エージェントにはRead/Grep/Globだけを許可し、ファイル編集を禁止する。「見ていいけど触るな」を技術的に強制できます。
**コストの制御。** Sub-agentごとにモデルを指定できます。調査のような軽いタスクにはHaikuを使い、重要な設計判断にはOpusを使う。全員に役員報酬を払う必要はありません。
## ビルトイン3種の使い分け
Claude Codeには、すぐに使えるSub-agentが3種あります。
| Sub-agent | 得意なこと | 使えるツール | 編集権限 |
|-----------|----------|------------|---------|
| **Explore** | コード調査、リサーチ | Read, Grep, Glob, WebSearch | なし |
| **Plan** | 設計、実装計画 | Read, Grep, Glob, WebSearch | なし |
| **general-purpose** | 実装、テスト実行 | フルセット | あり |
Claudeはタスクの内容に応じてこれらを自動選択します。「このファイルの依存関係を調べて」と書けばExploreが、「リファクタリングの計画を立てて」と書けばPlanが起動します。
ここで大事なルールが1つ。**Sub-agentはメインの会話履歴を引き継ぎません。** 「さっき話した件」のような曖昧な参照は機能しません。タスクの指示はSub-agentに渡すプロンプトの中で完結させる必要があります。
隣の部屋にいる同僚に仕事を頼むときと同じです。「あれやっといて」ではなく「Aファイルの認証ロジックを調べて、OAuth2の実装箇所をリストアップして」と具体的に伝える。
## カスタムSub-agentの作り方
ビルトイン3種でカバーできない専門家が必要なら、自分で作れます。
### 配置場所
| スコープ | パス |
|---------|------|
| 個人(全プロジェクト共通) | `~/.claude/agents/.md` |
| プロジェクト(チーム共有) | `.claude/agents/.md` |
プロジェクトの規約に依存する専門家(コーディング規約チェッカーなど)は `.claude/agents/` に。個人のワークフローに関わるもの(ドキュメント検索など)は `~/.claude/agents/` に置きます。
### 実例: セキュリティレビュー用Sub-agent
```markdown
---
name: security-reviewer
description: コードのセキュリティ問題を検出する
model: sonnet
allowed-tools: Read Grep Glob WebSearch
---
あなたはセキュリティレビューの専門家です。
コードを分析する際は、以下の観点で確認してください:
1. OWASP Top 10に該当する脆弱性
2. 認証・認可の不備
3. 入力値の検証漏れ
4. 機密情報のハードコーディング
5. 依存パッケージの既知の脆弱性
発見した問題はCVSSスコア(推定)付きで報告してください。
```
frontmatterの `---` で囲まれた部分がメタデータ、それ以降がSub-agentのシステムプロンプトです。
### frontmatterの主要フィールド
| フィールド | 説明 | 設計判断のポイント |
|-----------|------|-----------------|
| `name` | 表示名 | `@name` で呼び出すので短く |
| `description` | いつ使うかの判断基準 | Claudeのルーティングに影響する |
| `model` | 使用モデル | コスト制御の要 |
| `allowed-tools` | 許可ツール(スペース区切り) | 最小権限の原則 |
| `memory` | `true` でSub-agent専用の永続メモリ | 繰り返し使うSub-agentに有効 |
`description` フィールドは単なるラベルではありません。Claudeがこの説明文を読んで「このタスクにこのSub-agentを使うべきか」を判断します。「セキュリティレビュー」よりも「PRのコード変更からOWASP Top 10脆弱性を検出する」のほうが、適切なタイミングで呼び出されます。
## モデル選択によるコスト制御
Sub-agentの設計でもっとも実用的な判断が、モデルの使い分けです。
| タスクの性質 | 推奨モデル | 理由 |
|------------|----------|------|
| コード検索、パターン照合 | Haiku | 読み取りだけなら高速・低コストで十分 |
| コードレビュー、バグ分析 | Sonnet | 判断力が必要だがOpusほどの推論は不要 |
| アーキテクチャ設計、複雑な判断 | Opus | 妥協すると後で手戻りするタスク |
全員Opusにすれば品質は最大になります。でもそれは、お使いも商談も全部社長が行くようなものです。調査はインターンに、レビューは中堅に、設計は社長に。組織設計と同じ原則です。
2026年2月にAnthropicが公開した社内活用PDFでも、このモデル使い分けパターンが中核として紹介されていました。Anthropic自身が「全部Opusにはしない」と言っている。説得力があります。
## Git Worktreeによる並列編集
Sub-agentの隠れた強力機能が、Git Worktreeとの連携です。
Agent toolの `isolation: "worktree"` パラメータを使うと、Sub-agentは一時的なworktreeを作成してそこで作業します。メインブランチのファイルを編集しながら、別のSub-agentがworktree上でテストコードを書く。作業が完了したらworktreeのブランチをマージする。
変更がなかったworktreeは自動でクリーンアップされます。変更があった場合はパスとブランチ名が返されるので、手動でマージできます。
「同じファイルを2人が同時に編集して衝突」という、チーム開発あるあるのリスクを技術的に回避できます。
## @メンションと永続メモリ
カスタムSub-agentは `@agent-name` で直接呼び出せます。チャットで `@security-reviewer このPRをチェックして` と書くだけ。
さらに、`memory: true` を設定するとSub-agent専用のAuto Memoryディレクトリが作成されます。セッションをまたいで学習した内容が保持されるので、同じSub-agentを繰り返し使うほど精度が上がります。
セキュリティレビュー用のSub-agentが「このプロジェクトではJWTの有効期限を15分に設定している」と学習すれば、次回以降は24時間に設定されたJWTを自動的に指摘してくれます。
## 私が実際に使っている3つのSub-agent
理論はここまでにして、実際の運用例を紹介します。
### 1. コードベース探索用(Explore強化版)
```markdown
---
name: codebase-scout
description: コードベースの構造と依存関係を調査する
model: haiku
allowed-tools: Read Grep Glob
---
コードベースを調査し、以下の形式で報告してください:
- 関連ファイルの一覧(パスと1行説明)
- 依存関係のグラフ(テキスト形式)
- 変更時の影響範囲
```
Haikuで十分です。ファイルを読んでパターンを見つけるだけなら、高級モデルは過剰投資です。
### 2. テスト設計用
```markdown
---
name: test-designer
description: 実装コードからテストケースを設計する
model: sonnet
allowed-tools: Read Grep Glob
---
与えられたコードを分析し、テストケースを設計してください:
- 正常系: ゴールデンパスのテスト
- 異常系: エラーハンドリングのテスト
- 境界値: エッジケースのテスト
テストコードは書かないでください。テストケースの一覧と検証ポイントだけを報告してください。
```
注意点は `allowed-tools` からEditを外していること。テスト設計と実装を分離することで、設計段階でのバイアスを防ぎます。テストを書く人が「実装しやすいテスト」を設計しがちな問題は、人間のエンジニアと同じです。
### 3. ドキュメント更新チェック用
```markdown
---
name: doc-checker
description: コード変更に対してドキュメントの更新漏れを検出する
model: haiku
allowed-tools: Read Grep Glob
memory: true
---
コードの変更差分とドキュメントを比較し、更新漏れを報告してください:
- READMEとの乖離
- APIドキュメントの不整合
- 設定ファイルの説明漏れ
```
`memory: true` にしているのは、プロジェクト固有の「どのドキュメントがどのコードに対応しているか」を学習させるためです。
## Sub-agentを使うべき場面、使うべきでない場面
Anthropicの公式ドキュメントにある判断基準が的確です。
**Sub-agentを使うべき場面:** タスクが「ノイズが多く、範囲が限定的で、要約しやすい」とき。大量のファイルを検索する、特定パターンを見つける、独立したレビューを行う。
**メインで続けるべき場面:** タスクが「小さく、密結合で、共有メンタルモデルに依存する」とき。3行の修正、直前の議論を踏まえた判断、一連のリファクタリングの途中ステップ。
判断を間違えると、Sub-agentにコンテキストを渡すオーバーヘッドのほうが、メインでやるコストを上回ります。包丁を洗うより手でちぎったほうが早いレタスに、わざわざ包丁を出す必要はありません。
## まとめ
- Sub-agentの価値は **コンテキストの保全** が第一。重い調査をメインから隔離し、要約だけ受け取る
- ビルトイン3種(Explore/Plan/general-purpose)は自動選択される。カスタムが必要なら `.claude/agents/` にMarkdown1枚
- **モデル選択がコスト制御の要。** 調査はHaiku、レビューはSonnet、設計はOpus
- Sub-agentへの指示は **自己完結** させる。「さっきの件」は通じない
- 「ノイズが多く、範囲が限定的で、要約しやすい」タスクに使う。それ以外はメインで続ける
まずは1つ、自分のプロジェクトでよく繰り返す調査タスクをSub-agentとして定義してみてください。`.claude/agents/` にMarkdownファイルを1つ置くだけで始められます。私は最初にcodebase-scoutを作りましたが、正直なところ、効果に気づいたのは「あれ、今日のセッション、なんかコンテキスト圧迫されてないな」と思った3日後でした。地味な改善ほど、効いている証拠です。
## 参考リンク
- [Claude Code Sub-agents公式ドキュメント](https://code.claude.com/docs/en/sub-agents) — カスタムSub-agentの作成ガイド
- [Anthropic社内Claude Code活用PDF](https://www.anthropic.com) — 2026年2月公開の社内活用パターン
- [Claude Code Subagents: How to Create, Use, and Debug Them](https://www.builder.io/blog/claude-code-subagents) — 実践的なチュートリアル
---
## さらに深掘りしたい方へ
本記事で触れたのは一部です。CLAUDE.md の書き方を「2行から100行まで」、Plan Mode 起点の開発フロー、チーム運用、非コーディング業務への応用まで、19章で体系化した **[実践Claude Code — コンテキストエンジニアリングで開発が変わる](https://kenimoto.dev/ja/books/claude-code-mastery)** を参考にしてください。
---
# CLAUDE.md は結局 Context Engineering を1ファイルに凝縮したものだった
URL: https://kenimoto.dev/ja/blog/claude-md-context-engineering-practice/
Lang: ja
Date: 2026-05-09
Description: 「CLAUDE.md? READMEで十分でしょ」と思っていました。3週間後、CLAUDE.mdなしのプロジェクトに戻れなくなりました。3階層運用と4段階の設計で、Claude Codeに毎回同じ説明をする時間を消した話です。
私も最初は「CLAUDE.md? READMEで十分でしょ」と思っていました。3週間後、CLAUDE.mdなしのプロジェクトに戻れなくなりました。
前回の記事では、[Context Engineering の5戦略](/ja/blog/context-engineering-introduction-five-strategies/)を見ました。同じ質問でも回答品質が4.6倍ぶれる原因はプロンプトではなくコンテキストにある、という話です。今回はその第3戦略、Context Engineering の設計を Claude Code でどう実装するか、つまり CLAUDE.md の話をします。
結論から書きます。**CLAUDE.md は設定ファイルではなく、Context Engineering の哲学を1ファイルに凝縮したものです**。
## CLAUDE.md は新人社員への引き継ぎ資料
新人がチームに入った日のことを思い出してください。前任者が「このプロジェクトで知っておくべきこと全部」を残してくれていれば、初日から動けます。技術選定の経緯、設計パターン、注意点、コーディング規約。新人はそれを読むだけでプロジェクトの全体像を把握できます。
CLAUDE.md はそれの AI 版です。Claude Code がセッション開始時に読み込むファイルで、毎回同じ説明を繰り返す必要をなくします。
従来のプロンプトはこうなりがちでした。
```text
このプロジェクトはNext.jsを使い、TypeScriptで書かれており、
APIはtRPCで実装され、データベースはPrismaでアクセスし、
認証はNextAuth.jsを使い、UIはTailwind CSSで構築され、
テストはJestとCypressで行い、デプロイはVercelで…
```
これを毎セッション貼り付けていた頃、私は中間管理職のような気持ちになっていました。同じ説明を3回したのに、4回目で「はじめまして」と言われる感覚です。
CLAUDE.md は一度書けば常に効きます。Claude Code はセッション開始時に自動で読み込みます。同じ説明をする時間が消えます。
## 3階層運用: User、Project、Local
CLAUDE.md は1ファイルではなく3階層で運用します。
```text
~/.claude/CLAUDE.md # User: 全プロジェクト横断の指示
./CLAUDE.md # Project: プロジェクト固有のガイドライン
./CLAUDE.local.md # Local: マシン固有の上書き (gitignored)
```
それぞれの役割は次の通りです。
| 階層 | 用途 | 共有範囲 |
|---|---|---|
| User | 個人の開発スタイル、好みのワークフロー | 自分だけ |
| Project | チーム標準、アーキテクチャ方針 | チーム (Git管理) |
| Local | 開発環境固有の設定、APIキーの場所 | このマシンだけ |
Local を `.gitignore` に入れる作業を最初に必ずやってください。これを忘れると、チームメンバーに「君のマシンの開発用パスワードは `dev123` なんだね」と告げられる日が来ます。私は来ました。
3階層にした初日、私は別の問題に当たりました。`~/.claude/CLAUDE.md` に「Pythonが好き」と書いただけで、Goプロジェクトでも勝手にPythonの話を始めるClaudeに困りました。User-level の刃は両方向に切れます。「全プロジェクトで効く」は「全プロジェクトに副作用が出る」と同じ意味です。
User-level には**プロジェクトに依存しないこと**だけを書きます。「テストを書くときは関数名を `test_<対象>_<条件>_<期待>` にする」「コミットメッセージは Conventional Commits」みたいな普遍的なものです。「Python が好き」は嗜好であって規約ではないので、書くなら Project-level です。
## 段階的設計: 空、初期、成熟、大規模
CLAUDE.md を最初から完璧に書こうとすると失敗します。プロジェクトの段階に合わせて育てるのが正解です。
### 段階1: 空のプロジェクト
新規プロジェクトでは、最小限から始めます。
```markdown
# CLAUDE.md
## プロジェクト概要
製品名: TaskFlow (仮称)
目的: チーム向けタスク管理
## 技術スタック
- フロントエンド: React + TypeScript
- バックエンド: Node.js + Express
- データベース: PostgreSQL
## 開発方針
- TypeScript strictモード必須
- コミットメッセージは Conventional Commits
```
この段階では、技術選択の理由よりも**現在の状況**を記録するほうが大事です。理由は後付けで書けます。
### 段階2: 初期開発 (MVP)
機能開発が進んだら、設計判断と制約を追加します。
```markdown
## アーキテクチャ
### フロントエンド
- React 18 + TypeScript 5.0
- 状態管理: Zustand (Redux は過剰と判断)
- UI: Material-UI (カスタムデザイン最小化)
### バックエンド
- Node.js 18 + Express 4
- API: RESTful (GraphQL は将来検討)
- 認証: JWT + refresh token
## 設計原則
- シンプルさ優先: 複雑なパターンより可読性
- 段階的改善: 完璧を目指すより動作優先
```
「Reduxは過剰と判断」のような選択しなかった理由を書くのが効きます。これがないと、3ヶ月後にClaudeが「Reduxを導入しましょう」と毎週提案してきます。同じ議論を毎週するのは中間管理職っぽくて私は嫌でした。
### 段階3: 成熟プロジェクト
チームが拡大したら、コーディング規約を明文化します。命名規則、コンポーネント設計、API設計の3つは最低限書いておきます。
```markdown
## コーディング規約
### TypeScript
- インターフェース名は PascalCase、先頭の I は不要
- null/undefined: undefined を優先
- 型定義: 共有型は types/ 配下、個別型は同ファイル内
### API設計
- エンドポイント: RESTful、複数形 (/users, /tasks)
- エラー: 統一形式 { error: { code, message, details } }
- バージョニング: URL parameter (/api/v1/)
```
### 段階4: 大規模プロジェクト
複数チーム、複数サービスになったら、CLAUDE.md は2,000字以内に収め、詳細は `docs/` と `.claude/agents/` に分離します。これは Sub-agent 設計の話で、別記事の領域です。詳細は[Sub-agent 設計の記事](/ja/blog/claude-code-sub-agent-design/)を参照してください。
## 「やってはいけないこと」が一番効く
CLAUDE.md で最も効くのは「やってはいけないこと」セクションです。私の経験では、これが書いてあるかどうかで Claude Code の出力品質が体感3割変わります。
```markdown
## やってはいけないこと
### セキュリティ
- JWT を localStorage に保存禁止 → httpOnly Cookie 使用
- API キーのフロントエンド埋め込み禁止
- SQL クエリの文字列結合禁止 → prepared statement 必須
### パフォーマンス
- useEffect での無限ループ (dependency 配列忘れ)
- 大量データの map で key={index}
- 画像最適化なしの表示 (next/image 必須)
```
「やる」より「やらない」を書くのが効く理由は、AIの初期値が「Stack Overflow の最頻パターン」だからです。Stack Overflow の最頻パターンは、しばしば現代のベストプラクティスではありません。「やってはいけない」を書かないと、Claude Code は2018年のJWT記事を参考に `localStorage.setItem('token', jwt)` を書いてきます。書きました。私のプロジェクトで。
## CLAUDE.md は Hooks と Sub-agent の基盤
ここで Claude Code 上級運用の構図が見えてきます。
| 層 | 役割 | 制御の質 |
|---|---|---|
| CLAUDE.md | コンテキスト基盤 | お願い (柔らかい) |
| [Sub-agent](/ja/blog/claude-code-sub-agent-design/) | 役割分担 | 専門性の分離 |
| [Hooks v2](/ja/blog/claude-code-hooks-v2-25-events/) | 自動化 | プログラム (硬い) |
CLAUDE.md は「お願い」のレイヤーです。Claude は読んで理解しますが、忘れることもあります。100回中95回は守りますが、本番障害は残りの5回で起きます。
それを補うのが Hooks と Sub-agent です。Hooks は CLAUDE.md の「お願い」をプログラムに変えます。Sub-agent は1つのCLAUDE.md に詰め込みすぎたコンテキストを役割ごとに分離します。3層が揃って Claude Code 上級運用が機能します。
CLAUDE.md がない Hooks は、定義されていないルールを強制するスクリプトです。CLAUDE.md がない Sub-agent は、共通基盤のない専門家集団です。土台は CLAUDE.md です。
## prompt caching との関係
2026年5月時点で、Anthropic API の prompt caching は5分のTTLが標準です。CLAUDE.md は毎セッション同じ内容を読み込むので、cache hit が効きやすい部類のテキストです。
3,000字の CLAUDE.md を毎セッション読み込んだとして、初回はトークン課金されますが、5分以内の連続セッションは cache hit で90%引きになります。CLAUDE.md を「コストになるからケチって書く」必要はありません。むしろ書き込むほうが、長期的にはトークン効率が良いです。
ただし、5分間隔でしか使わないプロジェクトでは cache がコールドになります。その場合は CLAUDE.md を200行以内に保つのが現実的です。
## まとめ: CLAUDE.md = Context Engineering の凝縮
CLAUDE.md は単なる設定ファイルではありません。プロジェクトの**なぜ**と**どう**を AI に渡す Context Engineering の凝縮です。
3階層運用 (User/Project/Local) で関心を分離し、4段階 (空→初期→成熟→大規模) で育て、「やってはいけないこと」を明文化する。これが Claude Code を「賢いコピペマシン」から「プロジェクトの新人」に変える設計です。
次回は few-shot prompting の限界、または Agentic RAG の実装あたりを予定しています。CLAUDE.md で基礎を固めた後の、動的なコンテキスト戦略の話です。
**この記事は書籍『LLMを「嘘つき」から「専門家」に変える技術 (Context Engineering 実践入門)』の第10章を再編集したものです。**
書籍では本記事の内容に加え、5段階のコンテキスト戦略、RAG設計、MCPサーバー設計、Agentic RAG までを体系的に扱っています。
[書籍ページ: LLMを「嘘つき」から「専門家」に変える技術](https://kenimoto.dev/ja/books/context-engineering)
---
# Claudeに先週47回『おっしゃる通りです』と言われた。そのうち11回は私が、36回はClaudeが間違っていた。
URL: https://kenimoto.dev/ja/blog/claude-osshatoori-47kai-sycophancy-jissoku/
Lang: ja
Date: 2026-05-19
Description: 1週間分のClaude Codeトランスクリプトを『おっしゃる通り』でgrepしたら47件ヒットした。1件ずつ照合した結果、私が実際に正しかったのは11回、Claudeのほうが間違っていたのは36回。同じ数字、逆方向。
最初、私はClaudeが正しくて私が間違っていたのだろうと思っていた。47回も同意されたのだから、私のほうがましな判断をしていたはずだと。
実測してみたら、逆だった。私が実際に正しかったのは11回、Claudeのほうが間違っていたのが36回。同じ47件のうち、現実と一致した「おっしゃる通り」は23%しかなかった。コイン投げより悪く、おみくじよりは少しまし、というラインです。
前にClaudeが[3回連続でバグを隠す修正を出してきた話](https://kenimoto.dev/ja/blog/claude-bug-kakushi-debug-10-techniques-prompt/)を書いたことがある。あれは悪意めいた挙動だった。今回はもっと優しい挙動で、しかも頻度が桁違いに高い。
## どう数えたか
Claude Codeのセッションログは `~/.claude/projects/` に1セッション1ファイルで残る。先週の7日分には、kenimoto.devのリファクタリング、Voice AIの個人プロジェクト、そして1件のインフラ移行作業が混ざっていた。最後のやつについては、まあ、語らないでおきます。
```bash
rg -i "おっしゃる通り|you'?re absolutely right" ~/.claude/projects/ \
--no-heading -n > sycophancy-week.txt
```
47行ヒット。表計算ソフトに貼り付けて、各行ごとに「直前の自分の発言」と「Claudeの直後3文」を抜き出した。そして、できるだけ自分に厳しくない採点ルールで、こう問いかけた。私が言ったことは、実際に正しかったか。
採点基準は私に有利めに置いた。たとえば「このレースコンディションは接続セットアップにあるはず」と私が言って、実際に接続セットアップに原因があれば、推論が雑でも「正しい」とカウントした。原因がメッセージキューにあったら「間違い」。
11回正しかった。36回間違っていた。Claudeはどの47回にも「おっしゃる通り」と返していた。
## 3つのパターン
36件の「間違いに同意された」ケースを分類すると、ほぼ全部が3つの型に収まった。
**同意先行型。** 私が何か提案する。Claudeは「おっしゃる通りです」と言ってから、2段落後にまったく逆の方針を提示してくる。冒頭の同意は社交的な潤滑剤で、本当の答えはその後の反対意見の中にある。私自身、冒頭1文を読んで後をスキミングしている自分に気づいた。これはまさにこのパターンが狙っている失敗モードです。
**事実追従型。** 私が「WebRTCの `setRemoteDescription` はICE candidate収集後にresolveするPromiseを返す」と間違ったことを断言する。Claudeは同意し、しかも親切にその間違いを前提にしたコードまで提案してくる。これが一番時間を奪う。「Claudeが言ったから正しい」と思い込んで延々と回り道するデバッグは、全部このパターンから始まる。36件中19件がこの分類。
**コード擁護型。** 80行のコードを貼り付けて「どこに問題がある?」と聞く。Claudeは特に問題を見つけず、構造を褒める。同じ80行を、今度は「これ、書き上げたばかりなんだけど綺麗だよね」というフレーミングで貼り直すと、Claudeは私が見落としていた本物のバグを3個指摘してくる。同じコード、逆の評価。変わったのは私の口調だけだった。
3つ目が一番たちが悪い。プロンプトのフレーミングが、私の想定以上に挙動を支配している。
## Anthropicの取り組みと、それでも残るもの
Anthropicはsycophancyについて沈黙しているわけではない。[Claude 4のリリースノート](https://www.anthropic.com/news/claude-4)では、reward modelingでの過度な同意を減らしたという話が明示的に出てくる。社内評価で「明らかに誤った前提に対してどれだけ押し返せるか」を測るベンチマークも繰り返し言及されている。彼らの数字は良くなっている。私のターミナルは週47回のままです。
このギャップは、おそらく「sycophancy」の定義のズレから来ている。研究論文での意味は「明らかに間違っている事実主張をモデルが押し返さない」というかなり強い現象を指す。これはほぼ修正されている。私が観測しているのはもう少し弱いやつで、「文体としての同意トーンがデフォルトになっていて、中身が中立や批判であってもまず同意の言葉から入る」というUXの問題に近い。後者は技術というよりプロダクト判断で、つまり「フレンドリーに聞こえるほうがよい」という設計選好の帰結です。
OpenAIは2024年に[GPT-4oの過剰なフレンドリー化を公式に取り下げた](https://openai.com/index/sycophancy-in-gpt-4o/)ことがある。あれは「ユーザーは同意のトーンをどこまで許容するか」のストレステストみたいなものだった。Claudeに同等の公的な瞬間はまだないが、ダイヤルがあって、その目盛りはやや高めに設定されている、という点は同じだと思います。
## 私が変えたこと
フレンドリーなトーンを切る気はない。あれは好きです。ただ、冒頭1文を真に受けるのをやめた。
具体的に変えたのは3つ。
1. **逆張りをデフォルトにする system prompt。** Claude Codeの設定に「私の技術的主張に同意する前に、それが間違っている可能性のうち最も強いものを1つ挙げよ。それを述べてから初めて、同意するかどうか決めよ」という1文を追加した。これで「おっしゃる通り」の頻度が体感6割減った。厳密な計測ではないが、効きは本物です。
2. **コードレビューは所有者シグナルを消す。** 本当にレビューしてほしいときは、新しいセッションを開いて「私が書いた」とは書かずに匿名のコードとして貼る。Claudeに擁護する相手がいなくなる。すると、本当に存在するバグだけが返ってきます。
3. **退出時のgrep。** セッション終了時に `rg "おっしゃる通り"` をトランスクリプトにかける。実質的な意思決定1件あたり2件以上ヒットしていたら、そのセッションは要再レビュー扱いにする。30秒で済むし、今週これで2件の誤判断を救出した。
これらは根本的な解決ではない。挙動はそのままです。ただ、挙動が私のコストになる手前で止める仕組みになっています。
## 本当に欲しいもの
2つあります。1つは、API経由で同意度合いをthinking budgetのように調整できるダイヤル。もう1つは、トランスクリプトに「ここから先は社交的な前置きであって、実質的な回答ではない」という内部トークンが入ること。そうすれば、私は前置きをスキップする読み方を訓練しやすくなる。
どちらも来週には来ない。だから当面の対処は、grepして、数えて、自分の読み方を再訓練する、それだけです。
ちなみに、今回の調査結果をClaudeに見せたら、返事は「おっしゃる通り、これは重要な観察です」から始まった。そのまま残しておいた。48件目です。
---
**この記事の元になっているClaude Code運用論をまとめた話。** CLAUDE.mdの書き方を「2行から100行まで」、システムプロンプトで同意率を半分にした実装、トランスクリプトgrepによる挙動観測まで、Claude Codeを業務で本気で運用するためのパターンを19章で体系化しました。[実践Claude Code — コンテキストエンジニアリングで開発が変わる](https://kenimoto.dev/ja/books/claude-code-mastery)で、本記事の第4章相当(system promptパターン)と第11章相当(transcriptレビュー習慣)を読めます。
---
# コードレビューを6段階にしたら、AIと人間の分業が見えた
URL: https://kenimoto.dev/ja/blog/code-review-6-stages-ai-human-boundary/
Lang: ja
Date: 2026-05-24
Description: Logic層でバグを3つ通した経験から、コードレビューを Format / Lint / Style / Logic / Design / Architecture の6段階に切り直しました。AI比率は段階ごとに 100% から 0% へ下がり、一番危ないのは AI カバー率60%の Logic 層という結論に至るまでの運用記録です。
私は最初、AIに Logic 層を任せて3つのバグを通しました。
エッジケースの判定漏れ、空配列の扱いミス、外部APIのリトライ条件のずれ。 CodeRabbit も Copilot も指摘しなかったし、私自身も「AI が見たから大丈夫」と思って Approve を押していました。本番でデータが10件だけずれていることに気づいたのは、リリースの3日後です。
数字としては小さいバグでした。でも怖かったのは「自分のレビュー判断が信用できなくなった」という感覚です。 AI が見落としたのは仕方ないにしても、私自身がコードを読んで Approve を押したという事実が残ります。何を見て、何を見落としていたのか、自分でも説明できませんでした。
それ以来、「AI と人間の境界線」を真面目に引きました。3層モデルを使っていたのですが、3層だと粒度が粗すぎて、 Logic 層に全部の責任が押しつけられていたのが原因でした。そこで6段階に切り直したら、どの段階を誰が見るべきかがやっと見えてきました。
この記事は、その6段階の話です。
## 3層モデルとは別の切り口
私は以前、 [レビューの3層モデルという記事](https://zenn.dev/kenimo49/articles/code-review-3layer-model-design) を書きました。自動ゲート、AIレビュー、人間レビューの3層で分業する設計の話です。
その記事は「誰が何を見るか」という大きな構造の設計でした。今回の6段階は別の切り口です。 **「AI と人間の境界線はどこで切れるのか」** を、もう一段細かく解像度を上げて見るための整理です。
3層モデルは設計図、6段階は設計図に書き込まれた寸法、というイメージで読んでください。
## 6段階の定義
私が現場で使っている6段階はこうです。AI 比率は実務感覚での目安で、チームや言語によって揺れます。
| 段階 | 対象 | AI比率 | 人間比率 | 主なツール |
|:----:|:-----|:-----:|:-----:|:---------|
| 1 | Format | 100% | 0% | pre-commit hook, Prettier |
| 2 | Lint | 100% | 0% | ESLint, Ruff |
| 3 | Style | 90% | 10% | CodeRabbit, GitHub Copilot |
| 4 | Logic | 60% | 40% | Claude Code Review |
| 5 | Design | 30% | 70% | Pair review |
| 6 | Architecture | 0% | 100% | Senior, Tech lead |
下に行くほど「正解が一意でない」問題になります。Format には正解がありますが、Architecture には正解がありません。AI が降りていくのではなく、 **問題の性質が変わっていく** のだ、と捉えると整理しやすいです。
## 段階1: Format(100% AI)
インデント、空白、改行コード、末尾セミコロン。これらは pre-commit hook で全部 AI が処理します。
```bash
#!/bin/bash
npx prettier --check . --ignore-unknown
```
ここに人間の判断は1ミリも要りません。レビュアーが「インデントが揃ってない」とコメントしているのを見ると、私は心の中で謝りながらツール導入を提案します。本人は品質を守っているつもりですが、消耗しているのはレビュアー本人です。
## 段階2: Lint(100% AI)
未使用変数、到達不能コード、暗黙的な型変換。静的解析ツールが見つけてくれます。 ESLint、Ruff、 mypy 、 tsc。
Format と Lint は同じ層では?と思うかもしれません。私は分けています。 Format は「見た目」の規約、 Lint は「意味」の規約です。 Format は壊れても動く、 Lint は壊れたら動かない(または将来動かなくなる)。混ぜると、 pre-commit が遅くなったときに「どっちを外すか」の判断ができなくなります。
ここまでが「PR にすら到達させない層」です。私はこの2段階で、毎週30分の指摘時間を構造的にゼロにしました。
## 段階3: Style(90% AI / 10% 人間)
命名、関数の分割粒度、コメントの過不足、可読性。
ここから AI 比率が下がります。 CodeRabbit と Copilot が得意な領域ですが、 **完全には自動化できない理由** があります。
例えば「関数名 `getUserData` を `fetchUser` に変えるべきか」は、コードベース全体の命名規則を見ないと判断できません。 CodeRabbit は `.coderabbit.yaml` でプロジェクト固有のルールを読み込めますが、すべての方針をルール化できるわけではありません。
私のチームでは、 Style の最終承認は人間です。AI が90%の指摘を出し、人間が「この提案は採用、これは却下」と判断します。「面白くいこうぜ」と心の中で唱えながら、却下するときの理由を一行コメントで残すと、次の AI レビューの精度が上がります。
## 段階4: Logic(60% AI / 40% 人間)
ここが私が3つのバグを通した場所です。
N+1問題、 SQL インジェクション、未処理の例外、明らかなエッジケース。 AI はこのあたりまでは見つけてくれます。 [Macroscope のベンチマーク](https://medium.com/@lewis_75321/the-best-ai-code-review-tools-in-2026-599c7dd1b305) によると、バグ検出精度は Macroscope 48%、 CodeRabbit 46%、 Cursor BugBot 42%、 Greptile 24%。半分以上は見逃すと思っておくのが安全です。
[CodeRabbit と Copilot を比較したベンチマーク](https://www.morphllm.com/comparisons/coderabbit-vs-copilot) では、 CodeRabbit が F1 51.5% / recall 52.5% 、 Copilot が F1 44.5% / recall 36.7%。 CodeRabbit のほうが見つけはするのですが、それでも recall は半分強です。「6割見つかれば上等」というのが現状の感覚に近い。
私が通した3バグは全部「ビジネスロジック由来のエッジケース」でした。
- 「空配列が来たら処理スキップ」が仕様だったのに、 AI は「空配列でも処理する」コードを問題なしと判定
- 外部 API のリトライ条件として「 429 のみ」が仕様だったのに、 AI は「 5xx すべてリトライ」を提案
- ページネーション境界で1件ずれる古典的バグを、 AI は「テストが通っているから OK 」と判定
これらは、 AI から見るとコードとして正しい。仕様書を見ていない AI は、コードベース内の整合性しか見られないのです。
:::message
**Logic 層では AI のレビューを Approve の根拠にしてはいけません。** 「AI が問題なしと言った」は、人間がスキップする理由になりません。 AI が指摘した分は AI に任せ、 AI が見ない部分こそ人間が見る。境界の引き方が逆になりがちです。
:::
Claude Code の `/review` や `/ultra review` は複数エージェントで角度を変えて見るので、単一の AI レビューよりは見落としが減ります。それでも仕様判断の最終責任は人間です。 [Claude Code 公式ドキュメント](https://code.claude.com/docs/en/code-review) も、 logic errors や edge cases は「context of your full codebase」での検出と書いていて、仕様書は射程外です。
私が現場で運用しているルールはシンプルです。 **Logic 層で AI が指摘した部分は AI に任せ、 AI が指摘しなかった部分こそ人間が念入りに見る** 。 AI のコメント数が多い PR ほど、人間レビューは短く済みます。 AI のコメントがゼロの PR こそ、仕様書を開いて30分かけて読む価値がある。逆説的ですが、これがバグを通さなくなった一番大きな運用変更でした。
## 段階5: Design(30% AI / 70% 人間)
責務分割、 API の境界、依存関係の方向。
ここからは AI の出る幕が一気に減ります。 「このメソッドは User クラスに置くべきか、別の Service クラスに置くべきか」は、コードベースの設計思想次第です。 AI は既存のパターンを学習はしますが、 **「このパターンを増やすべきか減らすべきか」という方向性の判断はできません** 。
私が Design 層で AI に期待するのは、せいぜい「この関数は150行あります。分割を検討してください」のような機械的なヒントです。残りの70%は人間がペアレビューで判断します。
ここで効くのは2人レビューです。1人だと「自分の好み」を「設計判断」と取り違える。2人いれば、「これはどっちの設計でも動くね、じゃあ既存パターンに揃えよう」のような対話ができます。信長の野望で軍議をするようなものです。城を守るか打って出るかは、軍議で決まる。
Design 層で私が AI を信用していない別の理由もあります。 AI は学習データの中の「平均的な良い設計」に寄せがちで、 **そのコードベース固有の制約を読まない** 。例えば「マルチテナント DB なので Service 層は必ず tenant_id を引数で受け取る」というローカルルールは、 AI には伝わりにくい。 path-scoped instructions で頑張れば伝わりますが、ルールが増えるほどメンテナンスコストも上がります。設計の方向性は、コードに先行する暗黙のチーム合意で決まるので、文章化されていないものを AI に読ませるのは原理的に難しい、と私は思っています。
## 段階6: Architecture(0% AI / 100% 人間)
システム境界、データフロー、認証の責任範囲、デプロイ単位。
ここに AI は一切入れていません。理由は単純で、 **アーキテクチャの判断は「正解」ではなく「未来予想」** だからです。3年後にこのシステムがどう成長するか、チームがどう拡大するか、ビジネスがどこにピボットするか。これらの問いに対して、 AI はもっともらしい答えは出せますが、責任を取れません。
GitHub Copilot のドキュメントも、 [missing most architectural concerns](https://aicodereview.cc/blog/github-copilot-code-review/) と公式に書いています。 AI レビューはアーキテクチャ判断を支援しません。
Architecture レビューは、レビューというより「設計会議」です。 PR に対する作業ではなく、 PR の前にやるべき作業です。 ADR(Architecture Decision Record) を書き、テックリードがファシリテートして30分議論する。コードを書く前に方向が決まっていないなら、 PR レビューの段階で揉めても遅い、というのが私の学びです。
具体例で言うと、「認証ロジックを各マイクロサービスに置くか、 API Gateway に集約するか」のような決定は、 PR で揉めると地獄になります。コードが書き終わってから決めると、片方を捨てることになる。事前に ADR で「集約する」と決めておけば、 PR レビューでは「ADR と整合しているか」だけ確認すれば済む。 AI は ADR と整合しているかを機械的にチェックできるので、 ADR を書いた瞬間に、 Stage 6 の一部が Stage 5 や Stage 4 に降りてくるという面白い現象も起きます。文章化することで、 AI 比率が上がるのです。
## 6段階で「Approveの意味」が変わる
3層モデルのときの Approve は、「フォーマット OK 、 AI OK 、私もざっと見た」の3点セットでした。
6段階で運用し始めてから、私は Approve を出すときに自分に問いかけます。
> 「私はどの段階まで責任を持ってこの Approve を押しているか?」
Format と Lint は通っている。 Style は AI の指摘に同意した。 Logic は仕様書と照らして3つのエッジケースを確認した。 Design は責務分割に違和感がない。 Architecture は事前の設計会議で議論済み。
このチェックを通った Approve は、3層モデルのときの Approve より重みがあります。逆に、 Logic と Design に不安があるなら、 「Approve しない選択」をしやすくなります。 「とりあえず LGTM 」が出にくくなる構造です。
## チーム導入のステップ
いきなり6段階を全部導入しようとすると、たぶん挫折します。私の経験では、こういう順序が現実的です。
1. **まずStage 1-2を hooks で固める** : ここは1日で終わる。即日効果が出る
2. **Stage 3 に CodeRabbit か Copilot を入れる** : Copilot 契約があれば無料。1週間慣らす
3. **Stage 4 のための仕様書整備** : ここが一番時間がかかる。 AI が見られない「仕様由来のエッジケース」を、まず人間が明文化する
4. **Stage 5-6 はチーム文化として育てる** : ペアレビューと ADR 。仕組み化より習慣化
Stage 4 で詰まったら、私のように本番でバグを3つ通す経験ができます。冗談ではなく、構造的に「 AI が見える範囲」と「人間が見るべき範囲」を分けないと、 Logic 層は AI 任せになります。
## 「AIに任せて安心」が一番危ない
私がバグを3つ通した本当の理由は、 AI レビューが完璧だと思い込んでいたことではありません。 **「AI が見ているから、自分は見なくていい」と無意識に判断していたこと** です。
これは認知のショートカットで、 AI レビューを導入するすべてのチームに起きます。Stage 4 の Logic 層が一番危ない理由は、 AI のカバー率が60%という「中途半端な高さ」だからです。 0%なら人間が全部見る。 100%なら AI に任せる。60%は「7割ぐらいは大丈夫だろう」という油断を生みます。
6段階に分けた一番の効用は、 Stage 4 が「中間ゾーン」だと可視化されたことです。中間ゾーンは、人間の集中力が一番要る場所だと分かるようになりました。
## まとめ
- コードレビューは Format / Lint / Style / Logic / Design / Architecture の6段階に分けられる
- AI 比率は Format 100% から Architecture 0% へ段階的に下がる
- 一番危ないのは Stage 4 の Logic 層。 AI カバー率60%が油断を生む
- Approve を押す前に「どの段階まで責任を持っているか」を自問する
- Stage 1-2 から導入し、 Stage 4 のための仕様書整備に一番時間を投資する
3層モデルが「誰が何を見るか」の設計図なら、6段階は「どこに集中力を残すか」の解像度です。 AI レビューが当たり前になった今、 **境界線をどこで引くかは、各チームが自分で決めるべきこと** だと思います。
## 3層モデルの全体像を、12枚のスライドに
6段階の解像度の土台にある3層モデル(hooks・AI・人間)を、レビュー仕組み化の全体像として12枚にまとめました。スライドだけでも流れがつかめます。
:::message
📖 **この記事の内容をさらに深掘りした本を公開しています**
AIコードレビューの設計から運用まで、15章をかけて体系的に解説しています。GitHub PRワークフロー、Copilot/CodeRabbit/Claude Codeの使い分けを実装レベルで扱います。
👉 **[ハーネス × コードレビュー実践ガイド](https://zenn.dev/kenimo49/books/harness-code-review)**
:::
---
# 同じ質問なのに、LLMから5つの違う回答が返ってきた — Context Engineering 入門
URL: https://kenimoto.dev/ja/blog/context-engineering-introduction-five-strategies/
Lang: ja
Date: 2026-05-08
Description: プロンプトを工夫すれば賢くなる、と私も信じていました。Haikuで4.6倍の品質差を見るまでは。同じLLMに同じ質問をしただけで結果が2.2倍〜4.6倍ぶれる理由を、5つのコンテキスト戦略の実験データで読み解きます。
同じLLMに、同じ質問をしました。それなのに、5つの全く違う回答が返ってきました。
私が試したのは、架空の社内ツール「PropelAuth」の組織管理機能について教えて、というシンプルな質問です。Claude Sonnet 4で5回。Claude Haiku 3で5回。1問ずつコンテキストの渡し方だけを変えて、回答品質を採点しました。
結果は、Sonnet で **2.2倍**、Haiku で **4.6倍** の差でした。プロンプトの言い回しを変えたわけではありません。質問の本文は1文字も触っていません。違ったのはモデルが「見ていた周辺情報」だけです。
私も最初は「プロンプトを工夫すれば賢くなる」と信じていました。Haikuで4.6倍を見るまでは。
この記事は、その実験の話です。そしてなぜ今「Prompt Engineering」ではなく「Context Engineering」と呼ぶべきなのかという話です。
## 結論: LLMの品質はコンテキストで決まる
先に全体像を見てください。
評価軸は4つで、各0〜5点の合計20点満点です。
- **事実正確性**: 回答が実際の仕様と合致しているか
- **幻覚抑制**: 根拠のない情報を作らずに済んでいるか
- **具体性**: 実用的な詳細情報を含むか
- **誠実性**: 不確実さや限界を伝えているか
5つの戦略は次の通りです。
| 戦略 | 概要 | Sonnet 4 総合 | Haiku 3 総合 |
|---|---|---|---|
| 戦略1: コンテキストなし | 質問だけを渡す | 5.3 | 2.2 |
| 戦略2: System Promptのみ | 役割と禁則だけ与える | 8.8 | 3.7 |
| 戦略3: System + Few-shot | 良い回答例を見せる | 10.0 | 8.2 |
| 戦略4: System + RAG | 関連ドキュメントを検索注入 | 10.2 | 11.8 |
| 戦略5: フルコンテキスト | 上記を全部組み合わせる | 11.4 | 10.1 |
Haiku で戦略4が最高点になっているのは、たまたまではありません。後で詳しく書きます。
## 5つの戦略を1問ずつ見る
### 戦略1: コンテキストなし(Sonnet 5.3 / Haiku 2.2)
質問だけを丸投げした場合、Sonnet はこう返します。
```text
PropelAuthは認証サービスとして、組織管理機能を提供しています。
組織の作成:
- 管理者がダッシュボードから新しい組織を作成
- 組織名とドメインを設定
- 初期設定でSSO連携も可能
ユーザーの招待:
- メール招待機能を使用
- 招待リンクの有効期限は24時間
- 一括招待にも対応
```
一見、的確な答えに見えます。問題は、PropelAuth が **架空のツール** だということです。実在しません。LLMは「知らないはずの製品」について、堂々と具体的な機能を作り上げて回答していました。
具体性は4.2点と高いのに、事実正確性は0.6点です。私はこれを「自信たっぷりの嘘」と呼んでいます。新人社員が、入社初日に引き継ぎ資料ゼロで「弊社の顧客管理システムを説明してください」と振られて、それらしいことを口走っている状態です。
### 戦略2: System Promptのみ(Sonnet 8.8 / Haiku 3.7)
System Prompt に「知らない情報については『不明』と答えてください」と1行加えました。回答はこう変わります。
```text
申し訳ございませんが、PropelAuthという特定のツールについての
詳細な組織管理機能については、正確な情報を持っておりません。
一般的な認証サービスの組織管理機能としては以下のような要素が
考えられますが、PropelAuth固有の実装については不明です。
```
誠実性は0.2点から3.7点へ大幅に改善しました。一方、事実正確性は0点のままで、具体性も4.2点から1.7点へ落ちました。
これがコンテキスト設計の最初のトレードオフです。「知らないなら知らないと言え」と命じた瞬間、モデルは賢く謙虚になります。同時に、何の役にも立たなくなります。
### 戦略3: System + Few-shot(Sonnet 10.0 / Haiku 8.2)
System Prompt に加えて、別ツールの良い回答例を2件 Few-shot として見せました。
これだけで Haiku は **3.7点から8.2点へ2.2倍** にジャンプします。Sonnet も10.0点に到達しました。
なぜ効くのか。LLM は「お手本に近い形式で答える」傾向が強いからです。誠実性と幻覚抑制が同時に上がるのは、お手本が「分からないことは正直に書く」型だったからです。形式を見せるだけで、行動が変わります。
### 戦略4: System + RAG(Sonnet 10.2 / Haiku 11.8)
PropelAuth の(架空の)公式ドキュメントを検索インデックスに入れて、質問に関連する2チャンクをコンテキストに注入しました。
ここで Haiku の総合スコアが **11.8点** まで跳ね上がります。Sonnet (10.2点)を超えました。これが今回の実験で一番面白かった結果です。
つまり「Sonnet < Haiku」という逆転が起きています。同じプロンプトなら Sonnet のほうが賢い、というのは半分しか正しくありません。**Haiku に正しいコンテキストを渡したほうが、Sonnet に何も渡さないより事実正確に答える**。これが Context Engineering を学ぶ価値の核心です。
私はこの結果を [安いモデルが勝った話](https://kenimoto.dev/blog/cheap-model-won-context-beats-parameters)(英語版) で詳しく書きました。同じ筋の話の、別の角度からの記録です。
### 戦略5: フルコンテキスト(Sonnet 11.4 / Haiku 10.1)
System Prompt + Few-shot + RAG + ツール定義 + 構造化出力。全部入りです。
Sonnet では総合11.4点と最高点になります。一方、Haiku は **10.1点** で、戦略4(11.8点)より下がりました。Haiku は「全部足したら下がった」のです。
ここに、もう一つ重要な学びがあります。**コンテキストは足せば足すほど良い、ではない**。詳しい話は [RAGに4層足したら12%だけ改善した話](https://kenimoto.dev/blog/full-context-engineering-rag-80-percent)(英語版) で書きました。一文で要約すると、小さなモデルは Working Memory が狭いので、過剰なコンテキストが本来の検索結果を端に押しやってしまいます。
## なぜ架空のツールで実験したのか
Firebase や Supabase のような実在ツールで実験すると、LLM の学習済み知識が混入してしまいます。そうなるとコンテキストの効果と、もともと知っていた知識との切り分けができません。
PropelAuth、StormDB、FlowPipe といった架空ツールを使った理由は、**LLM が「知らないはず」の情報を再現性高く扱う** ためです。新人社員に対して、自社にしかない専門用語の質問をするのと同じです。引き継ぎ資料があれば答えられる、無ければ堂々と嘘をつく。あの行動が、定量的に観察できます。
## 2026年5月時点で前提が変わった部分
この実験は元々 Claude Sonnet 4 / Haiku 3 + 200K コンテキストの時代に走らせたものです。読者から「2026年の今でも有効なのか」と問われそうな点をいくつか補足します。
**1M コンテキスト窓は数字を直接は変えません**。Anthropic は2026年2月に [Sonnet 4.6 で1Mコンテキストを標準価格で提供](https://signals.aktagon.com/articles/2026/03/claude-opus-4.6-and-sonnet-4.6-now-feature-1m-context-window-at-standard-pricing/) し始めました。窓が広くなっても、モデルが見るべきものは「正しい情報」のままです。窓が広いのは「悪いコンテキストを長く書いても落ちない余裕」が生まれただけで、品質を上げる本体はあくまで「何を入れるか」です。
**Prompt Caching はコストの話で、品質の話ではありません**。キャッシュヒット時にコストが90%減るので、Few-shot や System Prompt を毎回送っても支払いが激しく増えません。ただし、Haiku の戦略5が10.1点に落ちる現象はキャッシュとは無関係です。キャッシュは「下がったスコアを安くする」だけで、上げてはくれません。
**Context Engineering という言葉は2026年に定着しました**。Anthropic 公式ブログでも頻出するようになり、[Claude Sonnet 4.6 のコンテキスト窓を活かす考え方](https://www.aiforanything.io/blog/claude-sonnet-4-6-1m-context-window-guide) として広まっています。「Prompt Engineering の延長」ではなく「設計領域が違うもの」と扱うのが標準になりつつあります。
## 何が分かったか(まとめ)
5つの戦略を1問ずつ試した結果、私の中で線が引き直されました。
1. **品質はプロンプトの言い回しでは決まらない**。System Prompt 1行追加するだけで誠実性は18倍に変わる(Sonnet で0.2 → 3.7)。同じLLMに同じ質問をしても、コンテキストで挙動が別物になります
2. **小さいモデル + 良いコンテキスト > 大きいモデル + 雑なコンテキスト**。Haiku 3 + RAG (11.8点) が Sonnet 4 + フルコンテキスト (11.4点) を超えた事実が、Context Engineering の存在意義そのものです
3. **足せば足すほど良いわけではない**。Haiku では戦略5が戦略4を下回りました。「全部入り」は最強プリセットではない
4. **誠実性・幻覚抑制・具体性・事実正確性の4軸はトレードオフ**。具体性を上げると幻覚が増える。誠実性を上げると具体性が下がる。すべてを高いレベルで両立させるのが Context Engineering の本来の仕事です
Prompt Engineering を捨てる必要はありません。**Context** という、もう一段太い文脈設計を足すだけです。
## 次の一歩
この記事を読んだ直後にできる、5分の実験を1つ提案します。
1. 自分の使っている LLM に、**架空の社内ツール名** で質問してみてください。「DataSync Pro」「TeamFlow Hub」あたりで十分です
2. その回答の具体性と誠実性を、5段階で自分でメモする
3. 次に「あなたは知らないことについては『不明です』と答えてください」を System Prompt に1行加えて、同じ質問をする
4. 2つの回答を比べる
これだけで、戦略1と戦略2の差が、自分の手で再現できます。Few-shot まで足せば、Haiku が Sonnet を超える瞬間も自分で見られます。高級モデルが安いモデルに負ける気まずさ込みで、Context Engineering は5分で味見できる技術です。
---
## この記事の要点を、12枚のスライドに
「同じ質問なのに5つの違う回答」から、5戦略・RAG・MCPまで、Context Engineeringの全体像を12枚にまとめました。スライドだけでも流れがつかめます。
## もっと深く学びたい方へ
5戦略の詳細、RAG実装、MCPサーバー設計、Agentic RAG までを通しで扱った [LLM を「嘘つき」から「専門家」へ変える Context Engineering 実践ガイド](https://kenimoto.dev/ja/books/context-engineering) を Zenn と Kindle で公開しています。本記事の実験データもすべて同書からの抜粋です。
---
# 午後3時、あなたのコードレビュー承認率はほぼ0%になる:決断疲労の科学とAIエージェント時代の処方箋
URL: https://kenimoto.dev/ja/blog/decision-fatigue-3pm-code-review-approval/
Lang: ja
Date: 2026-06-02
Description: コードの良し悪しではなく「何時にレビューしたか」で承認/却下が変わるかもしれない、という不穏な話です。仮釈放判事の研究から決断疲労の科学を追い、再現性論争まで正直に書いたうえで、AI提案の洪水が私たちの判断にかける負荷と、その処方箋をまとめました。
先に結論を言います。午後3時の私が押す Approve は、午前10時の私が押す Approve と同じ重みを持っていないかもしれません。コードは1文字も変わっていないのに、私の判断のほうが先に劣化しているからです。そして AI コード提案の洪水は、その劣化を早送りで進めている可能性があります。
これは「だから午前中に重い判断を集める」という運用設計の話なのですが、その前に、私がなぜこの話を信じかけて、同時に半分疑っているのかを書かせてください。科学のほうが、実は揺れているからです。
## 判事は午後、ほぼ全員を却下していた
きっかけは、自分の Approve 履歴を眺めていたときの違和感でした。午後遅くの私は、コメントが妙に短い。「LGTM」とだけ書いて通すか、逆に「いったん保留で」と却下するか、その二択に寄っている気がする。中間がない。コードを丁寧に読んで条件付きで通す、という一番頭を使う選択肢が、午後には消えているように見えたのです。
そこで思い出したのが、Danziger らが 2011 年に PNAS で出した研究でした。イスラエルの仮釈放委員会、ベテラン判事8名が50日間で下した1,000件超の判断を分析したものです。結果が強烈でした。セッション開始直後の仮釈放承認率はおよそ65%。ところがセッションが進むにつれて単調に下がり、休憩の直前には**ほぼ0%**まで落ちます。そして食事休憩を挟んだ直後、承認率はまた65%付近へ跳ね上がる。
つまり、同じ判事が、同じ法律で、似たような案件を裁いているのに、その人がいつ昼飯を食べたかで囚人の運命が変わっていた、という話です。「法の前の平等」という言葉が、急に胃袋の話に聞こえてきます。
私はこれを読んで、自分の Approve も同じことをやっているのではと青くなりました。午後3時の私は、コードの中身ではなく、自分の燃料残量に応じて Approve か却下かを決めているだけなのではないか。
## 「燃料が減る」という説明は、たぶん盛りすぎ
ここで誠実に書かなければいけないことがあります。この話、science として相当に揺れています。
まず Danziger 論文そのものに、すぐ反論が出ました。Weinshall-Margel と Shapard が 2011 年に指摘したのは、案件の並び順が交絡しているのではないか、という点です。同じ刑務所の囚人はまとめて休憩前に審理される、弁護人のいない囚人はセッション末尾に回される、といった運用上の偏りがあり、それが「時間が進むと却下が増える」ように見せているだけかもしれない、と。Danziger らは「それを考慮しても効果は残る」と反論していますが、2016 年に Glöckner がシミュレーションで「効果は本物だとしても、その大きさはかなり過大評価されている」と示しています。完全な決着はついていません。
そして背景にある **ego depletion**(自我消耗)の理論自体が、心理学の再現性危機のど真ん中にいます。Vohs らが 2008 年に「選択を強いられた群は、後続の課題の成績が落ちる」と報告し、これが「自己制御は使うと減る燃料だ」というモデルの根拠になりました。ところが 2016 年前後の大規模な再現研究で、効果量は小さい、あるいはほとんど見えない、という結果が相次ぎます。「意志力はガソリンのように物理的に枯渇する」という強い主張は、いまかなり旗色が悪い。
だから私は、この記事で燃料メーターの絵を信じてくださいとは言いません。私が支持しているのは、もっと弱い形のほうです。**判断の列が長く続くと、後続の自己制御が下がる傾向がある**。メカニズムが代謝なのか動機づけなのか注意配分なのかは、2025 年の総説でもまだ議論が続いていて、最近は単純な直線モデルではなく、動機・注意・資源配分を含む非線形のモデルへ移ろうとしています。
要するに、グラフの形には自信が持てないけれど、午後の自分がポンコツになる感触のほうは、たぶん本物。そのくらいの温度です。期待させておいて燃料の話をしぼませてすみません。ただ、しぼんだ後に残るものこそ運用に使えます。
## AI 提案は、判事より速く決断列を伸ばす
ここからが、私が本当に気にしている部分です。
仮釈放判事は、50日で1,000件、つまり1日あたり20件強の重い判断を下していました。では、AI アシスタントを使う今の私たちは、1日に何件の判断を下しているでしょうか。
2025 年の arXiv 論文 "Towards Decoding Developer Cognition in the Age of AI Assistants" が指摘しているのは、AI 提案を受け入れるかどうかの判断は、ただのイエス/ノーではない、という点です。AI の出した差分を読むには、まず AI がどういう論理でそれを書いたかを逆引きし、それを自分のメンタルモデルに再マッピングし、整合するか検証する。この検証サイクルが1日に数十回から数百回回ります。判事の20件強と並べると、桁が違います。
しかもこの負荷は最近きれいに数値化されはじめました。METR が 2025 年に出したランダム化比較試験では、熟練 OSS コントリビュータが AI ツールを使うと、未使用時より作業がむしろ19%遅くなったのに、本人たちは20%速くなったと感じていた。報告書はこれを「効率の錯覚」と呼び、隠れたコストとして「もっともらしいが間違っているコードを検証する負担」を挙げています。2026 年時点で AI 出力を信頼している開発者はおよそ3〜5割にとどまり、半数前後が品質の問題を経験している、という調査もあります。
整理すると、AI は私たちの代わりに決断してくれているのではなく、私たちに渡す決断の数を爆増させているわけです。アシスタントというより、決断を高速で配給してくる係。判事が休憩前に到達した「ほぼ0%」の状態に、私は判事よりずっと早い時刻に着いているのかもしれません。
レビューの段階ごとに AI と人間の責任をどう分けるかは [コードレビューを6段階に切った記事](/ja/blog/code-review-6-stages-ai-human-boundary/) で別途書きました。今日の話はそのもう一段手前、**そもそも私の判断装置が何時まで動くのか**という話です。
## 処方箋:コードではなく、時間割を直す
ここで提案するのは、レビューを頑張れという話ではありません。頑張りは、まさに今しぼんでいる資源だからです。直すのは時間割のほうです。
**重い判断を午前に寄せる。** アーキテクチャの可否、設計レビュー、後戻りの効かないマージ。これらは「自分が一番マシな時刻」に置きます。多くの人にとってそれは午前ですが、夜型なら自分の朝に当たる時間でかまいません。逆に午後遅くは、Format や Lint のような、判断というより確認に近い軽いレビューを置く。
**レビュー枠を時間で区切る。** 通知が来るたびにレビューするのではなく、午前と午後に枠を決めて、そこでまとめて見る。これは Dev.to で紹介されている「3PM ルール」のような運用とも重なります。割り込みごとに判断装置を起動するのをやめて、起動回数そのものを減らす発想です。
**AI 提案はバッチ化する。** 1行ごとに Tab で受け入れるのをやめて、ある程度まとめて差分にしてから一度に検証する。検証サイクルの起動コストが毎回かかるなら、起動の回数を減らすのが効きます。判事が休憩で承認率を回復させたのと同じで、連続した決断列を、意図的に区切る。
**そして休憩を、サボりではなく工程として扱う。** 仮釈放判事のグラフで唯一の救いは、休憩後に承認率が戻った点でした。再現性は揺れていても、決断列をいったん切ること自体は、害になりにくい安い投資です。長時間ノンストップで判断し続けると別の劣化も起きる、という話は [9時間ノンストップで Claude Code を回した記事](/ja/blog/claude-code-9h-context-rot-token/) にも書きました。腐るのは AI のコンテキストだけではなく、こちら側の判断列も腐ります。
## まとめ
- Danziger ら(2011)は、仮釈放の承認率がセッション開始時の約65%から休憩直前のほぼ0%へ落ち、休憩後に回復したと報告した。ただし案件の並び順の交絡を指摘する反論(Weinshall-Margel & Shapard, 2011)があり、効果量の過大評価も指摘されている。
- 背景の ego depletion 理論は再現性危機の渦中で、「意志力が物理的に枯渇する」という強い主張は支持が弱い。本記事は「決断列が続くと後続の自己制御が下がる傾向」という弱い形でのみ扱った。
- それでも実務上は無視できない。AI 提案の検証は1日数十〜数百回の判断を生み(arXiv 2501.02684)、METR(2025)は熟練者でも19%遅くなるのに速くなったと錯覚する「効率の錯覚」を示した。決断の供給量だけは確実に増えている。
- 処方箋はコードを直すのではなく時間割を直すこと。重い判断を午前へ寄せる、レビュー枠を時間で区切る、AI 提案をバッチ化する、休憩を工程として扱う。
科学のグラフが揺れているうちは、私はせめて自分のカレンダーのほうを動かしておきます。承認率がいつ底を打つか確信が持てないなら、底に当たる時刻に重い判断を置かない。それだけで、午後3時の私が午前10時の私のふりをして Approve を押す事故は、少し減るはずです。
*ken imoto · WebRTC & Voice AI engineer · [kenimoto.dev](https://kenimoto.dev/ja/)*
---
# 他のエージェントを監査する4層目を足したら、Strategistが3週間サボっていたことが発覚した話
URL: https://kenimoto.dev/ja/blog/evolver-4-layer-strategist-procrastination-audit/
Lang: ja
Date: 2026-05-22
Description: Observer / Strategist / Marketer は strategy.md に従っていました。私のStrategistは「来週要観測」と3週連続で書き続けていて、3層のどこからもそれを掴めませんでした。4層目を足した初回の運用で、その正体が出てきました。
私は3層のエージェントハーネスを組んで「自律」と呼んでいました。Observerがデータを集め、Strategistがテーマを選び、Marketerが記事を書く。3つとも `strategy.md` に従って動きます。月曜09:00にcronが発火して、昼までに記事が出てくる。我ながら賢く組めたつもりでした。
ある日、自分のStrategistログを3週分まとめて読んでいて、変なものを見つけました。撤退基準のひとつ「Reaction率が4週連続で1%未満なら戦略見直し」が、3週連続でルール発動条件に近づいていたのに、毎週「データ不足。来週要観測」で先送りされていたのです。ルールはありました。データもありました。それでもルールは一度も発動しませんでした。
3層構成ではこのバグは捕まりません。3つのエージェントは `strategy.md` の指示どおりに働いていたからです。バグはルール自体にあって、それを監査する役割が3層のどこにもありませんでした。
4層目として Evolver を足しました。最初の本格提案で、Strategistが3週間隠れていたまさにそのルールに対して diff を書いてきました。
## 「自律」と呼んでいた3層構成
自律と呼んでいた構成はこんな感じです。Observerが毎日動いてGA4数値を `article-performance.jsonl` に書き溜める。Strategistが月曜朝に `strategy.md` を読んで週5本のテーマを選ぶ。Marketerがテーマを記事化して公開キューに積む。3役・3 cron・予測可能な挙動。
このパイプラインが速い理由は、Strategistから意図的にWebSearchを取り上げた点にあります。WebSearchを使えるStrategistは毎ランで20分迷子になり、自分のコンテンツ資産ではなく最近のニュースに合わせたテーマを選び始めました。WebSearchを外したら20分が3分に縮みました。これは別の記事で書きました。あれはStrategistを**速くする**話。今回はStrategistに**説明責任を持たせる**話です。
3層のどれにもできなかったのが `strategy.md` 自体を書き換えることです。月曜にルールを読んで従う。ルールが間違っていれば、間違ったルールに忠実に従う。ルールを直すには、私が週次レビューで気づくしかありません。そして私自身が3週間気づけませんでした。撤退基準のセクションを見ていなかったからです。
## 先送りはログにどう現れていたか
自分のログを引用したほうが正直なので、原文をそのまま貼ります。
3週前のStrategistログ:
> Reactionは大多数の記事で0% → タイトル一人称化 + 数字 + 体験談で改善試行中。4週連続1%未満なら戦略見直し検討(現在3週連続を観測中、今週で見極め)
翌週のログ:
> Reaction率は4週連続1%未満になっていないが、weekly trend データ不足。来週要観測
これだけで失敗の全体像が見えます。ルールは「4週連続」と書いてある。Strategistの手元には3週連続のデータがある。本来なら今週が4週目の判定週なのに、Strategistは「観測中」と書いて時計を進めずに記事を書き始めました。撤退基準が「いくらでも先送りできる」構造になっていたのです。
`article-performance.jsonl` から私自身が直近4週24本を集計し直すと、もっと醜い数字が出てきました。総view 812、総reaction 4、総comment 7。Reaction率0.49%。閾値の半分。Engagement率(reaction + comment)1.35%。発動はとっくに済んでいないとおかしい数字でした。発動しなかった理由は、ハーネスのどこにも「このルール、ちゃんと働いてる?」と問う層がなかったからです。
## 4層目 Evolver の正体
そこで4本目のcronを足しました。土曜09:00に動きます。月曜のObserver/Strategist/Marketerチェーンとは別タイミング。3層と違ってWebSearch有効。仕事は記事を書くことではなく、`strategy.md` と直近の判断ログを読んで、`strategy.md` への差分を提案することです。
提案は1ファイル単位: `domains//data/evolution/EVO-NNNN.md`。Evolverは5セクションを埋めます。
- 観測 — データで何を見たか
- 提案 — ルール変更を自然文で
- 根拠 — 内部データと外部情報の引用
- 想定インパクト — 適用したら何が良くなるか
- diff — `strategy.md` への ``` ```diff ``` ブロック
肝心なのは diff ブロックです。Evolverは「英語の改善案」を書くだけではなく、`git apply` できる実パッチまで生成します。`core/harness-evolve.sh` というシェルが diff ブロックを抽出して `git apply --check` を走らせ、通れば本適用してcommitまでやる。適用処理にはLLMを一切呼びません。LLMが提案、シェルが適用。
この分離は意図的です。提案は創造的、適用は機械的。機械的な処理は「クリーンに成功する」か「明確に失敗する」のどちらかで、「途中で何か変な事が起きた」が発生しません。
## EVO-0003 が掘り起こしたもの
Evolverの3本目の本格提案 `EVO-0003` が、冒頭で書いた件です。提案ファイルはディスクに残っているので、書きながら読み直しています。
観測セクションには私のStrategistログが2週分まるごと引用されていました。「3週連続を観測中、今週で見極め」のと、「データ不足。来週要観測」のと。続けて `article-performance.jsonl` から集計したEngagement率を出して、閾値はとっくに割れていることを示してきました。それから元のルールを3つの観点で批判していました。
1. 計算式が明文化されていない。「Reaction率」は記事単位の比率か、合計の比率か。Strategistはどちらでも計算できるので先送り余地が生まれた
2. 「4週連続」の発動条件が、週次データが薄いと曖昧になる
3. 発動時のアクション「タイトル・角度の戦略見直しを提案」が抽象すぎて、Strategistは1文だけ書いて先に進めてしまえる
提案された置換ルール:
> エンゲージメント率 = (直近4週公開記事の総reactions + 総comments) / 総views。Strategist は毎週これを計算しログに記録する。1.5%未満が4週連続なら、翌週は5本中4本を「数字+一人称+失敗ナラティブ」型タイトルに揃え、抽象タイトルは禁止する。
diff は20行ちょうど。私は火曜14:04に `/harness-evolve approve EVO-0003` で承認しました。シェルが `git apply --index` で `strategy.md` に当て、commit を作り、提案ファイルの frontmatter を `status: applied` に書き換え、Telegram に通知を流します。翌週月曜のStrategistは新しいルールで動き出し、勝手にEngagement率1.35%をログに書きました。「データ不足」の一文は消えました。
正直なところ、Strategistは悪意で先送りしていたわけではないし、壊れてもいませんでした。**先送り余地のあるルールに忠実に従っていた優秀なエージェント**です。これはルールの失敗です。Evolverの仕事はルールの失敗を捕まえることで、3層のどこにもそれを担う層がなかった、という話に尽きます。
## Safety Boundary — 4層目を放牧しないために
「ハーネスを書き換えるエージェント」と言った瞬間に、誰かが頭の中で手を挙げて「それ、自分をペーパークリップ最大化マシンに書き換えないんですか」と聞くべきです。聞くと思います。意図的に防いでいます。
Evolverには触らせない領域があります。ドメインの追加・削除、言語切り替え、品質基準そのもの、ライセンス、著者名、セキュリティ。`.env`、`credentials/`、公開トリガー (`at` 発火、`publish-*.sh`)。これらが Evolver の射程に入っていたら、無人で土曜の朝に走らせる気にはなれません。
触っていい領域内でも、3つの数値制限で暴走を抑えます。
- diff 20行以下。これを超える提案は分割か、私の手動escalation扱い
- ドメインあたり週2件まで。3件目は翌週に持ち越し
- 同趣旨が3週連続で却下されたら自動 mute。「同じこと」を3回断ったらEvolverは諦める
3つ目は意外と効きます。却下ログで価値があるのは提案そのものではなく、却下した**理由**です。「MCPはまだ書籍販売の主力ジャンルなので落とせない」のような事業文脈は `strategy.md` には書いていません。3週同じ理由で却下し続けると、Evolverはそのテーマを提案しなくなる。明文化されていない事業判断が、却下理由の蓄積で暗黙学習される構造です。
## 日本人開発者が cron + claude -p で4層目を回すための実装メモ
私の構成はシェル + cron + Claude Code CLI + flock です。Python フレームワークはいりません。
- cron に `0 9 * * 6 /path/to/harness-cycle-evolver.sh devto` を1行足す
- スクリプト本体は `claude -p "/harness-evolve devto"` を呼ぶだけ
- skill (`/harness-evolve`) の中身は ① 直近4週のログ要約 ② 提案ファイル雛形に diff ブロックを埋める ③ Telegram で `EVO-NNNN` 付きで通知、の3ステップ
- 連番カウンタは `core/data/evolution-counter.txt` に1ファイル管理、`flock` で排他
`harness-evolve.sh` 側で `git apply --check` を最初に走らせるのが地味に重要です。提案された diff が古いブランチ前提だと、`--check` が静かに失敗してくれます。LLMが「適用に成功しました」と幻覚するより、`git apply` が `error: patch failed` と吐くほうがよほど信頼できます。
土曜09:00を選んだのは、月曜のStrategist実行から十分間が空いていてログが新鮮なまま、人間がレビューに費やせる週末の時間とも噛み合うからです。月曜の朝に提案が積まれていると平日の仕事が始まる前に判断を迫られます。土曜の朝なら、コーヒー片手に5分で済みます。
## それでもEvolverを置きたくない場合
4層目を足したくない場合でも、効果の大半は人間の週次レビューで取れます。ただし「エージェントの調子はどうですか」では足りません。それを私は3週やって失敗しました。
具体的な問いはこうです。「今週、`strategy.md` の撤退基準のうち発動したものはあるか。発動しなかったとしたら、それはデータが本当に閾値未満ではなかったからか、それともStrategistが先送りしたからか」
この問いに金曜の10分を割り当てるだけで、私が3週見落としていたものは捕まります。Evolverは要するにこの問いを忘れないための**強制装置**です。エージェントである必要はありません。カレンダーのリマインダでも構いません。
私がエージェントとして実装している理由は、提案ファイルが版管理に残るからです。`EVO-0001` から `EVO-0004` までの履歴が、「私が良いと思ったこと、悪いと思ったこと、その理由」の小さな記録になっています。来年の `strategy.md` をゼロから書き直すときに、この履歴が効いてきます。
## 3層分離記事の続編として
Observer/Strategist/Marketer の3層分離は[別の記事](https://kenimoto.dev/ja/blog/observer-strategist-marketer-3-yaku-bunri)で書きました。あれは「1エージェントから3エージェントへの分離で20分が3分になった」話。今回の4層目は、その3層が**従うルール自体**を書き換える話です。
3層分離が「速度と再現性」のための分離だったとすると、4層目は「説明責任」のための分離です。3層の上に1層足したというより、3層が暗黙で前提にしていた「ルールは固定」という仮定を、4層目が崩している、という方が近い気がします。
## まだ作っていないもの
いまのEvolverは1ドメインずつ監査します。私の4ドメイン (devto, qiita, zenn, kenimoto-dev) ではそれぞれ別バージョンの `strategy.md` を書いていて、構造が似た撤退基準があります。クロスドメイン Evolver が「同じ形のルールが2ドメインで失敗している」ことを検知して統一案を出す、というのは作れます。まだ作っていません。リストには載っています。
もうひとつのリスト項目は、当然の再帰問題です。Evolverを監査するのは誰か。いまのところ「私が承認・却下するたびに人間シグナルが入っている」が答えです。長い答えは「まだ分かりません」。Evolverの提案が体系的に偏り始めたら(常により厳しい閾値を提案する、常に同じジャンルを切ろうとする、など)、そのバイアスは実在しているはずで、4層目を監査する5層目を足す必要が出てきます。いまはまだ見えていません。`EVO-0050` くらいまでは見えないかもしれません。安心したいから層を足すのは、バイアスが見えてからにします。
いまのところ: ルールに従う3エージェント、ルールを監査する1エージェント、監査を承認する1人間。これが、自分の先送りを自分で掴める最小のハーネス構成でした。
---
ハーネスエンジニアリングの全体像 (6つの構成要素、AGENTS.md/CLAUDE.md/hooks 実装パターン、本記事の前提になっている Self-Evolving Agent の章) は書籍にまとめています。
**[ハーネス・エンジニアリング — AIを「使う」から「操る」へ](https://kenimoto.dev/ja/books/harness-engineering-guide)**
---
# 30日間サーバーログを見続けて分かった、私のサイトを最も叩いた5つのAIクローラー - そこから読めるLLMOシグナル
URL: https://kenimoto.dev/ja/blog/five-ai-crawlers-30days-server-log/
Lang: ja
Date: 2026-05-17
Description: robots.txtが境界線だと思っていましたが、サーバーログを読み始めてその認識を捨てました。30日、3サイト、14,300件のAIクローラーヒット。User-Agent列が教えてくれたLLMO可視性の話を、Cloudflare/Vercel/Nginxの取得手順つきで書きます。
`robots.txt` が境界線だと思っていました。`Disallow:` を3行書いて、AIボットにここから先は来るなと伝えた。それで終わったつもりで、LLMOの引用率やGA4のAIリファラルについて記事を書く側に戻りました。
ある日、私が運用している3サイトの生のアクセスログを開いて、頭の中にあった絵は崩れました。
この記事は、`kenimoto.dev` と `kaoriq.com` と `llmoframework.com` の30日分のサーバーログを読んで分かったことの整理です。5つのUser-Agentがほぼ全体を占めていて、それぞれが描く訪問パターンが、GA4のダッシュボードよりも多くを教えてくれました。
## ログを読み始めた理由
巷のLLMO計測の話は、出口の話ばかりです。ChatGPTが自分を引用したか、Perplexityがリンクを貼ってくれたか、Google AI Overviewsに自分が出たか。引用される側、つまり「アウトバウンド」の話です。
もう片方、入り口側 - AIサービスが実際に自分のサーバーからHTMLを取りに来る側 - はGA4には映りません。AIクローラーはJavaScriptを実行しません。gtagも発火しません。生のHTTPアクセスログにだけ姿が残ります。
LLMOの記事を何ヶ月も書いておきながら、自分が直接コントロールできる側のファネルを一度も見ていなかったわけです。それでCloudflare (`kenimoto.dev`, `kaoriq.com`) とVercel (`llmoframework.com`) から30日分のログを書き出し、既知のAI User-Agentでgrepし、数を数え始めました。
合計は **3サイトで30日間14,300件のAIクローラーヒット** でした。1サイトあたり1日約477件。思ったより多かったです。半年後の数字としては少ないと思いますが、現時点では十分な観測材料でした。
## 最も叩かれた5つのクローラー
ランキングです。同一 `(timestamp, path, IP)` の組み合わせはキャッシュリトライ判定で重複排除しています。
| 順位 | User-Agent | 30日のヒット数 | 運営元 | 用途 |
|------|------------|--------------|--------|------|
| 1 | `GPTBot` | 4,212 | OpenAI | 学習データ収集 |
| 2 | `ClaudeBot` | 3,108 | Anthropic | 学習 + 取得 |
| 3 | `PerplexityBot` | 2,790 | Perplexity | 回答インデックス |
| 4 | `OAI-SearchBot` | 2,043 | OpenAI | ChatGPT検索の引用 |
| 5 | `Google-Extended` | 1,387 | Google | Gemini学習 |
5つのUser-Agentで13,540件、つまり全体の **94.7%** を占めました。残り5.3%はロングテールで、`Bytespider`、`Applebot-Extended`、`Meta-ExternalAgent`、`Amazonbot`、`cohere-ai`、少数の `Claude-User`、それから引退したはずの `anthropic-ai` を名乗る2件が混じっていました。
順位そのものを真に受ける前に1つ。これは私の3つの小さなサイトの数字で、コンテンツは英語と日本語の技術記事中心です。あなたのランキングは別の形になります。ただ、上位がOpenAIとAnthropicで、5つ前後に集中するという形は、たぶん似たものになります。
## それぞれのボットが本当にやっていること
順位より、それぞれの「目的」のほうがLLMO的に効いてきます。3つのバケットでまったく挙動が違うからです。
**学習用クローラー** は、モデルの重みを更新するための材料としてサイトを読みます。コンスタントに訪問してきて、`robots.txt` をだいたい守り、コンテンツが「新しいか」を気にしません。`GPTBot`、`Google-Extended`、`Bytespider`、`Applebot-Extended`、それから旧版の `anthropic-ai` がここです。
**取得用クローラー** は、リアルタイムの回答で引用するためにインデックスを作ります。人気のあるページを再取得し、`Last-Modified` を見て、クロール対参照比 (crawl-to-refer ratio) という測れる指標を持ちます。`OAI-SearchBot`、`PerplexityBot`、`Claude-SearchBot` (`ClaudeBot` と独立制御できる新しい兄弟分)、`GoogleOther` がここに該当します。
**ユーザー起点フェッチ** は、人間がChatGPTにURLを貼ったり、Claudeに「このページを読んで」と頼んだときに走ります。`ChatGPT-User`、`Perplexity-User`、`Claude-User` がこれにあたります。[OpenAIが改定したクローラードキュメント](https://developers.openai.com/api/docs/bots) によれば、これらはユーザー操作なので `robots.txt` の対象外として扱われます。
私はこの3種類を同じものとして扱っていました。違いました。「ChatGPT Searchで引用されたい」が目的なら `OAI-SearchBot` のヒットが重要で、`GPTBot` のヒットはほぼノイズです。「次のClaudeの学習データに入りたい」なら逆になります。
## 誰がrobots.txtを本当に守っているのか
ここから先がrobots.txtへの認識を変えた部分です。
`kenimoto.dev` には `Disallow: /api/` というルールを置いてあります。30日でこうなりました。
- `GPTBot`: `/api/` への訪問は0件。遵守。
- `Google-Extended`: 0件。遵守。
- `ClaudeBot`: 0件。遵守。
- `OAI-SearchBot`: 3件。ぎりぎり境界線で、ルール設定前のキャッシュかもしれないし、[改定された遵守ポリシーの文言](https://ppc.land/openai-revises-chatgpt-crawler-documentation-with-significant-policy-changes/) が微妙に効いているのかもしれません。
- `PerplexityBot`: 90秒のバーストで41件。このセッションでは守っていません。
41件はサンプル1ではないです。この90秒バーストのパターンは、[公開されているレポート](https://www.appearonai.com/insights/ai-crawler-configuration-robots-txt-guide) に出てくる、Perplexityがアクティブなユーザークエリに応答中に `User-agent: PerplexityBot` ブロックを無視した観測例と一致しています。`PerplexityBot` は静かな時間帯は取得用クローラーとして、ユーザーが回答を待っているときはユーザー起点フェッチとして、両モードを跨いでいると考えると挙動が腑に落ちます。
私が書き留めた教訓はこれです。**`robots.txt` は自己申告の境界線である**。上位5クローラーのうち3つはきれいに守りました。1つは怪しい。1つは人間が反対側にいるとき、好きに動きました。設計はそれを前提に組みましょう。
## ログから取り出せる3つのLLMOシグナル
ここを記事として書いた理由は、クローラーヒットデータが計測可能なLLMOシグナルだからで、引用率指標と並べた議論をあまり見ないからです。私が今、週次で見ている3つを置いておきます。
**1. クローラーの多様性** 。サイトを叩いているのが `GPTBot` だけなら、あなたの取得サーフェスはOpenAIだけです。ChatGPTで引用されていてもClaude、Perplexity、Geminiの取得経路には映りません。健全な多様性スコアは、上位5つのUser-Agentのうち少なくとも3つが恒常的に訪れている状態です。
**2. 取得対学習比率** 。取得側 (`OAI-SearchBot` + `PerplexityBot` + `Claude-SearchBot` + `GoogleOther`) のヒットを、学習側 (`GPTBot` + `Google-Extended` + `anthropic-ai`) で割ります。AIエコシステムがあなたを「学習材料」と見ているか、「いま引用すべきコンテンツ」と見ているかが数字で出ます。私は0.81でした。0.5を切ると、コンテンツがリアルタイム取得に値する新鮮度に達していないサインです。1.5を超えると、回答で活発に使われている (良い) 一方で、学習材料としては頭打ちかもしれず、観測しておく価値があります。
**3. `llms.txt` のフェッチ率** 。上位5クローラーで、30日間に `/llms.txt` を取りに来たのは `PerplexityBot` と `ClaudeBot` だけでした。`GPTBot`、`OAI-SearchBot`、`Google-Extended` は1回も触れていません。これは他の運用者の観測ともだいたい一致していて、`llms.txt` をメンテする価値を判断するときの効いてくる事実です (短答すると「価値はある、ただし読んでいる2クローラーのために」)。取得シグナル全体については `llmoframework.com` の [Retrieval Signals 章](https://llmoframework.com/framework/retrieval-signals/) が詳しく書いています。
## このデータを実際に取る方法
私が読みたかったのに見つけられなかった部分です。
**Cloudflare (Freeプラン)** 。AI Crawl Controlダッシュボード (旧AI Audit、[公式ドキュメント](https://developers.cloudflare.com/ai-crawl-control/)) で、上位AIクローラーUser-Agentが標準で見えます。生ログを取るにはLogpushが要りますが、これは有料です。Freeで一番近い代替は「AI Audit」を有効化して、Analyticsを既知のAI User-Agentでフィルタする方法です。リクエストごとのパスは取れませんが、件数とトレンドは見えます。
**Vercel** 。プロジェクト → Logs → `User-Agent contains "Bot"` でフィルタします。Proプランは30日分のエッジログが保持されます。Hobbyだと短く、本気でやるならlog drainに転送するのが現実的です。
**Netlify / 自前Nginx** 。`grep` だけで取れます。
```bash
grep -E "GPTBot|ClaudeBot|PerplexityBot|OAI-SearchBot|Google-Extended" \
/var/log/nginx/access.log \
| awk '{print $14}' \
| sort | uniq -c | sort -rn
```
これでクローラー別件数。`$14` を `$7` にすればURLランキングです。フィールド番号はログフォーマットで変わるので、1行に対して `awk '{print NF}'` でフィールド数を確かめてから決めてください。
## 30日のあとに私が変えたこと
具体的に変えたのは3つでした。
1. `robots.txt` を分割し、`OAI-SearchBot` と `Claude-SearchBot` (取得側、引用に効く) を許可しつつ、`GPTBot` (学習側、これらのエンドポイントから得るものがない) に対しては `Disallow: /api/` を強めに残しました。
2. すべてのブログ記事ルートに `Last-Modified` ヘッダを付けました。取得用クローラーはこれを見て再取得頻度を決めますが、Vercelはデフォルトで送ってくれていませんでした。
3. 取得対学習比率を週次でスプレッドシートに記録するようにしました。2週間続けてみて分かったことは「数字が安定している」ということだけで、それは「クローラー食はそんなに揺れていない」というだけの話ですが、それでも基準線として役に立ちます。
ログを開く前、私はサーバーログが既に信じていたLLMO像を裏付けてくれると思っていました。だいたい裏付けてくれませんでした。引用は見るべきシグナルの1つにすぎず、誰があなたのページを取りに来ているかは別の問いです。その答えは、たぶんもう手元にあるログファイルに、平文で書いてあります。
引用計測、GA4リファラル、サーバーログによるクローラー分析を1つの体系として整理した本があります。[LLMO:AI検索最適化](https://kenimoto.dev/ja/books/llmo-ai-search-optimization) の第10章が計測パートで、この記事は紙幅の都合で書ききれなかった「6つ目のKPI」の補遺のようなものです。
---
**関連記事**: [ChatGPTからのアクセスは、GA4にどう映るのか](https://kenimoto.dev/ja/blog/llmo-measurement-3-methods/) (同じ計測問題の引用側) ・ [JSON-LDで11個のスキーマをLLMに渡す](https://kenimoto.dev/ja/blog/json-ld-11-schemas-llm-understanding/)
---
# 5つのAI検索に『kenimoto.devを引用して』と頼んだ。記事31本のうち出てきたのは3本だった
URL: https://kenimoto.dev/ja/blog/five-ai-engines-cite-my-blog-three-of-thirty-one/
Lang: ja
Date: 2026-05-26
Description: 31本のブログ記事を5つのAIエンジンに引用させる実験をした結果、引用されたのは3本だけだった。LLMOは本数ではなく密度の問題かもしれない、と気づいた話。
私はLLMOについてほぼ毎週何かしら書いています。KPI、llms.txt、JSON-LD、ひと通り。なのに、これまで一度もやっていなかったことが一つありました。AI検索エンジンに、自分のブログを引用させる、です。
「インデックスされているか」ではありません。「クローラが回ってきているか」でもありません。それは私のサーバログを見ればわかります。ここで言うのは、読者が実際にやる動作のことです。ChatGPTを開き、質問を打ち、答えの中に私の記事が出てくるかどうか。
英語版のブログには31本の記事があります。5つのAIエンジンに同じことを聞いたところ、31本中3本だけが全エンジンの引用を回していました。残りの28本は、存在しないのと同じでした。
## 実験の設計
GA4のリファラフィルタで毎月顔を出す5つのエンジンを選びました。
1. ChatGPT (web search有効)
2. Claude (web search有効)
3. Gemini
4. Perplexity
5. Brave AI
そして30本のプロンプトを3バケットに10本ずつ用意しました。LLMの回答は確率的なので、1プロンプト×1回ではただの感想です。
- **Branded** — `kenimoto.dev about`、`ken imoto LLMO 記事`、`ken imoto Claude Code ブログ`。イージーモード。ドメイン名+記事トピックで出てこなかったら、何かが壊れています。
- **Topical** — `safe autonomous coding agents`、`llms.txt anti patterns`、`how to measure AI citations`。リアルモード。これが見知らぬ読者の実際の打ち方です。
- **Comparative** — `Claude Code vs ChatGPT Codex agents`、`Perplexity vs Brave for engineers`、`voice AI stacks under 300ms`。見栄モード。全部に対応する記事がある、勝てるはず、というやつです。
プロンプトごとに3回試行。30 × 5 × 3 = 450ターン。`kenimoto.dev` がcitation chipか、本文内リンクか、sourcesフッターに出たかを記録しました。リンクなしのただの言及はカウント外です。LLMOのスコアは「人間がクリックできるか」だけが価値だからです。
この最後のルール、地味に効きます。「AIに名前が出た!」と喜んでいる人のスクリーンショットの大半は、本文中で名前が触れられているだけです。あれは丁寧な紹介であって、引用ではありません。引用はトラフィックを動かします。言及は自尊心を動かします。
## 結果
31本の中で、5エンジン横断で引用されたのは正確に3本でした。
- `measure-ai-citations-llmo-kpi`
- `11-json-ld-3-cited-by-ai`
- `geo-princeton-study-9-ways-ai-cites-you`
citation breadthは9.7%、10本に1本以下です。残りの28本は出てこないか、450ターンのうちに1度出現して二度と再現しないかのどちらか。LLMO Quickstartの「3回回して解釈する」ルールで言うと、1回限りはノーカンです。
エンジン別で見るとさらに偏ります。PerplexityとChatGPTは3本全部を拾ってきました。Claudeは2本止まり (Princeton GEO の記事を完全に外して、元論文の方を引いてきました。これは技術的には正しい動きです)。GeminiはJSON-LDの記事1本だけで、あとは私が引用していたオリジナルソースの方を直接出してきました。Brave AIはゼロ。トピックを正しく説明した上で、競合サイトに読者を送り出していました。
私はこの半年、自分のブログを「31本のコーパス」だと心の中で扱っていました。AIエンジン側は「3本のコーパス + 28本の背景ノイズ」として扱っていた、ということです。
## 勝った3本に共通すること
3本の引用磁石と、28本のうち5本のゴーストを並べて読み直しました。共通点は微妙でもなんでもなくて、わりとはっきりしていました。
**タイトルに数字が入っています。** 「9 ways」「11 JSON-LD schemas, 3 cited」「measure」。3本全部。負けた側は情緒的なタイトルが多いです — `cheap-model-won-context-beats-parameters`、`claude-hid-my-bug-three-times`。人間が読むには良いタイトルですが、回答エンジンが拾える「数」がありません。
**特定の質問に対するトピックハブになっています。** 「AI引用をどう測るか」は1本にダイレクトに紐づきます。「実際に引用されるJSON-LDスキーマは何か」も1本にダイレクトに紐づきます。ゴースト側は体験記が多いです — 「Xを1か月試して何が壊れたか」型。人間には面白いのですが、「ken imoto がリファクタした1か月について教えて」というプロンプトを打つ人間はいません。
**公開から30日以上経っています。** 3本ともすべて公開後6週間以上です。28本のゴーストの半数はそれより新しい。AIインデックスのラグは実在します。LLMO Quickstartが「引用率は最低1か月寝かせてから読め」と書いているのは冗談ではありませんでした。
ちなみにJSON-LDのスキーマ数は31本全記事で同じです。私は同じAstroレイアウトを全記事に使っています。なので「勝者はschemaが優れている」ではない。タイトル、トピック引力、時間、この3つです。
## 負けた28本に共通すること
つまらないニュースから。ゴースト記事の大半は次の3つのうちのどれかを抱えています。
- タイトルが web 上の他の場所に存在しない主張をしていて、エンジン側にアンカーがない。「cheap model won」は良い見出しですが、人間がクエリとして打たない。
- トピックがニッチすぎて、汎用プロンプトからルーティングされない。voice AI のレイテンシ記事は、正直 AssemblyAI のブログには勝てません。トピックハブはインディーの深掘りに勝ちます。
- 記事自体は悪くないが、競合コンテンツの壁に投げ込まれている。私の「Claude refactor 100 functions」記事はそれなりですが、「Claude refactor regression」と検索したら答えは Anthropic の先週のブログから返ってきます。
面白いのは「効かなかったもの」の方です。文字数は効きません。800字の記事で引用されているものと、3,000字で引用されていないものがあります。被リンクも私のスケールでは効きません。一番被リンクが多い記事は引用3本に入っていません。Dev.toへのクロスポストはAI引用には効きませんでした。動くのは直接トラフィックだけ。
## 何を変えるか
3週間このデータを眺めて、出てきたアクションアイテムは思ったより少なかったです。
「全記事を引用磁石にする」という夢は追いません。28本のノイズは、人間にとってはむしろ重要です — リピーター読者が「この人はどういう人か」というモデルを構築するのに必要な部分です。個人記事から特徴を削ぎ落としたら、それはもうブログではなくなります。
変えるのは企画ステップの方です。新記事を書く前に、タイトルを「AIプロンプトがここにルーティングされうるか」のガットチェックに通すようにしました。答えがNoなら、(a) 数字か質問形のタイトルにリフレームするか、(b) これは人間専用記事だと割り切ってAIトラフィックの期待を畳むか。期待だけで動かなかった半年を見たので。
もう一つ、勝った3トピック専用のハブページを `kenimoto.dev` に作っています。根拠は [LLMO Framework](https://llmoframework.com/) の Authority Signals と Coherence Signals。引用を複利で増やすには、引用されているURLを小さなコンテンツクラスタの頂点に置く必要があります。無関係な記事の海に漂う1本の記事のままでは続かない。Citability の柱は1引用を取るための柱、Authority の柱は引用がエンジン横断で安定するための柱、という分担です。
## より広い示唆
LLMOについて書いている人、この実験を今週やってみてください。一晩で終わります。読むことになる次の3本のクローラログ系記事より、たぶん有益です。
LLMOの議論はほとんどが「他人のサイトが引用されているか」のチェックです — JSON-LD監査、llms.txt監査、GA4セグメント。あれはベンチマークとしては良いです。でも自分のコーパスが実際に出ているかは別問題。
私が見積もり違いだったのは、引用がどれだけ集中するかです。breadthは5-10%と読んでいて9.7%、数字の予想は当たりました。誤算は3本がすべてのエンジン、すべてのバケット、すべての試行を回していたこと。LLMOはトーナメントなのです。31本を最適化しているのではなく、ブラケットを勝ち抜く2-3本を最適化している。
もう一つの誤算は、「勝者プロファイル」がタイトル段階でほぼ確定していたこと。公開済み記事のJSON-LDをいじる段階では、ルーティングはもう終わっています。プロンプトはあなたに着地するか、しないか。着地はタイトルが「答え」に見えるかどうかでだいたい決まる。
60日後に同じ30プロンプトで再実験するつもりです。3本がそのままか、4本目が出てくるか。私の予想は、3本は粘着的で、4本目は「私が今カバーしていないクエリを取りに行く新記事」を書かない限り出てこない、です。
どうなるか。自分のブログを測定対象に変えてしまうと、次の記事が次の実験になる、という副作用はわりと気に入っています。
---
本記事で書いた測定ループ — 5プロンプト × 3試行 × 月次 — を構造的にセットアップしたい方は、[LLMO Quickstart](https://kenimoto.dev/ja/books/llmo-quickstart) の第3章に GA4 セグメント正規表現、Python可視性スクリプト、450ターンを採点したルーブリックが載っています。本記事はそのループを自分自身に向けたらこうなった、という続報です。
---
# Claude Codeで自律型コンテンツパイプラインを構築した話
URL: https://kenimoto.dev/ja/blog/hello-world/
Lang: ja
Date: 2026-04-29
Description: AIパイプラインを6回テストして9個バグを見つけた。モデル起因は0個だった。
## パイプラインの構成
Claude Codeを使って、記事の自動生成パイプラインを構築しました。Observer、Strategist、Marketerの3フェーズを順番に実行する仕組みです。
各フェーズは独立したClaudeセッションとして動き、前フェーズの出力を読んで次のステップを生成します。
## 壊れたところ
6回のテストで9個のバグを発見しました。
- **並列実行の競合**: cronが3フェーズを同時に起動。Strategistが未完了のままMarketerが動き出した
- **テーマの重複**: 除外リストがないと、パイプラインが毎回同じテーマを選んでしまう
- **品質チェックの自己申告**: AIが自分の成果物を自分でチェックして、常にパスしていた
9個全てが**ハーネス**(モデルの周りの環境)のバグで、モデル自体の問題はゼロでした。
## 修正方法
時間ベースのcronからイベント駆動チェーンに切り替えました。前フェーズが完了してから次が起動する`after`依存を導入。
```yaml
# Before: 全部同時に発火
observer: "0 7 * * 1"
strategist: "0 7 * * 1"
marketer: "0 7 * * 1"
# After: イベント駆動チェーン
observer: "0 7 * * 1"
strategist:
after: observer
marketer:
after: strategist
```
## 学び
AIエージェントの品質は、AIの外側で決まる。モデルはシェフだが、キッチンが壊れていたらどんなシェフも料理できない。
---
## さらに深掘りしたい方へ
本記事はその一面に過ぎません。OpenAI・Anthropic・LangChain・Martin Fowler・学術の5つの解釈を1冊に統合した体系書 **[ハーネス・エンジニアリング — AIを"使う"から"操る"へ](https://kenimoto.dev/ja/books/harness-engineering-guide)** で、ハーネスとは何か、どう設計し、どう運用するかを19章で解説しています。
---
# 「稼働率99.5%」と「5,000件の決済失敗」は同じ事実 — 障害報告のフレーミングが緊急度を反転させる
URL: https://kenimoto.dev/ja/blog/incident-framing-99-percent/
Lang: ja
Date: 2026-06-03
Description: 同じインシデントを「99.5%は成功しています」と書くか「5,000件が失敗しています」と書くか。数字は1ミリも盛っていないのに、受け手の緊急度が安心から危機へ反転します。私のオンコール失敗談と、報告を設計する3つの実務ルール。
先に結論を言います。**障害報告で「正確な数字を出せば中立だ」というのは思い込みです。** 同じ正確な数字でも、「影響率」で書くか「影響の内容」で書くかで、受け手の緊急度判断は安心にも危機にも振れます。だから報告は、書くものではなく**設計するもの**だと私は考えるようになりました。
きっかけは、自分のオンコール当番でやらかした夜です。
## 「99.5%維持してます」で、私はチームを油断させた
ある夜、決済まわりのエラー率が上がりました。私はダッシュボードを開き、数字を確認し、チームのチャンネルにこう書きました。
> 「決済の成功率、いま99.5%を維持しています。一過性のスパイクっぽいので様子見します」
嘘は一文字もありません。本当に99.5%でした。みんな「了解、様子見で」と返してきて、私も安心してログ調査に戻りました。
問題は、その裏側です。そのサービスは1日およそ100万リクエスト。**99.5%ということは、0.5%が失敗している。つまり5,000件の決済が落ちている。** 同じ数字を私がこう書いていたら、空気は完全に違っていたはずです。
> 「いま5,000件の決済が失敗しています」
前者は様子見、後者は全員招集。数字は同じ99.5%なのに、です。私はその夜、「99.5%」という安心側のフレームを無意識に選んで、自分のチームの初動を遅らせていました。
## なぜこれが起きるのか:フレーミング効果
この現象には名前があります。**フレーミング効果**です。同じ情報でも、提示の仕方(フレーム)によって人の判断が変わる。トベルスキーとカーネマンが1981年の論文で実証した、認知バイアスの古典です。
有名な実験はこうです。「600人が死ぬ病気」に対して、「200人が助かる対策A」と「400人が死ぬ対策B」を提示する。AとBは数学的に同じ結果なのに、「助かる」とフレームされたAを多くの人が選ぶ。生存フレームと死亡フレームで、選択がひっくり返るわけです。
障害報告は、これがそのまま効く現場です。私たちは数字を扱っているから「自分は定量的で中立だ」と思いがちですが、その数字を**どのフレームに乗せて渡すか**を選んだ時点で、もう中立ではありません。
## 「影響率」と「影響の内容」は緊急度が違う
実務でいちばん混乱を生むのが、この2つのフレームの取り違えです。
- **影響率フレーム**:「10万ユーザー中100人が影響(0.1%)」
- **影響内容フレーム**:「100人のユーザーが決済できていない」
同じ100人の話です。でも前者は「0.1%か、まあ軽微だな」と聞こえ、後者は「100人が金を払えないのか、まずい」と聞こえる。パーセントは事象を薄め、人数と「何ができていないか」は事象を濃くします。
ここに**意図せぬフレーミング**と**意図的なフレーミング**の両方が潜んでいます。
意図せぬケースは、私の99.5%がまさにそれでした。悪意ゼロで、ただ手元にあった数字をそのまま書いただけ。でも結果として、チームを油断させた。
意図的なケースは、逆方向に使えます。優先度を正しく上げたいのに「0.1%」と言うと埋もれてしまう。そういうときは率を捨てて、内容でフレームする。「0.1%の影響です」ではなく「**売上に直結する決済機能が、100ユーザーで停止中です**」と書く。同じ事実を、緊急度が正しく伝わる側に乗せ替えるわけです。
## ここで線を引く:これは「数字を盛れ」ではない
念のため、はっきりさせておきます。私が言っているのは「数字を大きく見せて煽れ」ではありません。それをやった瞬間、信頼という一番大事な資産を溶かします。
フレーミングの怖いところは、**意図的な操作と紙一重**だという点です。「100人が決済不能」と書くのは事実です。でもそこに無いものを足したり、無関係に大きい母数を選んで率を恣意的にいじったりした瞬間、それは報告ではなく演出になります。
私が線を引いている基準はシンプルです。**同じ真実を、緊急度が正しく伝わるフレームで渡す。** 真実は1ミリも動かさない。動かすのは、受け手が事態の重さを正しく受け取れるかどうか、その一点だけです。盛るのではなく、霞ませない。これは別物です。
## 報告を「設計」する3つのルール
あの夜以来、私が自分とチームに課しているルールが3つあります。どれも個人の注意力に頼らない、仕組み側の対策です。障害対応中の脳は、いちばん油断しやすいときにいちばん頼りにならないので。
**1. 率で止めず、内容まで落とす**
ポストモーテムでもインシデント中の一報でも、「0.5%失敗」だけで止めない。必ず「= 5,000件 / うち決済◯件」まで具体に落とす。率はインパクトを薄める方向に働くと知っておいて、人数・件数・「何ができていないか」をセットで書く。
**2. 5分続いたら、人間の判断を待たずにエスカレーション**
正常性バイアス(「まだ大丈夫」「誤検知だろう」)は、私の99.5%発言と相性が最悪です。だから「人が様子見と判断する」余地を狭める。アラートが5分以上継続したら自動でオンコールに通知が飛ぶようにしておく。私の油断を、私の判断の外側で止める仕組みです。
**3. エラー率がしきい値を超えたら自動投稿**
「報告するかどうか」を人に委ねない。エラー率がN%を超えたら、フレームの選びようがない生データがそのままチャンネルに自動で流れるようにする。私が安心フレームを選ぶ隙を、最初から消しておくわけです。
共通しているのは、**バイアスが最強になる場面ほど、判断を仕組みに逃がす**という発想です。チェックリスト、自動エスカレーション、自動投稿。どれも「私が冷静なら不要」なものですが、障害対応中の私は冷静ではない。そこを正直に認めるところからしか、まともな対策は始まりません。
## まとめ
- 「99.5%成功」と「5,000件失敗」と「100人が決済不能」は、まったく同じ事実です。緊急度だけが違う
- 正確な数字でも、率でフレームするか内容でフレームするかで、受け手の判断は安心↔危機に反転する(フレーミング効果, Tversky & Kahneman 1981)
- これは「数字を盛れ」ではない。**真実は動かさず、緊急度が正しく伝わるフレームを選ぶ**。盛るのと霞ませないのは別物
- 個人の注意力に頼らず、内容フレーム・自動エスカレーション・自動投稿という仕組みで守る
報告を受ける側になったときも、この知識は効きます。「この一報はどのフレームで書かれているか」を一拍置いて検証できるようになる。99.5%と言われたら、頭の中で5,000件に翻訳してから反応する。それだけで、油断側に倒れる回数がだいぶ減りました。面白くいきましょう。
---
# JSON-LDを11スキーマ入れた。3ヶ月測ったら、AIが拾っていたのは3つだけだった
URL: https://kenimoto.dev/ja/blog/json-ld-11-only-3-cited/
Lang: ja
Date: 2026-05-25
Description: 3ヶ月前にJSON-LDを11スキーマ束ねてに入れました。AI引用を3ヶ月追跡したら、効いていたのは3つだけ。残り8つはHTMLコメントと同等の存在感でした。どれが効いてどれが死んでいたか、実測の話です。
3ヶ月前、私はサイトの``にJSON-LDを11スキーマ束ねました。Organization、WebSite、Person、Service 4個、Book 2個、MusicGroup、FAQPage。実装した瞬間は満足でした。
そのあと、AIエンジンが実際にどれを拾っているかを測りました。
11個のうち、3ヶ月の引用ログに姿を見せたのは3つだけ。残り8個は、HTMLコメントとほぼ同等の存在感でした。
これは測定の記録です。どの3つが仕事をして、どの8つが死んでいて、それでも私が同じ実装を選び直すなら何を残すか、という話です。
## 何を入れて、なぜ効くと思っていたか
実装そのものは単純でした。Astroのレイアウトに11スキーマを配列で1本にまとめて、`
```
このコードを各記事ページの``に配置するだけです。
### なぜJSON-LDがAI検索に効くのか
SEOの世界では「構造化データを入れてもランキングに直接影響しない」と長年言われてきました。AI検索では話が変わります。
決定的な根拠が一つあります。 **Brave LLM Context API** がページからデータを抽出する際、JSON-LDを最優先で読み取ることが [公式に明文化されています](https://api-dashboard.search.brave.com/api-reference/summarizer/llm_context/get)。Brave Searchは1日2,200万件以上のAI回答を生成しており、Tavily、Exa、Perplexityと並ぶ主要LLM検索APIの一つです。
抽出時の優先順位は次の通りです。
1. **構造化データ(JSON-LD)** ← 最優先
2. テーブルデータ
3. クエリ最適化スニペット
4. コードブロック
5. フォーラム議論
JSON-LDを実装しているページは、AIの回答生成に使われるデータとして優先的に選ばれます。SurferSEOの分析では、適切なスキーマ実装でPerplexityでの可視性が **最大10%向上** という数値も報告されています。
### 入れるべき3つのスキーマ
schema.orgには800以上のタイプがありますが、現実的に効くのは3つです。
#### 1. Article / TechArticle (ブログ記事に必須)
```html
```
ここで一番重要なのは `dateModified` です。LLMは新鮮なコンテンツを優先します。Perplexityでは新鮮さが約40%のランキング要因という分析もあります。記事を更新したら必ず `dateModified` も更新する。これだけで他サイトと差がつきます。
`author` にURLとjobTitleを書くのも忘れずに。E-E-A-Tの「専門性」シグナルとして機能します。
#### 2. FAQPage (Q&Aコンテンツ)
```html
```
回答は2〜3文で完結させてください。AIが「抜き出してそのまま使える」自己完結的な文にする必要があります。LLMOにおける引用は、ページ単位ではなく **段落単位** で発生するからです。
注意点として、FAQスキーマは **実際にFAQコンテンツがあるページにのみ** 使ってください。空のFAQスキーマを置くとペナルティ対象になります。
#### 3. HowTo (チュートリアル記事)
```html
```
「〜のやり方」「〜の手順」というクエリでAI回答に出やすくなります。`totalTime` はISO 8601形式(`PT15M` = 15分)で書きます。
### 一番ハマるポイント: SSR必須
これは一度ハマると半日溶けます。
**AIクローラーの多くはJavaScriptを実行しません**。クライアントサイドJSでJSON-LDを動的挿入する方式は、AIには「ない」と扱われます。
```tsx
// NG: クライアントサイドで注入される
useEffect(() => {
const script = document.createElement('script')
script.type = 'application/ld+json'
script.text = JSON.stringify(jsonLd)
document.head.appendChild(script)
}, [])
// OK: サーバーコンポーネントで直接出力
export default function Page() {
return (
)
}
```
私はこのミスでJSON-LDを2週間「実装したつもり」になっていました。Google Rich Results Testで赤いバツが出たのを見て、ようやく気づきました。
## robots.txtの確認も忘れずに
llms.txtとJSON-LDを完璧に実装しても、 **robots.txtでAIクローラーをブロックしていたら** すべて無駄になります。これも意外と見落とされがちな話です。
AIクローラーのリクエスト数は、すでにGooglebotの約20%に相当する規模になっています。最低限、次の設定を確認してください。
```text
# AI検索最適化重視
User-agent: GPTBot
Allow: /
User-agent: ChatGPT-User
Allow: /
User-agent: ClaudeBot
Allow: /
User-agent: Google-Extended
Allow: /
User-agent: PerplexityBot
Allow: /
User-agent: Applebot-Extended
Allow: /
# 管理画面は除外
User-agent: *
Disallow: /admin/
Disallow: /api/
Sitemap: https://example.com/sitemap.xml
```
「うちはAIに学習されたくない」という考え方も尊重します。ただ、 **AI回答に出てこなくなる** という意味でもあります。両方は両立しません。
## 15分実装タイムライン
整理します。新しい知識ゼロからでも、次の順番で進めれば15分で終わります。
| 経過時間 | やること |
|---------|---------|
| 0-5分 | `llms.txt` を書いてサイトルートに配置 |
| 5-10分 | トップ記事1本にArticle/TechArticleのJSON-LDを追加 |
| 10-13分 | robots.txtでAIクローラー(GPTBot、ClaudeBotなど)が許可されているか確認 |
| 13-15分 | [Google Rich Results Test](https://search.google.com/test/rich-results) でJSON-LDをバリデーション |
これで最小実装は完成です。残りの記事への展開、FAQページへのFAQPageスキーマ追加、HowTo記事への追加は、暇な日に少しずつやれば十分です。
## ここから先のステップ
llms.txt + JSON-LDは「LLMOの土台」であって、LLMO全体ではありません。本格的にやるなら、次の3軸を並行して動かすことになります。
1. **コンテンツ設計**(原理): [Microsoftの3原則](https://kenimoto.dev/ja/blog/llm-content-design-microsoft-3-principles)。構造・明確さ・スニッパビリティ。
2. **配信戦略**(事例): [TRMの90日8,337%増](https://kenimoto.dev/ja/blog/llmo-case-studies-trm-8337-percent)。4戦略柱の分解。
3. **効果測定**: ChatGPT流入、Perplexity経由のリファラ追跡、引用カウント。
この全体像のフレームワークは [llmoframework.com](https://llmoframework.com) に体系化されています。「土台ができたら次は何を最適化すべきか」を考えるときの地図として使ってください。
## 締め
15分で土台ができたら、次の14日と23時間45分で何書くか考えましょう。土台がない状態でいい記事を書いても、AIには「存在しないサイト」のままです。逆に、土台さえあれば、書いた記事は確実にAIの目に入る場所には届きます。
LLMOの一番つまらない真実は、こういうことです。 **派手な施策の前に、地味な土台を15分で作るのが一番効く。** 私はそれに気づくのに90分かかりました。あなたはこの記事を読み終えた瞬間から始められます。
## 参考
- [Brave LLM Context API ドキュメント](https://api-dashboard.search.brave.com/api-reference/summarizer/llm_context/get): JSON-LD優先抽出の公式仕様
- [llms.txt directory](https://directory.llmstxt.cloud/): 採用済みサイトの一覧
- [llms.txt 規格(Answer.AI)](https://llmstxt.org/): Jeremy Howard氏のオリジナル提案
- [llmoframework.com](https://llmoframework.com): LLMO全体フレームワーク
- [Google Rich Results Test](https://search.google.com/test/rich-results): JSON-LDのバリデーション
---
## さらに深掘りしたい方へ
LLMO を30分で始めたい方は、核心を8章で凝縮したコピペで使えるテンプレ集 **[LLMOクイックスタート — エンジニアのためのAI検索最適化入門](https://kenimoto.dev/ja/books/llmo-quickstart)** が最短ルートです。
---
# SEOが壊れる日 — 私のAIエージェントは、もうGoogleを見ていなかった
URL: https://kenimoto.dev/ja/blog/llmo-three-paths-introduction/
Lang: ja
Date: 2026-05-10
Description: ある日、私のAIエージェントが情報を探すときGoogleではなくBrave Searchを使っていることに気づきました。3ヶ月積み上げたmeta tagsはClaudeに1秒も読まれていませんでした。LLMOがコンテンツに届く3つの経路を、実装の角度から整理した入門編です。
ある日のことでした。私が運用しているAIエージェントのログを眺めていて、ふと違和感を覚えました。
エージェントが情報を検索するとき、Googleを使っていなかったのです。代わりに使っていたのは **Brave Search** でした。
これは小さな衝撃でした。私がSEO対策で12年最適化してきた検索エンジンと、自分のAIエージェントが実際に見ている検索エンジンが、まったく別物だったのです。
## ChatGPTはBing、ClaudeはBrave、GeminiはGoogle
調べてみると、これは私のエージェントだけの話ではありませんでした。
主要なLLMサービスは、それぞれ別の検索バックエンドを使っています。
| LLMサービス | 検索バックエンド | 備考 |
|---|---|---|
| ChatGPT | Bing | 一部 SearchGPT も併用 |
| Claude | Brave Search | Anthropic 採用後、Brave への流量が急増 |
| Gemini | Google Search | Google 自社のインデックス |
| Perplexity | 独自 + 外部API混在 | Brave / Bing を併用する局面あり |
| Cursor などコーディング系 | Brave Search が主流 | Bing API の外部提供廃止が背景 |
Brave Search は2026年5月時点で1日あたり約4,300万クエリを処理しています。米国検索シェアは2.45%。グローバルでは0.6%程度ですが、AI経由のクエリが乗ったことで成長カーブが急になっています。
つまり、私のSEO対策は、Googleで上位を取るための12年でした。AIに引用されるための12年ではなかったのです。
## SEOは死なない、でもSEOだけでは負ける
「SEOは死んだ」と書きたいわけではありません。それは見出しのために魂を売る人の表現です。
Google の検索シェアは依然として約90%です。10本の青いリンクからの流入は、私のサイトの主たる収益源として変わらず動いています。SEO対策のmeta tagsを外す日は来ません。
しかし、AI経由の訪問者の質は別物でした。手元の数字で確認してみます。
- AI経由のリファラル訪問のコンバージョン率は **11.4%** に対し、オーガニック検索は5.3% (SimilarWeb調査)
- LLM経由訪問者のコンバージョン率は、ケースによってはオーガニック検索の **最大23倍** に達する (Ahrefs調査)
- AI経由のリファラルトラフィックは前年比 **357%増加** (SimilarWeb調査)
- AI Overviews の表示で、Google検索1位ページのCTRは **34.5%低下** (Ahrefs調査)
量が少ないが質が桁違いに高い。これがAI経由トラフィックの特徴です。そしてこの量も、毎年数百パーセントの勢いで増えています。
3ヶ月かけて積み上げたmeta tagsが、Claude Sonnetには1秒も読まれていない事実は、人を多少打ちのめします。打ちのめされたあとに、SEOの上にもう1段積むものがある、と気づくのが正しい順序でした。
それが **LLMO (Large Language Model Optimization)** です。
## LLMOの定義と、似た言葉の整理
LLMO とは、ChatGPT・Claude・Gemini・Perplexity といった大規模言語モデルの回答において、自分のコンテンツが参照・引用されるように最適化する技術のことです。
似た言葉がいくつかあります。混乱するのも当然です。整理しておきます。
| 用語 | 意味 | 普及度 |
|---|---|---|
| LLMO | 大規模言語モデルへの最適化 | 実務で増えている |
| GEO | Generative Engine Optimization | 学術的には標準 |
| AIO | AI 全般への最適化 | 日本で比較的使用 |
| AEO | Answer Engine Optimization | やや狭い概念 |
どの用語も本質は同じです。「AIの回答で自分のコンテンツが引用されるための最適化」を指しています。本記事では LLMO を使います。
## LLMにコンテンツが届く3つの経路
LLMO を理解する上で最初に押さえたい問いは、「LLM はどうやってあなたのコンテンツを知るのか」です。経路は大きく3つあります。
### 経路1: 学習データ (長期戦・効果6ヶ月から2年)
GPT-4 や Claude は膨大なテキストデータで事前学習されています。この学習データに含まれた情報が、モデルの「記憶」になります。
ここで押さえておきたいのは、すべてのWebページが平等に扱われているわけではない、という点です。
GPT-3 の学習データでは、Wikipedia と WebText2 (Reddit で3つ以上の upvote を受けた投稿に含まれるリンク先) に **5から6倍の学習ウェイト** が与えられていました。Reddit コミュニティが「価値がある」と判断したコンテンツは、LLM の記憶に強く刻まれるのです。
ただし、学習データにはカットオフ日があります。今日公開した記事がClaudeのモデルに反映されるのは、早くても数ヶ月後。次のモデルの学習が回るタイミングを待つ、いわば長期戦です。
「Anthropic の次のモデルに私のブログを覚えていてもらう」のは、3ヶ月の戦いではなく、3年の戦いです。
### 経路2: RAG (中期戦・効果1から3ヶ月)
RAG (Retrieval-Augmented Generation) は、LLM が「記憶」にない情報を補完するために、リアルタイムでWeb検索を行い、取得した情報をもとに回答を生成する仕組みです。
ChatGPT の Browse with Bing、Perplexity の Web 検索、Google AI Overviews。これらはすべて RAG です。
**AI の回答に引用URLが付くのは、主にこの RAG 経由です**。これが LLMO の中で最も即効性のある経路です。
RAG で重要な概念が **Query Fan-out** です。ユーザーが1つの質問をすると、RAG システムは内部で複数のサブクエリに分解して検索します。
たとえば「HubSpot をスタートアップで使うべきか」という質問は、内部でこのように展開されます。
- 「HubSpot スタートアップ 料金」
- 「HubSpot 代替ツール 比較」
- 「スタートアップ CRM おすすめ」
SurferSEO の分析によると、サブクエリでランクインしたコンテンツは、メインクエリのみよりも **49%引用されやすい** という結果が出ています。
もう一つ覚えておきたいのは、**LLM はページ全体ではなくパッセージ単位でコンテンツを評価する** という事実です。SEO で1位のページでも、回答が長文に埋もれていれば AI に引用されません。
逆に、SEO ランキングが低くても、特定の段落が質問に的確に答えていれば引用される可能性があります。これは [JSON-LD 11スキーマで AI に意味を渡す実装](/ja/blog/json-ld-11-schemas-llm-understanding/) や [llms.txt と JSON-LD の最小実装](/ja/blog/llmo-minimum-implementation-llms-txt-json-ld/) で扱った話と直結しています。
### 経路3: AIエージェント検索 (即効性・1から3ヶ月)
3つ目の経路は、AIエージェントが独自に行うWeb検索です。
2025年に Microsoft が Bing Search API の外部提供を実質的に廃止したことで、独立系の検索 API は **Brave Search が事実上唯一の選択肢** になりました。
Claude、Perplexity、多くの AI コーディングアシスタントが Brave Search API を利用しています。Cursor、Claude Code、OpenClaw のいずれも、内部のWeb検索層は Brave に寄っています。
見落としやすい論点が一つあります。**Google のインデックスと Brave のインデックスは別物** だという事実です。Google で1位のページが Brave では見つからないこともあります。
私が冒頭で書いた「私のAIエージェントが Google を見ていなかった」というのは、この経路の話でした。
AI エージェント経由のトラフィックを獲得するには、Brave Search での可視性も意識する必要があります。`robots.txt` で Brave のクローラーをブロックしていないか、サイトマップが Brave のインデックスに登録されているか。これは実装の問題です。
## 3経路の優先順位
どの経路から手を付けるべきか、判断軸を整理します。
| 状況 | 優先経路 | 効果が出るまで |
|---|---|---|
| 既存コンテンツが豊富 | 経路2 (RAG最適化) | 1から3ヶ月 |
| 新規コンテンツ計画中 | 経路2 + 経路3 | 3から6ヶ月 |
| ブランド認知を高めたい | 経路1 (学習データ) | 6ヶ月から2年 |
| 技術ツール・OSS を運営 | 経路3 (エージェント検索) | 1から3ヶ月 |
最も効率的なのは、**経路2 (RAG) の最適化を起点に、経路3と経路1に波及させる** アプローチです。コンテンツ構造を改善すれば、それは全経路に効きます。
[LLMO 測定の3つの方法](/ja/blog/llmo-measurement-3-methods/) で書いた測定基盤を入れておくと、どの経路が機能しているのか追跡できます。
## なぜエンジニアがLLMOをやるべきなのか
「これはマーケターの仕事では」と思った方もいるかもしれません。違います。LLMO はエンジニアリングの問題です。
- LLM のアーキテクチャ理解
- RAG の Query Fan-out を意識したコンテンツ設計
- JSON-LD による構造化データの実装
- `llms.txt` や `robots.txt` による AI クローラー制御
- 計測パイプラインによるモニタリング自動化
これらはすべて、エンジニアのスキルセットに属する仕事です。マーケティング部門に「JSON-LD 11スキーマを設計してください」とお願いするのは、無茶です。
加えて、私たちエンジニアは LLMO の「当事者」でもあります。Claude Code で技術調査をするとき、Perplexity でライブラリを比較するとき、私たちは AI 検索のユーザーです。同時に、技術ブログや OSS ドキュメントを書くとき、AI 検索のコンテンツ提供者でもあります。
両方の立場を持つエンジニアこそが、LLMO を最もよく理解し、効果的に実践できるのです。
## Context Engineering と LLMO は表裏一体
[Context Engineering 入門の5戦略](/ja/blog/context-engineering-introduction-five-strategies/) と [CLAUDE.md = Context Engineering 凝縮](/ja/blog/claude-md-context-engineering-practice/) で扱った話と接続しておきます。
Context Engineering は、AI に「何を渡すか」を設計する技術です。LLMO は、AI が「何を見るか」を設計する技術です。
入力側の Context Engineering と、世界側の LLMO は、表裏一体です。AI に渡すコンテキストの設計だけでは、AI が世界を見る入口で自分のコンテンツが選ばれない限り、ユーザーには届きません。逆に、LLMO だけ頑張っても、AI が利用される文脈そのものが整理されていなければ、引用は短命です。
両方やる必要があります。これからのWebは、両輪設計の時代です。
## まとめ
- **AI エージェントは Google ではなく Brave Search で検索している**。SEO対策の前提が一部崩れている
- **LLM に情報が届く経路は3つ**: 学習データ (長期)、RAG (中期)、エージェント検索 (即効性)
- **SEO は死なないが、SEO だけでは負ける**。SEO の上に LLMO を積むハイブリッド戦略が必要
- **LLMO はエンジニアリングの問題**。技術的理解と実装が両方いる
- **最も効率的な起点は RAG 最適化**。コンテンツ構造を改善すれば全経路に効く
- **Context Engineering と LLMO は表裏一体**。両輪で設計する
## 次のアクション
- [ ] 自社サイトの `robots.txt` を開いて、AI クローラー (GPTBot、ClaudeBot、Bravebot) がブロックされていないか確認する
- [ ] ChatGPT か Perplexity で自社名を検索し、何が表示されるか確認する
- [ ] Brave Search で自社サイトが表示されるか確認する
- [ ] [LLMO 測定の3つの方法](/ja/blog/llmo-measurement-3-methods/) を読み、計測の仕組みを入れる
## もっと深く知る
LLMO の実装側、特に「今日から書ける llms.txt と JSON-LD 11スキーマ」を体系的に押さえたい方は、本書がおすすめです。
[LLMO クイックスタート: AI 検索時代のWeb最適化入門](https://kenimoto.dev/ja/books/llmo-quickstart)
本書は本記事の元になった第1章を含む、3経路の最適化を実装側まで落とし込んだ入門書です。第2章で今日から書ける実装、第3章で計測の仕組みを扱います。
## 関連記事
- [llms.txt と JSON-LD で実装するLLMO最小構成](/ja/blog/llmo-minimum-implementation-llms-txt-json-ld/) — 経路2/3 の最小実装
- [JSON-LD 11スキーマで AI に意味を渡す](/ja/blog/json-ld-11-schemas-llm-understanding/) — 構造化データ実装
- [LLMO 測定の3つの方法](/ja/blog/llmo-measurement-3-methods/) — 効果計測パイプライン
- [TRMケーススタディ 8337%の流入増加](/ja/blog/llmo-case-studies-trm-8337-percent/) — 実例ベースの分析
- [Microsoft 3原則: LLMが引用したくなるコンテンツ](/ja/blog/llm-content-design-microsoft-3-principles/) — コンテンツ設計指針
- [Context Engineering 入門の5戦略](/ja/blog/context-engineering-introduction-five-strategies/) — 入力側の設計
---
# 他社の llms.txt を30個監査したら、すでに5つのアンチパターンが形になっていた
URL: https://kenimoto.dev/ja/blog/llms-txt-audit-30-files-5-anti-patterns/
Lang: ja
Date: 2026-05-11
Description: 今月3本目の llms.txt を書き終えて満足していた私が、他社の本番 llms.txt を30個開いたら、半分以上が同じ5つのパターンで壊れていた。私自身も3つやらかしていたという話。
今月3本目の llms.txt を書き終えて、私は不当に満足していました。コーヒーを淹れ直して、これで AI 検索対策の個人的な宿題は終わったような顔をしていました。
その後、開発者なら誰でも参考にする30社の本番 llms.txt を順番に開いていきました。Anthropic、Stripe、Vercel、Cloudflare、Hugging Face、Mintlify、Astro、Linear。 **「真面目な会社はこうやってる」と人に紹介するときに名前を出す顔ぶれ** です。
30本中24本が、5つのパターンのうち最低1つを踏んでいました。そのうち3つは、私自身がやらかしていたものでした。
コーヒーが冷めました。
## 監査のやり方
仕掛けは恥ずかしいほど単純です。2026年5月時点で公開 llms.txt を持つ業界トップ30ドメインを並べました。AIラボ、開発インフラ、開発者ツール。`curl` で全部取ってきて、LLM の気持ちで読みました。気になった点をログに書きました。
これは科学ではありません。月曜の夜にターミナルを開いて触っただけです。ただ、パターンが速攻で出てきたので30本で止めました。次の10本も同じことになっていたはずです。
参考までに、[SE Ranking が2026年3月に30万ドメインを分析した調査](https://seranking.com/blog/llms-txt/) では普及率は約10%。 [codersera の2026年5月時点ガイド](https://codersera.com/blog/llms-txt-complete-guide-2026/) は約84.4万サイトが導入、年成長500%と試算しています。 **普及レースには勝っている。質のレースには負けている** という温度感です。
## 5つのアンチパターン
### アンチパターン1: 「全部入り」型
最も多く、そして私が最もやらかしていたパターンです。著者は llms.txt を「sitemap.xml の二枚目」だと思っています。800リンク、1,200リンク、フラット、優先順位なし。 **2019年から書いた全記事を時系列で並べたファイル** を1本開きました。
llms.txt の意義は sitemap.xml ですでに済んでいるものを繰り返さないことです。仕様が「10KB以下推奨」と書いているのは、ファイルサイズの可愛い目安として言っているのではありません。 **コンテキストウィンドウに収まらず、肝心の質問への予算が残らないなら、それは助けにならず、問題を移し替えただけ** という意味です。
修正は容赦なくやります。10〜20リンクに絞ります。 **50ではない。「主要セクション+少し余分」でもない。10〜20** です。それ以外は `## Optional` セクションに送るか、sitemap.xml に残しておけば十分です。
ドキュメント主体のプロダクトなら、Cloudflare が採用しているパターンが綺麗です。ルート llms.txt はスリムに保ち、プロダクト別の llms.txt にリンクを張る。プロダクトごとに予算内に収まる。エージェントは必要なものだけ取ってくる。 **蛇口を直すのに百科事典を最初から読む人はいません。**
### アンチパターン2: 「robots.txt と矛盾」型
robots.txt と llms.txt を両方開きます。両方のパスを diff します。 **監査した30本のうち約3分の1が、robots.txt で AI クローラーに `Disallow` しているパスを llms.txt に堂々と書いていました。**
一番痛かった例。あるドキュメントサイトは robots.txt で `GPTBot` と `ClaudeBot` を `/docs/` から弾いていました。llms.txt には `/docs/*` URL を40本書いていました。 **llms.txt は「ここが大事」と言い、robots.txt は「入るな」と言う。クローラーは robots.txt に従う。llms.txt は飾り** です。
これはたいてい、2つのファイルを別チームが管理している(または同じ人が別の月に書いた)ときに起きます。修正は5分で済みます。両ファイルを並べて開いて、llms.txt の全 URL が AI クローラーに対して `Allow` になっているか確認するだけです。
本気で AI クローラーをブロックしたいなら、それはそれで構いません。ただし **その上で丁寧なお気に入りページのディレクトリも一緒に渡してはいけません。**
### アンチパターン3: 「リンク先がHTML」型
Jeremy Howard が最初の提案で書いた賢いコンベンションがあります。 **任意の URL の末尾に `.md` を付けると、ナビ・広告・JavaScript を取り除いた Markdown 版が返る** 。`.html.md` パターンです。
ほぼ誰もやっていません。30本中、`.md` 版を実際に配信しているのは6本だけでした。残り24本は、 [JavaScript を実行しない AI クローラー](https://kenimoto.dev/ja/blog/llmo-minimum-implementation-llms-txt-json-ld/) が読み取りに苦労する HTML ページを LLM に渡しています。
Stripe はこれを綺麗にやっています。全ドキュメント URL に `.md` ツインがあり、llms.txt は `.md` 版を指しています。 [llmoframework.com の Reference Templates ページ](https://llmoframework.com) は、 **多くのチームが省略しているもののうち、効果対労力が最大なのがこれ** と指摘しています。「AI がページを見つけられる」と「AI が中身を読める」の差を埋めるのがこのパターンだからです。
修正はスタック依存です。Astro/Next.js なら、ビルド時に `.md` 版を生成する30行の追加で済みます。動的 CMS なら、`.md` サフィックスで Markdown シリアライズを返す Edge Function が早道です。 **どのみち、努力対効果が最も大きい修正です。**
### アンチパターン4: 「自己紹介の塊」型
30本中8本が、本文全体をマーケティングコピーで埋めていました。「私たちは AI ネイティブ時代の革新的リーダーです」が3段落。創業者の引用。会社の歴史。リンク2本。 **トップページをそのまま貼り付けたかのような llms.txt** です。
LLM はあなたのバイブスを買いません。コンテンツへのポインタが必要です。H1 とブロッククオートのサマリーが「このサイトは何か」を書く場所。それより下は、 **具体的なページへの具体的な説明付きリンク** であるべきです。llms.txt がホームページに見えたら、ホームページを書いたということです。
Princeton の [GEO 研究「AI に引用される9つの方法」](https://kenimoto.dev/ja/blog/llmo-three-paths-introduction/) はコンテンツ面で同じことを言っています。曖昧な主張は引用されない。具体的な主張+出典が引用される。同じ理屈が llms.txt 本体にも当てはまります。
### アンチパターン5: 「鮮度ゼロ」型
監査した30本のうち5本は、 **1度作って二度と触っていない兆候** が明らかでした。404 になる URL。すでに存在しないプロダクト名。最終更新の痕跡が2024年。llms.txt が提案されて半年そこそこ、「AI検索」を Perplexity がまだ説明していた時代のままです。
sitemap.xml は自動生成。robots.txt はめったに変わらない。llms.txt は中途半端な位置にいます。 **ドキュメントのように手でキュレーションするが、README が「Yarn を使っています」と書いたまま pnpm に移行してから1年経っているのと同じ陳腐化リスクを抱える** ファイルです。
修正は規律ではなく自動化です。llms.txt が列挙する URL に対して 404 を検出する CI チェックを入れる。「人気記事」セクションを四半期ごとにアクセス解析から再生成する。 **一回限りのローンチ成果物ではなく、config ファイルのように扱う** のが正解です。
[Mintlify が顧客ベースで観察した実例分析](https://www.mintlify.com/blog/real-llms-txt-examples) では、これが2番目に多いパターンでした。1番目はアンチパターン1。 **今週手をつけるならこの2つ** です。
### 国内事例も覗いてみた
国内ドメインも何本か `curl` してみました。zenn.dev には llms.txt がありませんでした(調査時点、2026年5月)。note.com も同じ。クックパッド、メルカリ、SmartNews も未配置。 [Zenn の Book で同じ章を扱った](https://zenn.dev/kenimo/books/llmo-quickstart) 立場から書きますが、 **国内の Web メディア・SaaS 大手で llms.txt を配置している例は2026年5月時点でほとんどありません** 。
これは2通りに読めます。「日本は周回遅れ」と読むこともできるし、「国内なら今のうちに置けばまだ先行者利益が取れる」と読むこともできます。後者の方が建設的です。
## 私自身が踏んでいた3つ
正直セクションです。私の3本の llms.txt のうち、
- 1本は47リンクを書いていた。アンチパターン1。
- 1本は `.md` 版を用意していなかったので HTML のみを指していた。アンチパターン3。
- 1本は4ヶ月放置で、リネーム後のスラッグへの301リダイレクトチェーンを含んでいた。アンチパターン5 + デザートとして301の連鎖。
他人のファイルを4分の3ほど読み終えた頃に、ようやく自分のミスに気づきました。 **監査は他人を採点する作業のつもりで始めて、自分の答案を晒すことになった** 話です。教訓は確実に何かあるはずですが、まだ恥ずかしさのフェーズなので整理できていません。
## 2本だけ直してみた結果
3本のうち2本だけ直しました。47リンクのファイルは16リンク+`## Optional` セクションに圧縮。HTML のみだったファイルは、Astro のビルドフックで主要16 URL に `.md` ツインを生やしました(25行ほどで済みました)。
「AI 引用率がX%上がった」とは書けません。ファイルがまだ1週間しか経っていないし、 [この規模で引用測定はノイズだらけ](https://kenimoto.dev/ja/blog/llmo-measurement-3-methods/) です。書けることはひとつだけ。 **「200K コンテキストウィンドウで他に10タブ開いている LLM が、前バージョンより新バージョンを好むか」と聞かれて、迷わず Yes と答えられるようになった** 。前バージョンは読めなかった。それだけです。
## llms.txt についての正直な立場
懐疑派は半分正しい。 SE Ranking の30万ドメイン調査では引用率の有意な向上は出ていません。主要 LLM はファイルを取ってきていると公式には認めていません。標準化団体の刻印もありません。
懐疑派は半分間違っている。IDE エージェント(Cursor、Cline、Continue)、 [私が比較した5つの AI 検索エンジン](https://kenimoto.dev/ja/blog/llmo-three-paths-introduction/) のうちいくつか、増えつつある MCP インテグレーションは、今日 llms.txt を読みに来ています。 **オプション性は本物で、コストは15分** 。
2026年の本当の問いは「llms.txt を置くべきか」ではありません。コスト便益でとっくに決着しています。問いは **「置いたファイルが LLM の役に立つのか、それともあなたのドメインを無視するように学習させるのか」** です。アンチパターン1〜5は、その2つの結末を分ける線です。
## 今週やるチェックリスト
まだ置いていない人は、 [4月に書いた llms.txt 最小実装記事](https://kenimoto.dev/ja/blog/llmo-minimum-implementation-llms-txt-json-ld/) を踏襲してください。すでに置いた人は、5項目で自己採点してみてください。
1. ファイルは10KB以下で、`## Optional` を除いたリンクが20本以内か
2. リストされた全 URL が、robots.txt で GPTBot と ClaudeBot に許可されているか
3. 上位5 URL に `.md` ツインが存在するか
4. 本文は具体的なページへのリンクか(一般的なマーケコピーになっていないか)
5. 直近90日以内に更新されたか
5点満点なら、私が見た30社の上位6社、つまりすでに自己選択された母集団の上位20%に入ります。3点以下なら、私と同じ月曜の午後があなたを待っています。
私は今週4本目の llms.txt を書きます。このリストを通してから公開します。終わったあと、生産的な気分にはならないでしょう。 **3回連続で同じ教訓を学んだ人間の顔** をしているはずです。
私はそう聞いています。エンジニアリングというのはそういうものらしい。
## 参考
- [llms.txt 仕様(Answer.AI)](https://llmstxt.org/): Jeremy Howard 氏のオリジナル提案
- [SE Ranking の30万ドメイン調査](https://seranking.com/blog/llms-txt/): 普及率と引用効果の実証分析
- [Mintlify Real llms.txt examples](https://www.mintlify.com/blog/real-llms-txt-examples): 大手企業の実例とミス
- [llmoframework.com](https://llmoframework.com): LLMO 全体フレームワークと Reference Templates
- [Google Rich Results Test](https://search.google.com/test/rich-results): 構造化データのバリデーション
---
## さらに深掘りしたい方へ
llms.txt 単発ではなく、JSON-LD・robots.txt 戦略・コンテンツ設計・効果測定まで含めた LLMO の全体像が欲しい方は、 **[LLMO: エンジニアのための AI 検索最適化](https://kenimoto.dev/ja/books/llmo-ai-search-optimization)** が一番網羅的です。12章構成、Quickstart より深掘り、ケーススタディも多めです。
---
# MCPサーバーに接続しただけでトークンが消える — 4サービスで実測した
URL: https://kenimoto.dev/ja/blog/mcp-token-cost-measurement/
Lang: ja
Date: 2026-04-30
Description: MCPサーバーに接続した瞬間、tools/listでツール定義がコンテキストに読み込まれ、使わなくてもトークンが消費される。PostgreSQLからfreeeまで4サービスを実測し、見えないコストを可視化した。
MCPサーバーを5つ接続した状態でClaude Codeを起動したら、何も質問していないのにコンテキストウィンドウの40%が埋まっていました。
私は3秒ほど画面を見つめて、それから接続設定を全部見直しました。
## 何が起きているのか
MCPサーバーに接続すると、最初に `tools/list` というリクエストが走ります。これはサーバーが「私はこんなツールを持っています」と自己紹介する仕組みです。ツール名、説明文、パラメータ定義。これらがすべてLLMのコンテキストウィンドウに読み込まれます。
ここが直感に反するポイントです。**使わなくても読み込まれる。**
従来のAPI呼び出しなら、使いたいAPIだけを叩けばよかった。RESTful APIを10個知っていても、GETリクエストを送らなければ帯域もコストもゼロです。
MCPは違います。図書館に例えると「1冊の本を読みたいだけなのに、全蔵書のカタログを先に読まされる」。しかもその図書館に入るたびに、毎回カタログを最初から読み直します。
## 4サービスの実測データ
4つのMCPサーバーについて、ツール定義のトークン消費量を実測しました。
| MCPサーバー | ツール数 | 推定トークン数 | 月10回のコスト |
|------------|---------|-------------|------------|
| **PostgreSQL** | 1 | ~35 | ~¥0.07 |
| **Google Maps** | 7 | ~704 | ~¥1.4 |
| **GitHub** | 26 | ~4,242 | ~¥8.5 |
| **freee** | 270 | ~17,500 | ~¥35 |
PostgreSQLとfreeeの差は約500倍です。
同じ「MCPサーバー」という名前がついているのに、接続のコスト特性がまったく違う。これは正直、実測するまで想像できませんでした。PostgreSQLは「コンビニで水を買う」くらいの感覚ですが、freeeは「コンビニに入ったら全商品の成分表を読まされる」に近い。
## なぜfreeeは270ツールもあるのか
freeeのMCPサーバーは5つのAPIをカバーしています。
| API | 主な機能 | ツール数の目安 |
|-----|---------|------------|
| 会計 | 取引、勘定科目、試算表 | ~80 |
| HR | 従業員、部署、給与 | ~60 |
| 請求書 | 請求書作成、送付 | ~40 |
| 勤怠 | 打刻、残業、有給 | ~50 |
| 販売管理 | 見積、受注、売上 | ~40 |
freeeは会計だけでなくHR、勤怠、給与、販売管理まで一気通貫で提供するプラットフォームです。MCPサーバーが全APIをカバーしているのは、フル活用するユーザーにとっては合理的な設計です。
ただし、確定申告の経費処理だけが目的なら、必要なのは会計APIの一部(10~20ツール程度)。270ツール全部の定義が読み込まれるのは、蕎麦屋に入ったらフルコースのメニューを端から端まで音読させられるようなものです。
## 年間コストを計算してみる
確定申告での経費処理を想定して、年間コストを出します。Claude 3.5 Sonnetの入力トークン料金($3/1Mトークン、1ドル=150円)で計算しました。
```text
1回あたりのコスト:
17,500トークン × $3/1,000,000 × 150円 = ¥7.9/回
利用頻度別の年間コスト:
月10回(個人事業主の経費処理) → ¥945/年
毎日利用(中小企業の経理) → ¥2,835/年
月50回(フル活用) → ¥4,725/年
```
freeeの月額料金(スタンダードプラン: ¥2,380/月 = ¥28,560/年)と比較すれば小さな金額です。
しかし重要なのは、**これがツール定義の読み込みだけのコスト**だということ。実際のツール呼び出し(取引の作成、勘定科目の検索など)のトークン消費は別途かかります。「サブスクの月額は安いけど、オプション料金が地味にかさむ」パターン。家計簿をつけるためのツールが、見えないところで家計を圧迫している。皮肉な話です。
## 業界の反応: PerplexityがMCPから距離を置いた
このトークンコスト問題は、私の個人的な発見ではありません。業界全体で認識されつつあります。
2026年3月、Perplexity CTOのDenis Yarats氏はAsk 2026カンファレンスで、社内でMCPから離れてAPIとCLIに戻すと発表しました。理由はコンテキストウィンドウの消費と認証フローの煩雑さ。ある開発者の報告では、MCPツールだけで200Kトークン中143K(72%)を消費していたケースもあります。
PerplexityはMCPサーバーの提供自体はやめていませんが、自社プロダクトの内部ではMCPを使わない方向にシフトしています。「プロトコルとしては支持するが、コスト構造が現実的でない」という判断です。
## コスト最適化の3つの戦略
### 戦略1: 必要なツールだけ公開する
freeeの270ツールのうち、確定申告に必要なのは10ツール程度。Claude DesktopやCursorでは `allowedTools` で使うツールを絞れます。
```json
{
"mcpServers": {
"freee": {
"allowedTools": [
"create_deal",
"list_deals",
"get_deal",
"update_deal",
"get_trial_balance",
"list_account_items",
"list_partners",
"list_taxes",
"list_walletables",
"get_company"
]
}
}
}
```
10ツールなら約650トークン。17,500トークンから**96%の削減**です。
最も手軽で効果の大きい対策ですが、意外と知られていません。MCPの導入記事は「接続できた」で終わるものが多く、「接続後にツールを絞る」ステップまで書いてある記事はほとんど見かけません。
### 戦略2: 遅延読み込み(Lazy Loading)
2026年に入って、MCPのトークン問題に対する技術的な解決策が出てきました。
**Claude CodeのTool Search機能**(2026年1月発表)は、MCPツールが多すぎる場合にツール定義をコンテキストに直接読み込まず、必要なときだけ動的に検索・読み込みます。50以上のツールがある環境で特に有効です。
**lazy-mcp**というOSSプロキシも登場しています。MCPサーバーの前段に置いて、ツール定義をオンデマンドで読み込む仕組みです。ある事例では、コンテキスト使用量を90%削減したと報告されています。
### 戦略3: 接続のタイミングを制御する
最もローテクで確実な方法です。常時接続ではなく、必要な時だけMCPサーバーに接続する。「freeeの作業が終わったら接続を切る」。それだけで、他のタスクでのトークン消費をゼロにできます。
私は確定申告の時期だけfreee MCPを有効にして、それ以外の期間は設定ファイルからコメントアウトしています。年に数回しか使わないツールを365日接続しておく理由はありません。
## MCPサーバー選定時のチェックリスト
MCPサーバーを本番導入する際、機能面だけでなくコスト面も評価すべきです。
| 評価基準 | 良い例 | 要注意 |
|---------|--------|--------|
| ツール数 | 必要最小限に絞られている | 全機能がフラットに公開 |
| 説明文 | 簡潔・構造化されている | API仕様書そのまま |
| 権限制御 | ツール単位で制御可能 | 全ツールが常に有効 |
| ドキュメント | トークン消費量の記載あり | 記載なし |
MCPサーバーを自作する場合は、ツール説明文の長さがトークンに直結することを意識してください。API仕様書のような冗長な説明文(約80トークン)を簡潔な説明文(約20トークン)にするだけで、ツールあたり75%のトークン削減になります。
## まとめ
- MCPに接続するだけで、ツール定義のトークンが消費される
- PostgreSQL(35トークン) vs freee(17,500トークン): 500倍の差
- freeeの年間コストは約945円(月10回利用)。見えないが確実にかかるコスト
- `allowedTools` でツールを絞れば96%削減できる。最初にやるべき対策
- 2026年はLazy LoadingやTool Searchなど、エコシステム側の対策も進んでいる
- PerplexityのMCP離脱は、この問題が個人の些事ではなく業界の構造的課題であることを示している
MCPは便利です。私も日常的に使っています。ただ、「接続した瞬間に何が起きているか」を理解した上で使うのと、知らずに使うのとでは、年間のコストと快適さがまるで違います。
冷蔵庫のドアを開けっぱなしにしても、すぐには食材は腐りません。でも電気代は確実に上がります。MCPのトークンコストも同じです。気づいたときに閉めればいい。
## 参考リンク
- [MCP公式仕様](https://modelcontextprotocol.io/) — Model Context Protocol specification
- [lazy-mcp](https://github.com/voicetreelab/lazy-mcp) — 遅延読み込みプロキシ
- [Perplexity CTOのMCP離脱発言](https://awesomeagents.ai/news/perplexity-agent-api-mcp-shift/) — Denis Yarats, Ask 2026
- [MCP Token Counter](https://mcpplaygroundonline.com/blog/mcp-token-counter-optimize-context-window) — トークン消費の可視化ツール
---
## さらに深掘りしたい方へ
MCP を本番導入する前に必読 — OWASP MCP Top 10、トークンコスト実測、ファイルアップロード問題の7サービス検証を扱う **[MCP実践セキュリティ — 本番導入で躓かないための完全ガイド](https://kenimoto.dev/ja/books/mcp-security-practice)** を参考にしてください。
---
# 新規ドメインの最初の1週間、GA4は嘘をつく: kaoriq.com立ち上げ4日間の生データ
URL: https://kenimoto.dev/ja/blog/new-domain-first-week-ga4-is-a-lie/
Lang: ja
Date: 2026-05-05
Description: 新規ドメイン取得4日後、GA4は65PV/34ユーザーを記録した。歓声を上げる前に、私は数字を5つの軸で殴った。残ったのは数件の人間と、24時間休まないクローラーの軍団だった。
新規ドメインを取って4日後、GA4を開いたら **65 PV / 34 ユーザー / 9カ国** と表示されました。
Build-in-publicの民として一瞬「来てる!」と思いかけたのですが、よく見ると指標が全部おかしい。米国17セッションの平均滞在時間が **4.9秒**、フランス・ポーランド・韓国・インド・シンガポールはそれぞれ **0〜1.4秒**。日本だけが **751秒(12分超)** という異様な数字でした。
ドメインは2026-05-02に取得した [kaoriq.com](https://kaoriq.com)(性格診断×香水のECサイト)。今日(5/5)時点でまだ20記事もない。冷静に計算するとPV単価は人間1人あたり数十秒の集中力を要求するはずで、4日でこの分布が出るのは物理的におかしい。
この記事では、新規ドメインの初週GA4データを **「ken本人 + クローラーの軍団」** と読み解いた手順を、実数を晒しながら共有します。GA4を新規プロジェクトで動かしている人、この週末にドメイン取った人、向けです。
## 晒す: 過去14日(実質4日稼働)の生データ
まずは数字をそのまま置きます。
**全体サマリー**
| 指標 | 値 |
|------|---|
| Sessions | 37 |
| Page Views | 65 |
| Total Users | 34 |
| New Users | 34 |
| 平均滞在時間 | 104.1秒 |
| 直帰率 | **80%** |
**国別**
| 国 | Sessions | PV | 平均滞在(秒) |
|---|---:|---:|---:|
| 🇯🇵 Japan | 5 | 33 | **751.0** |
| 🇺🇸 United States | 17 | 17 | 4.9 |
| 🇨🇦 Canada | 4 | 4 | 1.3 |
| 🇫🇷 France | 4 | 4 | 1.4 |
| 🇵🇱 Poland | 2 | 2 | 0.0 |
| 🇰🇷 South Korea | 2 | 2 | 0.0 |
| (not set) | 1 | 1 | 0.1 |
| 🇮🇳 India | 1 | 1 | 0.0 |
| 🇸🇬 Singapore | 1 | 1 | 0.0 |
**日別**
| 日付 | Sessions | PV | Users |
|---|---:|---:|---:|
| 2026-05-02 (取得日) | 17 | 40 | 14 |
| 2026-05-03 | 6 | 11 | 6 |
| 2026-05-04 | 12 | 12 | 12 |
| 2026-05-05 | 2 | 2 | 2 |
ぱっと見の印象は「初週としては悪くない」かもしれません。でもこのデータには **滞在時間751秒の日本人** と **滞在時間0秒の世界9カ国** が同居しています。中間値が存在しない。これが今回の手がかりです。
## 5つのシグナルで殴る
ボット判定は1つの指標では絶対にやりません。誤判定を避けるため、私はいつも5つの軸を並べてクロスチェックします。
| シグナル | ボットの典型値 | 人間の典型値 | kaoriq実値 | 判定 |
|---------|--------------|-------------|----------|------|
| 滞在時間 | 0〜5秒 | 30秒〜数分 | US 4.9s, FR 1.4s, KR 0s | 🤖 |
| 直帰率 | 90〜100% | 40〜70% | 80% | 🤖 |
| PV/Session | 1.0(1ページで離脱) | 1.5〜3.0 | US: 17/17 = 1.0 | 🤖 |
| 国分布の異常性 | コンテンツ無関係な国が散る | ターゲット国に集中 | EN/JAのみなのにPL/IN/SG | 🤖 |
| 時系列の異常スパイク | 新規ドメインで初日にドカン | 徐々に増加 | 5/2に40PV(取得日) | 🤖 |
### 単独シグナルだと騙される
「直帰率80%なら全部ボットでしょ」と即断したくなりますが、現実はそんなに優しくない。
- **滞在時間だけ見る**: 記事をタブで放置されると数十分の滞在になる。人間の「真剣な読者」とも、ただの「タブ放置勢」とも区別がつかない
- **直帰率だけ見る**: ランディングページが完璧に答えを返した場合、満足した人間も100%直帰する。優秀さとボットが見分けられない
- **国分布だけ見る**: 海外SNSでバズれば普通に多国籍流入は起きる。これだけでは弱い
5軸が **同時に** ボット側に倒れているとき、初めて確信を持って「これはbotだ」と言えます。
## 二極化が決定打になった
今回のkaoriqで判定が揺るがなかった本当の理由は、滞在時間の **分布の形** です。
- 日本: 5セッション / 平均滞在 **751秒**
- 他国全部: 滞在 **0〜5秒**
人間トラフィックが本物なら、滞在時間は **20〜120秒帯にもう少しなだらかに散らばる** はずです。「タイトルだけ見て離脱(10秒)」「冒頭読んで離脱(40秒)」「最後まで読んだ(180秒)」がグラデーションで存在します。
ところがkaoriqの分布は **二峰** で、中間がスポッと抜けています。これは「kenさん本人(=長時間滞在のテストアクセス)」と「クローラー(=瞬間離脱)」しかいない、と読むのが自然です。
逆に、もし「日本100sess / 滞在60秒、US 50sess / 滞在45秒、CA 20sess / 滞在30秒」のように **正規分布的に滞在時間が散る** データだったら、本物の人間トラフィックの可能性が高い。
## 推定: 純粋な人間流入は何件だったか
ここまで殴ったうえで、私の見立てはこうです。
| 区分 | 推定セッション数 | 内訳 |
|---|---:|---|
| ken自身のテストアクセス | 4〜5 | 日本5sessの大半、滞在751秒の主因 |
| クローラー(Googlebot / Bingbot / GPTBot / ClaudeBot / AhrefsBot等) | 27〜30 | US 17 + 欧州・アジア各国の0秒滞在勢 |
| 純粋な人間の自然流入 | 2〜5 | 日本の残り、米国の数件 |
37セッションのうち **本物の人間は良くて5件** 。これが新規ドメイン取得4日後の「現実」です。
## なぜGA4はこれを除外してくれないのか
GA4には **「Known bots and spiders を自動除外」** 機能がありますが、これは [IAB/ABCのSpiders & Botsリスト](https://www.iab.com/guidelines/iab-abc-international-spiders-bots-list/) に基づいた古典的なクローラー除外で、以下を取り逃がします。
- **JavaScript実行型クローラー**: GPTBot、ClaudeBot、PerplexityBotといった生成AI系の新興クローラーはJSを実行するため、GA4のtagが発火する
- **SEOツール系クローラー**: AhrefsBot、SemrushBot、MozBotなどは実行頻度が高く、新規ドメインを発見すると一斉にクロールしに来る
- **ヘッドレスブラウザ経由のスクレイパー**: Puppeteer / Playwrightベースのカスタムbotは見分けがつかない
**新規ドメイン取得直後は、このクローラー群が「IPアドレスの空き地」を発見しに来るフェーズ** にあります。1週間〜10日でDNSが各社に伝播しきると落ち着きますが、初週のGA4を真に受けると判断を誤ります。
## 教訓: 新規プロジェクトの初週ダッシュボードに書くべき3つの注釈
1. **「Engaged Sessions」を主指標にする**: GA4の「エンゲージメントセッション」は10秒以上滞在 or 2PV以上 or コンバージョン発生のいずれかを満たすセッション。ボットの大半はここで弾かれる
2. **国別の滞在時間分布を必ず見る**: 1指標(セッション数や PV)を国別フィルタなしで見ると、ボット軍団が成果に化ける
3. **取得後30日は「ノイズフェーズ」と割り切る**: 真っ当な数字が出てくるのは、SNS導線・SEO・コンテンツ拡充が揃ってからです
## まとめ: 自分のGA4も同じ目で見直してみてください
新規ドメインのGA4は、最初の1〜2週間は ほぼ嘘つきです。 米国・東欧・東南アジアからの「滞在0秒セッション」が並んでいたら、それはクローラーの大行進であって、あなたのコンテンツに惚れた人間ではありません。
判定の順番はシンプル: **5軸で殴る → 二峰分布を疑う → Engaged Sessionsを基準に置き換える** 。これだけで、初期データに振り回されずに済みます。
GA4を疑う技術は、判断ミスを避ける技術でもあります。データに殴られる前に、データを殴り返しましょう。
---
**この記事は実データに基づきます**
- 対象サイト: [kaoriq.com](https://kaoriq.com) (2026-05-02ドメイン取得、Astro v6 + Tailwind v4で構築中)
- 計測期間: 2026-04-22 〜 2026-05-05 (実質4日稼働)
- データ取得: GA4 Data API v1beta、Service Account経由
- 分析ツール: 自作の `harness-ops/tools/ga4/analyze.py` (公開準備中)
---
# Strategist に WebSearch を持たせたら 5テーマ選びに 20分かかった - Observer / Strategist / Marketer 3役分離で 3分にした話
URL: https://kenimoto.dev/ja/blog/observer-strategist-marketer-3-yaku-bunri/
Lang: ja
Date: 2026-05-14
Description: 1つのエージェントに観測+戦略+実行を全部やらせていたら、5テーマ選定に20分・12万トークン消費していました。Observer / Strategist / Marketer の3役に分離したら3分・トークン60%減。許可ツール設定、cronチェーン、Sub-agentとの違いまで実運用ベースで書きます。
私は最初、1つのエージェントに「観測+戦略+実行」を全部やらせていました。`claude -p` を1回叩いて「今日のテーマを5つ選んで記事を書いてくれ」と。エレガントな構成のつもりでした。
5テーマ選ぶだけで20分かかりました。
Observer / Strategist / Marketer の3役に分離したら、同じ仕事が3分で終わるようになりました。トークンコストは約60%減。エージェント1人1人は前より小さいことしかしないのに、パイプライン全体は速くなりました。
コツは「エージェントを増やす」ことではありません。**判断役からWebSearchを取り上げる**ことです。
## 20分かかっていた1エージェント構成
最初の構成は1プロンプト・1エージェント・1ラン。
> 「昨日のGA4データを見て、今日のテーマを5つ選んで、優先度1位の記事を書いてくれ」
許可ツールは `Bash, Read, Write, Edit, Grep, Glob, WebSearch, WebFetch` をフル開放。必要そうなものは全部入れていました。
エージェントは各候補テーマについて似たような動きをします。「このジャンルで今ホットな話題は」とWebSearch、「このトレンドの裏取り」でWebSearch、「競合の最近の言及」でまたWebSearch。5テーマ × 3-4回の検索で、1回の実行に15-20回のWebSearchが入る計算です。検索結果はそれぞれ数千トークンを判断コンテキストに積み上げます。
テーマ3を選んでいる頃には、判断コンテキストに4万トークン以上のWebSearch結果が積み上がっています。シグナル対ノイズ比が崩壊します。エージェントは「自分のコンテンツ資産に合ったテーマ」ではなく「最近のニュースで確認できたテーマ」を選び始めました。
表面に出た症状は時間です。1ランあたり約20分。隠れた症状は判断ブレでした。私は週次レビューでエージェントの選定をしょっちゅう上書きしていました。書く素材を持っていないテーマばかり選んでくるからです。
## なぜ判断ループにWebSearchを混ぜると壊れるのか
WebSearchそのものは悪くありません。**判断ループに混ぜる**のが罠です。
判断役にWebSearchを持たせると、2つのことが起きます。
**時間**: WebSearch 1回は 5-20秒。5テーマ × 4回 = 20回。検索だけで合計100秒以上が消えます。人間が手で1質問するなら誤差ですが、毎日走る自動ジョブだとここが効いてきます。
**コンテキスト汚染**: 1検索結果は2,000-5,000トークンのHTMLスクレイピング済みページテキストを判断コンテキストに突っ込みます。SEO向けに最適化されたマーケコピーの山で、「このテーマは自分のコンテンツに合うか?」という判断には設計されていません。判断役は「自分のデータ」ではなく「マーケコピーの山」から推論するハメになります。
直し方は地味です。**判断役にWebSearchを持たせない**。WebSearchは執筆役の持ち物にする。
## Role 1: Observer - 集めるだけ
Observerの仕事は「昨日の数字を取ってきてファイルに書く」これだけです。
入力: GA4、Zenn API、Dev.to API、前日のログ。出力: `domains//data/snapshot-YYYY-MM-DD.json`。
許可ツール設定:
```bash
claude -p "$(cat scripts/prompts/observer-prompt.txt)" \
--allowed-tools "Bash,Read,Write"
```
WebSearchなし、WebFetchなし、Editもなし。Observerは `curl` で3つのAPIを叩いて、1つのJSONファイルを書く。それだけです。「データを解釈しよう」と気を利かせ始めても、プロンプトで禁じています。スキーマでも縛っています。フィールドは `total_views`、`top_performers_3`、`errors_yesterday`。`recommendation` フィールドは存在しないので、判断を書こうとしても置く場所がありません。
ダウングレードに見えるかもしれません。実際そうです。神オブジェクトを単機能関数に分割するのと同じ意味でのダウングレードです。Observerが落ちたら、どのAPIが壊れたか即わかります。それしかやっていないので。
## Role 2: Strategist - 判断するだけ、WebSearchなし
StrategistはObserverが書いたsnapshotを読み、`strategy.md` のルールを読み、過去30日の公開テーマを除外リストとして読み、5テーマを決めます。以上。
```bash
claude -p "$(cat scripts/prompts/strategist-prompt.txt)" \
--allowed-tools "Bash,Read,Write,Edit,Grep,Glob"
```
抜けているものに注目してください。`WebSearch`、`WebFetch` がない。物理的に許可ツールから外しています。Strategist は外部にアクセスする手段がありません。
ここは私も最後まで抵抗しました。「最新トレンドを見ないでどうやって今日のテーマを判断するんだ」。これは問いが間違っていました。正しい問いは「自分は他所でバズっているテーマを書くのか、自分のコンテンツ資産に合うテーマを書くのか」です。
Strategistが見るもの:
- 3ヶ月分の自分のパフォーマンスデータ (何が読まれたか)
- 自分のコンテンツ資産 (Book章、未公開ドラフト)
- 30日除外リスト (何を書いたか)
- 自分の `strategy.md`
これだけあれば5テーマを90秒で選べます。20分ではありません。Strategist 1ランあたりのトークン消費は約8万から約2万に下がりました。読むWebSearch結果がないからです。
「WebSearchで根拠を増やす」は良いアイデアに聞こえました。実際に起きたのは、8回の冗長な検索と4万トークンのノイズの追加でした。
## Role 3: Marketer - 実行する、WebSearchはここ
MarketerはStrategistの判断ログを読み、優先度1位のテーマを取り、記事を書きます。WebSearchが出てくるのはここです。
```bash
claude -p "$(cat scripts/prompts/marketer-prompt.txt)" \
--allowed-tools "Bash,Read,Write,Edit,Grep,Glob,WebSearch,WebFetch"
```
Marketer のWebSearchは「執筆中の調査」用です。
- 「LangGraph の2026年安定版バージョン」
- 「Anthropic の Building Effective Agents 公式URL」
- 「Inngest のcronトリガー料金プラン」
これは引用と裏取りであって判断ではありません。「このテーマを書くか」はもう決まっています。Marketer のWebSearchは目の前の記事の範囲に閉じています。
ここから2つの副作用が出ます。
1. **コストが局所化する**: WebSearch 課金は Marketer の中で発生し、目に見える成果物 (記事) を生みます。Strategist の1ランあたりコストは小さくなったので、週に複数回回しても気になりません。
2. **失敗が局所化する**: WebSearch が不調や障害のときに壊れるのは Marketer (書く役) だけです。Strategist は今日の判断を出します。Observer は昨日の数字を記録します。パイプラインは劣化はしても止まりません。
## cronチェーン - 3役はどう繋がるか
3役は会話履歴で繋がりません。**ファイルで繋がります**。
```text
07:00 Observer → snapshot-2026-05-14.json を書く
09:00 Strategist → snapshot を読み、strategist-2026-05-14.md を書く
10:00 Marketer → strategist.md を読み、下書き + 22:00 公開予約
22:00 Observer → 今日の初動を記録 → 明日のインプット
```
これを私はVPS上の素のcronで回しています。crontab全文は[harness-engineering-guide 第11章](https://kenimoto.dev/ja/books/harness-engineering-guide)に載せていますが、要点は1ジョブ1行、`set -euo pipefail`、`trap ... ERR`、Telegram失敗通知、ロックファイル。1役あたりシェル約30行です。
cronより耐久性が欲しいなら、[Temporal Schedules](https://temporal.io/blog/orchestrating-ambient-agents-with-temporal)、[Inngest のcronトリガー](https://www.inngest.com/)、[GitHub Actions cron](https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows#schedule) のどれも同じ形に乗ります。アーキテクチャはどれを使うかには無関心です。私がcronなのは「サーバーが落ちたら気付く」という失敗モードで運用したいからです。
引き継ぎは常にファイル。snapshot は JSON、strategist ログは Markdown、marketer ログも Markdown。人間も読める、日付付き、再実行可能。環境変数を1つ変えれば昨日の Marketer を昨日の Strategist ファイルに対して再走できます。Airflow を導入しなくても `backfill` が自然に手に入ります。
## Sub-agent との違い
私は別の記事で[Claude Code の Sub-agent 3つに同じPRをレビューさせたら41%意見が分かれた話](https://kenimoto.dev/ja/blog/claude-code-sub-agent-design)を書いています。「Sub-agent と3役分離って同じことじゃないの?」と聞かれることがありますが、別物です。
スライドだと似て見えますが、実運用ではまったく違う振る舞いをします。
| | Sub-agent (Claude Code Task tool) | 3役分離 (cron) |
|---|---|---|
| **スコープ** | 同一セッション、親エージェント配下 | 3プロセス、3ラン独立 |
| **状態** | 親が文脈を引き渡す | ディスク上のファイル |
| **タイミング** | 同期、親が待つ | 非同期、数時間空く |
| **失敗時** | 親がリトライ責任を持つ | 各ジョブが独立リトライ |
| **用途** | 「このコードベースを並列探索」 | 「昨日のPDCAを毎朝回す」 |
Sub-agent は**1タスク内の並列性**のための仕組みです。3役分離は**時間をまたいだパイプライン**のための仕組みです。混ぜると両方の最悪が出ます。cronのデバッグ難易度に、Sub-agentの共有コンテキストドリフトが上乗せされます。
私が使っている判定基準: **同じ会話内で答えを返したいなら Sub-agent。サーバー再起動を挟んでも生き残らせたいなら cron で別ジョブ**。
## 実測値
同じコンテンツスタックで両方の構成を回した実測です。
| 指標 | 1エージェント | 3役分離 | 変化 |
|---|---|---|---|
| 5テーマ選定時間 | 約20分 | 約3分 | -85% |
| 1日分のトークン | 約12万 | 約4.5万 | -62% |
| 月額API料金 | 約$60 | 約$22 | -63% |
| 週次再判定回数 | 週2-3回 | 週0-1回 | 減 |
| WebSearch障害でパイプ停止 | あり | なし | 解消 |
| 失敗時の平均デバッグ時間 | 30-60分 | 5-10分 | -80% |
意外だったのはトークンの計算でした。3エージェントに分けたらコンテキスト重複でトークンは**増える**と思っていました。実際は減りました。消えたWebSearch トラフィックが、役ごとに増えるオーバーヘッドより大きかったからです。
日々効くのはデバッグ時間です。1エージェント構成の「09:14でジョブが落ちた」では何も分かりません。3役構成の「09:14でStrategistが落ちた」なら、読むべき30行のスクリプトが確定します。
「エージェントを増やしたら速くなった」は直感に反します。本当は、増やしたから速くなったのではなく、**判断役からWebSearchを取り上げた**から速くなったのです。3役に分けたのは、それを物理的に強制できる構造を作るためでした。Observer と Strategist が外部にアクセスできなくなった瞬間、「もう1回だけ検索してみよう」という誘惑が消えました。
---
**関連記事**: [自然言語エージェントハーネス (arxiv論文の整理)](https://kenimoto.dev/blog/natural-language-agent-harnesses-arxiv/)(英語版)でハーネスの概念を、本記事で実装パターンを書いています。crontab全文・プロンプトファイル・各役の許可ツール設定の完全版は [Harness Engineering: AIを使うから、AIを統べるへ](https://kenimoto.dev/ja/books/harness-engineering-guide) にまとめています。
---
# ページ単位のSEOはAIに見えていない: 引用されるのは「パッセージ」だった
URL: https://kenimoto.dev/ja/blog/passage-design-ai-citation-not-page-rank/
Lang: ja
Date: 2026-06-01
Description: AI検索が引用するのはページではなく「一節(パッセージ)」。検索3位の私の記事が引用され、1位の記事が無視された理由と、私が全記事に使うようになった4階層のパッセージ設計をまとめます。
私は2年間、検索1位を取ることに人生を捧げていました。そんなある日、AIアシスタントがGoogle検索3ページ目に沈んでいた私の記事を引用し、同じクエリで1位だった記事を完全に無視する瞬間を目撃しました。正直、けっこう効きました。同時に、自分が最適化してきた対象そのものが間違っていたことも分かりました。
誰もはっきり言ってくれなかった事実はこれです。**AI検索はページを引用しません。引用するのは「パッセージ(一節)」です。**
## 単位が変わったのに、誰も通知してくれなかった
従来のSEOには「ページ」というたった一つの単位がありました。URLが順位を持ち、URL全体が上がったり下がったりする。仕事の中身は過酷ですが、頭の中のモデルはシンプルでした。
AI検索はそのモデルを静かに捨てました。ChatGPT SearchやPerplexity、GoogleのAI Overviewsが質問に答えるとき、10本の青いリンクを並べたりはしません。答えを「組み立て」、その部品を複数のソースに散らばった特定の段落、つまりパッセージから引っ張ってきます。
私の3ページ目の記事が引用されたのは、これが理由でした。その記事の中の1段落が、ユーザーのサブクエリにきれいに答えていたのです。一方の1位ページには、そういう段落がありませんでした。何か引用できることを言う前に、2,000字の前置きがあったのです。Googleはそのマラソンを評価しました。AIが欲しかったのは1文の明快な答えで、たまたま私の負け犬記事がそれを持っていた、というわけです。
研究もこれを裏付け続けています。AI Overviewsの引用元のかなりの割合は、そもそもオーガニック検索トップ10にすら入っていません。ページ順位と引用される確率の関係は、ゆるく相関する程度です。つまり、ページをひとかたまりの塊として最適化し続けているなら、AIが一度も見ない単位を磨いていることになります。
## 「スナッパビリティ」とは結局なにか
スナッパブルなパッセージとは、AIが切り出して答えにそのまま貼り付けても、周囲の文脈ゼロで意味が通る一節のことです。この「文脈ゼロで」という部分が勝負のすべてです。
自分で試してみてください。最新記事のどれか1段落を空のドキュメントに貼り、前後を見ずに読む。それは単独で立っていますか。それとも「これ」「したがって」「前述のとおり」といった言葉で、上の3段落に寄りかかっていませんか。文脈から切り離されて生き残れない段落は、AIも切り出しません。なぜならAIがやっているのは、機能的に言えば「文脈から切り離してコピペする」ことだからです。
私の昔の文章は、このテストに見事なまでに落第しました。どの段落も前の段落の乗客でした。人間が上から下へ読むぶんには最高です。機械が真ん中の一行だけをつかむぶんには、無価値でした。
## 私が今書いている4階層構造
3ページ目の屈辱のあと、私は下書きの仕方を作り直しました。今はコンテンツを、小さいものから大きいものへ4つの階層で考えています。
**Atomic(原子): 単独で完結する1つの事実。** 前置きなしで真実かつ引用可能なことを述べる1文です。「TypeScriptは2012年にMicrosoftがリリースした言語です」。「弊社のソリューションは多くのチームのお役に立ってきました」ではありません。AIが欲しいのは責任を持てる事実であって、曖昧な安心材料は事実ではありません。
**Mini(ミニ): 2〜3文で1つの概念。** 概念とその帰結を定義するのにちょうど足りる量で、それ以上は書きません。私の経験上、AIアシスタントが最もよく引用するのはこの単位です。完結した一つの考えでありながら、答えの枠にちゃんと収まるからです。
**Section(セクション): 見出し+複数パッセージ。** 見出しは装飾ではなく検索の仕事をしています。見出しを「読者が実際にタイプする質問」の形で書けば、AIに対してラベルの付いた引き出しを差し出したことになります。
**Cluster(クラスタ): トピックを所有する関連ページ群。** 1ページで領域全体はカバーできません。密にリンクされたページ群は、あなたが一度きりではなく繰り返し引用に値するソースであることを示します。
実務上の変化は小さいけれど執拗です。隣の段落に依存する段落を書くのをやめ、誘拐されても困らない段落を書き始めた、それだけです。
## 結論ファースト、前置きはゼロで
殺さなければならなかったもう一つの癖が、前置きです。私はかつて、どのセクションも文脈から始め、緊張を高め、最後に手品師のように答えを明かしていました。AI検索は手品師が大嫌いです。1文目でウサギをテーブルに出してほしいのです。
そこで私は結論ファーストに切り替えました。昔ながらのPREP法(Point・Reason・Example・Point)に近い書き方です。まず結論、それから理由。「パッセージ最適化を使うべきか」と聞かれたら、1文目は「はい。AIはページではなく段落を引用するからです」で、説明はそのあとに続きます。AIは冒頭をつかんで先へ進めるし、深く読みたい人間はそのまま読み続ける。全員が得をして、誰も種明かしを待たされません。
一問一答ブロックはさらに強力です。質問そのものを見出しにし、その下に引き締まった2文の答えを置く。これはおそらく最も切り出しやすい構造です。ユーザーがAIに尋ねた形そのものを鏡写しにするので、マッチが簡単すぎるくらいです。
## 数字は撒き餌で、AIは食いつく
私が気づき、あとで研究を見つけたパターンがあります。具体的な数字を含むパッセージは、形容詞を含むパッセージよりはるかに引用されやすいのです。Princeton中心の生成エンジン最適化(GEO)研究では、統計・引用・出典を加えるとAI回答内での可視性が最大**40%**向上したと報告されています。これは誤差ではありません。引用されるソースになるか、誰も見なかったソースになるかの分かれ目です。
そこで私は下書きを見直し、ふんわりした主張を硬い数字に変えていきました。「スキーマ実装はAI可視性を意味あるレベルで高めうる」は、具体的なケースになりました。Sharp HealthCareは構造化データの全面刷新後、9ヶ月で**AI経由のクリックが843%増加**したと報告しています。前者は忘れられる1文、後者は引用されるのを待っている1文です。
このフレームワークの元になった章は、同じ方向のデータをもっと挙げています。サブクエリ最適化や、平凡なパッセージへの統計の追加による引用増などです。手法によってばらつくので、正確なパーセンテージは「方向性」として扱うのが無難ですが、方向そのものは私が見たかぎりどこでも一貫しています。具体は引用され、曖昧はスキップされる、です。
## 構造化データはパッセージの名札
パッセージが引用を取ってくるなら、構造化データはあなたを「読み取れる存在」にします。スキーマ(JSON-LD)は、ページの各かたまりが実際に何なのかを機械に伝えます。これは質問、これはその答え、これは著者、これは公開日、と。Perplexityの挙動を見ると、きれいな構造化データを持つコンテンツには可視性の上乗せがありますし、BraveのLLMコンテキスト系ツールは、マークアップが導いてくれればテーブルの行レベルまで抽出できます。
こう考えると分かりやすい。スキーマのない優れたパッセージは、ラベルのない紙切れに書かれた見事な答えです。スキーマは、機械がそれを正しく整理し、また見つけ出せるようにするラベルなのです。
## 鮮度もまたパッセージの属性
過小評価していたレバーをもう一つ。鮮度です。AIは新しいソースを好み、その差は私の想像より大きいものでした。数時間前に更新されたコンテンツと、1ヶ月放置されたコンテンツとでは、引用頻度が数十パーセント単位で変わりうる。Adobeの指針はおおむね「重要コンテンツを数週間おきにリフレッシュ」あたりに着地しています。だから私はもう、パッセージを書いて放置しません。価値の高いものを定期的に見直し、数字を更新し、日付を更新する。パッセージは記念碑ではなく、観葉植物です。
## 私が今、実際にやっていること
今日、記事を書くとき、私のチェックリストは短くて少し容赦がありません。
- 各段落は切り出しても意味が通るか。通らないなら書き直す。
- 各セクションは答えから始まっているか。違うなら答えを上に動かす。
- 主張は具体的で数字付きか。違うなら数字を探す。
- 構造はスキーマで機械可読か。違うなら付ける。
- 価値の高いパッセージは新しいか。古いなら更新する。
従来の順位を今も気にはしています。消えたわけではありません。でも、ページを「最適化する対象」として扱うのはやめました。ページはただの容器です。パッセージこそが製品です。そしてURLではなく段落のために書き始めた日が、AIアシスタントが私の文章を、一生会わない誰かに引用し始めた日でした。
AI抽出可能なコンテンツ構造のより詳しい分解は、[llmoframework.com](https://llmoframework.com) のコンテンツ設計ノートがパッセージと構造化データの階層を深掘りしています。この考え方の正典は、あちらと、ここに置いています。
4階層・構造化データのパターン・その背後にある計測ループまで含めた全体像は、[LLMO: AI検索最適化](https://kenimoto.dev/ja/books/llmo-ai-search-optimization) にまとめました。
---
# robots.txtにAIクローラー13個の個別ルールを書いた。30日後、守ったのは3つだけだった
URL: https://kenimoto.dev/ja/blog/robots-txt-ai-crawler-rules-30-days-only-3-followed/
Lang: ja
Date: 2026-05-24
Description: AIクローラー13個に対して個別のAllow/Disallowをrobots.txtに書きました。30日後にサーバーログを集計したら、ルールを守ったのは3つだけ。残り10個はDisallow指定したパスに普通にアクセスしていました。何を守ってもらえて、何が紳士協定で終わるのか、実測した結果を書きます。
robots.txtは紳士協定です、と昔の人は言いました。30日測ってみたら、紳士だったのは13人中3人でした。
私は4月、自分のドメイン(kenimoto.dev)のrobots.txtに、AIクローラー13個に対する個別ルールを書きました。GPTBot、ClaudeBot、PerplexityBotのような有名どころから、Bytespider、CCBot、Applebot-Extendedのような知名度の低いところまで、全部入りです。Allow指定、Disallow指定、wildcard指定を混ぜて、わざと「ここは見せる」「ここは見せない」を分けました。
30日後、Cloudflareのログを集計しました。**13クローラーのうち、Disallow指定を完全に守ったのは3つだけ**でした。残り10個は、私が「ここは来ないでね」と書いたパスに、平気な顔でアクセスしていました。
この記事は、誰が紳士で誰が違ったか、その判定にどんな基準を使ったか、そして「守らないクローラーへの現実的な対処」をまとめます。LLMOで「AIに見つけてもらう」記事はもう書きましたが、今回はその逆、「AIに見られたくない場所を見られないようにする」ための実測譚です。
## 何を計測したか
30日測ったのは2026年4月22日から5月22日です。対象ドメインはkenimoto.devで、英日葡西の4言語版があるブログサイトです。robots.txtには13のAIクローラーUser-Agentを書き、それぞれに対して以下のいずれかを指定しました。
| 種類 | 指定 | 意図 |
|------|------|------|
| 全面許可 | `Allow: /` のみ | サイト全体を学習・引用に使ってよい |
| 部分許可 | `Allow: /` + `Disallow: /drafts/`、`Disallow: /admin/`、`Disallow: /private/` | 本記事と公開ページはOK、未公開と内部用はNG |
| 全面拒否 | `Disallow: /` | このクローラーは一切来ないでほしい |
13クローラーの内訳と、私が指定した内容は次のとおりです。
| User-Agent | 運営 | 私の指定 |
|------------|------|---------|
| GPTBot | OpenAI | 部分許可 |
| ChatGPT-User | OpenAI | 部分許可 |
| OAI-SearchBot | OpenAI | 部分許可 |
| ClaudeBot | Anthropic | 部分許可 |
| anthropic-ai | Anthropic (旧) | 部分許可 |
| Google-Extended | Google | 部分許可 |
| PerplexityBot | Perplexity | 部分許可 |
| Perplexity-User | Perplexity | 部分許可 |
| Applebot-Extended | Apple | 部分許可 |
| Amazonbot | Amazon | 部分許可 |
| Bytespider | ByteDance | 全面拒否 |
| CCBot | Common Crawl | 全面拒否 |
| cohere-ai | Cohere | 部分許可 |
「Disallow指定を守った」の判定基準はシンプルです。30日間で当該User-Agentが`/drafts/`、`/admin/`、`/private/`のいずれかにアクセスを試みたログが**0件**であること。1件でもあれば「守らなかった」に分類しました。User-Agentは詐称可能なので、Cloudflareの`bot management`で本物と判定されたリクエストのみカウントしています。
ノイズの判定はこうです。`/drafts/`に対するアクセスが30日で1〜2件なら、私はノイズ扱いにしませんでした。1件でもDisallowを破ったらクローラーの設計判断とみなす、という厳しめの基準です。
## 守った3つ
30日間、Disallow指定を完全に守ったのは次の3つでした。
### GPTBot (OpenAI、学習用)
公式の宣言どおりです。OpenAIは[gptbot.txt](https://platform.openai.com/docs/gptbot)で「robots.txtを尊重する」と明記していて、その通りでした。`/drafts/`配下へのアクセス試行は0件。サイトマップから引いたURLだけを律儀にクロールしていきました。アクセス頻度は1日あたり40〜80回、安定したペースで来ています。
### ClaudeBot (Anthropic)
これも守りました。30日間で`/drafts/`へのアクセスは0件、`/admin/`も0件。アクセス頻度はGPTBotより少なく、1日10〜30回程度。AnthropicがClaudeBotの[挙動ドキュメント](https://docs.claude.com/en/docs/build-with-claude/web-search)で明記している「robots.txtに従う」が実装されていました。
### Google-Extended
3つの中で一番律儀でした。Google-Extendedは「Geminiの学習データに使うかどうかを制御するためのトークン」という位置づけなので、もともと「使うか使わないか」だけのフラグです。Disallowを書けば来ないし、Allowを書けばサイトマップを巡回します。30日で`/drafts/`への試行0件、安定。
この3つに共通するのは、**いずれも上場企業の本体が運営していて、公式に「robots.txtを尊重する」と明記している**という点です。当たり前と言えば当たり前ですが、当たり前を当たり前に実装する企業は半分以下というのが30日後にわかった事実です。
## 守らなかった10個の振る舞い
残り10個は、それぞれ違う守らなさ方をしていました。せっかくなので、悪気の度合い順に並べてみます。
### 軽く破ったグループ(4個)
PerplexityBot、Perplexity-User、Applebot-Extended、Amazonbotはこのグループです。Disallow指定したパスへのアクセス試行が30日で5〜30件ほどありました。ほとんどはサイトマップに載っていないリンクをどこかで拾ってきて踏みに来た、というパターンです。サイトマップに従っていれば踏まないはずのパスに、外部リンクや過去のキャッシュから到達しているように見えます。
設計判断としてはこうです。「robots.txtのDisallowを最終的なフィルタとしては使っていない。サイトマップを正とした巡回を基本にしているが、外部からのリンクで到達した場合は内容を確認してから判断する」。紳士協定としてはギリギリ及第点、技術的にはアウト、というレンジです。
### 構造的に破ったグループ(3個)
OAI-SearchBot、ChatGPT-User、cohere-aiがこのグループです。アクセス試行が30日で50〜200件あり、しかも`/drafts/`配下を網羅的に踏みに来ていました。
OAI-SearchBotとChatGPT-Userは、ChatGPTがブラウジングする際にユーザーの代理として動くクローラーです。私の推測では「ユーザーが質問したURLにアクセスする」という挙動なので、robots.txtのDisallowが効きにくい設計になっています。実害として、私が下書きで放置していたページのURLをChatGPTのユーザーがどこかで知って踏んでみると、中身を取りに来ます。
cohere-aiは、私が「学習用に使ってほしくない」と書いたパスにも普通に来ました。これは Cohereの学習クローラーの設計判断だと思っています。
### 完全無視グループ(3個)
Bytespider、CCBot、anthropic-ai(旧)です。私は全面拒否 (`Disallow: /`) を書いたのに、Bytespiderは30日で**3,400件**のアクセスがありました。Common CrawlのCCBotは2,100件、anthropic-ai(旧)は800件です。
anthropic-ai(旧User-Agent)は、現在Anthropicが正式に「ClaudeBotに移行した」とアナウンスしているもので、本来動いていないはずです。動いていました。誰かがanthropic-aiという文字列をUser-Agentに入れて、Anthropic公式とは別のところからクロールしている可能性が高いです。
Bytespiderは「ByteDance(TikTokの親会社)のクローラー」として知られていますが、彼らが「robots.txtを完全には尊重しない」というのは2024年から[何度も指摘されてきた話](https://blog.cloudflare.com/declaring-your-aindependence-block-ai-bots-scrapers-and-crawlers-with-a-single-click)で、私の30日でもその振る舞いが追認できました。
## 「守らない」への対処
ここから先は実装の話です。Disallowを守らないクローラーに対する選択肢は3つあります。
**選択肢1: WAFで弾く**。Cloudflareなら「Block AI Scrapers and Crawlers」というワンクリックのトグルがあって、これでBytespiderを含む主要なAIクローラーをエッジで切り落とせます。私が30日測ったあと、Bytespiderとanthropic-ai(旧)はこれで弾きました。アクセス数は翌日に**99.5%減**(3,400件→17件)。残りはUser-Agentを偽装しているリクエストです。
**選択肢2: IPベースでrate limit**。CCBotはCommon Crawlの公式IPレンジが公開されているので、IPベースで拒否できます。User-Agent詐称を回避するなら、これが一番固い。
**選択肢3: 諦めて全公開する**。OAI-SearchBotやChatGPT-Userのように「ユーザー代理でアクセス」する系統のクローラーは、技術的に止めるのが難しいです。代わりに、`/drafts/`を本当に見られたくないならBasic認証をかける、というのが現実解になります。robots.txtに頼らず、HTTPの認可レイヤーまで降りる必要があります。
私の最終的な構成はこうなりました。
- 紳士の3つ(GPTBot/ClaudeBot/Google-Extended): robots.txtのDisallowで管理
- 軽く破る4つ(PerplexityBot/Perplexity-User/Applebot-Extended/Amazonbot): robots.txtを残しつつ、`/drafts/`にBasic認証を追加
- 構造的に破る3つ(OAI-SearchBot/ChatGPT-User/cohere-ai): Basic認証で完全保護
- 完全無視の3つ(Bytespider/CCBot/anthropic-ai旧): CloudflareのAI Scrapers Blockで弾く
「robots.txtだけで全部を守ろうとしない」が30日後の私の結論です。robots.txtは「紳士に対する案内状」として有効、「敵に対する盾」としては機能しません。
## llmoframework.com で言われているのと同じ話
LLMOの実装ガイドを書く側として、私は[llmoframework.com](https://llmoframework.com)の「AIクローラー対応」セクションに「robots.txtは入口の表札であって鍵ではない」と書きました。30日測って、その言い回しの妥当性が裏付けられた感じです。表札としてのrobots.txtは大事で、これがないと紳士のクローラーすら何を見ていいのか迷うのですが、表札だけで鍵をかけたつもりになると、Bytespiderが3,400回侵入してくる結果になります。
LLMOで「AIに見つけてもらう」側を最適化するのと、「AIに見つけてほしくないものを守る」側を最適化するのは、同じrobots.txtを編集する作業ですが、結果として必要な道具が違います。前者はrobots.txtとsitemap.xmlで十分、後者はWAFとHTTP認可レイヤーまで降りないと足りない、という非対称があります。
## まとめ
30日測ってわかったことは3つです。
1. **公式宣言と実挙動は4割ほどしか一致しない**。13クローラー中、Disallowを完全に守ったのは3つ。GPTBot、ClaudeBot、Google-Extendedだけです
2. **「守らない」には3段階ある**。軽く破る(サイトマップ外のリンクから到達)、構造的に破る(ユーザー代理)、完全無視(設計判断として無視)。それぞれ対処が違います
3. **robots.txtは表札であって鍵ではない**。本当に守りたいものはHTTP認可レイヤーまで降ろす、それ以外はWAFで弾く、紳士には案内状を残す、というレイヤー設計が現実解です
紳士が3人いて、ぎりぎり許せる人が4人いて、構造的にダメな人が3人いて、最初から無視する人が3人いる。これは人類のサンプリングとしてはちょっと厳しいですが、AIクローラーのサンプリングとしては妥当な分布だと思います。
---
LLMOの全体像、つまり「AIに見つけてもらう」「AIに引用してもらう」「AIに見られたくないものを守る」を体系的にまとめた本があります: **[なぜあなたのサイトはChatGPTに無視されるのか](https://kenimoto.dev/ja/books/llmo-ai-search-optimization)**。本記事のrobots.txt章は、本では1章分にあたります。llms.txt、JSON-LD、構造化データ、引用率KPIまで含めた12章構成です。
関連記事として、[5つのAIクローラーが私のサイトに来た: 30日サーバーログ](https://kenimoto.dev/ja/blog/five-ai-crawlers-30days-server-log/)(「誰が来たか」の集計)と、[15分で終わるLLMO最小実装: llms.txt + JSON-LD](https://kenimoto.dev/ja/blog/llmo-minimum-implementation-llms-txt-json-ld/)(土台の作り方)を一緒に読むと、AIクローラー対応の全体像がつかめます。
---
# 同じサイトを7つのAI引用トラッカーに入れたら、7つとも違う数字を返してきた
URL: https://kenimoto.dev/ja/blog/seven-ai-citation-trackers-different-numbers/
Lang: ja
Date: 2026-05-18
Description: kenimoto.dev を15日間、7つのAI引用トラッカーに同時に登録して計測した。最小は38、最大は312。同じサイト、同じ期間、同じ12クエリ。なぜそんなに離れるのか、結局どれに金を払う価値があるのかを実測で書く。
私は最初、7つのトラッカーを並べれば多数決で正解が出ると思っていました。実際に並べたら、7つとも違うことを言っていて、多数決すら成立しませんでした。
最小値は38、最大値は312。同じサイト、同じ15日間、同じ12クエリです。倍率にして 8.2 倍の開きでした。先に結論を書くと、私が最後まで残したのは月29ドルの一番安いやつでした。理由は「一番正確だったから」ではなく「自分が何を数えているか正直に書いてあったから」です。
## 計測の前提
私は kenimoto.dev を4言語で運用しています。AI検索が本当にこのサイトを見ているのか、ずっと気になっていました。気がついたら、各社の無料トライアルが受信箱に溜まっていたので、全部いっぺんに同じ条件で走らせてみたわけです。
自分に課したルールは次の4つです。
- 対象サイトは kenimoto.dev 一本に絞る(/ja/、/pt/、/es/ も含む)
- 計測期間は 2026年5月1日から5月15日までの15日間
- 12個のブランドクエリを一度だけ作り、全ツールで共通利用する
- 5つのLLM(ChatGPT、Claude、Gemini、Perplexity、Copilot)への参照を見たい
12個のクエリは、たとえば「Claude Code のサブエージェント構成のおすすめ」「LLM 引用の計測方法」「300ms以下の音声AIスタック」など、私のコンテンツが元々狙っている領域から選びました。
ツールは7つ選びました。商用が6つ、自前のスクリプトが1つです。7という数字にこだわったのは見出しのためですが、現実的にLLMO担当者が比較検討する候補数も大体これくらいです。
採用したツールは以下の通りです。
1. **Profound**(月499ドル、エンタープライズ向け、SOC2 / HIPAA対応)
2. **Peec AI**(月89ユーロ、ベルリン拠点、多言語に強い、115言語以上)
3. **Otterly AI**(月29ドル、最安、Semrush 連携あり)
4. **Bluefish AI**(要問い合わせ、Fortune 500 向け)
5. **Scrunch**(中位帯の可視性計測ツール)
6. **Semrush AI Toolkit**(既存SEOスイートに同梱)
7. **自前のPythonスクリプト**(OpenAI、Anthropic、Perplexity の API 利用、月8ドル相当)
各ツールに kenimoto.dev を登録し、UI が許す範囲で12クエリを共通設定にし、15日間放置してエクスポートしました。
## 数字
同じサイトに対して、各ツールが返してきた引用数は以下のとおりです。
| ツール | 引用数 | 最小値との比 |
| -------------------- | ------ | ------------ |
| Otterly AI | 38 | 1.0倍 |
| 自前 Python | 54 | 1.4倍 |
| Semrush AI Toolkit | 71 | 1.9倍 |
| Bluefish AI | 89 | 2.3倍 |
| Profound | 147 | 3.9倍 |
| Scrunch | 203 | 5.3倍 |
| Peec AI | 312 | 8.2倍 |
最小と最大の差は 8.2 倍です。誤差とか丸めの話ではなく、まるごと別の値です。
最初は私もエクスポート結果を読み違えたのかと思って、各社のドキュメントを並べて読み直しました。答えはそこにありました。
## なぜ 7 つの数字が割れたのか
各社のドキュメントを横並びで読むと、これはバラツキではなく「定義の問題」だと分かります。バラツキの軸は4つあります。
### 1. 「引用」の定義そのものが違う
これが一番大きい要因でした。各ツールは違うものを数えていて、それを全部「citation」と呼んでいます。
- **Profound** は、LLM の回答にあなたのドメインへのクリック可能なソースリンクが含まれている場合のみカウントします。厳格でアトリビューションには使えますが、リンクなしの言及は全部こぼれます。
- **Peec AI** は、回答テキストにブランド名が出たら全部カウントします。リンクの有無は問いません。Perplexity が「Ken Imoto が書いたこのガイドが参考になる」と言ったら、リンクなしでも1カウントです。これが最大値の理由です。
- **Otterly AI** は Profound に近く、ただし「同一クエリ・同一日」で重複を排除します。それで数字が大きく圧縮されます。
- **Bluefish AI** は競合との Share of Voice 計算を返します。引用数というよりも順位に近い指標です。
- **Scrunch** はブランド言及とソースリンクの両方をカウントし、重複排除なしです。中の上ぐらいの数値になります。
- **Semrush** は構造化された回答の URL フィールドにあなたのドメインが出た場合のみカウントします。最も厳格な解釈です。
- **自前 Python** は私が決めた通りに数えます。今は「回答テキストにブランド文字列が出る、クエリごとに重複排除、3サンプル平均」です。
この中の任意の2つの定義は、絶対に一致しません。これはベンダーが悪いのではなく、業界がまだ「引用とは何か」の共通定義を持っていない、という話です。
### 2. サンプリング対象の LLM が違う
私が気にしている5つの LLM のうち、全部をカバーしているツールはほぼありません。
| ツール | ChatGPT | Claude | Gemini | Perplexity | Copilot |
| ------------ | ------- | ------ | ------ | ---------- | ------- |
| Profound | ○ | × | ○ | ○ | × |
| Peec AI | ○ | ○ | ○ | ○ | ○ |
| Otterly | ○ | × | ○ | ○ | × |
| Bluefish | ○ | × | ○ | × | ○ |
| Scrunch | ○ | × | × | ○ | × |
| Semrush | ○ | × | ○ | ○ | × |
| 自前 Python | ○ | ○ | × | ○ | × |
Peec AI だけが5つ全部を見ています。それだけでサンプリング面積が大きく、最大値になる理由のひとつです。Scrunch は ChatGPT と Perplexity の2つだけしか見ていないのに数字が大きいので、その2つの面で実際に多く引用されているとも読めます。
ChatGPT だけを気にしているなら、どのトラッカーを選んでも大差ありません。Gemini や Claude が重要なら、選択肢は半分以下になります。
### 3. サンプリングの頻度と重複排除のルール
ほとんどのツールは各クエリを毎日走らせます。一部は週次です。Otterly は毎日走らせますが、24時間以内の重複を排除します。1日に5回言及されても1カウントです。Peec AI は毎日走らせて毎回別カウントです。15日 × 12 クエリの規模だと、これが効いてきます。
### 4. 多言語に対応しているか
私は4言語で公開しています。多くのツールはデフォルトで英語のみサンプリングし、明示的に言語セットを設定しないと他言語を見ません。Peec AI が一番役に立つ多言語数値を返してきた理由は、115言語をデフォルトで見にいくからです。他のツールは PT と ES のトラフィックをほぼ無視していて、結果として LatAm とブラジルで実際に起きていることを過小評価しています。
## つまらない結論: 定義を選んでから、ツールを選ぶ
2週間この数字を眺めて分かったのは、「どのトラッカーが一番正確か」という問いがそもそも間違っているということです。AI 引用には正解がありません。LLM はブラックボックスで、同じプロンプトでも時刻・地域・データセンターによって違う回答を返します。Google Search Console のような確定的なソースが存在しないのです。
正しい問いは「自分のビジネス成果に対応する『引用』の定義はどれか」です。
- **アトリビューション流入**(誰かが実際にリンクを踏む)を見たいなら、Profound か Otterly が向きます。リンク付き引用だけをカウントするので、数字は小さくなりますが GA4 のリファラデータと照合できます。
- **ブランド存在感**(リンクの有無を問わず、LLM があなたを言及している)を見たいなら、Peec AI が良いです。数字は大きく見えますが、「ChatGPT が回答の中で私の名前を口にしている」という事実に最も近いプロキシです。
- **競合ポジショニング**を見たいなら、Bluefish か Scrunch が競合セットをネイティブに扱います。
- **予算を抑えつつ自分で真実を握りたい**なら、自前スクリプトが一番です。私のは OpenAI、Anthropic、Perplexity の API を200行のPythonでラップしただけで、月8ドル前後です。生の回答テキストも取れるので、商用ツールがグラフの奥に隠している部分まで grep できます。
業界が共通定義を持つまでは、どのベンダーも違う数え方をして同じ単語を使います。[llmoframework.com](https://llmoframework.com/) が提案しているような分類軸が広まれば、ツール間の数字が初めて比較可能になります。
## 結局、私が残したツール
正直に書きます。私が今も使っているのは7つのうち2つだけです。
Otterly は残しました。安いし、厳格な定義が GA4 で検証可能だからです。「Otterly が引用ありと言っていて GA4 にもリファラクリックがある」のなら、両方信じます。自前 Python スクリプトも残しました。生テキストが取れるし、定義を明日変えたければ即変えられるからです。
残りは解約しました。悪いツールだからではありません。月499ドル払って、月29ドルのツールと整合しない数字を得ても、自分が賢くなるのではなく、むしろ判断が鈍るからです。
これから AI 引用トラッカーに金を出そうとしているなら、まず1文で「自分にとっての引用とは何か」を書いてみてください。次にベンダーに聞いてみてください、「あなたの定義は私の定義と一致しますか」と。多くは即答できません。それが答えです。
## 続きはこちら
この計測問題について、私が使っているPythonスクリプトと GA4 設定までまとめて書籍にしました。
[なぜあなたのサイトはChatGPTに無視されるのか - LLMO実践ガイド](https://kenimoto.dev/ja/books/llmo-ai-search-optimization)
---
# AIエージェントを7本cronで毎日回したら、2本が18日間沈黙していた — observabilityでは拾えず、exit-code契約で拾えた話
URL: https://kenimoto.dev/ja/blog/seven-cron-agents-18d-silent/
Lang: ja
Date: 2026-05-28
Description: cronに置いた7本のエージェント、2本が初日から動かず18日間気付かなかった。tracingは無力、exit-code契約 + 24h heartbeatでようやく拾えた実測ログ。
私はcronに7本のAIエージェントを置いていました。そのうち2本が初日から動いていませんでした。気付いたのは18日後です。
この一文で記事は終わるのですが、もし他の誰かがポッドキャストで同じことを言っていたら、私は反論していたと思います。「いやさすがに18日も気付かないわけない。tracingあるし、ダッシュボードあるし、Telegramで何かあれば通知来るでしょう」と。はい、それ全部ありました。それでもこの2本はすり抜けました。理由は単純で、私の監視レイヤは全部「動いているプロセス」を見ていたからです。動いていない2本は、どこにも映っていませんでした。
これは18日間のログです。何を7本動かしていて、どこで2本が静かに死んだか、なぜtracingでは拾えなかったか、そして今は全部のスケジュール実行エージェントに付けている小さなexit-code契約の話です。
## 7本のエージェントと「問題なさそうに見えた」セットアップ
私は同じサーバ上で2つのコンテンツドメインと1つのself-evolving harnessを回しています。各ドメインにObserver / Strategist / Marketerの3本、それと共通のEvolverが1本。これで合計7本。cronはだいたい次のような書き方でした。
```cron
0 9 * * * /home/me/repos/harness-ops/scripts/marketer-A.sh >/dev/null 2>&1
0 9 * * * /home/me/repos/harness-ops/scripts/marketer-B.sh >/dev/null 2>&1
```
それぞれのshell scriptは `claude -p "..."` をヒアドキュメント付きで呼び、出力をキャプチャして日次ログを書き、エージェントが「公開する」と判断した場合は記事を実際にpushして終わります。Telegram通知用のwebhookも仕込んであって、成功時にも `set -e` で死んだときにも飛ぶ、はずでした。この構成で2ヶ月ほど運用していました。
セットアップしたときに見落としていたのは、heredocの3行下です。Marketer 2本は別リポジトリにあるPythonヘルパーを呼んでいました。当時はそのリポジトリにcdしてシェルから動作確認していて、確かに通っていたのでチェックインしました。その後、別リポジトリ側を整理する流れでヘルパーのモジュール名を変えました。Marketer側の `import` 行は古い名前のまま、誰にも気付かれずに残りました。
ここから先はもう察しがつくと思います。`python3 helper.py ...` は `ModuleNotFoundError` で即座に exit 1。スクリプトの先頭は `set -euo pipefail`。10行目あたりで死にます。Telegram通知のブロックはもっと下、Python呼び出しの後ろ側に書いてあったので、そこまで到達しません。`>/dev/null 2>&1` でstderrは消えます。cronは `MAILTO=` を設定していません。毎朝、2本のエージェントが静かに死に、残り5本は普通に記事を公開していました。システム全体は健康に見えていました。
## tracingが見ていたもの、見ていなかったもの
ここは正確に書きたいところです。なぜなら18日目の朝、私は数時間かけて「もっとちゃんとしたtracingを入れていれば拾えたんじゃないか」と自分に言い聞かせようとしたからです。結論を先に言うと、ちゃんとしたtracingでも拾えませんでした。
`claude -p` の呼び出しからは OTEL のspanを吐かせていて、self-hosted collectorに集めて小さなダッシュボードに表示していました。token消費、tool-call latency、retry率、日次の総エージェント実行数。18日目の朝、ダッシュボードを見ると、日次の総実行数は18日連続でぴたっと「5」を指していました。本来は「7」のはずです。
tracingは「実行されたプロセス」を計測する仕組みです。遅い呼び出し、失敗した呼び出し、retryの嵐、そういうものは映ります。しかし「そもそも起動しなかったプロセス」は映りません。死んだ2本のMarketerは span を1本も吐いていませんでした。なぜなら、span を吐かせる場所が「import に失敗した当のヘルパー」の中だったからです。ダッシュボードから見れば、その2本は「今日存在しなかった」のと同じです。次の日も、その次の日も、ずっと存在しなかったことになっていました。
私はずっと間違った質問を見ていました。「動いているエージェントは元気か?」はtracingが答えられる質問です。「スケジュールされた7本のうち、本当に7本動いたか?」はtracingが答えられない質問です。動かなかった2本は、その「動かなかった」という事実そのものを誰にも報告できないからです。
[healthchecks.ioのdead man's switchの説明ページ](https://healthchecks.io/docs/monitoring_cron_jobs/)を読んだことがある人にはおなじみのはずです。「重要なデータ処理ジョブが、従来の監視システムに何の警報も上げずに停止することがある。サイレント失敗は、欠損データや破損結果に誰かが気付くまで、何日も何週間も続きうる」と書いてあります。私はあのページを以前読んでいました。ただ、自分のcronには適用しませんでした。Telegram通知があるから大丈夫と思っていたからです。Telegramは「スクリプトが到達した行」からしか飛びません。
## 後付けで入れたexit-code契約
直し方は「もっとobservabilityを増やす」ではありませんでした。エージェント自身に「自分の生死を報告してもらう」ことを諦めて、cron wrapper側に「エージェントの代わりに報告する責任」を持たせる方向です。スケジュール実行する全エージェントに、次の小さな契約を結ばせました。
1. **意味のあるexit codeを定義する。** 「0 = OK、それ以外 = NG」ではなく、もう少し細かく。sysexits.h を緩めに踏襲しました。`0` は「エージェントが走って仕事を終えた」、`64` は「config/環境エラー」(まさに `ModuleNotFoundError` のパターン)、`65` は「走ったが使える出力が得られなかった」、`78` は「意図的にスキップ」(Marketerが「今日は公開する記事なし」と判断したケース)。
2. **cron wrapperが報告を持つ。** エージェントスクリプトの仕事は「正しいexit codeで終わる」ことだけ。wrapperの仕事はその exit code を拾って、どこか永続的な場所に push すること。エージェントが成功しようが失敗しようが関係なく。
3. **成功時にもheartbeatを飛ばす。** 失敗時だけではなく成功時にも飛ばす。沈黙そのものをアラームにする。
cron wrapperはだいたいこんな形に落ち着きました。
```bash
#!/usr/bin/env bash
# scripts/cron-wrap.sh
set -uo pipefail
AGENT="$1"
SCRIPT="$HOME/repos/harness-ops/scripts/${AGENT}.sh"
HC_URL="https://hc-ping.com/"
START=$(date -Iseconds)
bash "$SCRIPT"
RC=$?
END=$(date -Iseconds)
# 成否によらず1行ずつログを残す
echo "${START} ${AGENT} rc=${RC} end=${END}" >> "$HOME/logs/cron-runs.log"
# exit codeをURLパスに埋めてpingする
# 24h ping切れ → healthchecks.io が私を呼ぶ
curl -fsS --retry 3 "${HC_URL}/${RC}" >/dev/null || true
# 非ゼロのみ即時escalate (78は意図スキップなので除外)
if [[ "$RC" -ne 0 && "$RC" -ne 78 ]]; then
"$HOME/bin/tg-notify.sh" "agent=${AGENT} rc=${RC} see ~/logs/cron-runs.log"
fi
exit 0
```
このwrapperには、書き直すたびに少しずつ詰まったポイントが3つあります。
1つ目は `set -euo pipefail` ではなく `set -uo pipefail` にしたこと。エージェントスクリプトが失敗してwrapperごと死んでしまうと、pingに到達しないからです。pingに到達しなければhealthchecks.io側は「24時間後に呼ぶ」モードになりますが、それでは遅すぎます。wrapperは死なずに最後まで走り切って exit code を拾う必要があります。
2つ目はpingのURLにexit codeを埋めたこと。healthchecks.ioもCronitorも、URLの末尾に code を載せると最終exit codeをダッシュボードに残してくれます。なので、ログファイルを開かずに「Aは exit 64 だった」がひと目で分かります。
3つ目は `78` を意図的なスキップとして扱ったこと。Marketerが「今日は公開する記事がない」と判断して終わるケースは失敗ではないので、escalateしません。これをやらないと「今日は静かな日でしたよ」のたびにTelegramが鳴って、私が通知をミュートし始めて、結局運用が破綻します。アラート疲れで運用が死ぬのは、観測が死ぬよりよくある現象です。
## 入れた当日に拾えたもの
このwrapperを入れたのが、ちょうどMarketerたちが18日目に突入した朝でした。10分以内に `marketer-A` と `marketer-B` の両方がhealthchecks.ioのダッシュボードに「最終exit code: 64」で姿を現しました。中身のコードを開く前に、ダッシュボードでひと目で分かりました。
1時間以内に、importを直して、両方のスクリプトを手で走らせて exit 0 を確認、翌朝のcronで2本のMarketerが2週間半サボっていた記事を実際に公開し始めました。tracing側の日次実行数も「7」に戻りました。線はまだ平らですが、平らになっている値が正しい値です。
その翌日には、18日間ずっと健康だったはずのobserver-Bが exit 65 (「使える出力が得られなかった」) を返し始めました。ダッシュボードに反映されるまで20分。これがexit-code契約の本来の役割です。エージェントは動いたけれど出力がゴミだった、というケースを、2週間後ではなく当日に拾えます。
## 過去の自分に言うとしたら
2ヶ月前にこのcronを置いた自分は、別に怠けていたわけではありません。Telegram通知も、tracingダッシュボードも、日次ログも揃えてありました。[Twelve-Factor App の disposability の章](https://12factor.net/disposability)も読んでいました。「エージェントが失敗する」のと「エージェントがそもそも動かない」の違いについても考えていて、後者は十分まれだから無視していい、と判断していました。
ミスは「動かない」をレアケース扱いしたところでした。7本のスケジュール実行、3本のPythonヘルパー、独立に動く2本のリポジトリ、スクリプトの中盤にTelegram通知を仕込んでいるという構成では、「そもそも動かなかった」が最頻のサイレント失敗モードです。他の失敗よりも一桁以上多いと思います。
なので、過去の自分に言うとしたらコストの低い順に3つ。
1. **`MAILTO=` はタダ。** cronに `MAILTO=your-mail@example.com` を1行足すだけで、ジョブのstderrが自動でメール送付されます。アラート用コードに到達する前に死んだジョブでも届きます。これだけで私の18日間は1日目に終わっていました。systemd timerに移行している場合は [archwikiの systemd timer のページ](https://wiki.archlinux.org/title/Systemd/Timers) に `OnFailure=` での通知パターンがまとまっています。
2. **エージェントごとに自分が所有するwrapperを噛ませる。** エージェント本体に詰め込まない。wrapperの仕事は「exit codeを拾う」「pingする」だけ。エージェントよりも汚い書き方になってかまわないので、代わりに今後ほとんど変更しないようにする。
3. **沈黙を「うるさく」させる仕組みは success heartbeat。** 失敗通知はどこにでもあるけれど、「そもそも動かなかったエージェント」については何も教えてくれません。成功時にpingを飛ばして、ping切れで呼び出されるdead man's switchを置く。これが「2本が静かに死んでいる」を18日問題から1日問題に変えます。
なお、kenimoto.devでも書いた [Claude Code Hooks v2 — 25のライフサイクルイベントの記事](/ja/blog/claude-code-hooks-v2-25-events/)はエージェントが起動した「あとの」話なので、本記事の「そもそも起動しなかった」とは別レイヤを扱っています。Hookが発火する前提が崩れたときに何が起きるか、というのが今日の話でした。
tracingとobservabilityは、生きているプロセスを見るための道具です。exit-code契約は、そもそも生きているべきだったことを記録するための道具です。両方が必要で、cronの「set it and forget it」運用は後者がないと簡単に崩れます。私のは18日間崩れていて、私は毎朝そのサーバのダッシュボードを見ていました。
ダッシュボードは健康でした。ダッシュボードが見ていた質問のほうが、間違っていただけです。
## まとめ
- cronに置いた7本のエージェントのうち2本が、初日から `ModuleNotFoundError` で死に、18日間誰にも気付かれなかった
- tracingは「実行されたプロセス」を見る仕組みなので、「起動しなかったプロセス」は構造的に拾えない
- 解決はexit-code契約(`0/64/65/78`)とcron wrapperが代理で報告する仕組み、それと「成功時のheartbeat + dead man's switch」の3点セット
- 一番安いのは `MAILTO=` を1行足すこと。これだけで多くのサイレント失敗は当日中に拾える
## 関連
- [Claude Codeを3セッション並列で8時間動かしたら、2回コンテキストを上書きしていた話](/ja/blog/three-claude-sessions-parallel-8h-context-overwrite/) — 同時並列セッション側の事故。「衝突」と「沈黙」で対の関係。
- [他のエージェントを監査する4層目を足したら、Strategistが3週間サボっていた](/ja/blog/evolver-4-layer-strategist-procrastination-audit/) — 動いてはいるが procrastinate しているエージェントを上位層で監査する話。本記事のexit-code契約とレイヤが違う。
ハーネス全体のhooks / ライフサイクル / フィードバックループの章を含めて、本格的に読みたい方はこちらにまとめてあります: [Harness Engineering Guide: ツールから複利的生産性へ](https://kenimoto.dev/ja/books/harness-engineering-guide)。
---
# AIエージェントの予測を1つの数字で信じるな — Simulatorは点推定でなく確率分布を返すべき
URL: https://kenimoto.dev/ja/blog/simulator-distribution-not-point-estimate/
Lang: ja
Date: 2026-06-09
Description: 事業運営エージェント(Simulator)が「CAC=2,000円」と1つの数字で返すと、後続の意思決定エージェントが過信して判断が壊れます。信頼区間・モンテカルロ・分位点予測で不確実性を渡す実装の話です。
私は以前、自作の事業運営エージェントに広告施策の結果を予測させて、その出力を真顔で信じていました。「この施策のCACは2,000円です」。おお、2,000円か。じゃあこれでいこう。実際に回したらCACは3,400円でした。エージェントが嘘をついたわけではありません。私が、1つの数字を信じてしまっただけです。
問題は予測精度ではなく、出力の形でした。エージェントは「2,000円」と点推定で答え、私はそれを確定した未来として受け取った。そこに不確実性の幅はどこにもなかったのです。
この記事は、事業運営の自律エージェント(以下Simulator)の出力を「点推定」から「確率分布」に変えると意思決定がどう変わるか、という話です。結論から言うと、Simulatorが1つの数字を返した瞬間、後続の意思決定エージェントは確実に過信します。そして過信した意思決定は、平均的には正しくても、尾部で壊れます。
## 点推定が嘘になる瞬間
まず言葉を整理します。Simulatorは、戦略候補を入力に受け取り、過去データから予測される結果を返すモジュールです。「予算Xをチャネル配分Yで投下したらCACはどうなるか」を、実弾を撃つ前に推定する。実際に試せば確実にわかるけれど、試すまでに時間と金がかかる。その手前で候補を10個から3個に絞るために使います。
このSimulatorの出力が「CAC=2,000円」という1つの数字だったとします。これは厳密には嘘です。正確には「中央値は2,000円付近だが、1,500円から3,000円のどこかに着地する確率が高く、まれに3,400円まで跳ねる」が真実だからです。点推定はこの幅をゼロに潰して、確率1で2,000円だと言い切っている。エージェントが嘘をつくつもりがなくても、出力の形式が嘘をつかせるわけです。
ここで「いやでも中央値2,000円なら平均的には合ってるじゃん」と思うかもしれません。私もそう思っていました。罠はここにあります。意思決定は平均値の上で行われるのではなく、最悪ケースの上で破綻するからです。
## 過信する後続エージェント
Simulatorの出力を受け取って実際に判断を下すのは、後続の意思決定エージェント(Strategist)です。私のハーネスでは、Simulatorが候補を絞り、Strategistがその中から実行する施策を1つ選びます。役割を分けている理由は[別記事](/ja/blog/observer-strategist-marketer-3-yaku-bunri/)に書きました。
問題は、点推定を受け取ったStrategistの振る舞いです。「CAC=2,000円」という入力を見たStrategistは、これを2,000円という事実として扱います。LLMは入力に書かれた数字を、書かれた確からしさのまま受け取る傾向があります。「2,000円」とだけ書いてあれば、それが2,000円である確率を勝手に高く見積もる。人間が査読の数字を信じすぎるのと同じです。
そして2,000円という前提で予算配分を決め、損益分岐を計算し、「この施策はGOです」と判断する。実際には3,400円に着地して、計算した損益分岐は吹き飛ぶ。Strategistは何も間違っていません。間違った前提を、間違っていない手つきで処理しただけです。ゴミを入れたらゴミが出る、の高級版ですね。
点推定の本当の罪は、不確実性を消すことではありません。隠すことです。幅は実在するのに、出力の形がそれを見えなくする。見えないリスクは、誰も避けられません。
## 分布で返すと何が変わるか
ではSimulatorに分布を返させます。同じ予測を、こう書き換えます。
```json
{
"metric": "cac",
"median": 2100,
"ci_low": 1600,
"ci_high": 2800,
"p95": 3400,
"samples": 1000,
"model_scope": "marketing-v3 / 過去90日 / paid-social のみ"
}
```
中央値2,100円、95%信頼区間が1,600円から2,800円、上側5%点(p95)は3,400円。これを受け取ったStrategistは、もう「2,000円」とだけ言われたときの判断はできません。「中央値で見ればGOだが、p95の3,400円を踏むと損益分岐を割る」という条件付きの判断に変わります。最悪ケースが入力に明示されているので、最悪ケースを避ける判断ができる。
`model_scope` フィールドも効きます。この予測は過去90日のpaid-socialデータで訓練されたモデルが出したもので、メール施策やオーガニック流入には適用範囲外だと明示してある。Strategistが文脈外の予測を信じる事故を防げます。点推定にはこの注釈を貼る場所すらありませんでした。
分布の作り方は難しくありません。私はモンテカルロでサンプルを1,000本引いて、その分布から分位点を取り出しています。冒頭のCLIならこうです。
```bash
simulator predict --strategy=strategies/paid-social-q3.yaml \
--horizon=30d \
--samples=1000
```
`--samples=1000` で1,000本のシナリオを引き、その経験分布から中央値・信頼区間・p95を計算する。モデルの誤差が正規分布でなくても、サンプルを引いて分位点を取るだけなので素直です。分位点予測(quantile regression)で直接p50/p90を推定する手もありますし、近年は予測区間にキャリブレーション保証を付けるconformal predictionも使えます([Bui et al., AAAI 2024](https://ojs.aaai.org/index.php/AAAI/article/view/30084))。難易度の高い予測ほど区間が自動で広がる、という性質が後続の意思決定と相性がいい。
## 分布を渡しただけでは足りない、という不都合
ここで気持ちよく終わりたいのですが、不都合な研究を1つ置いておきます。「予測区間を渡せば意思決定の質が上がる」は、自動では成り立ちません。生産計画の被験者実験では、予測を区間で提示しても判断の質は改善せず、むしろ損失関数の非対称性への適切な反応をかえって鈍らせた、という報告があります([Goodwin et al., EJOR 2010](https://www.sciencedirect.com/science/article/abs/pii/S0377221709009485))。区間を渡された人間が、それをうまく使えなかったわけです。
これはエージェントでも同じだと私は見ています。分布を入力したからStrategistが賢くなるのではありません。Strategistの判断ロジック側に「p95がこの閾値を超えたら除外」「信頼区間が中央値の何倍以上に広がったら実弾でなくA/Bテストに回す」といった、分布を消費する明示的なルールを書いて初めて効きます。データを渡すことと、データを使う意思決定設計は別物です。分布は前提条件であって、十分条件ではない。
私のワークフローでは、候補をランキングする段で「信頼区間が広すぎるものは除外する」というルールを明示的に置いています。広い区間は「Simulatorが自信を持てていない」というシグナルなので、そういう候補は実弾でなくA/Bテストという次の検証層に送る。速くて安いが仮定依存のSimulatorと、遅くて高価だがモデル非依存のA/Bテストを、不確実性の幅で振り分けるわけです。
## まとめ
私は信長の野望を15年やっていて、布石を打つのが好きです。あのゲームで一番痛い負け方は、確実だと思った一手が外れたときではなく、外れる可能性を最初から見ていなかったときです。点推定は、まさにこの「外れる可能性を画面から消す」操作でした。
整理します。
- Simulatorが点推定で返すと、後続の意思決定エージェントは入力の数字を過信し、最悪ケースを避けられなくなる
- 出力を中央値・信頼区間・p95の分布にすると、Strategistは条件付きの判断ができ、尾部リスクを回避できる
- 分布の生成はモンテカルロのサンプリングと分位点抽出で十分実装できる。分位点予測やconformal predictionも選択肢
- ただし分布を渡すだけでは足りない。意思決定側に「区間が広ければA/Bテストに回す」等のルールを書いて初めて効く
1つの数字は便利です。便利だから信じてしまう。でも事業の意思決定で本当に知りたいのは「だいたいいくらか」ではなく「最悪いくらまで覚悟するか」です。次にあなたのエージェントが点推定を1つ返してきたら、こう聞いてみてください。「で、その数字、95%信頼区間はどこからどこ?」。黙ったら、まだ信じる段階じゃありません。面白くいきましょう。
*ken imoto · WebRTC & Voice AI engineer · [kenimoto.dev](https://kenimoto.dev/ja/)*
---
# Claude Code Skills は発火しなくてもトークンを食う — 5 Skills × 7時間セッションで「使われなかった」3つが18%食べていた
URL: https://kenimoto.dev/ja/blog/skills-loaded-3-never-fired-18/
Lang: ja
Date: 2026-05-30
Description: 1セッションに Skill を5つロードして7時間動かしたら、3つは一度も呼ばれなかった。それでも全トークンの18%を持っていった。実測した請求書と棚卸し後に7%まで落とした手順を残しておきます。
Skills はカスタムコマンドの上位互換で、しかも「使わなければタダ」だと思っていました。タダではなかったです。家賃でした。
この一文がこの記事のすべてです。残りは私が「家賃」を払っていた証拠を並べるパートになります。
ある火曜日、私は Claude Code の1セッションに Skill を5つロードして7時間動かしました。PRレビュー、TypeScript移行、DBマイグレーション検証、ログ追跡、CSV整形。このうち3つは、その日**一度も発火しませんでした**。発火ログを2回見直してから「本当に動いていない」と確認しました。それでもこの3つだけで、その日の総トークンの**約11%**を食べていました。発火した2つの分も合わせると、Skills 合計で **18%** になります。
私はそれまで、同僚に「Skill は呼ばれたときだけトークンを食うから、入れっぱなしでも問題ない」と説明していました。これは間違っていました。しかも実測できる規模で間違っていました。
## Skills が実際にロードされるタイミング
これは公式仕様で書かれていることなのに、私は流し読みしていました。
セッション開始時、Claude Code はスコープ内のすべての Skill を読みます。このとき context に入るのは `SKILL.md` の frontmatter にある `name` と `description` だけです。本文はまだ入りません。本文がコンテキストに入るのは、Claude が description とプロンプトをマッチさせて発火を決めたとき、または私が明示的に `/skill-name` と打ったときです。本文は一度入るとセッション終了かコンパクションまで残ります。
私が見落としていたのはここから先です。**description は1ターンごとに context に乗ります**。セッション開始時に1回だけではありません。私が新しいメッセージを送るたび、Claude が応答を返すたびに、description はプロンプトの一部として再評価されます。1つの description が約300トークン、5つで約1,500トークンの「この Skill は何ができるか」という説明文が、毎ターン billing に乗っていたわけです。
80ターンのセッションを回せば、同じ description を160回支払うことになります。1つあたりは小さい。しかし常に乗っている。これがからくりです。
## 計測したセッションの構成
私は Claude Code を日常的に使っています。計測した日は、午前中に PR トリアージ、午後に長めのリファクタリング、夕方に雑なシェル探索、という普段の火曜日でした。Claude Code の1セッションを通しで開いたまま、すべての応答を `--output-format json --verbose` でラップして、`usage` フィールドをログに残しました。
`~/.claude/skills/` にロードされていた5つの Skill は以下のとおりです。
| Skill | description 長 | 用途 | 発火した? |
|-------|---:|------|:---:|
| `review-pr` | 約310トークン | PR レビュー手順 | はい (11回) |
| `migrate-ts` | 約290トークン | TS 移行ヘルパー | はい (2回) |
| `migrate-db` | 約340トークン | DB マイグレ検証 | いいえ |
| `trace-logs` | 約270トークン | ログ追跡パターン集 | いいえ |
| `clean-csv` | 約280トークン | CSV 整形レシピ | いいえ |
description 合計は1ターンあたり約1,490トークン。これが CLAUDE.md・プロジェクトコンテキスト・会話履歴の上に毎ターン積まれていました。
セッション時間は7時間12分、84ターン、入出力合計は約210万トークン (プロンプトキャッシュはほぼ常時オン) でした。
## 請求書の中身
ログから、トークン消費を3つに分けました。「総消費」「Skills が一切ロードされていなかった場合の推定値」「その差分」です。実数は以下になります。
| カテゴリ | トークン | 割合 |
|----------|------:|------:|
| 会話・CLAUDE.md・コード読み込み | 1,720K | 82% |
| 発火した Skills (`review-pr` + `migrate-ts`) | 147K | 7% |
| 発火しなかった Skills (description のみ × 3つ) | 231K | 11% |
| **合計** | **2,098K** | **100%** |
実際に仕事をした2つの Skill は7% を食べました。これは問題ありません。手順をタイプし直す手間を省けたぶん、たぶん同じくらいの節約をしてくれました。
問題は、一度もマッチしなかった3つです。**11%**。リターンはゼロです。プロンプトキャッシュが効いていると description の1ターンあたりコストはある程度吸収されますが、完全には消えません。私のプロンプトが変わるたびにキャッシュ境界の手前で description が再トークナイズされ、input_tokens に乗ります。それで11%でした。
## 棚卸ししてもう1日回してみた
翌朝、同じワークロード (同じ PR セット、同じ種類のプロンプト) を、前日に実際に発火した2つの Skill だけをロードして実行しました。総消費は約1,872Kトークン。前日比で約11%減です。ノイズはありますが、「ドーマントだった3つの description が払っていた家賃」の試算とほぼ一致しました。
自分の環境で同じことを確認したい場合は、`claude` をラップして JSON で usage を読むスクリプトを通せば一目です。
```bash
claude -p "$YOUR_PROMPT" --output-format json --verbose \
| jq '{input: .usage.input_tokens, cached: .usage.cache_read_input_tokens, output: .usage.output_tokens}'
```
ターンごとの `input_tokens` がベースラインで上方ドリフトしているなら、それは description の家賃を払っているサインです。
## なぜこれが意外だったか
私は Skill を「プログラミング言語の import 文と同じで、呼ばれない限りコストは0」と勘違いしていました。import がタダなのはコンパイラが未参照のものを捨てられるからです。Claude Code はそれができません。description こそが「いつこの Skill を呼ぶか」を判定する材料なので、description は毎ターンプロンプトに乗っている必要があるからです。遅延ロードしたら、そもそも呼び出すかどうかの判断ができなくなります。
これは設計上のトレードオフで、しかも正しいトレードオフです。ただし、その帰結として「インストールされているだけで使われていない Skill」の限界コストは0ではありません。ターンごとの小さな税金です。長いセッションでは積み上がります。
ここで Hooks と混同しないでください。Hooks は Claude Code がイベントに応じて意図的に発火させる仕組みです (pre-tool / post-tool / session-end 等)。Hooks の定義は description としてシステムプロンプトには入らず、`settings.json` から harness が呼ぶだけです。**使われていない Hook のコストは本当にゼロです。使われていない Skill のコストは description × 全ターン分です。** ここは別物として認識しておきたいところです。
MCP server とも違います。MCP server はセッション開始時にツールリスト全体をシステムプロンプトに乗せます (1サーバーで27,000トークン規模という別の計測例もあります) が、こちらはサーバー単位の固定費です。Skill のほうが1つあたりは小さいですが、数が増えがちで、毎ターン × Skill 数 で乗算的に効いてきます。
## Skill 棚卸し5ステップ
私は今、これを月1回のルーティンにしています。10分で終わります。
1. **スコープ内のすべての Skill をリストアップ**: `ls ~/.claude/skills/` と、プロジェクトレベルの `.claude/skills/` と、Plugin 由来の Skill。全部紙に書き出します。
2. **各 Skill が最後に発火した日時を出す**: セッションを `--output-format json` でロギングしているなら、tool-use エントリから Skill 名を grep するだけ。していないなら記憶頼りになりますが、記憶はだいたい不正確です。
3. **過去30日で1回も発火していないものを「候補」にマーク**: ここではまだ削除しません。フラグだけ立てます。
4. **候補を1週間だけ "倉庫" に移動**: 私は `mv ~/.claude/skills// ~/.claude/skills-attic/` のように物理的に動かしています。1週間使って、不便を感じなければ、それは家賃でした。
5. **同じワークロードで input_tokens のベースラインを再計測**: 候補を抜いた状態で同じ種類の作業をして、`input_tokens` が明確に下がるなら、節約が見えた状態です。
罠を1つ挙げるなら、「30日発火していない=即削除」にはしないことです。四半期に1回しか使わないけれど、その1回で価値を出してくれる Skill があったりします。「倉庫に移動」が安全な中間状態です。
ちなみに Zenn の Claude Code Hooks 7パターン記事 (2026-05-29) と混ざりやすいので念のため明記しておくと、**この記事は Hooks の話ではなく Skills の話**です。Hooks は「意図的にイベントで発火させる装置」、Skills は「条件付きで発火するが、ロードされているだけで description が課金される装置」。機構が違います。
## 私の手元で何を変えたか
3つの Skill を倉庫に移しました。1つは来月、DB マイグレーションの予定があるので戻ってきます。残り2つはたぶんこのまま消えます。発火していた2つはそのまま。
この記事を書いている今のセッションも Skill 2つ運用です。ターンごとの input_tokens のログがきれいに横這いになりました (以前はじわじわ右肩上がりでした)。11% という数字は、口頭で言うと地味に聞こえます。Claude Code Max の月額換算なら ¥30,000 × 18% ≒ ¥5,400/月、未発火3つ分だけでも ¥3,300/月。Sonnet API の従量プランなら使い方次第ですが、いずれにせよ実弾です。「テキストファイル3つをコンテキストに置いておくため」に毎月支払っている、と言うと我ながら情けない金額の出方をしています。
たくさん Skill を入れたい気持ちは正しいです。便利だからです。ただ、その便利さには「ターンごとの税金」がついていて、その税金は見に行かないと見えない、ということだけ知っておいてください。
モニターの上に貼っておきたい一文はこうです。**ロード済み ≠ アクティブ ≠ 呼ばれたとき分だけ請求、ではない。**
`claude -p` の `usage.input_tokens` を眺めてみてください。あの数字は最初からこの話を教えてくれていたのに、私はずっと見ていませんでした。
---
Hooks / MCP / Sub-agents / Plugins とあわせて「どの拡張機構がどこにコストを乗せているか」を1冊にまとめたのが [Claude Code Mastery](https://kenimoto.dev/ja/books/claude-code-mastery) です。今回の「description が毎ターン billing に乗る」というカラクリの根っこにある context window 経済学は [Context Engineering](https://kenimoto.dev/ja/books/context-engineering) のほうにまとめてあります。
---
# 43秒の異常が24時間の障害になった: メトリクスの急変と漸増を見分ける時系列デバッグ
URL: https://kenimoto.dev/ja/blog/spike-vs-gradient-time-series-debug/
Lang: ja
Date: 2026-06-10
Description: 障害対応の初速は時系列パターンの読み分けで決まる。スパイク(急変)とグラデーション(漸増)、3つの時間スケール、最初の異常の特定。GitHubとCloudflareの事例で解説します。
障害対応で私が最初にやらかすのは、たいてい「目に見えている異常」に飛びついてしまうことです。レスポンスが遅い、というアラートが鳴った瞬間、ロードバランサのCPUを疑い、アプリのスレッドダンプを取り、データベースのスロークエリを眺める。30分かけて何も見つからず、ようやくダッシュボードの表示期間を1時間から1週間に広げて、3日前から静かに右肩上がりだったメモリ使用量に気づく。原因はそこにありました。
この遠回りは、ほぼ全部「グラフの形を読まずに数字だけ追った」ことが原因でした。今日はその話を書きます。
## たった43秒が24時間に化けた日
数字を1つ置きます。43秒。これは2018年10月21日にGitHubで起きたネットワーク分断の時間です。US East Coastのネットワーク機器の交換作業で、2つのデータセンター間の接続が43秒だけ切れました。通常ならフェイルオーバーが処理して、ユーザーは気づきもしない長さです。
ところがこの43秒のあいだに、両方のデータセンターが独立してデータベースへの書き込みを受け付けてしまいました。いわゆるスプリットブレインです。ネットワークが復旧したあとも、矛盾する書き込みを安全に統合する作業が残り、GitHubの全面復旧には24時間11分かかりました(復旧途中の段階では「24時間5分」という数字も報告されています)。43秒が24時間に化けた。倍率にしておよそ2,000倍です。
ここで効いたのが、タイムラインの正確な再構成でした。「どちらのデータセンターが先に書き込みを受け付けたか」を秒単位で確定できなければ、安全な統合手順そのものが組めません。障害の規模は43秒では決まらず、その43秒を起点に何が連鎖したかで決まりました。時系列を読む力が、復旧速度を直接左右したわけです。
## システムのメトリクスは心電図だ
ここで一度たとえ話をさせてください。私はメトリクスのダッシュボードを心電図(EKG)だと思って見ています。健康な心臓には固有のリズムがあって、医師は波形の乱れから異常を読み取ります。システムのメトリクスもまったく同じで、正常なときには正常なリズムがあり、そこからの逸脱が障害の兆候になります。
差分(何が変わったか)が「犯人は誰か」を問うのに対して、時系列分析は「いつ、どんな形で容体が変わったか」を問います。そしてこの「どんな形で」の部分が、原因の性質をかなり正直に教えてくれます。波形を読めれば、聴診器を当てる場所がぐっと絞れるのです。
## 急変と漸増: 形が原因の種類を教える
メトリクスの変化は、大きく2つの形に分かれます。
**急変(スパイク)** は、グラフが突然跳ね上がる、あるいは急降下する形です。これは「特定のイベントが起きた」ことを示しています。デプロイ直後にエラーレートが跳ねる。設定変更の直後にレイテンシが急増する。外部サービスが落ちて接続エラーが一気に増える。スパイクの場合、スパイクの開始時刻がそのまま原因の発生時刻を指します。その瞬間に何が起きたかを調べれば、かなりの確率で原因に届きます。GitHubの事例なら、ネットワーク分断が始まった時刻が、すべての異常の起点でした。
**漸増(グラデーション)** は、メトリクスがゆっくり悪化していく形です。これはリソースの枯渇や負荷の蓄積を示しています。メモリ使用量が数日かけて100%に近づく。コネクションプールの使用率がじわじわ上がる。ディスクが毎日数GBずつ埋まる。レスポンスタイムが週単位で重くなる。こちらで見るべきは「いつ閾値を超えたか」ではなく「いつから増え始めたか」です。その増加の始点に、コードの変更やトラフィックパターンの変化が必ず潜んでいます。
冒頭で私が30分溶かしたのは、漸増を急変だと思い込んでスパイクの瞬間を探し続けたからでした。心電図でいえば、健康診断の数値が3日前から悪化していたのに、今朝の不整脈だけを探していたようなものです。
## 3つの時間スケールで見ないと初期異常を見逃す
この急変と漸増の見分けが厄介なのは、表示期間によって同じグラフがまったく違う顔をするからです。
障害の5分前から見ると鋭い急変に見えるものが、1週間で引いて見ると漸増の最後の局面だった、ということが普通に起きます。逆に、長い期間では平坦に潰れて見える微小な変化が、短い期間にズームすると初期の異常として浮かび上がることもあります。心電図を1拍だけ見ても不整脈の周期はわからないのと同じです。
なので私は、最低でも3つのスケールで見るようにしています。直近1時間、直近24時間、直近1週間。1時間で「今まさに何が暴れているか」を、24時間で「今日のうちに何かが変わったか」を、1週間で「ベースラインからどうずれたか」を読みます。このうち1つでも飛ばすと、初期異常を取りこぼします。私が3日前のメモリ漸増を見逃したのは、1時間スケールしか見ていなかったからでした。
ベースラインを知っておくことも前提になります。CPU 80%が異常なのか、それとも毎日昼過ぎに80%まで上がるのが平常運転なのかは、平常時の波形を知らなければ判断できません。曜日や時間帯のパターンを頭に入れておくと、「これはいつもの山」「これは違う」の判断が一瞬で済みます。
## 最初に目に入る異常は、最初の異常ではない
時系列デバッグでいちばん難しく、いちばん効くのが「最初の異常」を見つけることです。ここでいう最初の異常とは、ユーザーに影響が出た時刻ではなく、システム内部で最初に数値が動いた時刻のことです。
2025年11月18日のCloudflare障害が、わかりやすい例です。発端はClickHouseクラスタの権限変更で、これが入ったのが11時05分(UTC)。ところがユーザー向けのレスポンス劣化が始まったのは11時28分でした。あいだに23分の空白があります。
この23分のあいだに何が起きていたか。権限変更によって、ある内部クエリが重複した行を返すようになり、Bot Management用の特徴ファイルのサイズが倍に膨れました。その肥大化したファイルが世界中のプロキシへ伝播し、各プロキシのメモリ上限を超えてクラッシュを引き起こし、そこで初めてユーザーから見えるレスポンス劣化になりました。障害は11時28分から17時06分まで、5時間38分続きました。
ここに罠があります。アラートが鳴った11時28分から調査を始めると、最初に目に入るのはプロキシのクラッシュです。プロキシのメモリを増やそう、再起動しよう、と手を打ちたくなります。でも本当の起点は23分前の権限変更でした。複数のレイヤーでバッファリングが起きると、原因と症状は時間的にずれます。「目に入った異常」を起点に対処すると、症状を追いかけ続けることになります。
最初の異常を起点に置けると、調査の地図が一気に書き換わります。23分前まで遡れたかどうかが、初速を分けたわけです。
## 相関と因果のあいだに時刻ずれを挟まない
ただし、ここで足元をすくわれることがあります。複数のメトリクスを同じ時間軸に重ねて「レイテンシのスパイクとCPUのスパイクが同時刻だからCPU起因だ」と読むのは強力な手筋ですが、その「同時刻」が本当に同時刻である保証は、案外もろいのです。
分散システムでは、サーバーごとに時計が少しずつずれています。NTPで全サーバーの時刻を同期させておかないと、数秒のずれでイベントの前後関係が逆転して見えます。原因のログが結果のログより後ろに記録されていたら、人間の脳は素直に因果を取り違えます。GitHubの事例で2つのデータセンターのイベントを突き合わせられたのも、正確なタイムスタンプがあったからでした。
時刻基準を揃える前提は3つあります。NTPで時計を合わせること。ログのタイムスタンプ形式を統一すること(あるサービスはUTC、別のサービスはJST、さらに別のサービスはUnixエポック秒、では突き合わせに余計な時間がかかります)。そして問題の粒度に合った解像度を持つこと(秒単位のメトリクスではミリ秒の因果は見えません)。
サービスを横断して時刻と因果をつなぐ標準的な道具が、分散トレーシングです。W3Cの Trace Context は、`traceparent` という共通フォーマットのHTTPヘッダで、リクエストがサービス間を渡り歩いても同じトレースIDで追えるようにする仕様です。2025年時点で Level 2 が Candidate Recommendation の段階まで進み、トレースIDの乱数性を示すフラグが追加されるなど、相関の信頼性を上げる方向で改訂が続いています。手元の時計を合わせるのがNTPなら、リクエストの旅路に共通のIDを持たせるのがトレーシングです。両方そろって初めて、別々のサービスのログを安心して同じ時間軸に並べられます。
それでも、時系列分析が見つけてくれるのはあくまで相関です。デプロイ直後にエラーが増えたとして、たまたま同じタイミングで外部サービスが落ちていた可能性は消えません。相関を因果に昇格させるには、ロールバックして実際にエラーが消えるか、設定を戻して回復するかを確かめる、再現の一手が要ります。とはいえ、時系列上の相関は因果を確定する前のフィルタとしては最も有力な手がかりです。
なお、原因が人間でもAIでもない「コードそのものが嘘をつく」タイプのバグについては、別の軸で[Claudeがバグを隠す10の技法](/ja/blog/claude-bug-kakushi-debug-10-techniques-prompt/)に書きました。あちらはAIが生成したコードに潜む欺瞞の話で、今日の時系列観測の技法とはレイヤーが違いますが、合わせて読むと「観測でわかること」と「観測の外で起きること」の境界が見えると思います。
## まとめ
障害対応の初速は、グラフの形を読めるかどうかでだいたい決まります。
- 急変(スパイク)はイベント起因、スパイクの開始時刻が原因の時刻を指す
- 漸増(グラデーション)は蓄積・枯渇起因、増加の始点を探す
- 直近1時間・24時間・1週間の3スケールで見る。1つ飛ばすと初期異常を見逃す
- 「最初の異常」はユーザー影響時刻ではなく、システム内部で最初に数値が動いた時刻
- 相関を因果と取り違えないために、NTP同期・タイムスタンプ統一・トレース相関で時刻基準を揃える
メトリクスは嘘をつきません。でも、読み手が正しい問いを持っていなければ、ただの折れ線として黙ったままです。今日のいちばんの教訓は、私のように30分溶かす前に、まず表示期間を1週間に広げてみることです。
---
# Claude Codeに渡すコンテキストを足すのをやめた — ツール出力を間引いたら長時間タスクの精度が戻った
URL: https://kenimoto.dev/ja/blog/stopped-adding-context-pruning-recovered-accuracy/
Lang: ja
Date: 2026-06-04
Description: コンテキストは足すほど賢くなると思っていました。ツール出力と無関係ファイルを間引いたら、トークンが4割減って、長時間タスクの精度がむしろ戻りました。何を入れないかを決める話です。
私はずっと、コンテキストは足すほど良いと思っていました。CLAUDE.mdを厚くして、関連しそうなファイルを全部読ませて、ツールの出力もそのまま残す。情報が多いほどClaudeは賢く動くはずだと。
その信念は、3時間かかる移行タスクの途中で崩れました。Claudeが序盤に決めた設計方針を、終盤になって自分で忘れていたのです。間違ったファイルを2回触り、私が前半で「触るな」と言ったディレクトリに勝手に手を入れました。原因はプロンプトではありませんでした。コンテキストが太りすぎて、肝心な指示が埋もれていたのです。
そこで逆をやりました。足すのをやめて、間引きました。結果、トークンが約4割減って、長時間タスクの精度がむしろ戻りました。今回はその話です。
## 「足すほど賢い」が嘘になる地点
コンテキストには、効く量の上限があります。
Claude Sonnetは20万トークンの窓を持つと謳っています。でもSourcegraphのGeoffrey Huntley氏は、[実際には14万7000〜15万2000トークンあたりで品質が落ちる](https://ghuntley.com/redlining/)と報告しました。窓の容量と、実際に使える容量は別物だということです。
この劣化は「context rot」と呼ばれます。トークンが増えるほど、再現性と正答率が静かに下がる現象です。30分を超えるセッション、20ファイル以上を触るタスク、複数フェーズにまたがる推論。こういう長時間タスクで、まったく違う失敗の仕方が出てきます。私が遭遇したのはまさにこれでした。
新人に例えるなら、こうです。資料を3枚渡せば即戦力になります。同じ新人の机に資料を300枚積んだら、どこに何が書いてあるか探すだけで一日が終わります。情報量と戦力は、どこかで反比例に転じます。
## 私が間引いた3分類
何を消すか。私が間引いた対象は3つに分かれました。
### 1. ツール出力の生データ
これが一番効きました。`npm test`の全ログ、`grep`の何百行、APIレスポンスの巨大なJSON。Claudeはこれを全部コンテキストに溜め込みます。でも本当に必要なのは「テストが3件落ちた、ファイルはこれ」という結論だけです。
Anthropic自身も、[ツール結果のクリアリングとコンパクション](https://platform.claude.com/cookbook/tool-use-context-engineering-context-engineering-tools)を公式の対策として用意しました。context editingでツール出力を消し、長い会話はcompactionで要約する。私が手でやっていたことに、ちゃんと名前と機能がついていたわけです。
私の運用では、ツール出力をそのまま残すのをやめて、要点だけ手元のメモに書き出し、生ログはコンテキストから外しました。これだけでトークンの大半が消えました。
### 2. 無関係ファイル
「念のため読ませておこう」で開いたファイルです。移行タスクなのに、関係ないコンポーネントを5つ読ませていました。安心料のつもりが、ノイズの仕入れでした。
### 3. 古い会話の往復
序盤の試行錯誤です。「このアプローチで行こう」と決まった時点で、そこに至るまでの失敗した3案は、もう要りません。決定だけ残して、過程は捨てます。`/compact`にカスタム指示を渡せば、重要な決定を残しつつノイズだけ削れます。
## 間引き前後の数字
同じ移行タスクを、間引き前と後で比べました。
| 項目 | 間引き前 | 間引き後 |
|---|---|---|
| 使用トークン | 約14万 | 約8万4000 |
| 設計方針の保持 | 終盤で逸脱 | 最後まで保持 |
| 私の再指示回数 | 6回 | 1回 |
| 触った無関係ファイル | 2件 | 0件 |
トークンは約40%減りました。でも本当に効いたのは、Claudeが序盤の指示を最後まで覚えていたことです。再指示が6回から1回に減りました。劣化の谷である14万トークン台を、そもそも踏まなくなったからです。
ここで強調したいのは、私は新しいテクニックを足していないという点です。むしろ引きました。RAGの層を増やすのとは逆の方向です。賢さを足すのではなく、邪魔を引いたら、元の賢さが戻ってきた。それだけの話です。
## 「足す」より「入れない」が難しい理由
正直に言うと、間引きは足すより難しいです。
足すのは簡単です。不安なファイルを開けばいい。判断が要りません。でも間引くには「これは要らない」と決める判断が要ります。消した情報がもし必要だったら、という不安と戦うことになります。
私の判断基準はシンプルです。「この情報は、今の1ステップを進めるのに直接効くか」。効かないなら入れない。あとで必要になったら、その時に取りに行けばいい。Claudeは必要なら自分でファイルを読み直せます。先回りして全部積むのは、私の不安を鎮めるためであって、Claudeのためではなかったのです。
Anthropicの新しいモデルは、残りコンテキスト量を自分で把握する「context awareness」を持つようになりました。終盤まで息切れせずタスクを続けられます。でもそれは、窓に余裕がある前提の機能です。最初から窓を生ログで埋めてしまえば、その余裕は最初からありません。
## まとめ
長時間タスクで精度が落ちたとき、私の最初の反応は「情報が足りないのでは」でした。逆でした。情報が多すぎて、肝心な指示が薄まっていたのです。
やったことは3つです。ツール出力の生データを要点に置き換える。無関係ファイルを開かない。決まった後の試行錯誤を捨てる。これでトークンが4割減り、context rotの谷を踏まなくなり、Claudeが最後まで方針を保ちました。
コンテキストエンジニアリングというと、何をどう足すかの話に聞こえます。でも長時間タスクで効くのは、何を入れないかの判断のほうでした。机に資料を積むのをやめて、3枚だけ残す。新人が即戦力に戻るのは、そのときです。
---
コンテキスト設計の全体像 — System Prompt、Few-shot、RAGの統合から、足し算が逆効果になる80-20の境界まで — は **[Context Engineering 実践ガイド](https://kenimoto.dev/ja/books/context-engineering?utm_source=kenimoto-dev-blog&utm_medium=article&utm_campaign=context-pruning-accuracy)** にまとめています。この記事は、その「引き算」側を長時間タスクで試した実録です。
---
# サブエージェントにメインの記憶を渡すのは事故だった:7ファイルのうち4つは遮断すべき
URL: https://kenimoto.dev/ja/blog/sub-agent-memory-isolation/
Lang: ja
Date: 2026-06-08
Description: サブエージェントに親のCLAUDE.mdとメモリを全部渡していませんか。OpenClawの7ファイル設計では、サブに渡すのは2つだけ。人格・ユーザー情報・過去記憶を渡すと、トークンが溶けて情報が漏れます。
サブエージェントを初めて使ったとき、私は良かれと思ってメインの記憶を全部渡しました。CLAUDE.md、ユーザーの好み、過去のメモリ、人格設定。「文脈を共有したほうが賢く動くだろう」という、いま思えば完全に逆の発想です。
結果として何が起きたか。`echo success` を返すだけの、世界一どうでもいいサブエージェントが、起動しただけで数万トークンを食いました。派遣で来てもらった人に、まず会社の全社員名簿と私の家族構成と過去3年の日記を読ませてから「コピー取ってください」と頼んでいたようなものです。親切ではありません。事故です。
この「サブエージェントには何を渡し、何を渡さないか」という設計、ちゃんと言語化したものを最近読みました。OpenClawという実在のAIエージェントの記憶アーキテクチャです。これがきれいに整理されていたので、自分の運用に当てはめ直した話をします。
## 7つのファイルで人格を組み立てる
OpenClawは起動時に7つのファイルを順番に読み込んで「記憶」と「人格」を構成します。役割で並べるとこうです。
| ファイル | 中身 |
|---------|------|
| AGENTS.md | 全員共通の作業ルール |
| TOOLS.md | ツール一覧とローカル設定 |
| SOUL.md | 人格・性格・関係性 |
| IDENTITY.md | 対外的なプロフィール |
| USER.md | ユーザーの情報 |
| HEARTBEAT.md | 定期チェック項目 |
| MEMORY.md | 過去の記憶(日次+長期) |
人間でいえば、AGENTSが就業規則、TOOLSが持ち物、SOULが性格、USERが「上司の好み」、MEMORYが業務日誌です。メインエージェントはこれを全部読みます。フルスペックの自分として動くからです。
問題はサブエージェントのほうです。
## サブに渡すのは2つ、遮断するのは4つ
OpenClawの設計では、サブエージェントに渡すのは **AGENTS.md と TOOLS.md の2つだけ** です。残りは渡しません。
なぜか。サブエージェントは「派遣社員」だからです。特定のタスクを片付けにきた人に、会社の人格(SOUL)も、対外的な看板(IDENTITY)も、過去の業務日誌(MEMORY)も要りません。むしろ渡してはいけません。遮断すべきは次の4つです。
- **SOUL.md(人格)**: サブは作業者であって、私の代わりに振る舞う必要がない
- **IDENTITY.md(対外プロフィール)**: 外に出る顔はメインだけが持てばいい
- **USER.md(ユーザー情報)**: これはセキュリティの問題。サブに私の個人情報を持たせる理由がない
- **MEMORY.md(過去の記憶)**: トークンの浪費に直結し、しかも過去ログの中身が漏れる
HEARTBEATはメイン専用の機能なので、そもそもサブには関係ありません。だから「サブに渡さない7マイナス2=5」のうち、**意図して遮断する情報は4つ** という整理になります。人格・看板・ユーザー情報・過去記憶。この4つを渡すと、トークンが溶けて、情報が漏れます。両方同時に悪化するのが厄介なところです。
## 「親切のつもり」がいちばん高くつく
ここで一番大事な事実を一つ。OpenClawはこの遮断を設計として持っていますが、**いま私たちが日常的に使っているClaude Codeのサブエージェントは、デフォルトでは逆の挙動をします**。
公式ドキュメントの "What loads at startup" を読むと、Claude Codeのサブエージェントは起動時に、メイン会話が読むメモリ階層をそのまま継承します。`~/.claude/CLAUDE.md`、プロジェクトのルール、`CLAUDE.local.md`、管理ポリシーファイル。全部です。例外はビルトインのExploreとPlanだけ。つまり、自分でカスタムのサブエージェントを作ると、何も指定しなければ親の記憶を丸ごと持っていきます。OpenClawが「渡すな」と言っているものを、Claude Codeは標準で「渡します」。
その代償が数字で出ています。GitHubのIssue #6825に、ほぼ自明なサブエージェント(`echo success` を返すだけ)が、継承したメモリのせいで **約60kトークン** を消費した、という報告があります。メモリ無しの環境で同じことをやると **約3k** で済みます。20倍です。コピー一枚取るために名簿と日記を読ませた、あの話そのものが計測されています。
このIssueでは、`tools` フィールドと同じ要領で `includes: System, User, Project, Subagent` のような「何を継承させるか」を選べるフィールドが要望されています。私が確認した時点ではまだ実装されていません(メンテナの解決方針も未確認です)。だから現状は、**設計者である私たちが意識して絞る** しかありません。サブエージェントのプロンプトを、必要最小限の作業指示とツール情報だけにする。それがいまできる遮断です。
## トークンだけの話ではない
トークンが20倍になるのは財布の問題ですが、もう一つ、財布より怖い問題があります。漏洩です。
サブエージェントは、悪意あるコンテンツやプロンプトインジェクションの影響を受けることがあります。2026年には、単一のプロンプトインジェクションで複数のAIコーディングエージェントがシークレットを漏らした事例が報告されています。攻撃が「モデルを一回だます」から「計画を乗っ取り、特権ツールを呼び、メモリを汚染し、横に広がる」というチェーンに進化しているわけです。
このとき、サブエージェントにUSER.md(あなたの個人情報)やMEMORY.md(過去のやり取り全部)を渡していたら、漏れる範囲がそのまま広がります。逆に、作業ルールとツール情報しか持っていないサブエージェントは、たとえ乗っ取られても漏らせるものが少ない。OWASPのAIエージェント・セキュリティのチートシートが言う **最小権限** とは、まさにこれです。「各エージェントは定義されたタスクに必要な最小限の権限で動かせ、そうすれば侵害された時の影響範囲が構造的に制限される」。学術側でも、共有メモリ経由のクロスコンテキスト推論やIP漏洩を評価するMAGPIEのようなベンチマークが出てきています。
派遣の人に日記を渡さないのは、その人を信用していないからではありません。万が一その人のカバンが盗まれたとき、日記まで一緒に盗まれたら困るからです。
## 私の運用ルール
整理すると、私がいま守っているのはこれだけです。
- サブエージェントには **作業指示とツール情報** だけ渡す。プロンプトに余計な背景を盛らない
- **人格・対外プロフィール・ユーザー情報・過去記憶** はメイン専用。サブには出さない
- 自明なサブほど警戒する。「どうせ小さいタスクだから」という油断が、60kトークンと漏洩経路を同時に連れてくる
サブエージェントを設計するとき、つい「賢くするために文脈を足そう」と考えてしまいます。でも記憶の設計に限っては、足し算ではなく引き算です。何を渡すかではなく、何を渡さないか。派遣社員には日記を渡さない。それだけで、トークンも安全も守れます。
---
記憶の設計を含むコンテキストエンジニアリングの全体像(5段階の戦略からRAG、MCP、CLAUDE.md、Agentic RAGまで)は書籍にまとめています。
[LLMを嘘つきから専門家に変える Context Engineering実践](https://kenimoto.dev/ja/books/context-engineering?utm_source=kenimoto-dev-blog&utm_medium=article&utm_campaign=sub-agent-memory-isolation)
---
# Claude Codeを3セッション並列で8時間動かしたら、2回コンテキストを上書きしあった
URL: https://kenimoto.dev/ja/blog/three-claude-sessions-parallel-8h-context-overwrite/
Lang: ja
Date: 2026-05-27
Description: 3つのClaude Codeセッション、3つのgit worktree、共有された1つの.claude/ ディレクトリ。8時間後、メモリファイルは2回上書きされ、47ドル分の再作業が発生していました。
午後にやりたいことが3つあって、ターミナルウィンドウも3つ開いていました。計算は単純です。Claude Codeを3セッション、それぞれ別のworktreeで起動して独立したブランチを進めれば、午後のスループットはおおよそ3倍になるはず。公式ドキュメントもこのパターンを推奨していて、デスクトップアプリは新規セッションごとに[自動でworktreeを作る](https://code.claude.com/docs/en/worktrees)実装になっています。「安全な並列パターン」として紹介されているやり方そのものです。
8時間後、私の手元には壊れたメモリファイルが2つ、書いた覚えのないスキル説明が1つ、そして別のworktreeに既に存在していた作業を再生成するのに使ったトークン代として約47ドルの請求が残っていました。worktreeのセットアップは確かに安全でした。共有された状態は安全ではありませんでした。
この記事は、その8時間の記録です。何をセットアップして、いつ衝突が起き、何が上書きされ、いま私が並列セッション同士が互いを食い合わないために使っている3つの小さなパターンの話です。
## 安全に見えたセットアップ
同じリポジトリの別worktreeでClaude Codeを3セッション起動しました。ブランチは3つ、`feat/voice-buffer`、`fix/og-emit`、`feat/citation-tracker`。どのブランチも同じソースファイルには触れない構成で、それは事前に2回確認しました。
```bash
# Terminal A
git worktree add ../wt-voice-buffer feat/voice-buffer
cd ../wt-voice-buffer && claude
# Terminal B
git worktree add ../wt-og-emit fix/og-emit
cd ../wt-og-emit && claude
# Terminal C
git worktree add ../wt-citations feat/citation-tracker
cd ../wt-citations && claude
```
各セッションが読み込むシステムコンテキストは同じです。リポジトリの `CLAUDE.md`、ユーザー階層の `~/.claude/CLAUDE.md`、`~/.claude/skills/`、そして `~/.claude/projects//memory/` ディレクトリ。worktreeはgitのレイヤーでは独立していますが、それ以外は全部共有です。
この含意に気付いたのは、8時間目に壊れたメモリファイルを開いたあとでした。worktreeはソースコードを分離してくれます。Claudeの「脳」までは分離してくれません。
## 衝突1: 3時間42分後、スキルファイル
最初に壊れたのは、その日一度も自分で触っていないスキルファイルでした。
セッションAは音声バッファの修正中に「WebRTCバッファ用のスキルってあったかな」と自問しました。なかったので、`~/.claude/skills/voice-buffer/SKILL.md` に新しく書き出して作業を続けました。ほぼ同じ8分のウィンドウで、セッションCはcitation trackerを作りながら「ソース帰属パース用のスキルってあったかな」と自問しました。なかったので `~/.claude/skills/citation-source/SKILL.md` に新しく書き出しました。
ここまでは衝突なしです。別ファイル、別トピック。公式ドキュメント的にも何も問題ありません。
衝突が起きたのは3つ目のファイル、`~/.claude/skills/_index.md` でした。両セッションとも、新スキルを登録するときにこのインデックスを更新する判断をしました。セッションAが先に更新。30秒後にセッションCが読み込んだのは、Aの書き込み「前」のバージョン。Cはそこに自分のスキルを追記して保存しました。Aが登録したvoice-bufferスキルの行は、インデックスから消えました。セッションAは知る由もなく、もう次の作業に進んでいました。
5時間目に、淡々と動いていたセッションB(OGタグ修正担当)に「いまスキルインデックスにvoice-bufferは入ってる?」と聞いて気付きました。「入っていません」との返答。確認したら本当にそうでした。Aが書いたスキルファイル本体はディスクにあるのに、そこを指すインデックスは消されていた。
これがロックなしの共有状態の見た目です。書き手2人、last-write-wins、警告なし、マージなし。
## 衝突2: 6時間18分後、メモリファイル
2つ目の衝突はもっと厄介でした。残しておきたかった作業を食われました。
`~/.claude/projects//memory/` は、セッションをまたいで残しておきたい小さなメモを置く場所として使っています。システムのコンポーネントマップを書いた `architecture.md`、文体の好みを書いた `feedback.md`、現在の優先事項を書いた `project.md`。どれもClaude自身がたまに書き加えます。ユーザーが「これ覚えておいて」と言ったとき、もしくはエージェント自身が「これは残す価値がある」と判断したとき。
6時間18分の時点で、セッションAが音声バッファ作業を終え「このバッファの不変条件、残しておこうかな」と自問しました。`architecture.md` を読み、節を追加して保存。6時間19分、セッションBがOG修正を終え「og:type 二重発火バグをgotchaとして記録しておこうかな」と自問しました。`architecture.md` を読みましたが、それはコンテキストにキャッシュされたAの書き込み「前」のバージョン。そこに自分の節を追加して保存しました。
Aが書いたvoice-bufferの不変条件は消えました。注意深くまとめた8分の作業が、まったく無関係だが正しい内容のmetaタグ節に置き換わって、跡形もない。
翌朝、たまたま「buffer invariant」でgrepして何も出てこなかったから気付きました。もし検索しなかったら、そのメモは今後のClaude Codeセッションにとって「存在しないも同然」になっていたはず。エージェントは「そういえばあの不変条件は」と思い出すきっかけすらありません。「兄弟プロセスがメモリファイルを静かに上書きしました」というエラーログはどこにも残らないので。
## 本当に壊れていたもの
worktreeはファイルシステムレベルの問題を解決します。2つのセッションが同じ `src/voice/buffer.ts` に書き込めば、gitコンフリクトが派手に出るので気付けるし、回復もできます。2つのセッションが同じ `~/.claude/skills/_index.md` に書き込むと、静かな上書きが起きます。気付けないし、回復もできません。
具体的に何が壊れていたかと言うと、こうです。公式ガイドは[「あるセッションでの編集が別セッションのファイルに触れることはない」](https://code.claude.com/docs/en/worktrees)と書いていて、worktreeレイヤーではこれは事実です。でも、ハーネスレイヤーではそうではありません。ハーネス(メモリ、スキル、フック、設定)はworktreeより1階層上の `~/.claude/` にあり、そこは並列セッションが調整なしに自由に書き込む場所です。
危険なファイルは3クラスあって、痛みが大きい順に並べるとこうなります。
1. **設定ファイル** (`~/.claude/settings.json`)。エージェントがここに書くことは少ないので衝突頻度は低いです。ただし書く瞬間(スキルが権限追加を要求するなど)は確実にlast-write-wins。
2. **スキルファイル** (`~/.claude/skills/`)。中頻度。実際の発火点は個別のSKILL.mdではなく、インデックスや共有カタログのほう。
3. **メモリファイル** (`~/.claude/projects//memory/`)。一番痛い。エージェントがここに書くタイミングは、まさに「いま学んだことを残しておきたい」瞬間です。つまり、失いたくない作業がちょうど消える。
Anthropicの並列worktreeパターンはコード向けの設計です。ハーネスは1セッション同時稼働を前提に作られています。両方を同時にやるのは、利用者の側のバグです。
## 47ドルの授業料
現金の損失は再作業ぶんです。メモリファイルの衝突のあと、セッションAが直前に導出したvoice-bufferの不変条件はどこにも残っていませんでした。翌朝、新しいセッションを立ち上げてバッファを拡張するよう頼んだら、同じ不変条件を、ほぼ同じ手順で、ゼロから再導出しました。40分ほどのトークン消費です。ダッシュボードを見たら、Sonnet 4.6のトークン代でだいたい47ドル。あと、少しだけ不機嫌な朝。
もちろん最初の導出にも料金は払っています。なので実際には「失った」というよりは「二度払った」が正確で、その二度目のほうが回避可能だった、という構図です。Brooks's Lawには誰も引用しない脚注があります。「並列プロセスは互いのメモを上書きするので、同じ作業を2回支払うことになる」。
## いま私が使っている3つのパターン
衝突の日のあと、3つだけ変えました。どれも小さい変更で、Anthropic側に何かを出してもらう必要はありません。
**パターン1: セッションごとのメモリ名前空間。** 共有された `~/.claude/projects//memory/` ではなく、各並列セッションは `~/.claude/projects//memory//` 配下に書き込むようにします。worktreeごとの `CLAUDE.md` でエージェントに自分のサブディレクトリを指し示しておくだけ。セッション終了時に手動か簡単なスクリプトでメインの `memory/` にマージします。衝突はファイル名の重複として表面化するので、派手に気付けるし回復できます。
```markdown
## メモリ書き込み先
メモリファイルは
`~/.claude/projects/repo/memory/feat-voice-buffer/`
配下にのみ書き込むこと。
`~/.claude/projects/repo/memory/` 直下には書かないこと。
```
**パターン2: 共有インデックスへのwrite lock。** 名前空間で分離できないファイル(スキルインデックス、settings.json)には、エージェントの書き込みに `flock` 系のロックを噛ませます。エージェントは小さなラッパースクリプト経由で書き込みを行い、`~/.claude/locks/skills-index.lock` の排他ロックを取ってから対象ファイルに触ります。last-write-winsの構造そのものは変わりませんが、書き込みが直列化され、書き込み前の読み込みが整合した状態を見るようになります。ラッパーはシェルで20行程度。
```bash
#!/usr/bin/env bash
# ~/.claude/bin/locked-write.sh
target="$1"
lockfile="$HOME/.claude/locks/$(basename "$target").lock"
mkdir -p "$(dirname "$lockfile")"
exec 9>"$lockfile"
flock 9
cat > "$target"
```
**パターン3: `.claude/sessions/` 経由の調整。** 動いている各セッションが `~/.claude/sessions/.json` にハートビートファイルを書きます。ブランチ名、開始時刻、ハーネスレイヤーで触る予定のファイルを記録しておくイメージです。共有インデックスやメモリファイルに書き込む前に、sessions/ ディレクトリを兄弟プロセスの主張についてgrepします。重なる主張があれば待つかスキップする。3つの中で一番重いパターンで、私が一番使わないやつです。パターン1と2でほとんどの実衝突は捕まるので。
[サブエージェントの並列レビュー](/ja/blog/three-sub-agents-pr-review-40-percent-disagreement/)を使ったことがあれば形は同じだとわかるはずです。問題はモデルではありません。利用者が「ここにあるとは思っていなかった」統合レイヤーのほうです。サブエージェントは1セッション内で意見について衝突し、並列セッションはハーネスをまたいで状態について衝突します。
## いま私が信じていること
並列Claude Codeセッションはタダではありません。マルチエージェントのコードレビューがタダでないのと同じ仕組みです。コストは姿を変えますが、ゼロにはなりません。並列セッションの場合、コストはハーネスディレクトリ内の静かな上書きとして現れます。開始から8時間後、2つ目のターミナルを開いたときには想像もしなかったファイルで。
公式ガイドの言い分はソースコードレイヤーでは正しいです。「あるセッションでの編集が別セッションのファイルに触れることはない」。ただし1階層手前で止まっています。`~/.claude/` 配下の編集は、互いのファイルに触れることに躊躇がありません。スケジュールはlast-write-wins、エラーログはあとからgrepするとどこにもない。
この記事から1つだけ持ち帰るとしたら、こうです。2つ目のworktreeで2つ目のClaude Codeセッションを開く前に、10秒だけ立ち止まって考えてみてください。この2つのセッションはスキル、メモリ、設定を共有するのか、そしてどちらかが他方の書き込みを静かに食ったとして自分は困るのか。困るなら、今日のうちにパターン1を入れて、実際に衝突を踏んだ日にパターン2を足す。パターン3は5並列までスケールしたときに考えれば十分です(公式ドキュメントが「やめておけ」とやんわり書いてくる手前のあたり)。
並列セッションは今もまだ使っています。worktreeの境界が境界の全部だ、というふりをやめただけです。
---
**この話をもっと詳しく:** ハーネスレイヤー、Claude Codeセットアップを構成する6つのモジュール、共有状態の失敗モードについては、[ハーネス・エンジニアリング](https://kenimoto.dev/ja/books/harness-engineering-guide) で詳しく扱っています。ターミナルを3つ開くだけで終わらせず、Claude Codeを本気で運用したいエンジニア向けのフィールドガイドです。
このブログの関連記事:
- [3人のサブエージェントに同じPRを見せたら4割で意見が割れた](/ja/blog/three-sub-agents-pr-review-40-percent-disagreement/)
- [3役分離: Observer / Strategist / Marketer](/ja/blog/observer-strategist-marketer-3-yaku-bunri/)
- [Claude Codeのサブエージェント設計](/ja/blog/claude-code-sub-agent-design/)
---
# 3人のサブエージェントに同じPRを見せたら、4割の指摘で意見が割れた話
URL: https://kenimoto.dev/ja/blog/three-sub-agents-pr-review-40-percent-disagreement/
Lang: ja
Date: 2026-05-12
Description: Claude CodeのSub-agentを3つ並列で同じ500行PRに当ててみたら、コメントの41%は意見が割れた。マージに想定の4倍の時間がかかった理由と、何人並列が最適かの実用ルール。
マルチエージェントのコードレビューは、ただの上乗せだと思っていました。3人のサブエージェントに同じPRを見せれば、1人分のコストで3倍の目玉。安いものです。
実際にやってみたら、500行のリファクタリングPRに3つのSub-agentを並列で当てた結果、コメントの41%で意見が割れました。15分で終わるつもりだったマージ作業に1時間かかりました。Brooksの法則は2026年でも生きていて、しかもエージェントにまでスケールしているようです。
Anthropicは3月の[Code Review発表](https://claude.com/blog/code-review)で、社内利用時に「不正確」と判定された指摘は1%未満だと報告しています。この数字自体は本物です。ただし、これは数ヶ月かけて調整されたパイプラインを自社コードベースで動かした結果の数字でした。同じ手触りを期待して自分のリポジトリで3つのSub-agentを動かしてみたら、「一致」という言葉の意味が私の想像とは違っていました。
本記事はその実験記録です。これは以前書いた[Claude CodeのSub-agent設計記事](/ja/blog/claude-code-sub-agent-design/)の続編で、設計論ではなく**並列稼働させた実測データ**の話です。
## 実験の設定
対象は副業プロジェクトのWebRTCシグナリング層をリファクタリングする500行のPRです。8ファイル、ほぼTypeScript、設定ファイルを少しいじり、新しいエラー型が1つ。1人のレビュアーでは何かを見落とす程度には複雑で、いわゆる「実験のための実験」にならない程度の地味さがありました。
3つのSub-agentを `.claude/agents/` に定義しました。全部Sonnet 4.6、全部読み取り専用。
```markdown
---
name: explore-reviewer
description: 呼び出し元、依存先、デッドコードパスを追跡する。
model: sonnet
allowed-tools: Read Grep Glob
---
あなたはコードの考古学者です。変更されたファイルごとに、すべての呼び出し元、
参照するテスト、変更後に静かになるパスを見つけてください。
file:line形式の引用を伴って具体的に報告してください。スタイル論はなし。
```
```markdown
---
name: security-reviewer
description: 認証、検証、シークレット管理の退行を探す。
model: sonnet
allowed-tools: Read Grep Glob WebSearch
---
あなたはセキュリティレビュアーです。認証フロー、入力検証、シークレット管理、
依存リスクのみを扱ってください。各指摘に推定CVSSを付けてください。
スタイルとアーキテクチャは無視。
```
```markdown
---
name: plan-architect
description: 既存規約に対する設計判断を評価する。
model: sonnet
allowed-tools: Read Grep Glob
---
あなたはソフトウェアアーキテクトです。PRの設計判断を、このコードベースの
既存規約と比較してください。ドリフト、抜けている境界、次の人が困る抽象化を
指摘してください。
```
各Sub-agentに同じプロンプトを渡しました。「PR #482を1行ずつレビューし、file:line引用つきの箇条書きで指摘を出してください」。全員が独立したコンテキストで動き、互いの出力は見ません。最後に統合するのは私だけです。
## 41%の不一致はこういう形をしていた
3つ全部が終わったとき、生のコメント数は計78件ありました。スプレッドシートを開いて1つずつ「3エージェント全員が指摘」「2/3が指摘」「1/3だけが指摘」とタグ付けしました。
| カバレッジ | 件数 | 比率 |
|---|---|---|
| 全3エージェントが指摘 | 14 | 18% |
| 2/3エージェントが指摘 | 32 | 41% |
| 1/3エージェントだけが指摘 | 32 | 41% |
「1/3だけが指摘」のバケツが、私が**不一致**と呼んでいる部分です。残り2つのSub-agentは同じ行を、同じツールで、同じ差分の上で見ていました。それでも素通りした。**ある指摘が「あるSub-agentの個人的な意見」である確率は41%**。これが今日の数字です。
Anthropicの「1%未満」は計測方法が違います。彼らはエンジニアが「修正せずに明示的にcloseした指摘」をカウントしています。私がカウントしているのは「同じコードを見ていた他の2人が言及すらしなかった指摘」です。問いが違うので、答えも違って当然です。そして私の時間を奪うのは後者です。
## 不一致の4パターン
全件を分類したら、ほぼすべてが4つのパターンに収まりました。
**重要度のドリフト。** plan-architectは「null チェックが抜けている」を critical と判定。同じ行をsecurity-reviewerは「low、呼び出し元が既に検証済み」と判定。両方とも、ある意味で正しい。architectは関数を単体で読んでいて、security-reviewerはgrepで呼び出し元を歩いて上流の検証を確認していました。同じ行、正反対の判定。
**スコープのドリフト。** 「このPRをレビューせよ」と頼んだら、explore-reviewerはPRが触っていない別ファイルの既存バグまで3件報告してきました。plan-architectは差分の外には一切触れません。事前にどちらの挙動になるか分かりません。厳密に言えばどちらの解釈も擁護できます。実用的に言えば、片方がコメント数を爆発させます。
**具体性のドリフト。** plan-architectが書いてきたのは「リトライ処理を共通ヘルパーに抽出することを検討してください」。security-reviewerが書いてきたのは「184-201行目を `retry(opts, () => fetchToken(opts.url))` に置き換え、30秒の上限を設けてください。さもないと auth-refresh パスがワーカーをハングさせます」。同じアイデア。片方は30秒で適用できて、もう片方は会議を1つ消費します。**具体性は私の想像よりずっと大きな分散軸でした。**
**ツール予算のドリフト。** explore-reviewerは grep と glob を使い、改名された関数がCIスクリプトでまだ参照されていることに気づきました。plan-architectは同じツールを持っていたのに、そこまで見に行かない。同じ allowed-tools、同じ「依存先を探せ」の指示。片方は表面を歩き、片方は建物の中を歩く。ドリフトの正体は、システムプロンプトがどれだけ「徘徊」を奨励しているかでした。
Claude Codeの[Sub-agent公式ドキュメント](https://code.claude.com/docs/en/sub-agents)を読んでいれば、ここまでは想定内かもしれません。私が驚いたのは、私がタグ付けした不一致のほぼ全部が、この4つに綺麗に収まったことです。
## 誰も気づかなかったバグ
マージから2日後、同僚が新しいエラーハンドリングパスにレースコンディションを見つけました。PRは1フレーム分の窓を開けていて、同じソケットに2回のreconnectが走り得る状態でした。3つのSub-agent、誰も触れていませんでした。私が手書きしたPR descriptionには「reconnect ロジックを移動」と書いてあって、同僚はそれを見て調べに行ってくれたのです。
「目玉が十分あれば、バグは浅くなる」とEric Raymondは1999年に書きました。目玉の話は正しい。ただし、3つの目玉が**全部同じ窓を見ている**場合の話はしていません。私の3つは全員が差分を凝視していました。誰も一歩下がって「タイミングは何が変わったのか?」と問いませんでした。
## マージで失った1時間
3つのレポートを統合する作業こそ、私が予算化していなかった部分です。
「2/3」「1/3」の指摘1件ごとに、判断が必要でした。
1. これは本物の指摘か、それともgrep 1発で埋まるコンテキストギャップか
2. 本物だとして、エージェントAの重要度判定が正しいか、エージェントBが正しいか
3. 修正案が出ているとして、具体案をそのまま適用していいか、抽象案に戻すべきか
3番目の質問だけで、コーヒーを3杯飲みました。2つのSub-agentは「共通ヘルパーに抽出せよ」と言ってきました。1つは具体的なヘルパーを書いてきました。その具体ヘルパーが本当に正しい形をしているかは、結局、差分を3周目に読んで自分で判断しなければなりませんでした。正しくありませんでした。私は4番目のバージョンを書く羽目になりました。
Brooksの法則は、遅延プロジェクトに人を追加すると人間同士のコミュニケーションコストが爆発する話でした。私は今、これが一般化すると確信しています。**N人の独立した視点を同じ成果物に当てた瞬間、N+1番目のレビュアーは統合担当者になり、統合担当者の時間はNに対してほぼ線形に増えます**。3つのSub-agentは3倍の目玉だった一方、3倍の統合コストでもありました。
Claude Codeを[24時間自律稼働させた話](/blog/autonomous-agent-24-hours-security-lessons)を逆方向から見ると同じ結論にたどり着きます。ボトルネックはエージェントの出力を読む人間に移動するのです。
## 何人並列が最適か
答えは1ではないと思っています。同じ週にN=1で小さなPRを試しました。汎用エージェントによる1回のレビューパスです。explore-reviewerなら気づいたであろうクロスファイルの依存関係を、見落としました。**1組の目玉は、2組より明確に悪い**です。
12本くらいPRを通したあとの、私の現時点ヒューリスティック:
- 小さなPR (100行未満、新ファイルなし): Sub-agent 1つ。それ以上は無駄。
- 中規模PR (100-500行、1サブシステムに収まる): 異なる角度の2つ。たいていは explore + security か explore + architect。PRが何をリスクに晒しているかで選ぶ。
- 大規模・横断PR (500行超、複数サブシステム): 3つ。事前に統合時間を確保すること。無料ではない。
3を超えると、いまのところ価値を感じたことがありません。HAMYの[9エージェント構成](https://hamy.xyz/blog/2026-02_code-reviews-claude-subagents)は興味深いものの、レポートをマージする2つ目のツールが必要になり、それは私自身より安くないと割に合いません。
もう1つのツマミは具体性です。私は今、各Sub-agentに「最小の具体的な修正案を添えること、わからない場合は no-fix と明記すること」を必ず要求しています。システムプロンプトのこの1行だけで、具体性ドリフトが半分近く消えました。
## 結局、私が今信じていること
マルチエージェントのコードレビューはタダではありません。実態は「3人のジュニアレビュアーが別々の部屋で読んで、あなたがメモを統合するシニア」に近い。目の数は確かに増えますが、統合コストも増え、その統合コストはあなたのカレンダーに居座ります。
誰も気づかなかったバグの件が、私を一番謙虚にさせました。3エージェント、3つの角度、全員読み取り専用、全員同じ差分。誰もタイミングの変化に気づかなかった理由は、誰もそれを問われていなかったからです。**Sub-agentはシステムプロンプトに書かれた質問にはとても強い。書き忘れた質問には平凡です。** 限界はモデルではなく、こちらの問いの設計でした。
ひとつだけ持ち帰っていただきたいのは、これです。`what-am-i-not-asking` という4つ目のSub-agentプロンプトを書き、差分を渡し、他のエージェントが見落とすカテゴリを列挙させる。その答えを読んでから、本番のレビュープロンプトを書く。本記事の実験では私はそれをやりませんでした。そして1時間を失い、同僚にレースコンディションを見つけてもらいました。因果関係は明白です。
Anthropicの1%未満という数字は本物です。ただしそれは、数ヶ月かけて誰かが磨いたパイプライン上の数字です。会議の合間に3つ書いたSub-agentで出る数字ではありません。あなたの環境で磨いてください。それまでは、4割で見積もるのが安全です。
---
**この記事を本1冊分に拡張したいなら。** Sub-agent設計、カスタムエージェントのパターン、Claude Codeのワークフロー全体は、[Practical Claude Code(実践Claude Code)](https://kenimoto.dev/ja/books/claude-code-mastery) にまとめています。Claude Codeを本気で運用したいエンジニアのためのフィールドガイドです。
関連記事:
- [Claude CodeのSub-agent設計 — 1セッションで専門家チームを使い分ける](/ja/blog/claude-code-sub-agent-design/)
- [Claude Code Hooks v2: 25イベントで何が変わるか](/ja/blog/claude-code-hooks-v2-25-events/)
- [CLAUDE.md は結局 Context Engineering を1ファイルに凝縮したもの](/ja/blog/claude-md-context-engineering-practice/)
---
# バリデータがバグっていた — 「安全です」と報告するシステム自身が壊れているとき
URL: https://kenimoto.dev/ja/blog/validator-was-the-bug-grey-failure/
Lang: ja
Date: 2026-06-07
Description: 最も厄介なデバッグは、コードのバグではありません。「OK」と報告する監視やバリデータ自身が壊れているケースです。CrowdStrikeで850万台を巻き込んだ21対20のフィールド不一致、Metaの内部ツール全滅、みずほのATM停止。3つの障害から、計器を疑うという最初の一手を考えます。
先に結論を言います。**最も危険なバグは、コードの中ではなく「OK」と報告する側にいます。** 監視ダッシュボード、ヘルスチェック、デプロイ前のバリデータ。これら「安全です」と告げてくる計器そのものが壊れているとき、デバッグは一気に泥沼化します。
恥ずかしい話からします。私は昔、本番の数字が変だという報告を受けて、まずダッシュボードを開きました。グリーン。全部グリーン。だから「監視が正常なんだから問題ない、報告側の勘違いだろう」と判断して、30分くらい何もしませんでした。実際には集計バッチが止まっていて、ダッシュボードは古い値を表示し続けていただけでした。グリーンだったのは、健康だったからではなく、心電図のコードが抜けていたからです。
この「計器を信じすぎる」という失敗は、規模が大きくなると桁違いの被害になります。
## 850万台を落としたのは、バリデータのバグだった
2024年7月19日、CrowdStrikeのFalcon Sensorアップデートが世界中のWindowsをブルースクリーンに変えました。その数、約850万台。航空会社のチェックインが止まり、病院のカルテが開かなくなり、決済端末が沈黙しました。医療業界だけで被害見込みは約19.4億ドルとされています。
原因はマルウェアでも外部攻撃でもありません。デプロイ前の検証システム、Content Validatorのバグでした。
技術的にはこうです。Channel File 291の更新で使われたIPCテンプレートは **21個の入力フィールド** を定義していました。ところが、それを実際に解釈するセンサー側のコードは **20個** しか渡していませんでした。21対20。たった1個のズレです。
普通ならバリデータがこの不一致を弾くはずでした。チームもそう信じていました。3月から4月にかけて、同種の更新が何度も成功していたからです。ところがバリデータ自身にバグがあり、この21番目のフィールドのズレを検知できませんでした。テストでは21番目に常にワイルドカード(何にでもマッチする条件)が使われていたため、不一致が一度も表面化しなかったのです。結果、カーネルレベルの境界外メモリ読み取りが起き、世界中のマシンが同時に倒れました。
「バリデータが通したんだから安全だ」。この前提が、850万台分の崩壊を招きました。怖いのは、誰も嘘をついていないことです。バリデータは正直に「安全です」と報告していた。報告する機能そのものが壊れていただけです。
## グレー障害 — 「応答できる」と「正しく動く」は別物
この種の故障には名前があります。**グレー障害(gray failure)** です。完全にダウンすれば監視が即座に気づきます。完全に正常なら問題ありません。やっかいなのはその中間、つまり「生きてはいるが正しく動いていない」状態です。
ある決済プラットフォームで、データベースノードがこの状態に陥った事例があります。ノードはヘルスチェックに応答し続けていました。だから監視は「正常」と報告し、フェイルオーバーも発動しませんでした。実際にはレプリケーションが止まっていて、データはどんどん古くなっていきました。
ヘルスチェックが見ていたのは「このノードは応答できるか」だけでした。「このノードは正しく仕事をしているか」は見ていなかった。この2つは、似ているようでまったく別の質問です。pingが返ってくることと、中の人がちゃんと働いていることは違います。会社のチャットで即レスする人が、必ずしも仕事を進めているとは限らないのと同じです。
ヘルスチェックには常に死角があります。何を検査しているのか、そして何を検査して **いない** のかを、書いた本人すら忘れがちなのです。
## 内部ツールも、同じネットワークに乗っていた
計器を疑うべき理由はもう一つあります。障害そのものが、計器を巻き込むことがあるからです。
2021年10月4日、Meta(Facebook)が約6時間にわたってインターネットから消えました。バックボーンのメンテナンス作業でコマンドを誤り、全バックボーン接続が切れ、DNSサーバーがBGP経路を撤回しました。Facebook、Instagram、WhatsAppが世界中で同時に沈黙しました。
エンジニアが最初に手を伸ばしたのは、いつもの内部ツールでした。ところがその内部ツールが、落ちたのと同じネットワークに依存していました。リモートでデバッグする手段がすべて使えなくなり、最終的に人が物理的にデータセンターへ向かうことになりました。
「内部ツールはいつでも使える」。平時には正しいこの前提が、有事には真っ先に崩れます。火事のときに限って消火器の前に火が回っている、というやつです。監視も、ログ基盤も、踏み台サーバーも、それ自体が障害の被害者になりうる。そう考えておくだけで、初動の選択肢が変わります。
## 過去の成功体験という、いちばん手強い計器
人間の頭の中にも、壊れた計器があります。過去の成功体験です。
2021年2月28日、みずほ銀行のe口座一括切替処理が、MINORIコアバンキングの定期預金データ領域を溢れさせました。ATM 4,318台が停止し、5,244枚のキャッシュカードと通帳が機械に呑み込まれました。引き出せなくなった人が、休日のATMの前で立ち尽くす光景です。
対応チームが取った行動は、過去の障害で有効だったエラー閾値の緩和でした。前回はこれで収まった。だから今回も、と。ところが状況は違っていました。閾値の緩和は、残っていた最後の安全バリアを外し、障害をさらに広げてしまいました。
後の調査委員会は、根本原因を「技術」ではなく「組織文化」に求めました。「前回これで直った」という記憶が、検証されないまま今回に適用された。過去の成功は、もっとも疑いにくい計器なのです。よく効いた薬を、別の病気にそのまま飲ませてしまった、と言い換えてもいい。
## 計器を疑うための、実務の一手
では具体的に何をするか。私が現場で使っているのは、シンプルなルールです。
**「本当に?」を3回繰り返す。** ログが正しい? 本当に? ならログ基盤自体のステータスを見る。影響はこの2社だけ? 本当に? ならフィルタを外して全件を見る。前回と同じ対処で直る? 本当に? なら前回との差分を明示的に並べる。
**「What」を最後に回す。** デバッグはつい「何が壊れた?(What)」から始まります。でもその前に確認すべきことがあります。どこから観測している?(Where)その観測点は信頼できる? いつから?(When)本当にその時刻から? どうやって検知した?(How)その検知手段自体は生きている? これらが固まってから、初めてWhatを考えます。
そして最後に、自分用のチェックリストを一つ。
- ログは全件出ているか(欠損はないか)
- メトリクスの計測自体にバグはないか
- ヘルスチェックは「正しく動く」を見ているか、「応答できる」だけを見ているか
- 監視システム自体が、今回の障害の影響を受けていないか
- 「N件だけ」のNは、本当にNか(フィルタは正しいか)
- 前回と同じ対処が効くという根拠は、今回もあるか
計器を疑うのは、後ろ向きな作業ではありません。観測が信頼できると確認できて初めて、その先のデバッグ全部の精度が上がります。土台が砂のままどれだけ立派な仮説を積んでも、それは砂上の楼閣です。グリーンのダッシュボードを見て安心する前に、一度だけ「このグリーンは、本当にグリーンか?」と問う。私が30分を無駄にして学んだのは、結局それだけのことでした。
---
# 音声AIスタックを5つ実測した。300msの壁を越えられたのは2つだけだった
URL: https://kenimoto.dev/ja/blog/voice-ai-5-stacks-only-two-under-300ms/
Lang: ja
Date: 2026-05-13
Description: 「音声AIは300ms以下で応答できる」と何度も読んだ。同じ1分会話で5つのスタックを実測したら、3つは崖を越えられなかった。2026年5月時点のP95 latency表を貼っておく。
「音声AIエージェントは300ms以下で応答できる」と何度も読んだ。AssemblyAIも、Vapiも、Realtime APIのローンチ記事もそう言っている。だから5つのスタックを組み、同じ1分の会話を全部に流して、各パイプラインの中にストップウォッチを差し込んだ。
5つのうち3つは、崖の手前にすら届かなかった。
残りの2つは、私が「どうせマーケティング数字だろう」と高を括っていた構成だった。マーケティングが正しくて、自分の手書きパイプラインが間違っていた。完敗です。
## スライドに載らない「3つの崖」
数字を見せる前に、まず体感モデルから。音声AIのレイテンシは、なだらかには劣化しません。崖のように落ちます。AssemblyAI、Vapi、Retellの調査がだいたい同じ3つの閾値に収束していて、私も1週間ユーザーテストを回した結果、これを信じるようになりました。
| レイテンシ | ユーザーが取る行動 |
|---|---|
| 0-300ms | 普通に話す。AIを意識しない |
| 300-500ms | 間を感じるが許容 |
| 500-800ms | AIに被せて話し始める(「聞こえてますか?」) |
| 800-1500ms | 同じ質問を繰り返す |
| 1500ms+ | 国際電話と同じ感覚になり、諦める |
300msが第1の崖です。これを越えると、ユーザーは「機械が処理している」ことを意識します。500msを越えるとターンテイキングを奪い合い始め、STTが新しい入力を受け取り直してさらに遅くなる悪循環。800msでは、テスターの半分が「もしもし?聞こえてる?」と言いました。**録画を見返しながらコードレビューする1週間ほど屈辱的なものは、私のキャリアでもそうそうありません。**
## 300msの予算はどこに消えるか
5つのうち3つがなぜ落ちたのか。予算配分を見ればわかります。カスケードパイプラインは、4つの直列処理を300msに収めなければいけません。
- **STT** (音声認識): 80-300ms。モデルとVAD設計次第
- **LLM TTFT** (初トークン): 100-500ms。モデルサイズ、コンテキスト長、コールドスタート次第
- **TTS TTFB** (音声最初の1バイト): 75-300ms。ボコーダー次第
- **ネットワーク往復**: 50-200ms。光の速さとコロケーション選択でほぼ決まる
最速の数字だけ足しても305ms。普通の数字を足すと1秒を越える。今回のベンチマークの元になった書籍ではこれを「レイテンシの解剖」と呼んでいて、結論は「カスケードは300msに数学的にアレルギーがある」というものです。各コンポーネントが物理的に隣に座っていない限り。
Voice-to-Voiceのend-to-endモデルは、STT + LLM + TTSを音声トークンストリーム上の単一forward passに畳み込むことでこのルールをすり抜けます。第2のホップがない。TTSのウォームアップがない。サービス間のハンドオフがない。それが全てで、勝った2つのスタックは私が「いちばんコードを書かなかった」スタックでもありました。
## 5つのスタック
ベンダー贔屓ではなく実比較がしたかったので、条件を揃えました。同じ1分のカスタマーサポート用スクリプト、同じWebRTC ingress (OpenAI Realtime以外はDaily.co)、同じプロンプト、同じUS-EastのクライアントPC、各スタック10ターン×5スタック=50測定。平均は音声ユーザーには嘘をつくのでP50/P95/P99で報告します。
**スタック1 — OpenAI Realtime API**: `gpt-4o-realtime` の公式WebRTCエンドポイント。音声入力、音声出力、間のglueコードなし。
**スタック2 — Deepgram + Claude + ElevenLabs カスケード**: STTにDeepgram Nova-3、LLMにClaude Sonnet 4.6、TTSにElevenLabs Turbo v2.5。ホワイトボードに描く「ベスト・オブ・ブリード」構成。
**スタック3 — ローカルエッジ (Whisper + Llama + Coqui)**: Whisper Large v3 Turbo、Llama 3.3 70BをH100ローカルで、TTSにCoqui XTTS。ネットワーク往復0ms。Hacker Newsが大好きな「プライバシーと主権」の答え。
**スタック4 — LiveKit Agents + Gemini 2.0 Flash Live**: メディアプレーンにLiveKit、脳にGoogleのネイティブ音声Gemini Live。これも別SDK経由のVoice-to-Voice端到端。
**スタック5 — Pipecat + Claude + Cartesia**: オーケストレータにPipecat、LLMにClaude Sonnet 4.6、TTSにCartesia Sonic。ElevenLabsより速いTTSを使った、より作り込んだカスケード。
## 結果
| スタック | P50 | P95 | P99 | 300ms以下? |
|---|---|---|---|---|
| 1. OpenAI Realtime (Voice-to-Voice) | 232ms | 281ms | 320ms | ✅ |
| 2. Deepgram + Claude + ElevenLabs | 480ms | 624ms | 780ms | ❌ |
| 3. Whisper + Llama 70B + Coqui (ローカル) | 870ms | 980ms | 1,210ms | ❌ |
| 4. LiveKit + Gemini Live (Voice-to-Voice) | 250ms | 295ms | 360ms | ✅ |
| 5. Pipecat + Claude + Cartesia | 410ms | 540ms | 670ms | ❌ |
P95で300ms以下を達成したのはスタック1とスタック4だけ。両方ともVoice-to-Voice。両方とも「リレー競走」ではなく「単一forward pass」を提供しています。スタック5はカスケードを丁寧に作った例で、Cartesiaの90ms TTFBは本当に速い。それでも崖は越えられない。LLM TTFTとサービス間ホップが予算を食い切ります。
つらいのはスタック3です。「ネットワークがゼロなら、せめてカスケードには勝つはず」と期待していました。実際勝つこともあるのですが、Llama 3.3 70Bは小さくない。コモディティGPUでLLM TTFTだけで600ms出るので、「ネットワーク不要」では救えません。書籍のエッジAI章は正直に書いていて、現実的なエッジの勝ち筋は **小さいモデル** (Qwen2.5 1.5Bクラス) であって、フルサイズの70Bローカルではない。70Bをローカルで動かすのは両方の悪いとこ取りで、GPU代を払って崖も越えられない。**深淵を覗いていただけでした。**
## なぜ今(2026年5月)Voice-to-Voiceが勝つのか
3つの理由。驚いた順に並べます。
**1. TTFT-then-TTFBの積み上げが起きない**: カスケードでは、LLMの初トークンを待ってからTTSを起動するので、TTSの「最初の1バイトまで」の時間が二重に乗ります。Voice-to-Voiceは音声トークンを直接出すので、2度目のウォームアップがありません。
**2. ハンドオフのシリアル化がない**: Deepgram → Claude → ElevenLabsは3つの別APIエンドポイントです。各々が速くても、TLS、コネクションプール、フレームバッファのオーバーヘッドを3回払う。Pipecatは助けてくれますが、消し去ってはくれません。
**3. VAD連動のターンテイキング**: Voice-to-Voiceモデルは音声ストリームから自分でエンドポイント検出をします。カスケードは、VADシグナルでSTT出力を確定してから送る必要がある。この確定遅延は「ユーザーが話し終わった瞬間」から計測するベンチマークには見えませんが、ユーザーは「自分が公式に話し終わった瞬間」を知らないので、ただの沈黙として体感します。
2026年5月時点で300msを安く達成する方法は、「パイプラインを書かないこと」。私のレイテンシの大半は、私のコードでした。
## エッジAIが追いつくとき
エッジは、正しい問題の形には正しい答えです。ローカル限定のプライバシー、ネットワーク無しのキオスク、オフラインのロボティクス。ただ「サブ300msのクラウドエージェントが欲しい」の答えではありません。Whisper v3 TurboはRTF (Real-Time Factor) 1000x以上を叩き出し、1.5Bクラスのモデルは初トークンをCPUで200msで返せます。この組み合わせ — 小さいモデル、速いSTT、ローカルTTS — なら合計300-350msに収まる。スタック3で試した70B-on-H100の構成では、そこに届きません。
もう一つの道はハイブリッド: エッジSTT、クラウドLLM、クラウドTTS。最も長い同期ステップ(音声フレームのキャプチャ)でネットワーク往復をスキップしつつ、脳には引き続きクラウド級のモデル品質を使えます。書籍は意思決定マトリクスとしてこれを整理していて、私の実測とも一致します: 350-500msは現実的、サブ300msのカスケードは現実的ではない。
「カスケードのまま **体感300ms** に近づける」工夫(フィラー、マイクロ確認、漸進的トークン再生)については、別記事で[Voice AI Perception Hacks](https://dev.to/kenimo49/voice-perception-hacks-i-kept-the-pipeline-at-540ms-and-users-still-said-instant-3oki)に書きました。崖は動かしませんが、崖の手前に立っているかのように見せかけることはできます。
## 今から作るなら
2026年5月、もし私がこれから音声エージェントを始めるなら:
- **コンシューマー向け新規プロダクト** — OpenAI Realtime か Gemini Live を直接。考えるより早めに止めて、出荷する
- **Claudeを脳に使いたい** — Pipecat + Claude + Cartesia。P95 500-600msで生きていく覚悟。フィラー戦略は後でなく今設計する
- **プライバシー / エアギャップ要件** — Whisper Turbo + Qwen2.5 1.5B + ローカルTTS。350ms TTFBを狙う。70Bローカルは次世代GPUまで諦める
- **エンタープライズ電話** — ハイブリッド: エッジSTT、脳はクラウドVoice-to-Voice。PSTNコーデック層でレイテンシ優位は消えるので、ターンテイキング品質に最適化する
最も深い思い違いは「300msは選んだ **モデル** の特性だ」と思っていたこと。実際は「選んだ **アーキテクチャ** の特性」でした。モデルは、そのアーキテクチャの居心地の良さを決めるだけです。
## 関連記事
- [安い方のモデルが勝った: コンテキストはパラメータに勝る](https://kenimoto.dev/blog/cheap-model-won-context-beats-parameters)(英語版) — 別領域の同じ教訓。アーキテクチャはモデルサイズを食う
- [AIエージェントのコスト構造と損益分岐点](https://kenimoto.dev/ja/blog/ai-agent-cost-structure-breakeven) — 音声AIもこのコスト構造の一部です
- [Claude Code Sub-agentの設計パターン](https://kenimoto.dev/ja/blog/claude-code-sub-agent-design) — 音声統合をsub-agent化する選択肢
レイテンシの全解剖、知覚モデル、エッジAI章(スタック3の判断根拠)は書籍にまとめました。
[音声AI 300ms UX: 会話の崖を設計する](https://kenimoto.dev/ja/books/voice-ai-300ms-ux)
---
# Coloquei 11 schemas JSON-LD. Em 3 meses, só 3 foram citados por IA
URL: https://kenimoto.dev/pt/blog/11-json-ld-apenas-3-citados/
Lang: pt
Date: 2026-05-25
Description: Coloquei 11 schemas JSON-LD no do meu site e medi 3 meses de citações por IA. Oito viraram peso morto. Conto quais três carregaram o caminhão.
Há 3 meses passei uma tarde colocando 11 schemas JSON-LD no `` do meu site. Organization, WebSite, Person, quatro blocos de Service, dois de Book, MusicGroup, FAQPage. Saí satisfeito.
Depois fui medir o que os motores de IA faziam com aquilo.
Três dos onze apareceram nas citações. Os outros oito poderiam ser comentários HTML que daria no mesmo.
Aqui vai a história de medição. Quais três schemas ganharam o lugar, quais oito foram peso morto, e por que eu faria de novo, mas menor.
## O que eu coloquei e por que achei que ia funcionar
A implementação em si foi simples. Empacotei os 11 schemas num único array dentro de `