Claudeコードエージェントのオーケストレーション:Chief of Staffパターン
Orchestrating Claude Code Agents: The Chief of Staff Pattern (asyncdot.com)
要約
この記事では、AIコーディングにおける「Chief of Staff」パターンを紹介しています。これは、単一のAIセッションのコンテキストの揮発性と報告の信頼性の低さという問題を解決するための組織的なアプローチです。このパターンでは、1つの「オーケストレーター」セッションが作業の調整、検証、状態管理を行い、複数の「ワーカー」セッションが実際のコーディング作業を実行します。状態はセッションのコンテキストではなく、永続的な外部ストア(例:プランニングボード)に保存され、すべての主張は再実行されて検証されます。これにより、長期間にわたるAIコーディングタスクの信頼性と堅牢性が向上します。
全文翻訳
Orchestrating Claude Code Agents: The Chief of Staff Pattern
AIによる長期的なコーディング作業が失敗するのは、エージェントがコードを書けないからではなく、そのコンテキストが一時的で自己報告が信頼できないためであることが多い。その解決策は技術的なものではなく、組織的なものである。1つのセッションが調整と検証を行い、別のセッションが実行を担当し、永続的な外部ボードが状態を保持し、すべての主張は信じられる前に再実行される。この形状はすでにオーケストレーター・ワーカー、コーディネーター・インプリメンター・検証者として知られている。チーフ・オブ・スタッフとは、私たちがそれを呼ぶだけの名称である。これはループ、それを実用的にするツール、そしてそれがキャッチするために存在する失敗モードをカバーする。
TL;DR
- オーケストレーションと実行を分離する。調整セッションはブリーフを作成し、主張を検証し、差分を読む。実装作業は行わない。
- 状態はコンテキストではなく、永続的なストアに置く。ボードやAPIを持つ任意の外部タスクシステムは、コンパクション、セッションの終了、ハンドオフを乗り越える。会話コンテキストはそうではない。
- すべてのエージェントレポートを指示ではなく証拠として扱う。コマンドを再実行する。終了コードが権威であり、要約は意図である。
- 永続的なチャネルに書き込む。セッション間のメッセージは遅延、保留、または期限切れになる可能性がある。コミットされたファイルやボードのカードは常に届く。
- 時間制限はサーフェシングのためであり、カットするためではない。固定間隔で報告する頻度を決定し、作業を停止する場所を決定しない。
- 自身のツールを信用しない。エージェントワークにおける最も高価なエラーは、実際には行われなかった作業に対して成功を報告するチェックから生じる。
この問題は何を解決するか?
単一のAIコーディングセッションは、1時間程度はうまく機能するが、それ以降は劣化する。3つの問題が発生する。
- コンテキストは有限で、損失が多い。長いセッションは圧縮される。3時間前に重要だった詳細は要約になり、その要約は詳細を有用にした具体性を失う。
- 自己報告は現実から乖離する。「テストに合格した」と言うエージェントは、その意図と記憶を報告しているのであって、新鮮な観察結果ではない。両者の間のギャップはセッションの長さとともに増大する。
- 何も積み重ならない。2時間目に苦労して学んだ教訓は、次のセッションで誰かがそれを書き留めておかない限り、失われる。
エージェントを増やすことはこれを解決しない。むしろそれを増幅する。今や、信頼できない報告者が複数いて、それらを調和させる者が誰もいない。
それを解決するのは、人間の組織から借用した「仕事をするのではなく、何が真実かを知る」ことを仕事とする者による、労働の分担である。
これは、プロトタイプから出荷製品を分離するのと同じ規律である。生成ステップが決してボトルネックではなかった。チェックステップがボトルネックである。
チーフ・オブ・スタッフとは、1つの長寿命セッションがコーディネーターとして機能し、作業を割り当て、主張を検証し、共有状態を維持するエージェントオーケストレーションの形状であり、個別の短寿命セッションが実装を実行する。
最も速いメンタルモデルを求めるなら、インテグレーションマネージャーと考えてほしい。Gitのインテグレーションマネージャーワークフローでは、貢献者は自身のレポジトリで作業し、1人のメンテナーが各変更をプルし、ローカルでテストし、参照レポジトリに何を格納するかを決定する。コーディネーターは、エージェントセッションの代わりに、そのジョブを実行する。
この名称は、私たちが有用だと見つける比喩である。確立された用語ではなく、認識する必要はない。その下の形状はよく知られており、いくつかの実際の名称がある。
通常は何と呼ばれているか
- オーケストレーター・ワーカー、またはスーパーバイザー、あるいは階層的オーケストレーション。
- コーディネーター・インプリメンター・検証者(CIV)。
- メーカー・チェッカー、または金融・オペレーションから借用した検証チェーン。
- インテグレーションマネージャー、人間のバージョンで、Gitの分散ワークフローでこれよりずっと前に文書化されている。そのオープンソースバリアントは、慈悲深い独裁者と副官である。
- チームリードとチームメイト、これはClaude Code自身のサブエージェントドキュメントがそれをどのようにフレームしているかである。
これらはすべて同じことを言っている:1つのエージェントが計画とチェックを行い、他が作業を行い、共有状態は単一のコンテキストウィンドウの外に存在する。先行事例を探しているなら、この名称ではなく、それらの用語で検索してほしい。
この記事が追加するのは形状ではない。それは、さらに下流の検証規律と、長い自律実行を破る特定の失敗モードである。
1つの明確化
チーフ・オブ・スタッフ・エージェントというフレーズは、別の目的で広く使われている:個人のカレンダー、受信トレイ、優先順位を管理し、専門エージェントに作業をルーティングするアシスタントである。Anthropicのクックブックには、まさにそのような種類のチーフ・オブ・スタッフ・エージェントがあり、スタートアップのCEOのために構築されている。同じ比喩だが、問題は異なる。この記事はコーディングループに関するものである。
コーディネーターの仕事
調整セッションは、しばしばオーバーウォッチと呼ばれる。その責任:
- 永続的なキューから、定義された順序で作業をプルして割り当てる。
- より弱いモデルでもコーディネーターの判断なしに実行できるようなブリーフを作成する。
- 実行セッションが実行したと主張するコマンドを再実行して主張を検証する。
- 差分を読む、トランスクリプトではない。格納されたものが重要であり、エージェントがそれについて言ったことは重要ではない。
- セッションが終了する前に、永続的な成果物に教訓を記録する。
- 作業を奪うことなく、セッションを誘導する。
明示的に行わないのは、実装を作成することである。コーディネーターがコーディングを開始した瞬間、検証を停止し、パターンは単一の過負荷セッションに崩壊する。
3つのコンポーネント
3つのものが必要である。特定のツールは交換可能だが、役割はそうではない。
1. エージェントランタイム:Claude Code
Claude Codeは、ツール使用、ファイル編集、シェルアクセス、セッション間のメッセージング能力を持つセッション自体を提供する。各セッションは独自のコンテキストウィンドウを持ち、それがポイントである。1つのセッションの混乱が他のセッションに汚染しないため、分離は機能である。
2. セッション基盤:cmux
cmuxはターミナルワークスペースを管理し、コマンドラインから駆動できるため、スクリプト可能である。コーディネーターは以下のように実行セッションを起動する:
cmux workspace create \
--name project-session-12 \
--cwd /path/to/repo \
--command 'claude "Read docs/briefs/current.md and do exactly what it says."'
そのコマンドの2つの点は負荷がかかるものであり、両方とも学習に実際の時間が必要である。
--commandはワークスペースのシェルにテキストを送信する。エージェントを開始するわけではない。エージェントを明示的に呼び出す必要がある。生の指示は実行できないシェルで入力され、ランチャーは成功を報告する。- プロンプトを短く保ち、ファイルを参照する。長いコマンド文字列は確実に実行されない。コミットされたブリーフを指す短いプロンプトの方が堅牢であり、ブリーフのレビューと再実行を可能にする。これはシェル履歴に埋め込まれた文字列ではできない。
3. 永続的な状態ストア:Plan Desk
Plan Deskは、プロジェクト、目標、依存関係エッジを持つタスク、リンクされた設計ドキュメント、コメントをMCP経由でエージェントに公開されたプランニングボードである。コーディネーターとすべての実行セッションは同じボードを読み書きする。
これは人々がスキップするコンポーネントであり、それをスキップすることがマルチエージェントセットアップが一晩で生き残れない理由である。ボードはメモリである。セッションは使い捨てだが、ボードはそうではない。
ボード上に存在するものは以下の通り:
- ビルド契約としてのタスク。問題定義、アクションアイテム、インターフェース、検証契約、非目標。実行セッションが作業を完了するために親ドキュメントを読む必要が全くないほど詳細である。
- 作業と原子的に切り替わるステータス。開始した瞬間に
in_progress、検証された瞬間にdone。セッションの終わりにバッチ処理されることはない。なぜなら、スタンドダウン時にのみ真実であるボードはボードではないからだ。 - それらが管理する設計ドキュメントへのリンク。
- コメント、人間が指示を残し、エージェントが理由を残す場所。
オペレーティングループ
一度に1つの作業項目。1回のディスパッチ。1回のコミット。
1. PULL ボードから次のブロックされていないタスクを取得する
2. READ 何も触る前に、リンクされた設計ドキュメントを読む
3. RED GATE まず検証を実行する:失敗しなければならない
4. DELEGATE 実行セッションにブリーフを作成するか、自分で構築する
5. PROVE 主張されたすべてのコマンドを再実行する;終了コードが決定する
6. OBSERVE 差分を1つずつ読む
7. GATE 承認レーンを解決し、理由を投稿する
8. SHIP ステータスを切り替え、その項目のみをコミットし、進捗を記録する
なぜレッドゲートが最初に来るのか
開始前にチェックがすでにグリーンであれば、作業は何の証明にもならない。実行されないチェック、何もマッチしないフィルター、またはすでに真実であると主張するテストから、正しい実装を区別することはできない。最初の検証実行は、古い作業も安価に検出する。実際には、キューに入れられたタスクの相当な割合が、別のカードの下で構築されたり、後の変更によって無効になったりして、すでに完了していることが判明する。1つのコマンドでグリーンに戻るレッドゲートは、数秒で済み、すでに存在するものを実装するためにコードを読むのに費やすであろう1時間を節約する。
なぜ項目ごとに1回のコミットなのか
Gitの履歴はボードと1対1で維持される。各コミットの件名はそのタスクの名前を付ける。3日後に何かが壊れた場合、症状から決定へのパスは1回のgit logでアクセスできる。
検証規律:パターンの心臓部
これは、この方法論を「複数のエージェントを同時に実行する」ことから区別する部分である。
レポートは指示ではなく証拠である
実行セッションが「スイートグリーン、49チェック、失敗ゼロ」と報告した場合、コーディネーターの仕事はその真偽を確かめることである。エージェントが嘘をつくからではなく、彼らが報告している対象と彼らがチェックした対象がしばしば異なるオブジェクトであるからだ。
内面化する価値のあるパターン:セッションがコミットハッシュを手作業でログファイルに書き込み、その後、シェルにある短いハッシュに対してgit cat-fileで検証したが、それは書き込んだ文字列に対してではなかった。両方のチェックがパスした。ファイルには、何も解決しないハッシュが含まれていた。チェックとレコードは2つの異なるオブジェクトであり、1つだけがテストされた。
そこから導き出されるルール:アーティファクトから値を読み出すことによってアーティファクトを検証する、そこに書き込んだと思う変数からではなく。
注意すべき欠陥クラス
エージェントエンジニアリングで最も一般的な単一の失敗は、実際には行われなかった作業に対して成功を報告するツールである。それは多くの形状を持つ。
| 形状 | 見た目 | どのように欺くか |
|---|---|---|
| 虚無的なアサーション | 機能が機能しているかどうかにかかわらずパスするテスト | テスト対象を削除してもグリーンになる |
| サイレントノーマッチ | 何もマッチしないgrep、フィルター、または述語 | ゼロの発見は「クリーン」と読まれる |
| エラーチェック | 全く実行されなかったコマンド | エラーは飲み込まれ、不在は証拠と読まれる |
| 間違った参照 | 「Xより新しい」でキー付けされたフィルター | その間に起こったことはすべて通過する |
| 古い前提 | 壊れたコードから読み取られた期待値を持つチェック | それはキャッチするために書かれたバグをパスする |
| スコープミスマッチ | 全体として提示されたサブセットに対するグリーンチェック | 分母は決して述べられない |
一般的な防御策:失敗する可能性のあるすべてのチェックは、その旨を伝える必要がある。ゼロのカウントと実行失敗は区別可能でなければならない。そして、不在のアサーションには、同じ実行でポジティブコントロールが必要である。なぜなら、何も実行されなかった場合、「悪いことは何も起こらなかった」がパスするからだ。
否定を信じる前に肯定を証明する
何かが不在であると結論付ける前に、それが存在する場合にそれを見つけることができることをツールで証明する。まず既知の良いケースにチェックを向ける。「クリーン」を報告するツールと壊れたツールは同一の出力を生成する。
永続的なチャネルは一時的なチャネルより優れている
セッションは直接メッセージを送り合うことができる。そのチャネルは本当に有用である。コーディネーターが実行中に質問に答える方法であり、実行セッションが回避するのではなく矛盾をフラグ立てする方法である。
しかし、それに依存するには十分に信頼できない。メッセージはビジーなセッションの後ろにキューイングされたり、受信セッションのパーミッションモードに応じて承認のために保留されたり、配信されずに期限切れになったりする可能性がある。沈黙は同意ではない。
したがって、到着する必要があるものはすべて永続的なチャネルに入る。
- コミットされたファイル。ブリーフ、ハンドオフドキュメント、制約。セッションは起動時にリポジトリを読む。
- ボードカードとコメント、作業固有のコンテキストが属する場所。
- 共有リンク。ほとんどのボードは、エージェント対応テキストをURLとしてタスクまたはドキュメントとしてレンダリングできる。コンテキストをそれに貼り付けるのではなく、起動プロンプトに
Context: <url>を入れる。プロンプトは短く保たれ、コンテキストは維持されている場所に留まる。
メッセージはナッジである。ファイルは契約である。
どこに何があるか
報われる小さな規律:永続的なポリシーを一時的な作業コンテンツから分離する。
ポリシーディレクトリは、ループ、ルーティングルール、標準など、すべてのサイクルを管理する契約を保持する。長寿命で、レビューされ、めったに変更されない。一度限りのブリーフ、タスクコンテキスト、セッション固有の指示は、ボードまたはスクラッチディレクトリに属する。それらを混在させると、6ヶ月後にどのファイルがまだ何らかのものを管理しているのか誰もわからなくなる。
時間制限:停止せずにサーフェシングする
長い自律実行は、数時間姿を消して大量の差分を持って戻ってくるのではなく、一定のペースで報告すべきである。
時間制限を機能させるルール:間隔はサーフェシングする頻度を決定し、作業を停止する場所を決定しない。タイマーがアイテムの途中で期限切れになったら、アイテムを完了し、検証し、コミットし、それから報告する。タスクの途中で実行をカットすると、作業が最も回復が難しい状態、つまり部分的に適用され、検証されておらず、正直に説明することが不可能な状態に stranded される。
チェックポイントはサーフェシングの瞬間であり、許可要求ではない。報告は送信され、次のアイテムは同じターンで開始される。「続行してもよいですか?」と書いている自分に気づいたら、それを削除する。見ている人間は中断し、見ていない人間は質問によって実行を殺されただけである。
試みたことではなく、証明されたことを報告する。検証結果のないアイテムは、完了ではなく、持ち越される。
学習に時間がかかる実用的なメカニズム
これらは小さく、それぞれが別のもののように見える失敗モードを持っている。
起動したセッションが実際に開始したことを検証する。ランチャーはワークスペースが作成されたと報告するが、それはエージェントが実行中であることと同じではない。プロセスを確認し、その作業ディレクトリを確認する:
launched_at=$(date +%s)
# ... セッションを起動 ...
# その後、$launched_at 以降に開始されたプロセスのみを受け入れる
起動の直前に参照タイムスタンプをキャプチャする。最後のセッションよりも「新しい」でキー付けされたフィルターは、その間に開始された無関係なセッションを喜んで受け入れる。そして、それが異なる作業ディレクトリを持っている場合、健全な起動は壊れているように見える。
セッション名は推測できるアドレスではない。ワークスペースに付けた名前は、メッセージングレイヤーが使用する名前と異なることが多い。アドレス指定する前にライブセッションを再リストし、以前に読んだ名前を再利用しない。
短い識別子は表示プレフィックスであり、キーではない。ボードは一般的に切り詰められたIDを表示する。それをフルレングスの識別子にパディングすると、存在しないがフォーマットされた値が得られる。代わりに、特徴的なラベル部分文字列を検索して解決する。
言い換えではなく、ラベルで検索する。ログエントリのタイトルは通常、書き込みセッションが何をしたかのフレームワークであるが、カードの実際のラベルではない。前者を検索すると何も見つからず、「そのようなカードはない」と読まれる。
共有作業ツリーでは、生のコミットを絶対に使用しない。git add <path>の後にgit commitを実行すると、同時セッションがステージングしたすべてのものを含むインデックス全体がコミットされる。名前を付けたものだけをコミットするように、git commit -- <paths>を使用する。
このパターンを使用すべき場合とそうでない場合
使用する場合:
- 作業が1つのコンテキストウィンドウを超えるセッションにまたがる場合。
- 複数のワークストリームが並行して進行できる場合。
- スピードよりも正確さが重要であり、間違った「完了」は高価である場合。
- プロジェクトが単一セッションのメモリを超えて存続する場合。
使用しない場合:
- タスクが単一の適切にスコープされた変更である場合。1つのセッション、儀式なし。
- コーディネーションのオーバーヘッドを許容できない場合。このパターンはコードを生成しない検証に実際のトークンを費やす。
- 永続的なストアがない場合。それなしでは、このパターンを実行しているのではなく、複数のセッションを実行して希望しているだけである。
オーバーヘッドがポイントである。結果を信頼できる能力を購入している。
始め方
- 永続的なボードをセットアップする。1つのプロジェクト、少数の目標、依存関係エッジを持つタスク。エージェントがAPIまたはMCP経由でそれに到達できることを確認する。
- 契約を書き留める。ループ、完了の意味、標準を記述した1つのファイル。それをコミットする。すべてのセッションは起動時にそれを読む。
- 1つのコーディネーターと1つのエグゼキューターを実行する。6つから始めない。2つで検証ループを正直にする。
- ハンドオフ成果物を追加する。コーディネーターが各セッションの終わりに現在の状態と学んだことを更新する単一のファイル。これがセッションを積み重ねるものにする。
- レッスンログを保持する。驚くべきことがあったら、次のセッションがそれを読む場所に、そのセッションが終わる前に書き込む。
それが機能しているかどうかの尺度は、どれだけ多くのコードが書かれたかではない。それは、任意の瞬間に「この作業の状態はどうなっているか?」と尋ねたときに、真実である答えを得られるかどうかである。
結びの言葉
AIコーディングエージェントが長期作業でパフォーマンスが低下した場合の衝動は、より良いモデルやより大きなコンテキストウィンドウに手を伸ばすことである。どちらも役立つ。どちらも実際の制約に対処しない。それは、誰もチェックしていないという実際の制約である。70%問題の背後にあるギャップ、そしてライブシステムを操作するエージェントで読み戻しステップがないものの背後にあるギャップと同じである。
コードを書かず、しかし何が真実かを知っている調整セッションは、別のエグゼキューターよりも価値がある。それは人間の組織からの古い教訓であり、転移することが判明した。