Skip to main content

Agentes personalizados e orquestração de subagentes

Defina agentes especializados com ferramentas e prompts com escopo definido e, em seguida, permita que o Copilot os orquestre como subagentes em uma única sessão.

Overview

Agentes personalizados são as definições de agentes leves que você anexa a uma sessão. Cada agente tem seu próprio prompt do sistema, restrições de ferramenta e servidores MCP opcionais. Quando a solicitação de um usuário corresponde à especialidade de um agente, o runtime do Copilot delega automaticamente para esse agente, como um sub-agent—executando-o em um contexto isolado enquanto transmite os eventos do ciclo de vida de volta para a sessão principal.

Diagrama: Fluxograma mostrando o processo descrito.

ConceitoDescription
Agente personalizadoUma configuração de agente nomeado com seu próprio prompt e conjunto de ferramentas
SubagenteUm agente personalizado invocado pelo runtime para lidar com parte de uma tarefa
InferênciaA capacidade do runtime de selecionar automaticamente um agente com base na intenção do usuário
Sessão principalA sessão que gerou o subagente; recebe todos os eventos do ciclo de vida.

Definindo agentes personalizados

Passe customAgents ao criar uma sessão. Cada agente precisa, no mínimo, de um name e um prompt.

Idiomas de código navigation

TypeScript
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: "approve-once" }),
});

Referência de configuração

PropertyTipoObrigatórioDescription
namestringIdentificador exclusivo para o agente
displayNamestring
Nome legível por humanos exibido em eventos
descriptionstring
O que o agente faz — ajuda o runtime a selecioná-lo
tools
string[] ou null
Nomes de ferramentas que o agente pode usar. null ou omitido = todas as ferramentas
promptstringComando do sistema ao agente
mcpServersobject
Configurações de servidor MCP específicas para este agente
inferboolean
Se o runtime pode selecionar automaticamente este agente (padrão: true)
skillsstring[]
Nomes de habilidade a serem pré-carregados no contexto do agente na inicialização

Dica

Um bom description ajuda o runtime a corresponder a intenção do usuário com o agente certo. Seja específico sobre a experiência e as funcionalidades do agente.

Além da configuração por agente acima, você pode definir agent na própria configuração da sessão para selecionar previamente qual agente personalizado está ativo quando a sessão é iniciada. Veja a seleção de um agente na criação da sessão abaixo.

Propriedade de configuração da sessãoTipoDescription
agentstringNome do agente personalizado a ser pré-selecionado na criação da sessão. Deve corresponder a um name em customAgents.

Habilidades por agente

Você pode pré-carregar habilidades no contexto de um agente usando a propriedade skills. Quando especificado, o conteúdo completo de cada habilidade listada é injetado ansiosamente no contexto do agente na inicialização– o agente não precisa invocar uma ferramenta de habilidade; as instruções já estão presentes. As habilidades são aceitas: os agentes não recebem habilidades por padrão, e os sub-agentes não herdam habilidades do pai. Os nomes de habilidade são resolvidos no nível da sessão skillDirectories.

const session = await client.createSession({
    skillDirectories: ["./skills"],
    customAgents: [
        {
            name: "security-auditor",
            description: "Security-focused code reviewer",
            prompt: "Focus on OWASP Top 10 vulnerabilities",
            skills: ["security-scan", "dependency-check"],
        },
        {
            name: "docs-writer",
            description: "Technical documentation writer",
            prompt: "Write clear, concise documentation",
            skills: ["markdown-lint"],
        },
    ],
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

Neste exemplo, security-auditor começa com security-scan e dependency-check já injetado em seu contexto, enquanto docs-writer começa com markdown-lint. Um agente sem um skills campo não recebe nenhum conteúdo de habilidade.

Selecionando um agente na criação da sessão

Você pode passar agent na configuração da sessão para predefinir qual agente personalizado deverá ser ativado quando a sessão for iniciada. O valor deve corresponder ao name de um dos agentes definidos em customAgents.

Isso é equivalente à chamada session.rpc.agent.select() após a criação, mas evita a chamada extra à API e garante que o agente esteja ativo desde o primeiro prompt.

Idiomas de código navigation

TypeScript
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
});

Como funciona a delegação de subagente

Quando você envia um prompt para uma sessão com agentes personalizados, o runtime avalia se deseja delegar a um subagente:

  1.           **Correspondência de intenção** – o runtime analisa o prompt do usuário em relação a `name` e `description` de cada agente
    
  2. Seleção de agente— Se uma correspondência for encontrada e infer não for false, o runtime selecionará o agente
  3. Execução isolada – o sub-agente é executado com seu próprio prompt e conjunto de ferramentas restritas
  4.           **Transmissão de eventos** — eventos de ciclo de vida (`subagent.started`, `subagent.completed` etc.) são transmitidos de volta para a sessão pai
    
  5. Integração de resultados – a saída do sub-agente é incorporada à resposta do agente pai

Controlando a inferência

Por padrão, todos os agentes personalizados estão disponíveis para seleção automática (infer: true). Defina infer: false para impedir que o runtime selecione automaticamente um agente, útil para agentes que você só deseja invocar por meio de solicitações explícitas do usuário:

