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.

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" }),
});
Python
from copilot import CopilotClient, PermissionDecisionApproveOnce

client = CopilotClient()
await client.start()

session = await client.create_session(
    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
    model="gpt-4.1",
    custom_agents=[
        {
            "name": "researcher",
            "display_name": "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",
            "display_name": "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.",
        },
    ],
)
Go
package main

import (
    "context"
    copilot "github.com/github/copilot-sdk/go"
    "github.com/github/copilot-sdk/go/rpc"
)

func main() {
    ctx := context.Background()
    client := copilot.NewClient(nil)
    client.Start(ctx)

    session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
        Model: "gpt-4.1",
        CustomAgents: []copilot.CustomAgentConfig{
            {
                Name:        "researcher",
                DisplayName: "Research Agent",
                Description: "Explores codebases and answers questions using read-only tools",
                Tools:       []string{"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:       []string{"view", "edit", "bash"},
                Prompt:      "You are a code editor. Make minimal, surgical changes to files as requested.",
            },
        },
        OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
            return &rpc.PermissionDecisionApproveOnce{}, nil
        },
    })
    _ = session
}
ctx := context.Background()
client := copilot.NewClient(nil)
client.Start(ctx)

session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
    Model: "gpt-4.1",
    CustomAgents: []copilot.CustomAgentConfig{
        {
            Name:        "researcher",
            DisplayName: "Research Agent",
            Description: "Explores codebases and answers questions using read-only tools",
            Tools:       []string{"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:       []string{"view", "edit", "bash"},
            Prompt:      "You are a code editor. Make minimal, surgical changes to files as requested.",
        },
    },
    OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
        return &rpc.PermissionDecisionApproveOnce{}, nil
    },
})
.NET
using GitHub.Copilot;
using GitHub.Copilot.Rpc;

await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
    Model = "gpt-4.1",
    CustomAgents = new List<CustomAgentConfig>
    {
        new()
        {
            Name = "researcher",
            DisplayName = "Research Agent",
            Description = "Explores codebases and answers questions using read-only tools",
            Tools = new List<string> { "grep", "glob", "view" },
            Prompt = "You are a research assistant. Analyze code and answer questions. Do not modify any files.",
        },
        new()
        {
            Name = "editor",
            DisplayName = "Editor Agent",
            Description = "Makes targeted code changes",
            Tools = new List<string> { "view", "edit", "bash" },
            Prompt = "You are a code editor. Make minimal, surgical changes to files as requested.",
        },
    },
    OnPermissionRequest = (req, inv) =>
        Task.FromResult(PermissionDecision.ApproveOnce()),
});
Java
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import java.util.List;

try (var client = new CopilotClient()) {
    client.start().get();

    var session = client.createSession(
        new SessionConfig()
            .setModel("gpt-4.1")
            .setCustomAgents(List.of(
                new CustomAgentConfig()
                    .setName("researcher")
                    .setDisplayName("Research Agent")
                    .setDescription("Explores codebases and answers questions using read-only tools")
                    .setTools(List.of("grep", "glob", "view"))
                    .setPrompt("You are a research assistant. Analyze code and answer questions. Do not modify any files."),
                new CustomAgentConfig()
                    .setName("editor")
                    .setDisplayName("Editor Agent")
                    .setDescription("Makes targeted code changes")
                    .setTools(List.of("view", "edit", "bash"))
                    .setPrompt("You are a code editor. Make minimal, surgical changes to files as requested.")
            ))
            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
    ).get();
}

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 excluído = 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 habilidades para precarregar no contexto do agente ao iniciar

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 carregar previamente 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 opcionais: os agentes não recebem nenhuma habilidade por padrão, e os subagentes não herdam habilidades do agente pai. Os nomes das habilidades são determinados a partir de skillDirectories no nível da sessão.

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.

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
});
Python
session = await client.create_session(
    on_permission_request=PermissionHandler.approve_all,
    custom_agents=[
        {
            "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
)
Go
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
    CustomAgents: []copilot.CustomAgentConfig{
        {
            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
})
.NET
var session = await client.CreateSessionAsync(new SessionConfig
{
    CustomAgents = new List<CustomAgentConfig>
    {
        new() { Name = "researcher", Prompt = "You are a research assistant. Analyze code and answer questions." },
        new() { Name = "editor", Prompt = "You are a code editor. Make minimal, surgical changes." },
    },
    Agent = "researcher", // Pre-select the researcher agent
});
Java
import com.github.copilot.sdk.json.*;
import java.util.List;

var session = client.createSession(
    new SessionConfig()
        .setCustomAgents(List.of(
            new CustomAgentConfig()
                .setName("researcher")
                .setPrompt("You are a research assistant. Analyze code and answer questions."),
            new CustomAgentConfig()
                .setName("editor")
                .setPrompt("You are a code editor. Make minimal, surgical changes.")
        ))
        .setAgent("researcher") // Pre-select the researcher agent
        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();

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 cada agente name e description
  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
}

Escutando 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 sub-agente

Inscrevendo-se para eventos

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",
});
Python
def handle_event(event):
    if event.type == "subagent.started":
        print(f"▶ Sub-agent started: {event.data.agent_display_name}")
        print(f"  Description: {event.data.agent_description}")
    elif event.type == "subagent.completed":
        print(f"✅ Sub-agent completed: {event.data.agent_display_name}")
    elif event.type == "subagent.failed":
        print(f"❌ Sub-agent failed: {event.data.agent_display_name}")
        print(f"  Error: {event.data.error}")
    elif event.type == "subagent.selected":
        tools = event.data.tools or "all"
        print(f"🎯 Agent selected: {event.data.agent_display_name} (tools: {tools})")

