Skip to main content

カスタム エージェントとサブエージェント オーケストレーション

スコープ指定されたツールとプロンプトを使用して特殊なエージェントを定義し、 Copilot 1 つのセッション内でサブエージェントとして調整できるようにします。

この機能を使用できるユーザーについて

GitHub Copilot SDK は、すべての Copilot プランで使用できます。

メモ

          Copilot SDK は現在 テクニカル プレビューです。 機能と可用性は変更される場合があります。

カスタム エージェントは、セッションにアタッチする軽量のエージェント定義です。 各エージェントには、独自のシステム プロンプト、ツールの制限、およびオプションの MCP サーバーがあります。 ユーザーの要求がエージェントの専門知識と一致すると、 Copilot SDK ランタイムは自動的にそのエージェントにサブエージェントとして委任します。これは、ライフサイクル イベントを親セッションにストリーミングバックしながら、分離されたコンテキストで実行します。 委任フローの視覚的な概要については、 github/copilot-sdk リポジトリを参照してください。

概念説明
          **カスタム エージェント** | 独自のプロンプトとツール セットを含む名前付きエージェント構成 |

| サブエージェント | タスクの一部を処理するためにランタイムによって呼び出されるカスタム エージェント | | 推論 | ユーザーの意図に基づいてエージェントを自動選択するランタイムの機能 | | 親セッション | サブエージェントを生成したセッション。は、すべてのライフサイクル イベントを受信します。 |

カスタム エージェントの定義

セッションの作成時に customAgents 渡します。 少なくとも、各エージェントには namepromptが必要です。

import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();
await client.start();

const session = await client.createSession({
    model: "gpt-4.1",
    customAgents: [
        {
            name: "researcher",
            displayName: "Research Agent",
            description: "Explores codebases and answers questions using read-only tools",
            tools: ["grep", "glob", "view"],
            prompt: "You are a research assistant. Analyze code and answer questions. Do not modify any files.",
        },
        {
            name: "editor",
            displayName: "Editor Agent",
            description: "Makes targeted code changes",
            tools: ["view", "edit", "bash"],
            prompt: "You are a code editor. Make minimal, surgical changes to files as requested.",
        },
    ],
    onPermissionRequest: async () => ({ kind: "approved" }),
});

Python、Go、.NET の例については、 github/copilot-sdk リポジトリを参照してください。

構成参照

財産タイプ必須説明
namestringエージェントのユニークID
displayNamestring
イベントに表示される人間が判読できる名前
descriptionstring
エージェントが実行すること - ランタイムがエージェントを選択するのを支援します
tools
          `string[]` または `null` |

| エージェントが使用できるツールの名前。 null または省略 = すべてのツール | | prompt | string | ✅ | エージェントのシステム プロンプト | | mcpServers | object | | このエージェントに固有の MCP サーバー構成 | | infer | boolean | | ランタイムがこのエージェントを自動選択できるかどうか (既定値: true) |

ヒント

適切な description は、ランタイムがユーザーの意図を適切なエージェントと照合するのに役立ちます。 エージェントの専門知識と機能について具体的に説明します。

エージェントごとの構成に加えて、agentの**** を設定して、セッションの開始時にアクティブなカスタム エージェントを事前に選択できます。

セッション構成プロパティタイプ説明
agentstringセッションの作成時に事前に選択するカスタム エージェントの名前。
          `name`の`customAgents`と一致する必要があります。 |

セッション作成時のエージェントの選択

セッション構成で agent を渡して、セッションの開始時にアクティブにするカスタム エージェントを事前に選択できます。 この値は、nameで定義されているいずれかのエージェントのcustomAgentsと一致する必要があります。

const session = await client.createSession({
    customAgents: [
        {
            name: "researcher",
            prompt: "You are a research assistant. Analyze code and answer questions.",
        },
        {
            name: "editor",
            prompt: "You are a code editor. Make minimal, surgical changes.",
        },
    ],
    agent: "researcher", // Pre-select the researcher agent
});

