はじめに
Claude CodeにはCLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1という環境変数で有効化する「agent teams(エージェントチーム)」という実験的機能がある。1つのセッションが「リード」となり、複数の「チームメイト」(それぞれ独立したClaude Codeインスタンス)にタスクを割り振り、チームメイト同士が直接メッセージをやり取りしながら共同作業する仕組みだ。公式ドキュメント(code.claude.com/docs/en/agent-teams)によれば、v2.1.178以降はTeamCreate/TeamDeleteという専用ツールが廃止され、Agentツールのnameパラメータを指定するだけで即座にチームメイトが立ち上がるようになったという。
このブログでは過去にクロスセッションメッセージング(2026-08-23)やバックグラウンドセッションのライフサイクル(2026-08-31)を扱ったが、複数のClaude Codeインスタンスが「チーム」として協調するagent teamsそのものはまだ扱っていなかった。YouTube「AI大学【AI&ChatGPT最新情報】」に該当する直近動画は見当たらなかったため、今回はarticles-index.mdの「今後の深掘り候補」の範囲内で、GitHubのCHANGELOG.mdとIssueを起点にこのテーマを選んだ。
ドキュメントには重要な一文がある。「Spawning teammates also requires an interactive session. In non-interactive mode with the -p flag, … Claude doesn’t spawn teammates, and a subagent that Claude names runs as an ordinary subagent even with agent teams enabled(チームメイトの起動には対話セッションが必要。-pフラグを使う非対話モードでは、Claudeはチームメイトを起動せず、Claudeが名前を付けたサブエージェントは、agent teamsが有効でも通常のサブエージェントとして動作する)」。
このブログの自動投稿パイプライン自身がclaude -p(非対話モード)で動いている。つまり、もし読者がこの記事の自動化スクリプトの中でagent teamsを有効化しても「チーム」は立ち上がらないということだ。この記述を鵜呑みにせず、実際にclaude -pでこの環境変数を立てて動かし、本当にチームが作られないのか、それとも見た目上はチームっぽく振る舞うのかを実機で確認した。
手順: 非対話モードでagent teamsが本当に機能するか検証する
ステップ1: 判断基準を先に押さえる(subagentとagent teamsの使い分け)
公式ドキュメントの比較によれば、両者の違いは次の通りだ。
- subagent: 呼び出し元と同じセッション内で動き、結果を要約して呼び出し元に返す。トークンコストは低め。単発の調査・確認作業向け
- agent teams: チームメイトは完全に独立したコンテキストを持ち、リードの会話履歴を引き継がない。チームメイト同士が直接メッセージを送り合い、共有タスクリストで作業を分担する。トークンコストは高め。複数の視点からの調査・レビュー、対立する仮説の検証、フロントエンド/バックエンド/テストのように独立して進められる複数モジュールの並行開発に向く
判断の起点は「チームメイトが互いに議論・反論し合う必要があるか」で、単に作業を並列化したいだけならsubagentで十分、というのが公式の立場だ。
ステップ2: プロジェクトスコープで安全に有効化する
ユーザー全体の設定(~/.claude/settings.json)に書くと、以後すべてのプロジェクトでClaudeが自発的に名付けたサブエージェントがチームメイトとして起動するようになる(ドキュメントに明記されている副作用)。検証用途では、影響範囲を絞るために使い捨てディレクトリの.claude/settings.local.jsonに書くのが安全だ。
mkdir -p /tmp/agent-teams-check/.claude && cd /tmp/agent-teams-check
git init -q
cat > .claude/settings.local.json << 'EOF'
{
"env": {
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
}
}
EOF
ステップ3: 実機検証A — 非対話(-p)モードでチームメイトを立ち上げてみる
~/.claude/teams/は、チームが実際に作られたときにClaude Codeがチーム設定を書き込むディレクトリだ(ドキュメントの「Architecture」節に記載)。検証前にこのディレクトリが存在しないことを確認してから実行した。
ls ~/.claude/teams/ 2>&1
# → ls: /Users/harkingbee/.claude/teams/: No such file or directory
cd /tmp/agent-teams-check
claude -p "Spawn 2 teammates named alpha and beta. Have alpha write one haiku about autumn and beta write one haiku about spring. Do not write the haikus yourself; delegate to the teammates and report what they returned." \
--settings '{"env":{"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS":"1"}}' \
--output-format json \
--permission-mode bypassPermissions > /tmp/agent_teams_test_output.json
実際の出力(resultフィールド):
Both teammates delivered their haikus:
**alpha (autumn):**
> Crimson leaves drifting
> Cool wind hums through empty fields
> Autumn sighs to sleep
**beta (spring):**
> Cherry blossoms fall
> soft pink whispers on the breeze
> spring wakes the still earth
一見、本当にチームメイトが作業したかのような報告文になっている。しかし実行後にもう一度~/.claude/teams/を確認すると、ディレクトリは存在しないままだった。
ls ~/.claude/teams/ 2>&1
# → ls: /Users/harkingbee/.claude/teams/: No such file or directory
さらに、-pモードの出力に含まれるsubagent_statsフィールドを見ると、実態がわかる。
python3 -c "
import json
d = json.load(open('/tmp/agent_teams_test_output.json'))
print(json.dumps(d['subagent_stats'], indent=2))
"
実際の出力:
{
"spawned": 2,
"completed": 2,
"failed": 0,
"by_type": {
"general-purpose": 2
}
}
by_typeがgeneral-purpose、つまり通常のサブエージェントとして2体起動しただけで、agent teams固有のteam-lead/チームメイトという構造にはなっていない。応答テキストが「teammates」という言葉を使っていても、内部的には通常のAgentツール呼び出しに過ぎなかった。
ステップ4: 対照実験 — 環境変数を立てなくても結果は同じか
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMSを一切設定せず、同じプロンプトの季節違い版を実行した。
cd /tmp/agent-teams-check
claude -p "Spawn 2 teammates named gamma and delta. Have gamma write one haiku about winter and delta write one haiku about summer. Do not write the haikus yourself; delegate to the teammates and report what they returned." \
--output-format json \
--permission-mode bypassPermissions > /tmp/agent_teams_control_output.json
実際の出力:
{
"spawned": 2,
"completed": 2,
"failed": 0,
"by_type": {
"general-purpose": 2
}
}
ls ~/.claude/teams/ 2>&1
# → ls: /Users/harkingbee/.claude/teams/: No such file or directory
環境変数の有無に関わらず、結果はまったく同じだった。subagent_stats.by_typeはどちらもgeneral-purpose: 2、~/.claude/teams/はどちらも作られない。ドキュメントが明言する通り、-pモードではCLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1を立てても何も変わらず、常に通常のサブエージェントとして処理されることを、2回の実行と対照実験で確認できた。
発見: 応答文の言葉づかいが、内部的な仕組みの違いを覆い隠す
今回の検証で一番の気づきは、機能が「動かない」こと自体はドキュメント通りで驚きは無かった一方、Claude自身の応答テキストが「teammates」という言葉を使い続けるせいで、出力を読んだだけでは通常のサブエージェントとの違いに気づけないという点だった。プロンプトで「alpha」「beta」という名前をつけて依頼すると、モデルはその名前をそのまま踏襲して「Both teammates delivered their haikus」のように報告してくる。これは指示に忠実に応答しているだけで、嘘や誇張ではないが、「agent teamsが有効なら本当にチームとして動いているはず」と思い込んでいると、~/.claude/teams/のような実データを確認しない限り、実際には通常のsubagent委譲に留まっていることに気づきにくい。
CI・自動化スクリプトのように-pモードで動くパイプラインの中でagent teamsを想定した並列レビューやチーム的な協調を期待しても、トークン消費はsubagentの範囲に収まり、チーム機能(共有タスクリスト、チームメイト同士の直接メッセージング、TeammateIdleフックなど)は一切働かない。この記事のブログ生成パイプライン自体もその一例で、claude -pで動く以上、agent teamsの恩恵は受けられない。
コピペ用プロンプト
1. 環境変数がどこで有効化されているか確認する
claude -p "Run: env | grep CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS; and check ~/.claude/settings.json and ./.claude/settings.json and ./.claude/settings.local.json for CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS. Report what you find, or report clearly if nothing was found." \
--allowedTools "Bash,Read" --permission-mode bypassPermissions
期待できる結果: シェル環境変数と3つの設定ファイルそれぞれについて、CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMSが設定されているかどうかの報告が返る。
成功判定: 「見つからなかった」も含めて、4箇所すべてについて明示的な回答が含まれていること。無回答・未確認の箇所が無い。
2. プロジェクトスコープで安全に有効化する(そのまま動く完結例)
mkdir -p /tmp/agent-teams-check/.claude && cd /tmp/agent-teams-check && git init -q
cat > .claude/settings.local.json << 'EOF'
{
"env": {
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
}
}
EOF
cat .claude/settings.local.json
期待できる結果: /tmp/agent-teams-check/.claude/settings.local.jsonが作成され、中身が表示される。このファイルはユーザー全体の設定(~/.claude/settings.json)より優先度が低い場所に置かれるため、このディレクトリ以外のプロジェクトには影響しない。
成功判定: catの出力に"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"が含まれること。
3. 非対話モードで本当にチームが作られるかを自分で検証する
mkdir -p /tmp/agent-teams-check2 && cd /tmp/agent-teams-check2 && git init -q
echo "before: $(ls ~/.claude/teams/ 2>&1)"
claude -p "Spawn 2 teammates named alpha and beta. Have alpha write one haiku about the ocean and beta write one haiku about mountains. Delegate, don't write them yourself." \
--settings '{"env":{"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS":"1"}}' \
--output-format json --permission-mode bypassPermissions \
| python3 -c "import json,sys; d=json.load(sys.stdin); print('subagent by_type:', d['subagent_stats']['by_type'])"
echo "after: $(ls ~/.claude/teams/ 2>&1)"
期待できる結果: beforeとafterの両方で「No such file or directory」が表示され、subagent by_typeは{'general-purpose': 2}のような通常のsubagent種別になる(実際にAPI呼び出しが発生し、モデルによって実行結果の文面は変わりうるが、by_typeがgeneral-purposeになる点と~/.claude/teams/が作られない点は変わらないはず)。
成功判定: ~/.claude/teams/が実行前後で変化しないこと。もし将来のバージョンでこの挙動が変わりteam-leadのような種別や~/.claude/teams/配下のディレクトリが現れたら、それは非対話モードの仕様が変わったサインなので、公式changelogで裏取りするとよい。
4. 対話セッションで実際にチームを立ち上げる指示文(未実行・テンプレート)
これは対話セッション(claudeを通常起動した状態)向けのプロンプト例で、この記事の検証環境(非対話の-pモード)では実行していない。
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1を設定した状態で、対話セッションで次のように伝える:
「認証モジュールのコードレビューのために3人のチームメイトを立ち上げてください。
1人はセキュリティの観点、1人はパフォーマンスの観点、1人はテストカバレッジの観点で
それぞれレビューし、最後に3人の指摘をまとめてください。」
期待できる結果(ドキュメント記載、未実行): リードのターミナル下部のエージェントパネルに3人のチームメイトの行が現れ、それぞれが独立したコンテキストでレビューを進める。全員が完了すると、リードが3つの指摘をまとめて報告する。
成功判定: 未実行のため読者自身の環境で確認してほしい。~/.claude/teams/配下にsession-<8桁>という名前のディレクトリが新規作成されていれば、今回検証した非対話モードとは異なり、実際にチームが立ち上がった証拠になる。
5. TeammateIdleフックで品質ゲートを設定する(テンプレート・未実行)
TeammateIdleはチームメイトがアイドル状態に入る直前に発火し、終了コード2を返すとアイドル化を阻止して作業を続けさせられる(公式hooksドキュメント記載)。今回の検証は非対話モードでチーム自体が作られなかったため、このフックの発火自体は確認できていない。設定例として掲載する。
{
"hooks": {
"TeammateIdle": [
{
"hooks": [
{
"type": "command",
"command": "echo 'チームメイトがアイドルになろうとしています。git statusで未コミットの変更が無いか確認してください' >&2; exit 0"
}
]
}
]
}
}
期待できる結果(ドキュメント記載、未実行): 対話セッションでチームメイトがタスクを終えてアイドルになろうとするたびに、このフックが標準エラーにメッセージを出す。exit 0ではなくexit 2にすれば、そのメッセージがチームメイトへのフィードバックとして渡され、アイドル化がブロックされて作業が続く。
成功判定: 未実行のため断定はできない。試す場合は、まずexit 0(ブロックしない)で意図通りメッセージが出るかを確認し、ブロックが必要と分かった場合のみexit 2に変更するのが安全だろう。
活用例
- 調査・レビューなど「複数の視点を対立させたい」作業をする人: 公式ドキュメントの例(セキュリティ/パフォーマンス/テストカバレッジの並行レビュー、複数の仮説を競わせるデバッグ)は、対話セッションでagent teamsを使う正攻法にあたる。ただし今回の検証結果から、CIやスクリプトに
-pで組み込んでも同じ効果は得られないと分かるので、このような用途は人が対話的に操作するセッションに限定して使うべきだ。 - 自動化パイプラインを設計している人: 「agent teamsを有効にすればCIの中で複数のチームメイトが並行レビューしてくれる」という期待は、今回の検証結果からは成立しない。並列化したいだけなら、
-pモードでも動く通常のサブエージェント(Agentツールに複数のnameを与えて一度に呼び出す、あるいは本ブログ2026-08-24の記事で扱った/code-reviewのようなsubagentベースの機能)を使うほうが実態に即している。 - agent teamsを初めて試す人: プロンプト2〜3の手順で、まず
~/.claude/teams/が作られたかどうかを自分の環境・自分の起動方法(対話か-pか)で確認してから本格的に使い始めると、期待と実態のズレを防げる。
注意点
- agent teamsは実験的機能であり、既定では無効。有効化にはユーザー・プロジェクト・管理者設定のいずれかで
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1を明示的に立てる必要がある(公式ドキュメント記載)。 - 今回確認できたのは「非対話(
-p)モードではCLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1を立てても~/.claude/teams/が作られず、subagent_stats.by_typeがgeneral-purposeのままである」という事実のみ。対話セッションでの実際のチーム立ち上げ・TeammateIdleフックの発火・split-pane表示(tmux/iTerm2)は、この記事の検証環境では実行しておらず、公式ドキュメントの記載をそのまま引用している箇所であることを本文中で明記した。 - 公式ドキュメントの「Limitations」節は「
/resumeと/rewindはin-processチームメイトを復元しない」と明記しているが、GitHub Issue #90453(2026-08-28作成、執筆時点でopen)では、/rewindに限っては実際にはチームメイトが生きたまま保持される(2.1.238で観測)という報告が上がっている。これは他ユーザーの報告であり、筆者自身が再現確認したものではない。ドキュメントと実際の挙動が食い違う可能性がある領域なので、/rewind後にチームメイトを再生成すべきか判断に迷う場合は、まずclaude agentsでチームメイトが本当に消えているか確認してから行動するとよい。 - 検証にかかった実費は2回の
claude -p実行で合計約0.58ドル(Sonnet 5、--output-format jsonのtotal_cost_usdより、$0.2085 + $0.3676)。agent teams自体は公式ドキュメントで「トークンコストが単一セッションより大幅に高くなる」と明記されており、対話セッションで実際にチームを組む場合はさらにコストがかさむ点に注意。 - 検証はすべて
/tmp配下の使い捨てディレクトリと.claude/settings.local.jsonで行い、~/.claude/settings.jsonなど本体設定には変更を加えていない。
締め
今日まず試せるのは、プロンプト1・3の2つだ。自分の環境でCLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMSが既にどこかで有効化されていないか確認し(意図せず有効になっていると、Claudeが自発的に名付けたサブエージェントがチームメイトとして起動してトークン消費が増える可能性がある)、次に自分がふだんClaude Codeを動かしている方法(対話セッションか、-pを使ったスクリプト・自動化か)で、実際に~/.claude/teams/が作られるかどうかを確かめてみてほしい。「ドキュメントに書いてある通りかどうか」を自分の実行環境で確認するところまでやって初めて、agent teamsが自分のワークフローで意味を持つ機能なのかどうかが判断できる。


Comments