# 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 で基礎を固めた後の、動的なコンテキスト戦略の話です。 --- # 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 (