Skip to main content

Benutzerdefinierte Agents und Sub-Agent-Orchestrierung

Definieren Sie spezialisierte Agents mit bereichsbezogenen Tools und Eingabeaufforderungen, und lassen Sie Copilot sie innerhalb einer einzigen Sitzung als Unter-Agents koordinieren.

Übersicht

Benutzerdefinierte Agents sind einfache Agentdefinitionen, die Sie an eine Sitzung anfügen. Jeder Agent verfügt über eine eigene Systemaufforderung, Tooleinschränkungen und optionale MCP-Server. Wenn die Anfrage eines Benutzers mit der Expertise eines Agenten übereinstimmt, delegiert die Copilot-Laufzeit automatisch an diesen Agenten als sub-agent, der in einem isolierten Kontext ausgeführt wird, während Lebenszyklusereignisse an die übergeordnete Sitzung zurückgestreamt werden.

Diagramm: Flussdiagramm mit dem beschriebenen Prozess.

KonzeptDescription
Benutzerdefinierter AgentEine benannte Agent-Konfiguration mit einer eigenen Eingabeaufforderung und einem eigenen Toolsatz
Unter-AgentEin benutzerdefinierter Agent, der von der Laufzeit aufgerufen wird, um einen Teil einer Aufgabe zu verarbeiten
SchlussfolgerungDie Fähigkeit der Laufzeit, einen Agent basierend auf der Absicht des Benutzers automatisch auszuwählen
Übergeordnete SitzungDie Sitzung, die den Teilagent erzeugte, empfängt alle Lebenszyklus-Ereignisse

Definieren von benutzerdefinierten Agents

Übergeben Sie customAgents beim Erstellen einer Sitzung. Jeder Agent benötigt mindestens ein name und ein 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();
}

Konfigurationsreferenz

EigentumTypErforderlichDescription
namestringEindeutiger Bezeichner für den Agent
displayNamestring
Lesbarer Name, der in Ereignissen angezeigt wird
descriptionstring
Was der Agent tut – hilft der Laufzeit, ihn auszuwählen.
tools
string[] oder null
Toolnamen, die der Agent verwenden kann.
null oder weggelassen = alle Tools
promptstringSystemaufforderung für den Agent
mcpServersobject
MCP-Serverkonfigurationen, die für diesen Agent spezifisch sind
inferboolean
Gibt an, ob die Laufzeit diesen Agent automatisch auswählen kann (Standard: true)
skillsstring[]
Skill-Namen, die beim Start in den Kontext des Agents vorgeladen werden

Tipp

Eine gute Methode description hilft der Laufzeit, die Benutzerabsicht mit den richtigen Agenten abzugleichen. Seien Sie spezifisch für das Fachwissen und die Fähigkeiten des Agenten.

Zusätzlich zur oben aufgeführten Konfiguration pro Agent können Sie für die agent selbst festlegen****, dass sie vorwählt, welcher benutzerdefinierte Agent aktiv ist, wenn die Sitzung gestartet wird. Siehe "Auswählen eines Agents bei der Sitzungserstellung " weiter unten.

SitzungskonfigurationseigenschaftTypDescription
agentstringName des benutzerdefinierten Agents, der bei der Sitzungserstellung vorab ausgewählt werden soll. Muss mit einem name in customAgents übereinstimmen.

Fähigkeiten pro Agent

Sie können Fähigkeiten mithilfe der skills Eigenschaft in den Kontext eines Agents vorab laden. Wenn angegeben, wird der vollständige Inhalt jeder aufgeführten Fähigkeit beim Start eifrig in den Kontext des Agenten eingefügt – der Agent muss kein Fähigkeitstool aufrufen; die Anweisungen sind bereits vorhanden. Fähigkeiten sind opt-in: Agents erhalten standardmäßig keine Fähigkeiten, und Sub-Agents erben keine Fähigkeiten vom Elternteil. Qualifikationsnamen werden auf Sitzungsebene skillDirectoriesaufgelöst.

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

In diesem Beispiel startet security-auditor mit security-scan und dependency-check, die bereits in seinen Kontext injiziert wurden, während docs-writer mit markdown-lint startet. Ein Agent ohne Feld skills erhält keinen Qualifikationsinhalt.

Auswählen eines Agents bei der Sitzungserstellung

Sie können agent in die Sitzungskonfiguration einfügen, um vorab auszuwählen, welcher benutzerdefinierte Agent aktiv sein soll, wenn die Sitzung beginnt. Der Wert muss mit dem name eines der in customAgents definierten Agenten übereinstimmen.

Dies entspricht dem Aufrufen session.rpc.agent.select() nach der Erstellung, vermeidet aber den zusätzlichen API-Aufruf und stellt sicher, dass der Agent von der ersten Eingabeaufforderung aus aktiv ist.

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

Funktionsweise der Sub-Agent-Delegierung

Wenn Sie eine Eingabeaufforderung an eine Sitzung mit benutzerdefinierten Agents senden, wertet die Laufzeit aus, ob sie an einen Unter-Agent delegiert werden soll:

  1. Intent-Abgleich – Während der Laufzeit wird die Aufforderung des Benutzers mit jedem name Agenten und description abgeglichen.
  2. Agentauswahl – Wenn eine Übereinstimmung gefunden wird und infer nicht false, wählt die Laufzeit den Agent aus.
  3. Isolierte Ausführung – Der Unter-Agent wird mit einer eigenen Eingabeaufforderung und einem eingeschränkten Toolsatz ausgeführt.
  4. Ereignisstreaming – Lebenszyklusereignisse (subagent.started, subagent.completedusw.) werden zurück zur übergeordneten Sitzung gestreamt
  5. Ergebnisintegration – Die Ausgabe des Unter-Agents wird in die Antwort des übergeordneten Agents integriert.

