Skip to main content

自定义代理和子代理编排

利用具有特定范围的工具和提示定义专用代理,并在 Copilot 一次会话中协调它们作为子代理。

谁可以使用此功能?

GitHub Copilot SDK 适用于所有 Copilot 计划。

注意

          Copilot SDK 当前处于 技术预览版. 功能和可用性可能会发生更改。

自定义代理是附加到会话的轻型代理定义。 每个代理都有自己的系统提示、工具限制和可选的 MCP 服务器。 当用户的请求与某个代理的专业知识匹配时,Copilot SDK 运行时环境会自动将请求委托给该代理,将其作为子组件在独立的上下文中运行,同时将生命周期事件流式传输回父会话。 有关委派流的直观概述,请参阅 github/copilot-sdk 存储库

概念说明
          **自定义代理** | 具名称的代理配置,具有其自己的提示和工具集 |

| 子代理 | 运行时调用的用于处理部分任务的自定义代理 | | 推理 | 运行时能够根据用户的意图自动选择代理 | | 父会话 | 生成子代理的会话;接收所有生命周期事件 |

定义自定义代理

创建会话时传递 customAgents 。 每个代理至少需要一个name和一个prompt

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代理的唯一标识符
displayNamestring
事件中显示的人类可读名称
descriptionstring
代理的作用是帮助运行时环境选择它
tools
          `string[]` 或 `null` |

| 代理可以使用的工具的名称。 null 或省略表示所有工具 | | prompt | string | ✅ | 代理的系统提示 | | mcpServers | object | | 特定于此代理的 MCP 服务器配置 | | infer | boolean | | 运行时是否可以自动选择此代理(默认值: true |

提示

一个好 description 方法有助于运行时将用户意向与正确的代理匹配。 具体介绍代理的专业知识和功能。

除了按代理配置,还可以在agent上设置****,以在会话启动时预先选择哪个自定义代理处于活动状态。

会话配置属性类型说明
agentstring在创建会话时预选择的自定义代理的名称。 必须与namecustomAgents匹配。

在创建会话时选择代理

可以传入 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 存储库

子代理委派的工作原理

向具有自定义代理的会话发送提示时,运行时将评估是否委托给子代理:

  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 存储库

构建代理树用户界面

子代理事件包括 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://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
    }
});