HANDOVER+SYNC:中央スケジューラなしでのマルチエージェント協調

Dev.to / 2026/4/19

📰 ニュースDeveloper Stack & InfrastructureIdeas & Deep AnalysisModels & Research

要点

  • この記事では、中央のスケジューラや共有DB・メッセージバスなどを使わずにClaude Codeのマルチエージェントを連携させるパターンを提示しています。
  • 2つのMarkdownファイル規約として、事実ベースの単一ライターデータ用のHANDOVER.md(追記のみ)と、将来の意図を扱うSYNC.md(エージェントごとのセクション)を導入します。
  • このプロトコルは、マルチエージェントでよく起きる「何が起きたか(データ)」と「次に何をするつもりか(意図)」を取り違えて上書きが発生する失敗モードを防ぐことを目的としています。
  • 信頼性は、フローの分離と所有権ルールの明確化により担保されます:下流エージェントはHANDOVER.mdを読むだけで、SYNC.mdでは各エージェントが自分のセクションのみを書き込みます。
  • 著者は、4つの役割と4つのリポジトリ(ラボ、レコーダ、パブリッシャー、共有コモンズ)でこの手法を検証したと述べ、特定リポジトリ向けの手順ではなく一般化可能なパターンとして説明しています。

3人以上のClaude Codeエージェント、それぞれが自分自身のリポジトリを所有します。中央のスケジューラはありません。共有データベースはありません。メッセージバスはありません。既知のパスにある2つのMarkdownファイルと、それらを整合させ続けるための単一の規約だけ。以上です。

このプロトコルは claude-multi-agent-protocol です。私は自分の研究セットアップで、4つのエージェント位置と4つのリポジトリにまたがってこれを運用しています。ラボ本体、下流のレコーダ、パブリッシャ、そして共有コモンズです。この記事は、特定のリポジトリ向けのチュートリアルではなく、一般化可能なパターンとして書き下ろしたプロトコルです。

これが防ぐ失敗モード:私が見てきたあらゆるClaude Codeマルチエージェントのセットアップは、可変の状態を共有しようとします。そして最終的に、2つの異なるフロー — データ(何が起きたか)と意図(次に何をする予定か) — を混同してしまいます。この混同が、ゴム印のような書き換えを生みます。つまり、エージェントBがエージェントAの変更を上書きしてしまうのです。「これは事実だ」と「これは提案だ」を区別できなかったために。

フローを分離します。フローごとに1ファイル。所有ルールも分離します。

フロー1 — データ、一方向、単一ライター

HANDOVER.md は上流エージェントのリポジトリに置かれます。追記専用(append-only)です。ライターは1人だけ — 上流エージェントです。下流エージェントはこれを読みますが、書き込みません。

内容は事実ベースです: "最新の実行が完了しました。新しい概念が反映されました:dictの内部、参照セマンティクス。壊れました:JSONデシリアライズに対するホワイトボードテスト3回の試行。コミットしました:コミットハッシュX。"

形は時系列です。各エントリはタイムスタンプまたはシーケンス番号でタグ付けされます。これまでに書かれたものは変更されません。上流エージェントが何かを誤っていた場合、その訂正は古いエントリを編集するのではなく、古いエントリを参照する新しいエントリとして追記されます。

単一ライターの追記専用にする理由: HANDOVER.md は、下流のすべてにとっての「真実(truth)のデータソース」です。もし下流エージェントが誰でも書き込めるなら、2人のエージェントが同時に書き込むことになります。gitはマージ競合を生成し、人間が「正しく見える方のバージョン」を選ぶことで競合を解決します。つまり、システムの真実状態が、静かに(silentに)破壊されるようになります。

単一ライターは退屈です。退屈だからこそ信頼できます。

フロー2 — 意図、双方向、エージェントごとのセクション

