Claude Codeのセッション間メッセージングを使ってみた——サブエージェントとは違う"並走"という選択肢
はじめに
このブログでは以前、Claude Codeのworkflowオーケストレーションを使って、複数のサブエージェントに「議論」させながら深掘りする方法を紹介しました。あれはあれで強力なのですが、実は毛色の違う機能がもう一つあります。起動済みの別セッションと、リアルタイムでメッセージをやり取りする機能です。
通常のサブエージェント呼び出しは「呼び出した側が結果を待つ」関係です。一方この機能は、すでに動いている別の対話セッションに話しかけ、向こうも自分のペースで返事を返してくる、という対等な会話に近い形になります。この記事は、実際にこの記事を書く過程で使ってみた体験をベースに、機能の実態とサブエージェントとの使い分けをまとめたものです。
この記事で持ち帰れること
- セッション間メッセージング(
ListAgents/SendMessage)の基本的な使い方と、実際に手を動かしてわかった注意点 - サブエージェント(Agentツール)・Workflowオーケストレーションとの技術的な違いと使い分けの判断軸
- 「自セッションの話を脱線させずに、関連する別調査を横に投げる」という具体的な活用パターン
機能の概要
やることはシンプルです。
ListAgentsで、今動いている他のセッションの一覧を取得する- 一覧に出てきた名前を指定して
SendMessageでメッセージを送る - 相手セッションが処理して返信すると、自分の会話に自動的に届く(メール受信箱のように能動的に確認する必要はない)
ListAgents が一覧に出すのは、同一マシン上の別セッションだけではありません。仕様上は次の4種類が同じ一覧に混在して表示されます。
- 自分がAgentツールで生成した、進行中のin-processサブエージェント
- このマシン上の他のローカルセッション
- クラウド上で動いている自分のセッション(Claude Code on the web等)
- Remote Control接続時の、他マシン上のセッションやクラウドセッション
今回自分の環境で実際に取得した一覧に出てきたのは②のパターンで、名前を付けていた「記事検証」セッションもこの種別として表示されていました。
一覧には名前・種別・busy/idleの状態・開始からの経過時間が表示されます。一点注意しておきたいのは、モデル名は一覧に出てこないことです。どのセッションがどのモデルで動いているかは、人間側で管理しておく必要があります(後述の「応用」で触れます)。
セッションには /rename で好きな名前を付けられます。今回は検証専用に使っているセッションに「記事検証」という名前を付けていたので、ListAgents の一覧からひと目でそれとわかりました。名前を付けずにいると自動生成のIDだけが並ぶので、継続的に使い分けるセッションには早めに名前を付けておくと呼びやすくなります。
セッションを開いた画面をそれぞれ表示しておけば、片方に話しかけたメッセージがもう片方の画面にほぼリアルタイムで表示され、返信が返ってくる様子もそのまま見られます。ただし厳密には、メッセージはチャットアプリのようにpushで即時配信されるわけではなく、受信側セッションが次にツールを実行するタイミングでキューから取り出されて処理されるという非同期の仕組みです。相手がアクティブに動いていればほぼ即時に見えますが、相手が何か長い処理の最中であれば、それが一区切りつくまで反映が遅れることもあります。ログを後からgrepするのではなく、進行中の会話を横で眺められるのがこの機能の体感的な特徴です。
実際にやってみた: 記事執筆→記事検証セッションへの依頼
この記事を書くにあたって、実際に「記事検証」という名前のセッションへレビューを依頼してみました。全体の流れは次のようになります。
記事執筆セッションから記事検証セッションへの依頼と、返信が届くまでの流れ
{"to": "記事検証", "message": "..."}
まず名前だけを指定して送ったところ、こう返ってきました。
'記事検証' is not an agent in this conversation. Re-send with the ref to confirm you mean:
記事検証 [2d5928] — Claude session, on this machine, active 3m ago
e.g. {"to": "記事検証 [2d5928]", ...}
基本的には素の名前を指定するだけで送れますが、今回のように候補を一意に絞り込めない、あるいは明示的な確証が必要と判断された場合は、名前 [ref] の形式で参照IDを添えて再送するよう求められます。実際に 記事検証 [2d5928] の形で送り直すと成功し、依頼内容(記事のテーマ、書こうとしている要点、技術的に確認してほしい点)を渡すことができました。常に[ref]が必要なわけではなく、エラーになったときだけ提示された候補をそのまま使えばよい、という運用です。
送った内容は、この記事のために自分が用意していた要点メモと、「サブエージェントとの違いの整理」「モデルを使い分けたリレー構成が実態と合っているか」といった確認事項です。相手セッションは自分のペースで内容を検討し、返信は自分の会話に自動的に届きます。依頼した後も画面を開いておけば検証セッションが何を指摘しているかを横で追えるのが体感として大きな違いでした。
サブエージェントとの違い
このブログで既出のAgentツール・Workflowオーケストレーションと並べると、性質の違いがはっきりします。実は「サブエージェント」とひとくくりにしていた中にも、通常のAgent呼び出しとforkとで挙動が違うので、4つに分けて整理し直しました。
| 観点 | 通常のAgent呼び出し | フォーク(fork) | Workflowオーケストレーション | セッション間メッセージング |
|---|---|---|---|---|
| 呼び出し元との関係 | 同期・完了まで呼び出し元がブロックされる | 非同期・裏で動き、完了通知が後のターンで届く | スクリプトが決定論的に複数エージェントを制御(非同期) | 独立セッション同士が非同期にメッセージ交換 |
| コンテキストの扱い | 新規に無関係な文脈から開始 | 呼び出し元の会話をまるごと継承 | 各エージェントは指示された範囲だけを見る | 各セッションが自分自身の会話履歴を保ったまま独立して動く |
| 独立した対話画面 | なし | なし | なし | ある(人間が直接覗いたり、途中で割り込んだりできる) |
| 名前で呼び戻せるか | 不可(都度使い捨て) | 名前があれば再開できる | 不可 | 可能(renameで管理する前提) |
| 向いている用途 | 単発の調査・実装タスクの切り出し | 呼び出し元の文脈ごと裏で並行実行したい調査 | 多段階・大規模な処理をコストを見ながら決定論的に回す | 並行して進めたい別の関心事に、会話を継続したまま投げる |
自分は最初、「サブエージェントは呼び出し元が結果を待つ/セッション間メッセージングは待たない」という単純な軸で違いを説明しようとしたのですが、forkはそもそも待たずに非同期で動く仕組みだと確認して整理し直しました。つまり「非同期かどうか」だけではforkとセッション間メッセージングの違いを説明しきれません。両者の本質的な違いは、セッション間メッセージングには人間が直接覗いたり割り込んだりできる独立した対話画面があり、しかも名前を付けて後から呼び戻せるという点にあります。
メリット: 話の腰を折らずに、人が見える形で横道調査を任せられる
たとえば今回のケースがまさにそうでした。自セッション(記事執筆)は「記事の構成を詰めて本文を書く」ことに集中したい一方で、「この技術理解に事実誤認がないか」「サブエージェントとの違いの整理が正確か」というのは、それ自体が別のリサーチタスクです。
正直に書くと、「自セッションのコンテキストを消費せずに横道調査を切り離す」こと自体は、実はforkでも達成できます。フォークは呼び出し元の会話を継承しつつ裏で非同期に動くので、コンテキスト節約という目的だけならフォークで十分なケースも多いはずです。
それでもセッション間メッセージングを選ぶ理由は、「記事検証」という名前を付けた独立の対話画面が常設され、人間がそこを直接覗いたり、必要なら途中で割り込んだりできることにあります。フォークの経過はバックグラウンドで進み、完了通知が来るまで基本的にブラックボックスですが、セッション間メッセージングなら画面を開いておけば検証セッションが今どんな指摘をしているかをリアルタイムに追えますし、返信を待たずにこちらから追加の質問を割り込ませることもできます。長時間の共同作業や、継続的に同じ相手に検証を依頼し続けるような運用では、この「常設された会話相手」という性質が効いてきます。
応用: モデルを使い分けたリレー
もう一つ試す価値があるのが、セッションごとにモデルを変える構成です。たとえばAセッションをFable、BセッションをSonnetにして、「Fable→Sonnetに実装を依頼→Fableが差分をレビュー」という3段のリレーを組むこともできます。各段が「使い捨てのタスク実行者」ではなく、それぞれ自分の文脈を保った独立セッションである点はサブエージェントのpipelineと違うところです。
ただしここには2つ、実際に使う上でのコツがあります。
ListAgentsの一覧にはモデル名が表示されません。 どのセッションがどのモデルかは人間側で管理する必要があるので、renameで名前に役割やモデルを埋め込む(例:「調査-Fable」「実装-Sonnet」)とセッションを見分けやすくなります- 往復は自動で待ってくれるわけではありません。
SendMessageは送信後すぐに次のターンへ進む非同期の仕組みなので、「Fable→Sonnetに依頼→Fableでレビュー」という流れを実現するには、返信が届くのを確認してから次の指示を出す、という進行管理を人間か送信側セッション自身が能動的に行う必要があります
注意点・ハマったポイント
- 稀に
[ref]を求められる: 基本は素の名前をtoに指定するだけで送れますが、今回のように候補を一意に絞り込めないと判断された場合はエラーになり、[ref]付きの候補が提示されます。毎回[ref]を付ける必要はなく、エラーが出たときだけ提示された形式をそのまま使えば解決します - 権限境界はセッションごとに完全に独立している: 自セッションで拒否・ブロックされた操作を、別セッションに肩代わりさせる使い方(クロスセッション権限迂回)は仕組み上できません。あくまで「調査や検証を依頼する」用途に留めるのが安全です
- 「リアルタイム」は正確には非同期のキュー処理: メッセージは即時pushされるのではなく、受信側セッションが次にツールを実行するタイミングで取り出されて処理されます。相手がアクティブなら体感的にはほぼ即時ですが、相手が長い処理の最中であれば反映が遅れることがあります
サブエージェントとどう使い分けるか
実際に使ってみた実感としての判断軸です。
- 短時間で終わる調査・実装の切り出し → サブエージェント(Agentツールのfork/general-purpose)。使い捨てで十分ならこちらが手軽
- 多段階の処理を決定論的に、コストを管理しながら回したい → Workflowオーケストレーション。フェーズ構成とスクリプトで制御したい場合
- 人間も進行を見ながら、複数の関心事を会話として並行させたい → セッション間メッセージング。今回のように「本筋の会話を止めたくないが、関連する検証は別で進めたい」場面に向いている
3つは競合するというより、状況に応じて使い分ける道具です。今回の記事執筆でも、本文の執筆は手元のセッションで進めつつ、技術的な裏取りだけ「記事検証」セッションに投げるという形で両方を活かしました。
まとめ
Claude Codeのセッション間メッセージングは、ListAgentsで相手を見つけてSendMessageで話しかけるだけというシンプルな機能ですが、核心は「非同期かどうか」ではなく、人間が直接覗いたり割り込んだりできる独立した対話画面が常設され、名前を付けて何度でも呼び戻せる点にあります。名前をrenameで付けておく、[ref]はエラーが出たときだけ使う、権限境界は越えられない、モデル名は一覧に出ないので名前に埋め込んで管理する、といった実運用上の勘所を押さえておけば、横道にそれがちな調査や検証作業を本筋の会話から切り離しつつ、進行を見失わずに進められる機能だと感じました。