Skip to main content

Agents personnalisés et orchestration de sous-agents

Définissez des agents spécialisés avec des outils et des invites délimités, puis laissez Copilot les orchestrer en tant que sous-agents au sein d’une même session.

Overview

Les agents personnalisés sont des définitions légères d’agents que vous associez à une session. Chaque agent possède son propre invite de commande système, ses restrictions d’outil et ses serveurs MCP facultatifs. Lorsque la demande d’un utilisateur correspond à l’expertise d’un agent, l’environnement d’exécution Copilot délègue automatiquement la tâche à cet agent en tant que sous-agent — en l’exécutant dans un contexte isolé tout en retransmettant en continu les événements du cycle de vie à la session parente.

Diagramme : Organigramme montrant le processus décrit.

ConceptDescription
Agent personnaliséConfiguration d’assistant nommé avec sa propre demande et son jeu d’outils
Sous-agentAgent personnalisé appelé par le runtime pour gérer une partie d’une tâche
InférenceCapacité du runtime à sélectionner automatiquement un agent en fonction de l’intention de l’utilisateur
Session parenteSession qui a généré le sous-agent ; reçoit tous les événements de cycle de vie

Définition d’agents personnalisés

Indiquez customAgents lors de la création d'une session. Chaque agent a besoin au minimum d’un name et prompt.

Langages de code 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" }),
});

Référence de configuration

PropriétéCatégorieObligatoireDescription
namestringIdentificateur unique de l’agent
displayNamestring
Nom lisible par l’homme affiché dans les événements
descriptionstring
Ce que fait l’agent : aide le runtime à le sélectionner
tools
string[] ou null
Les noms d’outils que l’agent peut utiliser. null ou omis = tous les outils
promptstringInvite de commande du système pour l’assistant
mcpServersobject
Configurations de serveur MCP spécifiques à cet agent
inferboolean
Indique si le runtime peut sélectionner automatiquement cet agent (par défaut : true)
skillsstring[]
Noms de compétences à précharger dans le contexte de l’agent au démarrage

Conseil

Une bonne description aide le runtime à faire correspondre l’intention de l’utilisateur à l’agent approprié. Soyez spécifique sur l’expertise et les capacités de l’agent.

En plus de la configuration par agent ci-dessus, vous pouvez définir agent sur la configuration de session elle-même pour pré-sélectionner l’agent personnalisé actif au démarrage de la session. Voir Sélection d’un agent lors de la création de session ci-dessous.

Propriété de configuration de sessionCatégorieDescription
agentstringNom de l’agent personnalisé à préélectionnez lors de la création de la session. Doit correspondre à un name dans customAgents.

Compétences par agent

Vous pouvez précharger des compétences dans le contexte d’un assistant à l’aide de la propriété skills. Lorsqu’il est spécifié, le contenu complet de chaque compétence répertoriée est injecté d’emblée dans le contexte de l’agent au démarrage — l’agent n’a pas besoin d’invoquer un outil de compétence ; les instructions sont déjà présentes. Les compétences sont opt-in : les agents ne reçoivent aucune compétence par défaut, et les sous-agents n’héritent pas des compétences du parent. Les noms de compétence sont résolus à partir du niveau skillDirectoriesde la session.

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

Dans cet exemple, security-auditor démarre avec security-scan et dependency-check déjà injectés dans son contexte, tandis que docs-writer démarre avec markdown-lint. Un agent sans skills champ ne reçoit aucun contenu de compétence.

Sélection d’un agent lors de la création de session

Vous pouvez passer agent la configuration de session pour pré-sélectionner l’agent personnalisé qui doit être actif au démarrage de la session. La valeur doit correspondre à name l’un des agents définis dans customAgents.

Cela équivaut à appeler session.rpc.agent.select() après la création, mais évite l’appel d’API supplémentaire et garantit que l’agent est actif à partir de la première invite.

Langages de code 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
});

Fonctionnement de la délégation de sous-agent

Lorsque vous envoyez une invite à une session avec des agents personnalisés, le runtime évalue s’il faut déléguer à un sous-agent :

  1. Correspondance d’intention—L'environnement d'exécution analyse l’invite de l’utilisateur par rapport à chaque agent name et description
  2. Sélection de l’agent : si une correspondance est trouvée et infer non false, le runtime sélectionne l’agent
  3.           **Exécution isolée** : le sous-assistant s’exécute avec son propre invite et un jeu d’outils restreint
    
  4. Streaming d’événements : les événements de cycle de vie (subagent.started, subagent.completed, etc.) retournent vers la session parente.
  5. Intégration des résultats : la sortie du sous-agent est incorporée dans la réponse de l’agent parent

Contrôle de l’inférence