Python、Go、.NET の例については、 github/copilot-sdk リポジトリを参照してください。

サブエージェントの委任のしくみ

カスタム エージェントとのセッションにプロンプトを送信すると、ランタイムはサブエージェントに委任するかどうかを評価します。

  1.        **意図の照合** - ランタイムは、ユーザーのプロンプトを各エージェントの`name`および`description`と比較し分析します。
    
  2.        **エージェントの選択** - 一致するものが見つかり、 `infer` が `false`されていない場合、ランタイムはエージェントを選択します。
    
  3.        **分離実行** - サブエージェントは、独自のプロンプトと制限付きツール セットを使用して実行されます
    
  4.        **イベント ストリーミング** - ライフサイクル イベント (`subagent.started`、 `subagent.completed`など) が親セッションにストリームバックされます
    
  5.        **結果の統合** - サブエージェントの出力が親エージェントの応答に組み込まれます
    

推論の制御

既定では、すべてのカスタム エージェントを自動選択 (infer: true) で使用できます。 infer: falseを設定して、ランタイムがエージェントを自動選択できないようにします。明示的なユーザー要求によってのみ呼び出すエージェントに役立ちます。

{
    name: "dangerous-cleanup",
    description: "Deletes unused files and dead code",
    tools: ["bash", "edit", "view"],
    prompt: "You clean up codebases by removing dead code and unused files.",
    infer: false, // Only invoked when user explicitly asks for this agent
}

サブエージェント イベントの監視

サブエージェントを実行すると、親セッションはライフサイクル イベントを生成します。 これらのイベントをサブスクライブして、エージェント アクティビティを視覚化する UI を構築します。

イベントの種類

イベント次の場合に生成されますデータ
subagent.selectedランタイムがタスクのエージェントを選択する
          `agentName`、`agentDisplayName`、`tools` |

| subagent.started | サブエージェントが実行を開始する | toolCallIdagentNameagentDisplayNameagentDescription | | subagent.completed | サブエージェントが正常に終了する | toolCallIdagentNameagentDisplayName | | subagent.failed | サブエージェントでエラーが発生する | toolCallIdagentNameagentDisplayNameerror | | subagent.deselected | ランタイムがサブエージェントから他のプロセスに移行する | — |

イベントに登録する

session.on((event) => {
    switch (event.type) {
        case "subagent.started":
            console.log(`▶ Sub-agent started: ${event.data.agentDisplayName}`);
            console.log(`  Description: ${event.data.agentDescription}`);
            console.log(`  Tool call ID: ${event.data.toolCallId}`);
            break;

        case "subagent.completed":
            console.log(`✅ Sub-agent completed: ${event.data.agentDisplayName}`);
            break;

        case "subagent.failed":
            console.log(`❌ Sub-agent failed: ${event.data.agentDisplayName}`);
            console.log(`  Error: ${event.data.error}`);
            break;

        case "subagent.selected":
            console.log(`🎯 Agent selected: ${event.data.agentDisplayName}`);
            console.log(`  Tools: ${event.data.tools?.join(", ") ?? "all"}`);
            break;

        case "subagent.deselected":
            console.log("↩ Agent deselected, returning to parent");
            break;
    }
});

const response = await session.sendAndWait({
    prompt: "Research how authentication works in this codebase",
});

Python、Go、.NET の例については、 github/copilot-sdk リポジトリを参照してください。

エージェント ツリー UI の構築

サブエージェント イベントには、実行ツリーを再構築できる toolCallId フィールドが含まれます。 エージェント アクティビティを追跡するためのパターンを次に示します。

interface AgentNode {
    toolCallId: string;
    name: string;
    displayName: string;
    status: "running" | "completed" | "failed";
    error?: string;
    startedAt: Date;
    completedAt?: Date;
}