unsubscribe = session.on(handle_event)

response = await session.send_and_wait("Research how authentication works in this codebase")
Go
package main

import (
    "context"
    "fmt"
    copilot "github.com/github/copilot-sdk/go"
    "github.com/github/copilot-sdk/go/rpc"
)

func main() {
    ctx := context.Background()
    client := copilot.NewClient(nil)
    client.Start(ctx)

    session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
        Model: "gpt-4.1",
        OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
            return &rpc.PermissionDecisionApproveOnce{}, nil
        },
    })

    session.On(func(event copilot.SessionEvent) {
        switch d := event.Data.(type) {
        case *copilot.SubagentStartedData:
            fmt.Printf("▶ Sub-agent started: %s\n", d.AgentDisplayName)
            fmt.Printf("  Description: %s\n", d.AgentDescription)
            fmt.Printf("  Tool call ID: %s\n", d.ToolCallID)
        case *copilot.SubagentCompletedData:
            fmt.Printf("✅ Sub-agent completed: %s\n", d.AgentDisplayName)
        case *copilot.SubagentFailedData:
            fmt.Printf("❌ Sub-agent failed: %s — %v\n", d.AgentDisplayName, d.Error)
        case *copilot.SubagentSelectedData:
            fmt.Printf("🎯 Agent selected: %s\n", d.AgentDisplayName)
        }
    })

    _, err := session.SendAndWait(ctx, copilot.MessageOptions{
        Prompt: "Research how authentication works in this codebase",
    })
    _ = err
}
session.On(func(event copilot.SessionEvent) {
    switch d := event.Data.(type) {
    case *copilot.SubagentStartedData:
        fmt.Printf("▶ Sub-agent started: %s\n", d.AgentDisplayName)
        fmt.Printf("  Description: %s\n", d.AgentDescription)
        fmt.Printf("  Tool call ID: %s\n", d.ToolCallID)
    case *copilot.SubagentCompletedData:
        fmt.Printf("✅ Sub-agent completed: %s\n", d.AgentDisplayName)
    case *copilot.SubagentFailedData:
        fmt.Printf("❌ Sub-agent failed: %s — %v\n", d.AgentDisplayName, d.Error)
    case *copilot.SubagentSelectedData:
        fmt.Printf("🎯 Agent selected: %s\n", d.AgentDisplayName)
    }
})

_, err := session.SendAndWait(ctx, copilot.MessageOptions{
    Prompt: "Research how authentication works in this codebase",
})
.NET
using GitHub.Copilot;

