サブエージェント処理
学習後にできること
- DCPがサブエージェントセッションで自動的に無効化される理由を理解する
- サブエージェントとメインエージェントのトークン使用における異なる戦略を知る
- サブエージェントでDCP機能を使用する際に生じる問題を回避する
現在の課題
お気づきかもしれません:OpenCodeの一部の対話では、DCPの修剪機能が「機能していない」ようです——ツール呼び出しがクリーンアップされず、トークン節約統計も変化しません。これは、コードレビュー、深層分析など、特定のOpenCode機能を使用する際に発生することがあります。
これはDCPの問題ではありません。これらの機能は**サブエージェント(Subagent)**メカニズムを使用しており、DCPはサブエージェントに対して特別な処理を行います。
サブエージェントとは
サブエージェント(Subagent)とは?
サブエージェントは、OpenCodeの内部AIエージェントメカニズムです。メインエージェントは複雑なタスクをサブエージェントに委任し、サブエージェントは完了後に摘要形式で結果を返します。
典型的な使用シーン:
- コードレビュー:メインエージェントがサブエージェントを起動し、サブエージェントが複数のファイルを注意深く読み取り、問題を分析し、簡潔な問題リストを返す
- 深層分析:メインエージェントがサブエージェントを起動し、サブエージェントが大量のツール呼び出しと推論を行い、最後に核心発見を返す
技術的には、サブエージェントセッションには親セッションを指す parentID 属性があります。
DCPのサブエージェントに対する動作
DCPは、サブエージェントセッションですべての修剪機能を自動的に無効化します。
なぜDCPはサブエージェントを修剪しないのか?
これには重要な設計理念があります:
| 役割 | トークン使用戦略 | 核心目標 |
|---|---|---|
| メインエージェント | トークンを効率的に使用する必要がある | 長い対話でコンテキストを維持し、コストを削減 |
| サブエージェント | トークンを自由に使用できる | 豊富な情報を生成し、メインエージェントの集約を支援 |
サブエージェントの価値は、「トークンを消費して情報品質を獲得する」ことにあります——大量のツール呼び出しと詳細な分析を通じて、親エージェントに高品質な情報集約を提供します。もしDCPがサブエージェントでツール呼び出しを修剪すると、以下の問題が生じる可能性があります:
- 情報損失:サブエージェントの詳細な分析プロセスが削除され、完全な摘要を生成できない
- 集約品質の低下:メインエージェントが受け取る摘要が不完全になり、最終的な意思決定に影響
- 設計意図の違反:サブエージェントは「トークンを惜しみなく品質と交換する」ために設計されている
結論:サブエージェントは、最終的に親エージェントに簡潔な摘要のみを返すため、修剪は必要ありません。
DCPはどのようにサブエージェントを検出するか
DCPは以下のステップで現在のセッションがサブエージェントかどうかを検出します:
// lib/state/utils.ts:1-8
export async function isSubAgentSession(client: any, sessionID: string): Promise<boolean> {
try {
const result = await client.session.get({ path: { id: sessionID } })
return !!result.data?.parentID // もしparentIDがあれば、サブエージェント
} catch (error: any) {
return false
}
}検出タイミング:
- セッション初期化時(
ensureSessionInitialized()) - 毎回のメッセージ変換前(
createChatMessageTransformHandler())
サブエージェントセッションにおけるDCPの動作
DCPはサブエージェントを検出すると、以下の機能をスキップします:
| 機能 | 通常セッション | サブエージェントセッション | スキップ位置 |
|---|---|---|---|
| システムプロンプト注入 | ✅ 実行 | ❌ スキップ | hooks.ts:26-28 |
| 自動修剪戦略 | ✅ 実行 | ❌ スキップ | hooks.ts:64-66 |
| ツールリスト注入 | ✅ 実行 | ❌ スキップ | hooks.ts:64-66 |
コード実装(lib/hooks.ts):
// システムプロンプトハンドラ
export function createSystemPromptHandler(state: SessionState, ...) {
return async (_input: unknown, output: { system: string[] }) => {
if (state.isSubAgent) { // ← サブエージェント検出
return // ← 直接リターン、修剪ツール説明を注入しない
}
// ... 通常のロジック
}
}
// メッセージ変換ハンドラ
export function createChatMessageTransformHandler(...) {
return async (input: {}, output: { messages: WithParts[] }) => {
await checkSession(client, state, logger, output.messages)
if (state.isSubAgent) { // ← サブエージェント検出
return // ← 直接リターン、すべての修剪を実行しない
}
// ... 通常のロジック:重複排除、上書き書き込み、エラークリア、ツールリスト注入など
}
}実際のケース比較
ケース 1:メインエージェントセッション
シーン:メインエージェントと対話し、コード分析を依頼
DCPの動作:
ユーザー入力:"src/utils.tsのユーティリティ関数を分析"
↓
[メインエージェント] src/utils.tsを読み取り
↓
[メインエージェント] コードを分析
↓
ユーザー入力:"src/helpers.tsも確認して"
↓
DCPが重複読み取りパターンを検出
↓
DCPが最初のsrc/utils.ts読み取りを修剪可能としてマーク ✅
↓
コンテキストがLLMに送信される際、最初の読み取り内容がプレースホルダーに置き換えられる
↓
✅ トークン節約ケース 2:サブエージェントセッション
シーン:メインエージェントが深層コードレビューのためサブエージェントを起動
DCPの動作:
ユーザー入力:"src/のすべてのファイルを深層レビュー"
↓
[メインエージェント] タスクが複雑であることを検出し、サブエージェントを起動
↓
[サブエージェント] src/utils.tsを読み取り
↓
[サブエージェント] src/helpers.tsを読み取り
↓
[サブエージェント] src/config.tsを読み取り
↓
[サブエージェント] さらに多くのファイルを読み取り...
↓
DCPがサブエージェントセッションを検出
↓
DCPがすべての修剪操作をスキップ ❌
↓
[サブエージェント] 詳細なレビュー結果を生成
↓
[サブエージェント] 簡潔な摘要をメインエージェントに返す
↓
[メインエージェント] 摘要に基づいて最終的な応答を生成よくある質問
Q: 現在のセッションがサブエージェントかどうかを確認するには?
A: 以下の方法で確認できます:
- DCPログを確認(debugモードが有効な場合):
2026-01-23T10:30:45.123Z INFO state: session ID = abc-123
2026-01-23T10:30:45.124Z INFO state: isSubAgent = true対話の特徴を観察:
- サブエージェントは通常、複雑なタスク(深層分析、コードレビューなど)を処理するときに起動
- メインエージェントは「サブエージェントを起動しています」などのメッセージを表示
/dcp statsコマンドを使用:- サブエージェントセッションでは、ツール呼び出しが修剪されない
- トークン統計で「修剪済み」数量が0
Q: サブエージェントでは全く修剪されないので、トークンの無駄遣いでは?
A: いいえ。以下の理由により:
- サブエージェントは短命:サブエージェントはタスク完了後に終了し、メインエージェントのような長い対話を行わない
- サブエージェントは摘要を返す:最終的にメインエージェントに渡されるのは簡潔な摘要であり、メインエージェントのコンテキスト負担を増加させない
- 設計目標が異なる:サブエージェントの目的は「トークンを節約する」ことではなく「トークンを品質と交換する」こと
Q: DCPを強制的にサブエージェントで修剪させることは可能?
A: できませんし、すべきでもありません。DCPの設計は、サブエージェントが高品質の摘要を生成できるように完全なコンテキストを保持できるようにすることです。強制的に修剪すると、以下の問題が発生する可能性があります:
- 摘要情報が不完全になる
- メインエージェントの意思決定品質に影響
- OpenCodeのサブエージェント設計理念に反する
Q: サブエージェントセッションでのトークン使用は統計されますか?
A: サブエージェントセッション自体はDCPによって統計されません。DCPの統計は、メインエージェントセッションでのトークン節約のみを追跡します。
本課のまとめ
- サブエージェント検出:DCPは
session.parentIDをチェックしてサブエージェントセッションを識別 - 自動無効化:サブエージェントセッションでは、DCPは自動的にすべての修剪機能をスキップ
- 設計理由:サブエージェントは高品質な摘要を生成するために完全なコンテキストが必要であり、修剪はこのプロセスを妨げる
- 使用境界:サブエージェントはトークン効率を追求するのではなく、情報品質を追求し、これはメインエージェントの目標とは異なる
次のレッスンの予告
次のレッスンでは よくある問題とトラブルシューティング を学びます。
学ぶ内容:
- 設定エラーの修正方法
- デバッグログの有効化方法
- トークンが減少しない一般的な原因
- サブエージェントセッションの制限
付録:ソースコード参照
クリックしてソースコードの場所を表示
更新日:2026-01-23
| 機能 | ファイルパス | 行号 |
|---|---|---|
| サブエージェント検出関数 | lib/state/utils.ts | 1-8 |
| セッション状態の初期化 | lib/state/state.ts | 80-116 |
| システムプロンプトハンドラ(サブエージェントをスキップ) | lib/hooks.ts | 26-28 |
| メッセージ変換ハンドラ(サブエージェントをスキップ) | lib/hooks.ts | 64-66 |
| SessionState 型定義 | lib/state/types.ts | 27-38 |
重要な関数:
isSubAgentSession():session.parentIDによりサブエージェントを検出ensureSessionInitialized():セッション状態の初期化時にサブエージェントを検出createSystemPromptHandler():サブエージェントセッションでシステムプロンプト注入をスキップcreateChatMessageTransformHandler():サブエージェントセッションですべての修剪操作をスキップ
重要な定数:
state.isSubAgent:セッション状態内のサブエージェントフラグ