Skip to content

SubagentRunner:子タスクの独立コンテキスト

源码版本v4.0.10

役割

SubagentRunner は Cline が「LLM を独自のコンテキストウィンドウで子タスクとして走らせる」ことをカプセル化した実行器である。主 agent が subagent というツール呼び出しに遭遇した時 (new SubagentRunner:215)、各 prompt に対して runner を起動する。runner 内部ではミニ agent loop を回す——ストリームを引く、tool_calls をパース、実行、結果を対話に押し戻す——が、完全に独立した conversation 配列と独立した ContextManager を使い、主 Task のメッセージ履歴と混ざらない。

位置は主 Task の下、具体ツールの上である。主 Task は prompt と TaskConfig を渡し、runner は SubagentBuilder を使ってこの subagent が使えるツールサブセット、システムプロンプト、API handler を算出し、自身の run でループする。子 agent が attempt_completion を呼べば終了し、結果文字列がそのまま主 agent に返る。主 agent には通常のツール結果として見える。この設計により主 Task は「コードを探索、複数ファイルを読み、答えを総合する」といった高コスト操作を隔離されたウィンドウ内に閉じ込め、中間 token で主コンテキストを汚さずに済む。

設計動機

  • 独立コンテキストウィンドウ:子 agent は自身の ClineStorageMessage[] 配列を使う。主 Task は子 agent の中間 tool 呼び出しを見られない (conversation init:463)。
  • ツールホワイトリスト:子 agent はデフォルトで読み取りのみ。SUBAGENT_DEFAULT_ALLOWED_TOOLS は file_read / list_files / search / list_code_def / bash / use_skill / attempt_completion のみを許可 (default allowed tools:14)。
  • attempt_completion の強制:ユーザー設定にかかわらず ATTEMPT は常に追加 (force ATTEMPT:96)。さもなくば子 agent が終了できない。
  • agent 設定でモデルを上書き:AgentConfigLoader が個別 agent の modelId を読み、SubagentBuilder.applyModelOverride が apiHandler を差し替える (applyModelOverride:57)。子 agent ごとに異なるモデルを使える。
  • 自動コンテキスト圧縮:runner は各ラウンドのリクエスト前に前回リクエストの token 数をチェックし、しきい値超過なら compactConversationForContextWindow で file read 最適化を先に行い、その後 truncation を呼ぶ (shouldCompactBeforeNextRequest:488)。
  • 空応答のリトライ:モデルがこのラウンドで tool_use を出さなければ空応答とみなし、noToolsUsed プロンプトを送って再問する。MAX_EMPTY_ASSISTANT_RETRIES (3) を超えたら直接失敗 (empty response retry:662)。
  • 並列マルチ runner の同時キャンセル:1 つの subagent ツール呼び出しは複数の prompt を持てる。複数 runner が並列実行され、abort を 100ms ごとにポーリング (abort poll:216)し、全 runner を同時に止める。

主要ファイル

データフロー

runner 起動時、まずシステムプロンプトと初期 user content を用意する。初期対話には 2 件だけ置く。ユーザーの prompt と workspace メタデータブロックで、後者は server-side task loop の検証用である:

typescript
// apps/vscode/src/core/task/tools/subagent/SubagentRunner.ts
const conversation: ClineStorageMessage[] = [
  {
    role: "user",
    content: [
      { type: "text", text: prompt } as ClineTextContentBlock,
      // Server-side task loop checks require workspace metadata to be present in the
      // initial user message of subagent runs.
      ...(workspaceMetadataEnvironmentBlock
        ? [{ type: "text", text: workspaceMetadataEnvironmentBlock } as ClineTextContentBlock]
        : []),
    ],
  },
];

while (true) {
  if (
    usageState.lastRequest &&
    this.shouldCompactBeforeNextRequest(usageState.lastRequest.totalTokens, api, providerInfo.model.id)
  ) {
    const compactResult = this.compactConversationForContextWindow(
      contextManager,
      conversation,
      contextState.conversationHistoryDeletedRange,
    );
    contextState.conversationHistoryDeletedRange = compactResult.conversationHistoryDeletedRange;
    // ...
  }
  // ...
}

この処理は conversation + while:463 にある。ループに入った後、各ラウンドで createMessageWithInitialChunkRetry がストリームを引き、stream chunk を usage/text/tool_calls/reasoning に分類する。ストリーム終了後、確定した tool calls を順に実行し、attempt_completion に命中した call は result を直接主 agent に返す (return on attempt:723)。他のツールは coordinator.getHandler(toolName).execute で実行し、結果を user content として押し戻してループを継続する。

境界と失敗

  • ツールがホワイトリスト外なら直接 toolError:子 agent が write_to_file などを呼んでも弾かれる。エラー文字列を user content に詰め (whitelist check:726)、ループは継続し、タスク全体は中断しない。
  • attempt_completion の result 欠損:result が空なら missingToolParameterError を詰めて終了しない (missing result:705)。
  • 先頭 chunk が context window exceeded ならリトライ:先頭 chunk 失敗かつエラーが context window 関連なら、即座に conversation を圧縮して再試行 (context window retry:1036)。最大 MAX_INITIAL_STREAM_ATTEMPTS 回。
  • abort 時の実行中コマンド:abort は API ストリームをキャンセルするだけでなく、cancelRunningCommandTool に委譲して現在の bash も止める (cancel running command:281)。
  • native vs non-native tool calls のフォールバック:non-native モードで構造化 tool_calls chunk を受け取った時、実行はするが結果をプレーンテキストにシリアライズして押し戻す。tool_result ペアのずれを回避するため (non-native fallback:631)。
  • stats の累積:各 chunk ごとに inputTokens/outputTokens/cacheWrite/cacheRead/totalCost を更新し、onProgress でリアルタイムにフロントへ送る (stats accumulation:534)。
  • empty assistant も 1 ラウンド扱い:assistant content が完全に空の時は「Failure: I did not provide a response.」を詰めてから noToolsUsed を送る (empty assistant:671)。

まとめ

SubagentRunner により、主 agent は「広範な探索」を隔離された mini-agent に外注できる。これが依存するコンテキスト圧縮戦略は context-manager、主 Task がどうツール結果を押し戻して再帰するかは agent-loop/task-class、MCP ツールが主 agent からどう呼ばれるかは mcp-hub を参照のこと。

公式資料: Cline 文档 · README