跳至主要内容

自定义代理和子代理编排

定义具有受限工具和提示的专用代理,让 Copilot 在单个会话中将其编排为子代理。

谁可以使用此功能?

GitHub Copilot SDK 在所有 Copilot 计划中均可使用。

注意

Copilot SDK 目前处于公开预览阶段。功能和可用性可能会更改。

自定义代理是可附加到会话的轻量级代理定义。每个代理都有自己的系统提示、工具限制和可选的 MCP 服务器。当用户的请求匹配代理的专长时,Copilot SDK 运行时会自动将其委派为子代理——在隔离的上下文中运行,同时将生命周期事件流式传回父会话。有关委派流程的可视化概览,请参阅 github/copilot-sdk repository

概念描述
自定义代理具有自定义提示和工具集的具名代理配置
子代理由运行时调用以处理任务一部分的自定义代理
推断运行时根据用户意图自动选择代理的能力
父会话生成子代理的会话;接收所有生命周期事件

定义自定义代理

在创建会话时传入 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 仓库。关于 Java,请参阅 github/copilot-sdk-java 仓库

配置参考

属性类型是否必填描述
namestring代理的唯一标识符
displayNamestring事件中显示的可读名称
descriptionstring代理的功能——帮助运行时进行选择
toolsstring[] or null代理可使用的工具名称。null 或省略 = 所有工具
promptstring代理的系统提示
mcpServersobject针对该代理的 MCP 服务器配置
inferboolean运行时是否可以自动选择此代理(默认:true

提示

良好的 description 有助于运行时将用户意图匹配到正确的代理。请具体说明代理的专长和能力。

除了每个代理的配置外,您还可以在session config 上设置 agent,以预先选择会话启动时激活的自定义代理。

会话配置属性类型描述
agentstring会话创建时预先选择的自定义代理名称。必须匹配 customAgents 中的 name

在会话创建时选择代理

您可以在会话配置中传入 agent,以预先选择会话启动时应激活的自定义代理。该值必须匹配 customAgents 中定义的某个代理的 name

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 仓库。关于 Java,请参阅 github/copilot-sdk-java 仓库

子代理委托工作原理

当您向包含自定义代理的会话发送提示时,运行时会评估是否委托给子代理。

  1. 意图匹配—运行时将用户的提示与每个代理的 namedescription 进行分析
  2. 代理选择—如果找到匹配且 infer 不为 false,运行时会选择该代理
  3. 隔离执行—子代理使用其自己的提示和受限工具集运行
  4. 事件流式传输—生命周期事件(subagent.startedsubagent.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子代理开始执行toolCallId, agentName, agentDisplayName, agentDescription
subagent.completed子代理成功完成toolCallId, agentName, agentDisplayName
subagent.failed子代理遇到错误toolCallId, agentName, agentDisplayName, error
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 仓库。关于 Java,请参阅 github/copilot-sdk-java 仓库

构建代理树 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.",
        },
    ],
});

注意

toolsnull 或省略时,代理会继承会话上配置的所有工具访问权限。使用显式工具列表以执行最小特权原则。

为代理附加 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:///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
    }
});
© . This site is unofficial and not affiliated with GitHub, Inc.