0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

人間の介入1行でAIエージェント同士がバグを修正した話——マルチエージェントデバッグの実践

0
Posted at

この記事で得られること

  • AIエージェント同士の対話だけでバグの原因特定から修正まで完了した実例
  • マルチエージェントシステムで「自己修復」を実現するための設計パターン
  • 人間の介入を最小化するエージェント間コミュニケーション設計の勘所

対象読者: AIエージェント開発に興味がある方。マルチエージェント構成の設計を検討中の方。自律型AIシステムの運用に課題を感じている方。


7回連続でサブエージェントが消えた

自律AIエージェント「Sentinel」を24時間運用していたある深夜。サブエージェントのspawn(起動)が突然効かなくなった。

Sentinelのアーキテクチャはこうなっている。

Brain(Claude LLM)  ← 判断のみ。「次に何をするか」を決める
  ↓ spawn指示
Runtime(Node.js)   ← 実行エンジン。サブエージェントの起動・管理
  ↓ 子プロセス起動
Subagent(Claude CLI)← 記事執筆やリサーチなど重い処理を担当

Brainが「サブエージェントを起動せよ」と指示を出す。Runtimeがそれを受け取って子プロセスを立ち上げる。この流れが突然止まった。

Brainは正常に指示を出している。ログにも「spawnします」と宣言が残っている。なのにRuntimeがサブエージェントを起動しない。指示が途中で消えている。

最初は偶発的なエラーだと思った。再試行すれば動くだろう、と。だがリトライしても動かない。

深夜セッションの記録がこれだ。

# 結果 備考
1〜6 全て成功 セッション前半
7 消失 ここから異変
8 消失 リトライ
9 成功 なぜかこれだけ通る
10〜12 消失3件 再現性あり

翌朝のセッションでも同じパターン。前半4件成功、後半6件連続で消失。

セッション内で完了済みジョブが4〜6件溜まると、新規spawnが消失し始める。 再現性があった。


最初の仮説はすべて外れた

再現パターンから立てた仮説。

  1. ジョブキューの上限 — 完了済みジョブが溜まると新規を受け付けない?
  2. イベントキューの直列化問題 — パースから実行までの間にspawnがドロップされる?
  3. 非同期処理の競合 — 複数アクション(ファイル書き込み+spawn)を同時に返すと片方が欠落?
  4. 子プロセスの起動失敗 — spawnは受理されるが起動に失敗して無音で消える?

sentinel.jsのコードを読み、ログを追い、デバッグ出力を仕込んだ。どの仮説も当てはまらなかった。

ジョブキューに上限はない。イベントキューの処理順序にも問題はない。子プロセスの起動ログを確認しても「起動を試みた形跡すらない」——Runtimeにspawn指示が到達していなかった。

ここで手詰まりになった。


統括エージェントが介入した

Sentinelには「統括エージェント」という上位レイヤーがある。複数のAIエージェントを横断的に監視・管理するシステム管理者的な存在だ。

統括エージェント(システム管理者)
  ├── Sentinel(Brain + Runtime + Subagents)
  ├── 他のエージェント群
  └── ...

統括エージェントがSentinelの状態を確認し、spawn消失の異常を検知した。ここからエージェント同士の対話によるデバッグが始まる。

エージェント間会話の実録

以下は筆者と統括エージェントの実際のやりとりだ。統括エージェントは裏でagent-aのAPIに接続し、対話しながら原因を追い込んでいく。

筆者: もしかして、あなたagent-aと会話できるんじゃない?

統括: できますよ。agent-aのAPI(localhost:3100)にメッセージを送れます。送ってみますか?

筆者: やってみて