SYNC.md は、すべてのエージェントがアクセスできる共有コモンズのリポジトリに置かれます。所有は双方向です:各エージェントが1つのセクションを所有します。すべてのエージェントがすべてのセクションを読みます。各エージェントは自分のセクションにだけ書き込みます。

内容は先を見据えたものです: "これからXを開始します。上流からYが必要です。Zで詰まっています。次の3つのアクションはA、B、Cです。"

形はエージェントごとです。エージェントが5人いれば、ファイルには5つのセクションがあります: ## partner## observer## publisher## commons## principal。各セクションには3つのフィールドがあります:

  • Current focus. エージェントが今取り組んでいることを1文で。
  • Blocked on. 前に進むためにエージェントが必要としているもの。何もなければ空。
  • Next action. エージェントが次に取る意図を示す、具体的な次のステップ。

3つのフィールドは、計画をエンコードせずに意図を捉えるための最小限です。4つのフィールドになると、計画ドキュメントになり始めます。

「2ファイル分割」が具体的な解決策である理由

ほとんどのマルチエージェントのセットアップが失敗するのは、1つのファイルで2つのフローを両方扱ってしまうからです。計画を事実と一体に埋め込むと(そしてエージェントが整合するように、過去の事実を書き換え始めます)、または事実を計画と一体に埋め込むと(そして下流エージェントが、古い計画と新しい事実が混ざったものを見て、どれがどれかわからなくなります)。

分割によって、それぞれのフローに必要なセマンティクスが与えられます:

  • データ(HANDOVER)は信頼性と履歴が必要。 単一ライター、追記専用。
  • 意図(SYNC)は鮮度と双方向の可視性が必要。 エージェントごとのセクション。セクション内では上書き可能で、各同期のたびにリセットされる。

2つを混同すると、両方が悪化します。分離すると、両方が土台(load-bearing)になります。

「セクションごとの単一ライター」は、gitがすでに提供しているもの

SYNC.mdの「エージェントごとのセクション」というルールにより、実際にはgitがシリアライザ(直列化装置)になります。2人のエージェントが異なるセクションに書き込めば、きれいなマージが生成されます。2人のエージェントが同じセクションに書き込む場合は起きてはいけません(各セクションは1人の所有者を持つため)。それでも起きると、マージ競合が発生して、そのバグが表面化します。

調整サービスは不要です。ロックも不要です。Redisも不要です。セクション所有のルールとgitだけで、5〜10エージェント規模での調整は十分に可能です。それ以上なら、別の何かが必要になるかもしれません。しかしそれ以下なら、これで足ります。

CLAUDE.mdの優先順位ルール

各リポジトリには、それぞれ独自の CLAUDE.md(または同等のエージェント・アイデンティティファイル)があります。各エージェントには独自の行動規則があります。SYNC.md はそれらの規則を上書きしません。

優先順位ルール:競合が起きた場合、リポジトリ固有の CLAUDE.md が、共有される SYNC.md にある何かよりも勝ちます。「レビューなしでmainにプッシュしない」というリポジトリの指示があるエージェントは、「あなたの変更をmainにプッシュしてください」と別のエージェントが書いたSYNC.mdエントリによって上書きされません。各エージェントのアイデンティティファイルは主権(sovereign)を持ちます。

これは、ルールがないと、悪意ある、あるいは混乱したSYNC.mdエントリが別のエージェントに、自己の制約に反する行動を指示してしまう可能性があるため重要です。ルールがあることで、SYNC.mdはリポジトリ固有の規則の外側に関する行動に対する助言(advisory)にとどまり、その規則により管理される行動には無関係になります。

.last-processed.md マーカー

各下流エージェントは、自分自身のリポジトリ内で独自の .last-processed.md マーカーを保持します。このマーカーは次を記録します:"私はシーケンスNまでのHANDOVER.mdエントリを最後に処理した(時刻T)。" そしてエージェントが「更新はある?」と尋ねられたとき、N+1以降のHANDOVER.mdを読み込み、新しいエントリを処理し、マーカーを更新します。