Par défaut, tous les agents personnalisés sont disponibles pour la sélection automatique (infer: true). Définissez infer: false pour empêcher le runtime de sélectionner automatiquement un agent, ce qui est utile pour les agents que vous souhaitez appeler uniquement par le biais de requêtes utilisateur explicites :

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

Écoute des événements de sous-assistant

Lorsqu’un sous-agent s’exécute, la session parente émet des événements de cycle de vie. Abonnez-vous à ces événements pour générer des interfaces utilisateur qui visualisent l’activité de l’agent.

Types d’événements

EventÉmis quandData
subagent.selectedLe runtime sélectionne un agent pour la tâche
agentName, agentDisplayName, tools
subagent.startedLe sous-agent commence l’exécution
toolCallId, agentName, agentDisplayName, agentDescription
subagent.completedLe sous-assistant se termine avec succès
toolCallId, agentName, agentDisplayName
subagent.failedUn sous-agent rencontre une erreur
toolCallId, agentName, agentDisplayName, error
subagent.deselectedLe runtime s’éloigne du sous-agent

Abonnement aux événements

Langages de code 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",
});

Création d’une interface utilisateur pour l’arborescence des agents

Les événements de sous-agent incluent des toolCallId champs qui vous permettent de reconstruire l’arborescence d’exécution. Voici un modèle pour le suivi de l’activité de l’agent :

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

Outils de portée par assistant

Utilisez la tools propriété pour restreindre les outils auxquels un agent peut accéder. Ceci est essentiel pour la sécurité et pour garder les agents concentrés :

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

Remarque

Lorsque la balise tools est null ou omise, l’agent hérite de l'accès à tous les outils configurés sur la session. Utilisez des listes d’outils explicites pour appliquer le principe du privilège minimum.

Outils réservés aux agents

Utilisez la defaultAgent propriété sur la configuration de session pour masquer des outils spécifiques de l’agent par défaut (l’agent intégré qui gère les tours lorsqu’aucun agent personnalisé n’est sélectionné). Cela force l’agent principal à déléguer aux sous-agents lorsque les fonctionnalités de ces outils sont nécessaires, en gardant le contexte de l’agent principal propre.

Cela est utile quand :

  • Certains outils génèrent de grandes quantités de contexte qui surchargeraient l’agent principal
  • Vous souhaitez que l’agent principal agisse en tant qu’orchestrateur, en déléguant le travail lourd aux sous-agents spécialisés
  • Vous avez besoin d’une séparation stricte entre l’orchestration et l’exécution

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

Fonctionnement

Outils répertoriés dans defaultAgent.excludedTools:

  1. Sont inscrits : leurs gestionnaires sont disponibles pour l’exécution
  2. Sont masqués de la liste d’outils de l’agent principal — le LLM ne les verra pas et ne les appellera pas directement
  3. Restez disponible pour n’importe quel sous-agent personnalisé qui les inclut dans son tools tableau

Interaction avec d’autres filtres d’outils

defaultAgent.excludedTools est orthogonal aux availableTools et excludedTools au niveau de la session :

FilterScopeRésultat
availableToolsÀ l’échelle de la sessionListe d’autorisation : seuls ces outils existent pour n’importe qui
excludedToolsÀ l’échelle de la sessionListe de blocage : ces outils sont bloqués pour tout le monde
defaultAgent.excludedToolsAgent principal uniquementCes outils sont cachés à l’agent principal, mais disponibles pour les sous-agents

Préséance:

  1. Le niveau availableTools/excludedTools de session est appliqué en premier (globalement)
  2. defaultAgent.excludedTools est appliqué en haut, limitant davantage l’agent principal uniquement

Remarque

Si un outil figure à la fois dans excludedTools (au niveau de la session) et dans defaultAgent.excludedTools, l’exclusion au niveau de la session est prioritaire : l’outil n’est disponible pour personne.

Attachement de serveurs MCP à des agents

Chaque agent personnalisé peut avoir ses propres serveurs MCP (Model Context Protocol), ce qui lui donne accès à des sources de données spécialisées :

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

Modèles et bonnes pratiques

Associer un chercheur à un éditeur

Un modèle courant consiste à définir un agent de recherche en lecture seule et un agent d’édition capable d’écrire. Le runtime délègue les tâches d'exploration au chercheur et les tâches de modification à l'éditeur.

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

Conserver des descriptions spécifiques de l’agent

Le runtime utilise description pour répondre à l'intention de l’utilisateur. Les descriptions vagues mènent à une délégation médiocre :

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

Gérer correctement les défaillances

Les sous-agents peuvent échouer. Écoutez toujours les événements subagent.failed et gérez-les dans votre application :

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