Steuern von Rückschlüssen

Standardmäßig sind alle benutzerdefinierten Agents für die automatische Auswahl (infer: true) verfügbar. Um zu verhindern, dass die Laufzeit automatisch einen Agenten auswählt – nützlich für Agenten, die nur über explizite Benutzeranforderungen aufgerufen werden sollen, legen Sie infer: false fest:

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

Abhören von Sub-Agent-Ereignissen

Wenn ein Unter-Agent ausgeführt wird, löst die übergeordnete Sitzung Lebenszyklusereignisse aus. Abonnieren Sie diese Ereignisse, um UIs zu erstellen, die Agentaktivitäten visualisieren.

Ereignistypen

EventWird ausgegeben, wennDaten
subagent.selectedLaufzeit wählt einen Agent für die Aufgabe aus.
agentName, agentDisplayName``tools
subagent.startedDer Unter-Agent beginnt mit der Ausführung.
toolCallId, agentName, agentDisplayName, agentDescription
subagent.completedUnteragent ist erfolgreich abgeschlossen.
toolCallId, agentName``agentDisplayName
subagent.failedBei einem Unteragent tritt ein Fehler auf.
toolCallId, agentName, agentDisplayName, error
subagent.deselectedDie Laufzeitumgebung wechselt vom Subagenten ab.

Abonnieren von Ereignissen

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

Erstellen einer Agentstruktur-UI

Sub-Agent-Ereignisse umfassen toolCallId Felder, mit denen Sie den Ausführungsbaum rekonstruieren können. Hier ist ein Muster für die Nachverfolgung von Agentaktivitäten:

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

Bereichsdefinitionstools pro Agent

Verwenden Sie die tools Eigenschaft, um einzuschränken, auf welche Tools ein Agent zugreifen kann. Dies ist wesentlich für die Sicherheit und dafür, dass Agenten fokussiert bleiben.

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

Hinweis

Wenn tools``null ist oder weggelassen wird, erbt der Agent den Zugriff auf alle in der Sitzung konfigurierten Tools. Verwenden Sie explizite Toollisten, um das Prinzip der geringsten Berechtigungen zu erzwingen.

Exklusive Tools für Agenten

Verwenden Sie die Eigenschaft defaultAgent in der Sitzungskonfiguration, um bestimmte Tools für den Standard-Agenten auszublenden (den integrierten Agenten, der die Interaktionsrunden verarbeitet, wenn kein benutzerdefinierter Agent ausgewählt ist). Dadurch wird der Haupt-Agent gezwungen, sich an Unter-Agents zu delegieren, wenn die Funktionen dieser Tools benötigt werden, damit der Kontext des Haupt-Agents sauber bleibt.

Diese Möglichkeit ist in folgenden Situationen nützlich:

  • Bestimmte Tools generieren große Kontextmengen, die den Hauptagenten überwältigen würden
  • Sie möchten, dass der Hauptagent als Orchestrator fungiert und schwere Arbeit an spezialisierte Sub-Agents delegiert.
  • Sie benötigen eine strikte Trennung zwischen Orchestrierung und Ausführung
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.",
        },
    ],
});

So funktioniert es

In defaultAgent.excludedTools aufgeführte Tools:

  1. Sind registriert – ihre Handler sind für die Ausführung verfügbar.
  2. Sind in der Toolliste des Haupt-Agents ausgeblendet – die LLM wird sie nicht direkt sehen oder anrufen.
  3. Bleiben Sie für jeden benutzerdefinierten Unter-Agent verfügbar, der sie in das tools Array einschließt.

Interaktion mit anderen Toolfiltern

defaultAgent.excludedTools ist orthogonal zu availableTools und excludedTools auf Sitzungsebene:

FilterGeltungsbereichAuswirkung
availableToolsSitzungsweitZulassungsliste – nur diese Tools sind für jeden vorhanden.
excludedToolsSitzungsweitSperrliste – diese Tools werden für alle blockiert.
defaultAgent.excludedToolsNur HauptagentDiese Werkzeuge sind für den Hauptagenten verborgen, aber für Unteragenten verfügbar

Vorrang:

  1. Sitzungsebene availableTools/excludedTools wird zuerst (global) angewendet.
  2. defaultAgent.excludedTools wird zusätzlich angewendet, wodurch nur der Hauptagent weiter eingeschränkt wird.

Hinweis

Wenn sich ein Tool sowohl in excludedTools (auf Sitzungsebene) als auch in defaultAgent.excludedTools befindet, hat der Ausschluss auf Sitzungsebene Vorrang – das Tool ist für niemanden verfügbar.

Anfügen von MCP-Servern an Agenten

Jeder benutzerdefinierte Agent kann über eigene MCP-Server (Model Context Protocol) verfügen, sodass er Zugriff auf spezialisierte Datenquellen erhält:

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

Muster und bewährte Methoden

Koppeln eines Forschers mit einem Editor

Ein gängiges Muster besteht darin, einen schreibgeschützten Recherche-Agent und einen schreibfähigen Editor-Agent zu definieren. Die Laufzeit delegiert Explorationsaufgaben an den Forscher und Änderungsaufgaben an den 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.",
    },
]

Agentbeschreibungen spezifisch beibehalten

Die Laufzeit verwendet description, um die Absicht des Benutzers zu erkennen. Vage Beschreibungen führen zu einer schlechten Aufgabenzuweisung.

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

Fehler geschickt bewältigen

Unter-Agents können fehlschlagen. Achten Sie immer auf subagent.failed Ereignisse, und behandeln Sie sie in Ihrer Anwendung:

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