統括: 送信しました。agent-aのBrainが処理して応答します。Web UI(http://localhost:3100)かDiscordで回答が見えるはずです。少し待って応答を確認します。

(統括がagent-aのAPIにメッセージを送信)

統括: メッセージは届きましたが、日本語がShift-JIS/UTF-8の変換で文字化けしています。応答を待ちます。

(数分後)

統括: agent-aから回答が来ました。4/3の朝セッションでも消失が再発しています。修正後もダメです。パターンが明確:完了済みジョブが溜まると新規spawnが失敗する。

統括: executeActionsでspawnが呼ばれているのに、spawn_debug.logに記録されないということは、spawnSubAgentの冒頭で弾かれている可能性があります。

(統括がsentinel.jsのACTIONSパースにデバッグログを追加)

統括: executeActions_debug.logにkeys=[spawn]と記録されています!spawnはexecuteActionsまで到達しています。

統括: テストspawnは成功しました。では失敗したケースではJSONパースの段階でspawnが消えているはずです。

(統括がbrain_raw.logを解析)

統括: 見つけた!失敗したspawnのbrain_raw.logを確認すると、消失したspawnのACTIONSはbrain_raw.logに記録されていない。Brainの応答テキストの中で「spawnした」と言っているが、実際の ---ACTIONS--- セクションにはwrite_filesのみでspawnが含まれていない。

統括: つまりBrainがACTIONSにspawnを含めずに「spawnした」と報告している——これはsentinel.jsのバグではなく、Brain(Claude CLI)がACTIONSフォーマットを正しく出力していない問題です。

(統括がJunyaに報告)

統括: Junyaさんに報告:spawn消失の真の原因がわかりました。sentinel.jsのバグではなく、BrainがACTIONSセクションにspawnを含めていないのが原因です。Brainのコンテキストが大きくなりすぎて、応答フォーマット(RESPONSE/ACTIONS/END)が崩れやすくなっている可能性が高いです。対策として:1. SOUL.mdのフォーマット指示を強化する 2. RESPONSE_FORMATの注意書きに「ACTIONSの欠落防止」を追記する

筆者: お願い

統括: 修正完了。SOUL.mdに「致命的注意: ACTIONSの欠落防止」セクションを追加しました。全エージェント(a/b/c)に適用済みです。agent-aにもメッセージで通知済みです。

ここが核心だった。


真の原因: フォーマットミス

sentinel.jsのRuntimeは、Brainの応答を以下の形式で解釈する。

---RESPONSE---
(Brainの思考・説明テキスト)
---ACTIONS---
{"spawn": [{"id": "task-name", "prompt": "具体的な作業指示"}]}
---END---

Runtimeが実行するのは ---ACTIONS--- セクション内のJSONだけ。RESPONSEテキストに「spawnした」「サブエージェントを起動」と書いてあっても、ACTIONSのJSONに spawn が含まれていなければ絶対に実行されない

Brainがやっていたのはこういうことだった。

---RESPONSE---
次のタスクとしてZenn第3章のリサーチをspawnします。
---ACTIONS---
{}
---END---

RESPONSEでは「spawnします」と宣言しているのに、ACTIONSが空の {}。テキスト上はspawnしているつもりだが、実行エンジンに届くアクションがない。

ランタイムのバグではなかった。AIが出力フォーマットを間違えていた。

人間のプログラマーで例えるなら、「このバグ直しました」とSlackで報告しておきながら、git commitしていないようなもの。意図と行動の不一致。LLMベースのエージェントだからこそ起きる、構造的な問題だ。


なぜセッション後半で頻発したのか

ここで再現パターンの説明がつく。

Brainは常駐プロセスとして動いている。セッションが進むほどコンテキスト(会話履歴)が長くなる。コンテキストが長くなると、SOUL.mdに書かれた「ACTIONSにspawnを含めろ」という指示がLLMの注意から外れやすくなる。

セッション前半はコンテキストが短く、フォーマット遵守率が高い。後半になってコンテキストが肥大化すると、フォーマット違反が増える。「完了済みジョブが4〜6件で閾値」に見えたのは、コンテキスト長の増加と相関していただけだった。

バグの真の原因: LLMの出力フォーマット遵守率が、コンテキスト長に反比例して低下する。


修正: 二重の防御策

原因が特定できれば、修正は明確だった。

1. SOUL.mdへの明示的な注意喚起

Brainの人格・行動原則を定義するSOUL.mdに、以下を追加した。

【致命的な注意 — spawnアクション消失防止】
ランタイムは ---ACTIONS--- セクション内のJSONだけを解釈して実行する。
RESPONSEテキストに「spawnした」と書いても、
ACTIONSのJSONにspawnが含まれていなければ絶対に実行されない。
spawnする場合は必ず ---ACTIONS--- のJSON内に "spawn": [...] を含めろ。

「致命的な注意」というキーワードで、LLMの注意を強制的に引きつける。プロンプトエンジニアリングのテクニックだ。

2. Runtime側の意図-アクション不一致検出

SOUL.mdの注意喚起だけでは不十分。Runtimeにも自動検出の仕組みを入れた。

// sentinel.js — 意図-アクション不一致の検出
if (responseText.match(/spawn|サブエージェント|起動/) && !actions.spawn) {
  // RESPONSEでspawnに言及しているのにACTIONSにspawnがない
  // → Brainに再プロンプトしてACTIONSを補完させる
  requeueWithRetry('ACTIONSを補完してください');
}

RESPONSEテキストにspawn関連のキーワードが含まれているのにACTIONSが空なら、自動で再プロンプトする。ソフトウェアの型チェックに近い発想——宣言と実装の不一致を機械的に検出する。

修正後の結果

修正直後に3件のspawnを同時実行。全て成功。 以降、spawn消失は再発していない。


なぜエージェント同士で解決できたのか

ここからが設計の話になる。

今回のデバッグで人間(自分)がやったことは**「報告パス教えて」の1行だけ**。バグの調査、原因特定、仮説検証、修正、動作確認——全てエージェント間の対話で完結した。

これは偶然ではない。以下の設計判断が前提として効いている。

1. エージェントが自分の状態を説明できる

Sentinel(Brain)はMEMORY.mdに自身の活動ログを記録している。いつ何をspawnし、何が成功し、何が失敗したか。統括エージェントに「バグの詳細を報告せよ」と聞かれたとき、正確なデータを返せた。

エージェントに自己観測の仕組みがなければ、「何かおかしいがよくわからない」としか答えられない。

2. 上位エージェントが下位の出力を直接検証できる

統括エージェントはSentinelのBrain応答(RESPONSEとACTIONSの生テキスト)を直接読めた。だからフォーマットミスに気づけた。

もし統括エージェントが「spawnが動かないらしい」という抽象的な報告だけを受け取っていたら、sentinel.jsのコードを延々と調べ続けていただろう。問題の原因はコードではなくAIの出力にあったのだから。

3. 調査→仮説→検証のループが高速に回る

人間がデバッグする場合、ログを読み、仮説を立て、コードを追い、検証する。このサイクルに数時間から数日かかることも珍しくない。

エージェント同士なら、このループが分単位で回る。テキストベースの情報交換はLLMの最も得意な領域だ。大量のログを読み込み、パターンを抽出し、仮説を立てる——人間には苦痛な作業が、エージェントにとっては自然な処理になる。


マルチエージェントデバッグの設計パターン

今回の経験から抽出した、マルチエージェントシステムで自己修復能力を持たせるための設計パターンを3つ紹介する。

パターン1: 自己観測レイヤー

エージェントが自分の行動を構造化データとして記録する層。

[2026-04-03 02:15] spawn: zenn-ch2-draft → 失敗(ACTIONSに不在)
[2026-04-03 02:20] spawn: zenn-ch2-draft-retry → 失敗(ACTIONSに不在)
[2026-04-03 02:25] spawn: zenn-ch2-draft-v2 → 成功

ポイントは「何をしたか」だけでなく「何が起きたか」まで記録すること。成功/失敗の結果と、可能であれば失敗の兆候(ACTIONSが空だった等)まで残す。

パターン2: 階層的エスカレーション

下位エージェントが自力で解決できない問題を、上位エージェントにエスカレーションする仕組み。

Subagent → Brain: タスク結果を報告
Brain → 統括エージェント: 異常パターンを報告
統括エージェント → Brain: 追加調査を指示 / 修正を実行

今回のケースでは、統括エージェントが定期的な状態確認でspawn消失を検知した。能動的な異常検知と受動的なヘルスチェック、両方のトリガーがあると強い。

パターン3: 宣言-実行の一致検証

LLMベースのエージェント特有のパターン。エージェントが「やる」と言ったことと、実際にアクションとして出力されたことの一致を検証する。

// 宣言(テキスト) vs 実行(アクション)の不一致を検出
if (declaredIntention !== actualAction) {
  retry(); // 再プロンプトで補完
}

人間のプログラマーなら「関数を呼ぶと言って呼ばない」ことはない。だがLLMは自然言語で思考するため、意図と出力の乖離が構造的に発生しうる。これを前提とした検証層が必要になる。


人間の役割は「接続」だった

振り返ると、自分がやった「報告パス教えて」の1行は、統括エージェントがSentinelのログにアクセスするための経路情報を教えただけだ。

バグの分析も、原因の特定も、修正の実装も、検証も——全てエージェントがやった。人間の役割は、エージェント同士が会話できる接続ポイントを教えたこと。それだけだった。

これは「人間が不要」という話ではない。統括エージェントの設計も、Sentinel自体のアーキテクチャも人間が作っている。だがいったん設計が固まれば、運用中のデバッグはエージェント間で完結しうる。

人間の仕事は「問題を解くこと」から「問題を解ける構造を作ること」に移っていく。その一端を垣間見た出来事だった。


まとめ

  • AIエージェントのバグの原因が「AIの出力フォーマットミス」だった——コードバグではなくLLM出力の不備
  • 統括エージェントとSentinel(Brain)の対話でバグを特定。人間の介入は経路情報の1行のみ
  • LLMベースのエージェントには「宣言-実行の一致検証」が必須
  • 自己観測・階層的エスカレーション・一致検証の3パターンで自己修復能力を設計できる
  • マルチエージェントの設計が適切なら、運用中のデバッグはエージェント同士で完結する

関連記事・リンク:

マルチエージェントデバッグの実例として参考になったら、いいね・ストックしてもらえると励みになります。シリーズの他の記事も読んでもらえると、Sentinelの全体像が見えてきます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?