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.

| Concept | Description |
|---|---|
| Agent personnalisé | Configuration d’agent nommé avec sa propre demande et son jeu d’outils |
| Sous-agent | Agent personnalisé appelé par le runtime pour gérer une partie d’une tâche |
| Inférence | Capacité du runtime à sélectionner automatiquement un agent en fonction de l’intention de l’utilisateur |
| Session parente | Session 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.
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" }),
});
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.",
},
],
)
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
},
})
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()),
});
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.*;
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();
}
Référence de configuration
| Propriété | Catégorie | Obligatoire | Description |
|---|---|---|---|
name | string | ✅ | Identificateur unique de l’agent |
displayName | string | ||
| Nom lisible par l’homme affiché dans les événements | |||
description | string | ||
| 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 | |||
prompt | string | ✅ | Invite de commande du système pour l'agent |
mcpServers | object | ||
| Configurations de serveur MCP spécifiques à cet agent | |||
infer | boolean | ||
Indique si le runtime peut sélectionner automatiquement cet agent (par défaut : true) | |||
skills | string[] | ||
| 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 session | Catégorie | Description |
|---|---|---|
agent | string | Nom 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 agent à 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.
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
});
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
)
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
})
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
});
import com.github.copilot.rpc.*;
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();
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 :
- Correspondance d’intention—L'environnement d'exécution analyse l’invite de l’utilisateur par rapport à chaque agent
nameetdescription - Sélection de l’agent : si une correspondance est trouvée et
infernonfalse, le runtime sélectionne l’agent - Exécution isolée : le sous-agent s’exécute avec son propre invite et un jeu d’outils restreint
- Streaming d’événements : les événements de cycle de vie (
subagent.started,subagent.completed, etc.) retournent vers la session parente. - 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-agent
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 quand | Data |
|---|---|---|
subagent.selected | Le runtime sélectionne un agent pour la tâche | |
agentName, agentDisplayName, tools | ||
subagent.started | Le sous-agent commence l’exécution | |
toolCallId, agentName, agentDisplayName, agentDescription | ||
subagent.completed | Le sous-agent se termine avec succès. | |
toolCallId, agentName, agentDisplayName | ||
subagent.failed | Un sous-agent rencontre une erreur | |
toolCallId, agentName, agentDisplayName, error | ||
subagent.deselected | Le runtime s’éloigne du sous-agent | — |
Abonnement aux événements
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",
});
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")
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",
})
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"
});
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();
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 agent
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
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.",
},
],
});
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,
)
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.",
},
},
})
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.",
},
],
});
Fonctionnement
Outils répertoriés dans defaultAgent.excludedTools:
- Sont inscrits : leurs gestionnaires sont disponibles pour l’exécution
- Sont masqués de la liste d’outils de l’agent principal — le LLM ne les verra pas et ne les appellera pas directement
- Restez disponible pour n’importe quel sous-agent personnalisé qui les inclut dans son
toolstableau
Interaction avec d’autres filtres d’outils
defaultAgent.excludedTools est orthogonal aux availableTools et excludedTools au niveau de la session :
| Filter | Scope | Résultat |
|---|---|---|
availableTools | À l’échelle de la session | Liste d’autorisation : seuls ces outils existent pour n’importe qui |
excludedTools | À l’échelle de la session | Liste de blocage : ces outils sont bloqués pour tout le monde |
defaultAgent.excludedTools | Agent principal uniquement | Ces outils sont cachés à l’agent principal, mais disponibles pour les sous-agents |
Préséance:
- Le niveau
availableTools/excludedToolsde session est appliqué en premier (globalement) defaultAgent.excludedToolsest 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
}
});