30万表示を記録した「Claude Codeアーキテクチャ解剖」の核心
2026年4月、@rohit4verse(Rohit氏)がXに投稿した長文記事が大きな反響を呼んだ。「Claude Codeのソースを全ファイル読んだ。これはteardownではない、blueprintだ」——30万表示、2,400ブックマークを記録した。
Claude Codeは55ディレクトリ・331モジュールのTypeScriptアプリケーション。チャットインターフェースの裏で同じClaudeモデルを動かしながら、数時間の無人稼働、APIダウンからの自動復旧、数千ターンのコンテキスト管理、複数サブエージェントの並列協調を実現している。
Rohit氏の分析で最も重要な指摘は、業界標準の「3層フレームワーク」では不十分だという点だ。
| 層 | 名称 | 役割 | 従来の理解 |
|---|---|---|---|
| Layer 1 | Model Weights | API経由で呼ぶ固定知能 | 最も投資される層 |
| Layer 2 | Context | プロンプト、会話履歴、取得文書 | RAGで改善される層 |
| Layer 3 | Harness | ツール、ループ、エラーハンドリング | 「天井」と見なされがち |
| Layer 4 | Infrastructure | マルチテナンシー、RBAC、状態永続化、分散協調 | ほとんど議論されない |
Princeton NLPのSWE-agent論文が証明した通り、同じGPT-4でもインターフェース設計を変えるだけでSWE-benchで64%の相対改善が得られる。モデル重みではなく、Layer 3と4に投資するチームが勝っている——これがRohit氏の主張だ。
非同期ジェネレータによるエージェントループ:whileループとの決定的な違い
Claude Codeの心臓部はquery.ts(1,729行)。最も重要な設計判断はループの実装方法にある。
// Claude Codeの実装: async generator
async function* agentLoop(deps: QueryDeps): AsyncGenerator<StreamEvent> {
// yield で値を逐次返す。呼び出し側がいつでも中断可能
}
多くのチュートリアルが教える実装はこうだ:
// 一般的な実装: while loop
while (true) {
const response = await callModel(messages);
if (response.stop) break;
messages.push(response);
}
この違いは本番環境で5つの問題を引き起こす。
| 問題 | while loop | async generator |
|---|---|---|
| ストリーミング | ユーザーは10-30秒の空白画面 | トークン到着ごとにyield |
| キャンセル | 外部からabort機構を別途実装 | .next()を呼ばないだけ |
| 合成可能性 | REPL・サブエージェント・テストで別実装が必要 | 1つのquery()関数を3つの呼び出し元が消費 |
| バックプレッシャー | モデルが高速生成するとメモリが膨張 | 消費側が停止すれば生産も停止 |
| エラー回復 | 外側のtry-catchに依存 | ループ内部の状態マシンで処理 |
各イテレーションは5つのフェーズを実行する。
Setup"] --> B["Phase 2
Model Invocation"] B --> C["Phase 3
Error Recovery
& Compaction"] C --> D["Phase 4
Tool Execution"] D --> E["Phase 5
Continuation
Decision"] E -->|"継続"| A E -->|"終了"| F["完了"] style A fill:#4A90D9,color:#fff style C fill:#E74C3C,color:#fff style F fill:#50C878,color:#fff
Phase 3が決定的に重要だ。prompt-too-longならコンパクションしてリトライ。max_output_tokensに到達したら32K→64Kにエスカレーション。コンテキストオーバーフローならメディア重いメッセージに対してリアクティブコンパクション。エラー回復がループの「外側」ではなく「内側」に組み込まれている——これがクラッシュするエージェントと稼働し続けるエージェントの差だ。
ループは依存性注入(QueryDepsインターフェース)で外部依存を受け取る:
interface QueryDeps {
callModel: (messages, options) => AsyncGenerator<StreamEvent>;
executeTools: (toolCalls) => Promise<ToolResult[]>;
getContext: () => ConversationContext;
}
これにより、モックのcallModelを注入すればコンテキストオーバーフロー処理やツール失敗のテストが実APIなしで可能になる。ほとんどのエージェントハーネスがテスト不能なのは、APIコールをループにハードコードしているからだ。
ツール並行実行とストリーミング実行:2〜5倍の高速化を安全に実現
Claude Codeには45以上の組み込みツールがある。数が重要なのではない。実行方法が重要だ。
ツールごとに並行性を分類する仕組みがある:
// ツール定義時の並行性分類
const toolDefinitions = {
Glob: { concurrency: "read-only" }, // 並列OK
Grep: { concurrency: "read-only" }, // 並列OK
Read: { concurrency: "read-only" }, // 並列OK
WebFetch: { concurrency: "read-only" }, // 並列OK
Edit: { concurrency: "write" }, // 直列実行
Write: { concurrency: "write" }, // 直列実行
Bash: { concurrency: "write" }, // 直列実行
};
toolOrchestration.tsがツールコールをバッチに分割する。読み取り専用ツール(Glob, Grep, Read, WebFetch)は最大10並列で実行。書き込みツール(Bash, Edit, Write)は直列。5ファイルを並列検索してから1ファイルを編集——並列の速度と直列の安全性を同時に得ている。
さらにStreamingToolExecutorは、モデルの生成完了を待たずにツール実行を開始する。ツールコールのJSON入力が完成した瞬間にGrepが走り始め、次のツールコールがストリームに到着する前に結果が返っている。3つのツールコールを含むターンで2〜5秒のレイテンシを隠蔽する。
ツール結果のバジェット管理も本番では不可欠だ。ユーザーが1MBのログファイルをcatしたら?バジェットシステムが動く:
- ツールごとに
maxResultSizeCharsを定義 - 超過した結果はディスクに永続化
- モデルにはファイルパス参照+先頭N文字のプレビューを返す
applyToolResultBudget()が毎API呼び出し前にトークン総量を制限
システムプロンプトのキャッシュ設計と4段階コンテキスト圧縮
プロンプトキャッシュの最適化
Claude Codeのシステムプロンプトは文字列ではない。キャッシュメタデータ付きの構造化配列だ。
SYSTEM_PROMPT_DYNAMIC_BOUNDARYマーカーがプロンプトを2ゾーンに分割する:
- 上側(〜80%): 全ユーザー・全セッションで同一。APIレベルでプロンプトキャッシュにヒット
- 下側(〜20%): セッション単位でメモ化、またはターンごとに再計算
ユーザーコンテキスト(git status、CLAUDE.mdの内容、現在の日付)はシステムプロンプトではなく、<system-reminder>タグで最初のユーザーメッセージに注入される。コンテキストが毎ターン変わってもシステムプロンプトのキャッシュが無効化されない。セッションあたり$0.02と$0.20の差を生む設計判断だ。
4段階のコンテキスト圧縮
ほとんどのエージェントは古いメッセージを切り捨てるか、コンテキスト上限でクラッシュする。Claude Codeは安い順に4つの戦略を実行する。
| 戦略 | トリガー | モデル呼び出し | コスト |
|---|---|---|---|
| Microcompact | 毎ターン | 不要 | ほぼゼロ |
| Snip Compact | トークン上限接近時 | 不要 | 低 |
| Auto Compact | snipで不十分な場合 | 要約に1回 | 中 |
| Context Collapse | 数時間の長時間セッション | 段階的圧縮に複数回 | 高 |
Microcompactは、同じファイルに対するReadの結果が前回と変わっていなければキャッシュ参照に置換する。Snip Compactは「保護された末尾(protected tail)」を残しつつ古いメッセージを削除。直近N回のやり取りは常にフル解像度を維持する。
多くのハーネスはいきなり要約に飛ぶ。要約は圧縮呼び出しと要約自体の両方にトークンを消費する。Microcompactとsnipがモデル呼び出しゼロで大部分のケースを処理するから、高価な圧縮は本当に必要な時だけ実行される。
7段階パーミッションと823行のリトライシステム
パーミッション: バイナリではなくスペクトラム
ほとんどのエージェントは「許可/拒否」の二択。Claude Codeは7段階のパイプラインで信頼を段階的に構築する。
Globパターンでツール名と入力をマッチング:
# ルール例
allow: bash(git *) # gitコマンドは許可
allow: bash(npm test) # npm testは許可
deny: bash(*rm -rf*) # 破壊的操作は拒否
ask: bash(*) # その他は確認
「全bashを許可」は粗すぎる。「全bashを拒否」はエージェントが無用になる。「gitとnpm testは許可、それ以外は確認」が実用的な落としどころだ。Hooksがエスケープハッチとして機能し、組織ごとのカスタムガードレールをソースコード変更なしで追加できる。
823行のリトライシステム
withRetry.tsの823行は全て本番障害から生まれている。
| エラー | 回復戦略 |
|---|---|
| 429 Rate Limited | Retry-After < 20秒→リトライ。> 20秒→30分クールダウン。overage-disabled→高速モード永久停止 |
| 529 Server Overloaded | 3連続でフォールバックモデルに切替。バックグラウンドタスクなら即中止 |
| 400 Context Overflow | エラーからトークン数をパース。available = limit - input - 1000(安全バッファ)で再計算。最低3,000出力トークンを保証 |
| 401/403 Auth | APIキーキャッシュクリア→OAuthトークン強制リフレッシュ→新資格でリトライ |
| Network Error | keep-aliveソケットプーリング無効化→新コネクションでリトライ |
バックオフ計算: delay = min(500ms × 2^attempt, 32s) + random(0, 0.25 × baseDelay)
CI/CDパイプライン等の無人セッションでは、429と529を無限リトライ。最大5分バックオフ、6時間リセット上限、30秒ハートビートでアイドルキルを防止。
fetchを3回リトライするラッパーは本番の信頼性ではない。全エラークラスのセマンティクスを理解し、それぞれに固有の回復パスを持つ状態マシンが本番の信頼性だ。
サブエージェントアーキテクチャ: Git Worktreeによる並列隔離
Claude Codeはサブエージェントを生成する——独立したコンテキスト、ツール、作業ディレクトリを持つエージェントループの独立インスタンスだ。
メインブランチ"] --> S1["サブエージェント A
worktree-feature-a"] P --> S2["サブエージェント B
worktree-feature-b"] P --> S3["サブエージェント C
worktree-fix-c"] S1 --> M["マージ検証"] S2 --> M S3 --> M P -.->|"AbortSignal
カスケード"| S1 P -.->|"AbortSignal
カスケード"| S2 P -.->|"AbortSignal
カスケード"| S3 style P fill:#4A90D9,color:#fff style M fill:#50C878,color:#fff
コードを変更するサブエージェントは自分専用のGit worktreeを取得する:
getOrCreateWorktree(repoRoot, slug)
→ スラッグ検証(最大64文字、パストラバーサル防止)
→ 既存worktreeチェック(高速レジューム)
→ git fetch(no-prompt環境変数付き)
→ git worktree add(新ブランチ: worktree-<slug>)
→ 大規模ディレクトリのシンボリックリンク(node_modules, .cache)
→ CLAUDE.md、プロジェクト設定、.envファイルのコピー
→ { path, branch, headCommit } を返す
1エージェント1worktree。並列エージェントがワークスペースを共有するとコンフリクトが発生する。Worktree隔離で各エージェントが独自ブランチで作業し、検証後にマージ。node_modulesのシンボリックリンクで5並列エージェントが依存関係の5コピーを持つ必要がない。
タスク協調にはディスクバックドのタスクリスト(~/.claude/tasks/<taskListId>/<taskId>.json)とファイルベースロッキングを使用。ロック競合は指数バックオフ(30リトライ、5-100ms)で処理。Claude CodeのAuto Modeと組み合わせることで、この並列アーキテクチャが最大限に活用される。
拡張性: ソースコード変更ゼロの4メカニズム
Claude Codeは4つの拡張メカニズムを持ち、いずれもソースコード変更が不要だ。
| メカニズム | 形式 | 用途 |
|---|---|---|
| Skills | YAMLフロントマター付きMarkdown | カスタムコマンド定義 |
| Hooks | シェルコマンド / LLM評価 / HTTP | イベント駆動自動化 |
| MCP | stdio / SSE / HTTP / WebSocket | 外部システム連携 |
| Plugins | ディレクトリ(Skills + Agents + Hooks) | パッケージ化された拡張 |
# Skillの例: Markdownファイルがコマンドになる
---
name: commit
description: Commit staged changes with a generated message
allowed-tools: [Bash, Read, Grep]
model: haiku
user-invocable: true
---
Review the staged changes and create a commit message...
4つ全てが同じ原則に従う: modification over composition(変更ではなく合成)。追加で拡張し、既存コードを変更しない。コアのアップデートが拡張を壊さず、拡張同士が干渉しない。
OpenHandsやBrowser Use等の他のAIエージェントフレームワークと比較すると、Claude Codeの拡張性設計は特にエンタープライズ向けのRBAC統合と4層ヒエラルキーの点で突出している。
まとめ: 4層目を初日から設計せよ
Rohit氏の分析から得られる最も重要な教訓を整理する。
- エージェントループはasync generator。ストリーミング・キャンセル・合成可能性・バックプレッシャーが言語レベルで手に入る
- ツールの並行性分類を定義時に行う。read-onlyは並列、writeは直列。2〜5倍の高速化とゼロ競合状態を同時に実現
- システムプロンプトはキャッシュ境界として設計。静的コンテンツを先、動的コンテンツを後。本番環境で最もレバレッジの高いコスト最適化
- 圧縮はヒエラルキー。安い戦略を先に実行し、高価な戦略は最後の手段
- エラー回復はループ内部の第一級状態。外側のtry-catchではなく、各エラー型に固有の回復戦略を持つ状態マシン
- 4層目を初日から設計する。セッション間の状態管理、チームへのパーミッション拡張、並列化時の協調——インフラを後付けするのは桁違いに難しい
モデルはコモディティだ。環境が結果を決める。
業界の多くがLayer 1(より大きなモデル、より高いベンチマークスコア)を最適化している。勝っているチームはLayer 3と4に投資している。より良い環境、より良いエラー回復、より良いパーミッションシステム、より良いコンテキスト管理、より良い協調。