Overview
カスタム エージェントは、セッションにアタッチする軽量のエージェント定義です。 各エージェントには、独自のシステム プロンプト、ツールの制限、およびオプションの MCP サーバーがあります。 ユーザーの要求がエージェントの専門知識と一致すると、Copilot ランタイムは自動的にそのエージェントに sub-agent として委任し、ライフサイクル イベントを親セッションにストリーミングしながら分離されたコンテキストで実行します。

| 概念 | Description |
|---|---|
| カスタム エージェント | 独自のプロンプトとツール セットを含む名前付きエージェント構成 |
| サブエージェント | タスクの一部を処理するためにランタイムによって呼び出されるカスタム エージェント |
| 推論 | ユーザーの意図に基づいてエージェントを自動選択するランタイムの機能 |
| 親セッション | サブエージェントを生成したセッション。は、すべてのライフサイクル イベントを受信します。 |
カスタム エージェントの定義
セッションの作成時に customAgents 渡します。 各エージェントには、少なくとも name と 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();
}
構成参照
| 財産 | タイプ | 必須 | Description |
|---|---|---|---|
name | string | ✅ | エージェントのユニークID |
displayName | string | ||
| イベントに表示される人間が判読できる名前 | |||
description | string | ||
| エージェントが実行すること - ランタイムがエージェントを選択するのを支援します | |||
tools | |||
string[] または null | |||
| エージェントが使用できるツール名。 | |||
null または省略 = すべてのツール | |||
prompt | string | ✅ | エージェントのシステム プロンプト |
mcpServers | object | ||
| このエージェントに固有の MCP サーバー構成 | |||
infer | boolean | ||
ランタイムがこのエージェントを自動選択できるかどうか (既定値: true) | |||
skills | string[] | ||
| 起動時にエージェントのコンテキストにプリロードするスキル名 |
ヒント
適切な description は、ランタイムがユーザーの意図を適切なエージェントと照合するのに役立ちます。 エージェントの専門知識と機能について具体的に説明します。
上記のエージェントごとの構成に加えて、agent自体の**** を設定して、セッションの開始時にアクティブなカスタム エージェントを事前に選択できます。 以下の 「セッション作成時のエージェントの選択 」を参照してください。
| セッション構成プロパティ | タイプ | Description |
|---|---|---|
agent | string | セッションの作成時に事前に選択するカスタム エージェントの名前。 |
nameのcustomAgentsと一致する必要があります。 |
エージェントごとのスキル
skills プロパティを使用して、エージェントのコンテキストにスキルを事前に読み込むことができます。 指定すると、リストされている各スキルの 完全なコンテンツ が起動時にエージェントのコンテキストに熱心に挿入されます。エージェントはスキル ツールを呼び出す必要はありません。手順は既に存在します。 スキルは オプトインです。エージェントは既定でスキルを受け取らず、サブエージェントは親からスキルを継承しません。 スキル名は、セッション レベルの skillDirectoriesから解決されます。
const session = await client.createSession({
skillDirectories: ["./skills"],
customAgents: [
{
name: "security-auditor",
description: "Security-focused code reviewer",
prompt: "Focus on OWASP Top 10 vulnerabilities",
skills: ["security-scan", "dependency-check"],
},
{
name: "docs-writer",
description: "Technical documentation writer",
prompt: "Write clear, concise documentation",
skills: ["markdown-lint"],
},
],
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
この例では、 security-auditor は security-scan で始まり、 dependency-check はコンテキストに既に挿入されていますが、 docs-writer は markdown-lintで始まります。
skillsフィールドを持たないエージェントは、スキルコンテンツを受け取らない。
セッション作成時のエージェントの選択
セッション構成で agent を渡して、セッションの開始時にアクティブにするカスタム エージェントを事前に選択できます。 この値は、nameで定義されているいずれかのエージェントのcustomAgentsと一致する必要があります。
これは、作成後に session.rpc.agent.select() を呼び出すことと同じですが、追加の API 呼び出しを回避し、最初のプロンプトからエージェントがアクティブになっていることを確認します。
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();
サブエージェントの委任のしくみ
カスタム エージェントとのセッションにプロンプトを送信すると、ランタイムはサブエージェントに委任するかどうかを評価します。
- 意図の照合 - ランタイムは、ユーザーのプロンプトを各エージェントの
nameおよびdescriptionと比較し分析します。 - エージェントの選択 - 一致するものが見つかり、
inferがfalseされていない場合、ランタイムはエージェントを選択します。 - 分離実行 - サブエージェントは、独自のプロンプトと制限付きツール セットを使用して実行されます
- イベント ストリーミング - ライフサイクル イベント (
subagent.started、subagent.completedなど) が親セッションにストリームバックされます - 結果の統合 - サブエージェントの出力が親エージェントの応答に組み込まれます
推論の制御
既定では、すべてのカスタム エージェントを自動選択 (infer: true) で使用できます。
infer: falseを設定して、ランタイムがエージェントを自動選択できないようにします。明示的なユーザー要求によってのみ呼び出すエージェントに役立ちます。
{
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
}
サブエージェント イベントの監視
サブエージェントを実行すると、親セッションはライフサイクル イベントを生成します。 これらのイベントをサブスクライブして、エージェント アクティビティを視覚化する UI を構築します。
イベントの種類
| イベント | 次の場合に生成されます | データ |
|---|---|---|
subagent.selected | ランタイムがタスクのエージェントを選択する | |
agentName、agentDisplayName、tools | ||
subagent.started | サブエージェントが実行を開始する | |
toolCallId、agentName、agentDisplayName、agentDescription | ||
subagent.completed | サブエージェントが正常に終了する | |
toolCallId、agentName、agentDisplayName | ||
subagent.failed | サブエージェントでエラーが発生する | |
toolCallId、agentName、agentDisplayName、error | ||
subagent.deselected | ランタイムがサブエージェントから他のプロセスに移行する | — |
イベントに登録する
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();
エージェント ツリー UI の構築
サブエージェント イベントには、実行ツリーを再構築できる toolCallId フィールドが含まれます。 エージェント アクティビティを追跡するためのパターンを次に示します。
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);
});
エージェントごとのスコープ ツール
tools プロパティを使用して、エージェントがアクセスできるツールを制限します。 これは、セキュリティとエージェントの集中を維持するために不可欠です。
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.",
},
],
});
メモ
toolsがnullまたは省略されると、エージェントはセッションで構成されているすべてのツールへのアクセスを継承します。 明示的なツール リストを使用して、最小限の特権の原則を適用します。
エージェント専用ツール
セッション構成の defaultAgent プロパティを使用して、既定のエージェント (カスタム エージェントが選択されていないときにターンを処理する組み込みエージェント) から特定のツールを非表示にします。 これにより、これらのツールの機能が必要になったときにメイン エージェントがサブエージェントに委任され、メイン エージェントのコンテキストがクリーンな状態が維持されます。
これは、次の場合に役立ちます。
- 特定のツールは、メイン エージェントを圧倒する大量のコンテキストを生成します
- メイン エージェントをオーケストレーターとして機能させ、特殊なサブエージェントに重い作業を委任する
- オーケストレーションと実行を厳密に分離する必要がある
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.",
},
],
});
どのように機能するのか
defaultAgent.excludedToolsに一覧表示されているツール:
- 登録済みであり、ハンドラーを実行できます
- メイン エージェントのツール リストから非表示になります。LLM はそれらを直接表示または呼び出しません
- それらを**** 配列に含む任意のカスタム サブエージェントで
tools
他のツールフィルターとの連携
defaultAgent.excludedTools はセッション レベルの availableTools と excludedToolsに直交します。
| Filter | Scope | 影響 |
|---|---|---|
availableTools | セッション全体 | Allowlist - すべてのユーザーに対してこれらのツールのみが存在します |
excludedTools | セッション全体 | ブロックリスト - すべてのユーザーに対してこれらのツールがブロックされます |
defaultAgent.excludedTools | メイン エージェントのみ | これらのツールはメイン エージェントには表示されませんが、サブエージェントで使用できます |
優先順位:
- セッション レベルの
availableTools/excludedToolsが最初に適用されます (グローバルに) defaultAgent.excludedToolsが上に適用され、メイン エージェントのみがさらに制限されます
メモ
ツールが excludedTools (セッション レベル) と defaultAgent.excludedToolsの両方にある場合、セッション レベルの除外が優先されます。ツールはすべてのユーザーが使用できません。
エージェントへの MCP サーバーのアタッチ
各カスタム エージェントは独自の MCP (モデル コンテキスト プロトコル) サーバーを持つ可能性があり、特殊なデータ ソースにアクセスできます。
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"],
},
},
},
],
});
パターンとベスト プラクティス
研究者と編集者のペアリング
一般的なパターンは、読み取り専用の研究者エージェントと書き込み可能なエディター エージェントを定義することです。 ランタイムは探索タスクを研究者に委任し、変更タスクをエディターに委任します。
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.",
},
]
エージェントの説明を固有に保つ
ランタイムは、 description を使用してユーザーの意図と一致します。 あいまいな説明は、委任が不適切になります。
// ❌ 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" }
エラーを適切に処理する
サブエージェントは失敗する可能性があります。 常に subagent.failed イベントをリッスンし、アプリケーションで処理します。
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
}
});