public static class SubAgentEventsExample
{
    public static async Task Example(CopilotSession session)
    {
        using var subscription = session.On<SessionEvent>(evt =>
        {
            switch (evt)
            {
                case SubagentStartedEvent started:
                    Console.WriteLine($"▶ Sub-agent started: {started.Data.AgentDisplayName}");
                    Console.WriteLine($"  Description: {started.Data.AgentDescription}");
                    Console.WriteLine($"  Tool call ID: {started.Data.ToolCallId}");
                    break;
                case SubagentCompletedEvent completed:
                    Console.WriteLine($"✅ Sub-agent completed: {completed.Data.AgentDisplayName}");
                    break;
                case SubagentFailedEvent failed:
                    Console.WriteLine($"❌ Sub-agent failed: {failed.Data.AgentDisplayName}{failed.Data.Error}");
                    break;
                case SubagentSelectedEvent selected:
                    Console.WriteLine($"🎯 Agent selected: {selected.Data.AgentDisplayName}");
                    break;
            }
        });

        await session.SendAndWaitAsync(new MessageOptions
        {
            Prompt = "Research how authentication works in this codebase"
        });
    }
}
using var subscription = session.On<SessionEvent>(evt =>
{
    switch (evt)
    {
        case SubagentStartedEvent started:
            Console.WriteLine($"▶ Sub-agent started: {started.Data.AgentDisplayName}");
            Console.WriteLine($"  Description: {started.Data.AgentDescription}");
            Console.WriteLine($"  Tool call ID: {started.Data.ToolCallId}");
            break;
        case SubagentCompletedEvent completed:
            Console.WriteLine($"✅ Sub-agent completed: {completed.Data.AgentDisplayName}");
            break;
        case SubagentFailedEvent failed:
            Console.WriteLine($"❌ Sub-agent failed: {failed.Data.AgentDisplayName}{failed.Data.Error}");
            break;
        case SubagentSelectedEvent selected:
            Console.WriteLine($"🎯 Agent selected: {selected.Data.AgentDisplayName}");
            break;
    }
});

await session.SendAndWaitAsync(new MessageOptions
{
    Prompt = "Research how authentication works in this codebase"
});
Java
session.on(event -> {
    if (event instanceof SubagentStartedEvent e) {
        System.out.println("▶ Sub-agent started: " + e.getData().agentDisplayName());
        System.out.println("  Description: " + e.getData().agentDescription());
        System.out.println("  Tool call ID: " + e.getData().toolCallId());
    } else if (event instanceof SubagentCompletedEvent e) {
        System.out.println("✅ Sub-agent completed: " + e.getData().agentName());
    } else if (event instanceof SubagentFailedEvent e) {
        System.out.println("❌ Sub-agent failed: " + e.getData().agentName());
        System.out.println("  Error: " + e.getData().error());
    } else if (event instanceof SubagentSelectedEvent e) {
        System.out.println("🎯 Agent selected: " + e.getData().agentDisplayName());
    } else if (event instanceof SubagentDeselectedEvent e) {
        System.out.println("↩ Agent deselected, returning to parent");
    }
});

var response = session.sendAndWait(
    new MessageOptions().setPrompt("Research how authentication works in this codebase")
).get();

Construindo uma interface do usuário de árvore de agente

Eventos de subagente incluem campos que permitem toolCallId 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
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.",
        },
    ],
});
Python
from copilot import CopilotClient
from copilot.tools import Tool

heavy_tool = Tool(
    name="analyze-codebase",
    description="Performs deep analysis of the codebase",
    handler=analyze_handler,
    parameters={"type": "object", "properties": {"query": {"type": "string"}}},
)

session = await client.create_session(
    tools=[heavy_tool],
    default_agent={"excluded_tools": ["analyze-codebase"]},
    custom_agents=[
        {
            "name": "researcher",
            "description": "Deep codebase analysis agent",
            "tools": ["analyze-codebase"],
            "prompt": "You perform thorough codebase analysis.",
        },
    ],
    on_permission_request=approve_all,
)
Go
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
    Tools: []copilot.Tool{heavyTool},
    DefaultAgent: &copilot.DefaultAgentConfig{
        ExcludedTools: []string{"analyze-codebase"},
    },
    CustomAgents: []copilot.CustomAgentConfig{
        {
            Name:        "researcher",
            Description: "Deep codebase analysis agent",
            Tools:       []string{"analyze-codebase"},
            Prompt:      "You perform thorough codebase analysis.",
        },
    },
})
C#
var session = await client.CreateSessionAsync(new SessionConfig
{
    Tools = [analyzeCodebaseTool],
    DefaultAgent = new DefaultAgentConfig
    {
        ExcludedTools = ["analyze-codebase"],
    },
    CustomAgents =
    [
        new CustomAgentConfig
        {
            Name = "researcher",
            Description = "Deep codebase analysis agent",
            Tools = ["analyze-codebase"],
            Prompt = "You perform thorough codebase analysis.",
        },
    ],
});

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 o agente principal somente

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 escrita. 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 subagent.failed eventos 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
    }
});