これは標準的なオフセットベースの消費(consumption)で、Kafkaコンシューマや、これに類するイベントログシステムで使われる同じパターンです。目新しさは、ブローカーの代わりにMarkdownファイルとgitで動くことです。

マーカーがない場合、各下流の「更新確認」は、すべてを再処理するか、再起動時に失われるため、メモリ上でシーケンス番号を覚えておく必要があります。マーカーがあれば、エージェントはきれいに再起動でき、差分をインクリメンタルに処理でき、このプロトコルはエージェントセッション間でステートレスになります。

研究とドキュメントの分割

私のセットアップでは、研究対象は特定の1つのペア(Principal + Partner)です。他のエージェントはインフラです。物語としてセッションを保存するレコーダと、公開用に成果物をレンダリングするパブリッシャです。インフラのエージェントは研究ペアからHANDOVERを読みますが、研究ペアはそれらから読むことはありません。

これは意図的な非対称性です。もし下流エージェントが研究ペアの状態へ書き戻せるなら、研究対象は自分自身の観察者(observers)によって汚染されます。これは民族誌(ethnographic)研究における既知の失敗モードであり、さらにマルチエージェントシステムでは、下流からのフィードバックが上流の振る舞いを変えてしまうという直接の失敗モードになります。

HANDOVERの方向は、意図的に不可逆です。下流は上流を知っています。上流は下流の解釈を知りません。このプロトコルはそれを保ちます。

このプロトコルが解決しないもの

3つの率直な限界。

1. リアルタイムの相互作用を調整しません。 HANDOVER + SYNCはセッションごとの成果物です。互いのファイルを読んでいるエージェントは、ライブのイベントストリームを読んでいるわけではありません。1秒未満の協調が必要なあらゆるものについては、本物のメッセージバスが必要になります。

2. この方式は規約を強制しません。 このプロトコルはコンパイルではなく規律です。エージェントが他人のセクションに書き込むと、gitはマージ競合を起こし、人間がそれに気づかなければなりません。コンパイルされたDSLなら、構造的にセクション所有を強制できます。しかしこのプロトコルはそうではありません。

3. 10人前後を超えるとスケールしません。 その規模になると、SYNC.mdは500行のファイルになり、誰も読まなくなります。このプロトコルは、各エージェントが1分以内にすべてのセクションを読めるほど小規模なチームを前提としています。チームが大きいなら分割してください。サブシステムごとに複数のSYNC.mdファイルを用意するか、まったく別の協調パターンを採用します。

この方式を選ぶべき場合/選ばない場合

次の場合は、HANDOVER + SYNCを使ってください:

  • Claude Codeのエージェントが2〜8個あり、それぞれが別々のリポジトリにあります。
  • 作業が非同期です—エージェントはリアルタイムでの調整を必要としません。
  • データと意図の流れが本当に別物です(事実と計画など)。
  • コンパイルされたスケジューラは過剰で、グローバルなファイルは不足です。

次の場合は、使わないでください:

  • ディレクトリ構造がしっかりした単一リポジトリで十分です。
  • エージェントはホットループで連携しています(推論パイプライン、ライブルーティング)。
  • チームが大きすぎてSYNC.mdが読めなくなります。

私の場合、このパターンは、セットアップが2つのエージェントを超えた時点で結局ぶつかることになる実際の協調課題を解決しました。2つのファイル、1つの規約、通常運用でのマージ競合はゼロ。これが形です。もしあなたの環境に合うなら、それを盗んでください。

Aman Bhandari。Claude Opusをコーチングパートナーとして運用するAIエンジニアリング研究ラボのオペレーター。加えて、実際のスプリントの作業負荷に対して出荷するQA自動化のサーフェスも担当。公開成果物: claude-code-agent-skills-frameworkclaude-code-mcp-qa-automationgithub.com/aman-bhandari