{
    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
}

Escutar eventos de subagente

Quando um subagente é executado, a sessão pai emite eventos de ciclo de vida. Assine esses eventos para criar UIs que visualizam a atividade do agente.

Tipos de evento

AcontecimentoEmitido quandoDados
subagent.selectedO runtime seleciona um agente para a tarefa
agentName, agentDisplayName, tools
subagent.startedSubagente inicia a execução
toolCallId, agentName, , agentDisplayName``agentDescription
subagent.completedO subagente foi concluído com êxito
toolCallId, agentName, agentDisplayName
subagent.failedO subagente encontra um erro
toolCallId, agentName, , agentDisplayName``error
subagent.deselectedO runtime muda seu foco do subagente

Inscrevendo-se para eventos

Idiomas de código navigation

TypeScript
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",
});

Construir uma interface de usuário de árvore de agente

Eventos de subagente incluem campos toolCallId que permitem reconstruir a árvore de execução. Aqui está um padrão para acompanhar a atividade do agente:

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);
});

Ferramentas de escopo por agente

Use a tools propriedade para restringir quais ferramentas um agente pode acessar. Isso é essencial para a segurança e para manter os agentes focados:

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.",
        },
    ],
});

Observação

Quando tools é null ou omitido, o agente herda o acesso a todas as ferramentas configuradas na sessão. Use listas de ferramentas explícitas para impor o princípio do privilégio mínimo.

Ferramentas exclusivas do agente

Use a defaultAgent propriedade na configuração de sessão para ocultar ferramentas específicas do agente padrão (o agente interno que manipula turnos quando nenhum agente personalizado é selecionado). Isso força o agente principal a delegar a sub-agentes quando as funcionalidades dessas ferramentas são necessárias, mantendo o contexto do agente principal limpo.

Isso é útil quando:

  • Determinadas ferramentas geram grandes quantidades de contexto que sobrecarregariam o agente principal
  • Você deseja que o agente principal atue como um orquestrador, delegando trabalho pesado a sub-agentes especializados
  • Você precisa de separação estrita entre orquestração e execução

Idiomas de código navigation

TypeScript
import { CopilotClient, defineTool, approveAll } from "@github/copilot-sdk";
import { z } from "zod";

const heavyContextTool = defineTool("analyze-codebase", {
    description: "Performs deep analysis of the codebase, generating extensive context",
    parameters: z.object({ query: z.string() }),
    handler: async ({ query }) => {
        // ... expensive analysis that returns lots of data
        return { analysis: "..." };
    },
});

const session = await client.createSession({
    tools: [heavyContextTool],
    defaultAgent: {
        excludedTools: ["analyze-codebase"],
    },
    customAgents: [
        {
            name: "researcher",
            description: "Deep codebase analysis agent with access to heavy-context tools",
            tools: ["analyze-codebase"],
            prompt: "You perform thorough codebase analysis using the analyze-codebase tool.",
        },
    ],
});

Como funciona

Ferramentas listadas em defaultAgent.excludedTools:

  1. Estão registrados– seus manipuladores estão disponíveis para execução
  2. Estão ocultos na lista de ferramentas do agente principal– a LLM não os verá nem os chamará diretamente
  3. Permanecer disponível para qualquer subagente personalizado que os inclua em sua tools matriz

Interação com outros filtros de ferramenta

defaultAgent.excludedTools é ortogonal a availableTools e excludedTools no nível da sessão:

FiltroScopeEfeito
availableToolsEm toda a sessãoLista de permissões – somente essas ferramentas existem para qualquer pessoa
excludedToolsEm toda a sessãoLista de bloqueios – essas ferramentas são bloqueadas para todos
defaultAgent.excludedToolsSomente agente principalEssas ferramentas estão ocultas do agente principal, mas estão disponíveis para sub-agentes

Precedência:

  1. O nível availableTools/excludedTools da sessão é aplicado primeiro (globalmente)
  2.           `defaultAgent.excludedTools` é aplicado na parte superior, restringindo ainda mais apenas o agente principal
    

Observação

Se uma ferramenta estiver em ambos excludedTools (nível de sessão) e defaultAgent.excludedTools, a exclusão no nível da sessão tiver precedência, a ferramenta não estará disponível para todos.

Anexando servidores MCP a agentes

Cada agente personalizado pode ter seus próprios servidores MCP (Model Context Protocol), dando-lhe acesso a fontes de dados especializadas:

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"],
                },
            },
        },
    ],
});

Padrões e melhores práticas

Emparelhar um pesquisador com um editor

Um padrão comum é definir um agente de pesquisa somente leitura e um agente editor com capacidade de gravação. O tempo de execução delega tarefas de exploração ao pesquisador e tarefas de modificação ao editor.

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.",
    },
]

Manter descrições de agente específicas

O runtime usa o description para corresponder à intenção do usuário. Descrições vagas levam à má delegação:

// ❌ 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" }

Lidar com falhas de forma elegante

Sub-agentes podem falhar. Sempre ouça eventos subagent.failed e manipule-os em seu aplicativo:

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
    }
});