const agentTree = new Map<string, AgentNode>();

session.on((event) => {
    if (event.type === "subagent.started") {
        agentTree.set(event.data.toolCallId, {
            toolCallId: event.data.toolCallId,
            name: event.data.agentName,
            displayName: event.data.agentDisplayName,
            status: "running",
            startedAt: new Date(event.timestamp),
        });
    }

    if (event.type === "subagent.completed") {
        const node = agentTree.get(event.data.toolCallId);
        if (node) {
            node.status = "completed";
            node.completedAt = new Date(event.timestamp);
        }
    }

    if (event.type === "subagent.failed") {
        const node = agentTree.get(event.data.toolCallId);
        if (node) {
            node.status = "failed";
            node.error = event.data.error;
            node.completedAt = new Date(event.timestamp);
        }
    }

    // Render your UI with the updated tree
    renderAgentTree(agentTree);
});

エージェントごとのスコープ ツール

          `tools` プロパティを使用して、エージェントがアクセスできるツールを制限します。 これは、セキュリティとエージェントの集中を維持するために不可欠です。
const session = await client.createSession({
    customAgents: [
        {
            name: "reader",
            description: "Read-only exploration of the codebase",
            tools: ["grep", "glob", "view"],  // No write access
            prompt: "You explore and analyze code. Never suggest modifications directly.",
        },
        {
            name: "writer",
            description: "Makes code changes",
            tools: ["view", "edit", "bash"],   // Write access
            prompt: "You make precise code changes as instructed.",
        },
        {
            name: "unrestricted",
            description: "Full access agent for complex tasks",
            tools: null,                        // All tools available
            prompt: "You handle complex multi-step tasks using any available tools.",
        },
    ],
});

メモ

          `tools`が`null`または省略されると、エージェントはセッションで構成されているすべてのツールへのアクセスを継承します。 明示的なツール リストを使用して、最小限の特権の原則を適用します。

エージェントへの MCP サーバーのアタッチ

各カスタム エージェントは独自の MCP (モデル コンテキスト プロトコル) サーバーを持つ可能性があり、特殊なデータ ソースにアクセスできます。

const session = await client.createSession({
    customAgents: [
        {
            name: "db-analyst",
            description: "Analyzes database schemas and queries",
            prompt: "You are a database expert. Use the database MCP server to analyze schemas.",
            mcpServers: {
                "database": {
                    command: "npx",
                    args: ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"],
                },
            },
        },
    ],
});

パターンとベスト プラクティス

研究者と編集者のペアリング

一般的なパターンは、読み取り専用の研究者エージェントと書き込み可能なエディター エージェントを定義することです。 ランタイムは探索タスクを研究者に委任し、変更タスクをエディターに委任します。

customAgents: [
    {
        name: "researcher",
        description: "Analyzes code structure, finds patterns, and answers questions",
        tools: ["grep", "glob", "view"],
        prompt: "You are a code analyst. Thoroughly explore the codebase to answer questions.",
    },
    {
        name: "implementer",
        description: "Implements code changes based on analysis",
        tools: ["view", "edit", "bash"],
        prompt: "You make minimal, targeted code changes. Always verify changes compile.",
    },
]

エージェントの説明を固有に保つ

ランタイムは、 description を使用してユーザーの意図と一致します。 あいまいな説明は、委任が不適切になります。

// ❌ Too vague — runtime can't distinguish from other agents
{ description: "Helps with code" }

// ✅ Specific — runtime knows when to delegate
{ description: "Analyzes Python test coverage and identifies untested code paths" }

エラーを適切に処理する

サブエージェントは失敗する可能性があります。 常に subagent.failed イベントをリッスンし、アプリケーションで処理します。

session.on((event) => {
    if (event.type === "subagent.failed") {
        logger.error(`Agent ${event.data.agentName} failed: ${event.data.error}`);
        // Show error in UI, retry, or fall back to parent agent
    }
});