תיעוד 181
סוכני משנה ב-SDK
הגדירו והפעילו סוכני משנה כדי לבודד הקשר, להריץ משימות במקביל, ולהחיל הנחיות מתמחות ביישומי Claude Agent SDK שלכם.
סוכני משנה הם מופעי סוכן נפרדים שהסוכן הראשי שלכם יכול ליצור כדי לטפל בתת משימות ממוקדות. השתמשו בהם כדי לבודד הקשר, להריץ כמה ניתוחים במקביל, ולהחיל הנחיות מתמחות בלי להוסיף להנחיה של הסוכן הראשי.
#סקירה
אפשר ליצור סוכני משנה בשלוש דרכים:
- תכנותית: השתמשו בפרמטר
agentsבאפשרויות שלquery(). ראו את ההפניות לTypeScript ולPython - מבוססת מערכת קבצים: הגדירו סוכנים כקבצי markdown בתיקיות
.claude/agents/. ראו הגדרת סוכני משנה כקבצים - מטרה כללית מובנית: Claude יכול להפעיל את סוכן המשנה המובנה
general-purposeבכל עת דרך כלי Agent בלי שתגדירו דבר
המדריך הזה מתמקד בגישה התכנותית, המומלצת ליישומי SDK.
#יתרונות השימוש בסוכני משנה
מכיוון שסוכני משנה הם מופעי סוכן נפרדים, האצלת עבודה אליהם נותנת ארבעה יתרונות:
- בידוד הקשר: כל סוכן משנה רץ בשיחה משלו, שמתחילה מחדש אלא אם סוכן המשנה הוא fork. כך או כך, קריאות לכלי ותוצאות ביניים נשארות בתוך סוכן המשנה. רק ההודעה הסופית שלו חוזרת להורה. סוכן משנה
research-assistantיכול לחקור עשרות קבצים בלי שתוכן זה יצטבר בשיחה הראשית. ההורה מקבל סיכום תמציתי, לא כל קובץ שסוכן המשנה קרא. ראו מה סוכני משנה יורשים לגבי מה בדיוק נמצא בהקשר של סוכן המשנה. - מקביליות: כמה סוכני משנה יכולים לרוץ בו זמנית, כך שתת משימות בלתי תלויות מסתיימות בזמן של האיטית ביותר, במקום בסכום של כולן. במהלך סקירת קוד אפשר להריץ את סוכני המשנה
style-checker,security-scannerו-test-coverageבו זמנית במקום ברצף. - הנחיות וידע מתמחים: לכל סוכן משנה יכולה להיות הנחיית מערכת מותאמת עם מומחיות ספציפית, שיטות עבודה מומלצות ואילוצים. לסוכן משנה
database-migrationיכול להיות ידע מפורט על שיטות עבודה מומלצות ב-SQL, אסטרטגיות rollback ובדיקות שלמות נתונים, שהיו רעש מיותר בהנחיות של הסוכן הראשי. - הגבלות כלים: אפשר להגביל סוכני משנה לכלים ספציפיים, וכך להקטין את הסיכון לפעולות לא מכוונות. לסוכן משנה
doc-reviewerיכולה להיות גישה רק לכלי Read ו-Grep, כך שהוא יכול לנתח אבל לא לשנות בטעות את קבצי התיעוד.
#יצירת סוכני משנה
#הגדרה תכנותית (מומלץ)
הגדירו סוכני משנה ישירות בקוד באמצעות הפרמטר agents. Claude מפעיל סוכני משנה דרך הכלי Agent.
רוב הדוגמאות בדף הזה מדפיסות רק את התוצאה הסופית. כדי לאשר ש-Claude האציל לסוכן משנה במקום לענות ישירות, ראו זיהוי הפעלה של סוכן משנה.
הדוגמה הזו יוצרת שני סוכני משנה: סוקר קוד עם גישה לקריאה בלבד, ומריץ בדיקות שיכול להריץ פקודות.
Python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
async def main():
async for message in query(
prompt="Review the authentication module for security issues",
options=ClaudeAgentOptions(
# Auto-approve these tools
allowed_tools=["Read", "Grep", "Glob", "Agent"],
agents={
"code-reviewer": AgentDefinition(
# description tells Claude when to use this subagent
description="Expert code review specialist. Use for quality, security, and maintainability reviews.",
# prompt defines the subagent's behavior and expertise
prompt="""You are a code review specialist with expertise in security, performance, and best practices.
When reviewing code:
- Identify security vulnerabilities
- Check for performance issues
- Verify adherence to coding standards
- Suggest specific improvements
Be thorough but concise in your feedback.""",
# tools restricts what the subagent can do (read-only here)
tools=["Read", "Grep", "Glob"],
# model overrides the default model for this subagent
model="sonnet",
),
"test-runner": AgentDefinition(
description="Runs and analyzes test suites. Use for test execution and coverage analysis.",
prompt="""You are a test execution specialist. Run tests and provide clear analysis of results.
Focus on:
- Running test commands
- Analyzing test output
- Identifying failing tests
- Suggesting fixes for failures""",
# Bash access lets this subagent run test commands
tools=["Bash", "Read", "Grep"],
),
},
),
):
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())TypeScript
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Review the authentication module for security issues",
options: {
// Auto-approve these tools
allowedTools: ["Read", "Grep", "Glob", "Agent"],
agents: {
"code-reviewer": {
// description tells Claude when to use this subagent
description:
"Expert code review specialist. Use for quality, security, and maintainability reviews.",
// prompt defines the subagent's behavior and expertise
prompt: `You are a code review specialist with expertise in security, performance, and best practices.
When reviewing code:
- Identify security vulnerabilities
- Check for performance issues
- Verify adherence to coding standards
- Suggest specific improvements
Be thorough but concise in your feedback.`,
// tools restricts what the subagent can do (read-only here)
tools: ["Read", "Grep", "Glob"],
// model overrides the default model for this subagent
model: "sonnet"
},
"test-runner": {
description:
"Runs and analyzes test suites. Use for test execution and coverage analysis.",
prompt: `You are a test execution specialist. Run tests and provide clear analysis of results.
Focus on:
- Running test commands
- Analyzing test output
- Identifying failing tests
- Suggesting fixes for failures`,
// Bash access lets this subagent run test commands
tools: ["Bash", "Read", "Grep"]
}
}
}
})) {
if ("result" in message) console.log(message.result);
}#הגדרת AgentDefinition
| שדה | סוג | חובה | תיאור |
|---|---|---|---|
description | string | כן | תיאור בשפה טבעית של מתי להשתמש בסוכן הזה |
prompt | string | כן | הנחיית המערכת של הסוכן, שמגדירה את התפקיד וההתנהגות שלו |
tools | string[] | לא | מערך של שמות כלים מותרים. אם משמיטים אותו, הוא יורש כל כלי שזמין לסוכני משנה |
disallowedTools | string[] | לא | מערך של שמות כלים להסרה מקבוצת הכלים של הסוכן. מתקבלות גם תבניות ברמת שרת MCP: mcp__server או mcp__server__* מסירים כל כלי מהשרת הזה, ו-mcp__* מסיר כל כלי MCP מכל שרת |
model | string | לא | דריסת מודל לסוכן הזה. מקבל כינוי כמו 'fable', 'opus', 'sonnet', 'haiku', 'inherit', או מזהה מודל מלא. 'inherit' משתמש במודל הראשי. כשמשמיטים אותו, Claude Code בוחר את המודל לפי סדר מודל סוכן המשנה |
skills | string[] | לא | רשימת שמות מיומנויות לטעינה מראש להקשר של הסוכן בהפעלה. לכל היותר 32 השמות הייחודיים הראשונים ברשימה נטענים מראש. מיומנויות שלא ברשימה נשארות ניתנות להפעלה דרך כלי Skill |
memory | 'user' | 'project' | 'local' | לא | מקור זיכרון לסוכן הזה |
mcpServers | (string | object)[] | לא | שרתי MCP הזמינים לסוכן הזה, לפי שם או הגדרה מוטמעת |
initialPrompt | string | לא | נשלח אוטומטית כתור המשתמש הראשון כשהסוכן הזה רץ כסוכן של השרשור הראשי. מתעלמים ממנו כשהסוכן מופעל כסוכן משנה |
maxTurns | number | לא | מספר מרבי של תורות סוכניים לפני שהסוכן עוצר. כשהסוכן מגיע למגבלה, Claude Code מחזיר את הפלט שלו מסומן כחלקי, ואפשר לחדש את הסוכן כדי להמשיך. הסימון החלקי דורש Claude Code מגרסה v2.1.246 ואילך |
autoCompactWindow | number | לא | הנמכת חלון הדחיסה האוטומטית לסוכן הזה כשהוא רץ כסוכן משנה, למספר טוקנים מ-100000 עד 1000000. דורש TypeScript Agent SDK מגרסה v0.3.296 ואילך. ל-AgentDefinition של Python SDK אין את השדה הזה |
background | boolean | לא | הרצת הסוכן הזה כמשימת רקע לא חוסמת כשהוא מופעל |
omitClaudeMd | boolean | לא | השארת קבצי CLAUDE.md של המשתמש, הפרויקט והמקומי מחוץ להקשר ההתחלתי של הסוכן הזה. קבצי מדיניות מנוהלים עדיין נטענים. מתעלמים מזה כשהסוכן רץ כסוכן של השרשור הראשי. דורש TypeScript Agent SDK מגרסה v0.3.271 ואילך. ל-AgentDefinition של Python SDK אין את השדה הזה |
effort | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | number | לא | רמת מאמץ החשיבה לסוכן הזה |
permissionMode | PermissionMode | לא | מצב הרשאות להרצת כלים בתוך הסוכן הזה. כללי הירושה של סוכן משנה קובעים מתי זה חל |
ב-Python SDK, שמות שדות מרובי מילים כמו disallowedTools ו-mcpServers שומרים על איות camelCase כדי להתאים לפורמט ה-wire, במקום מוסכמת snake_case של Python. ראו את ההפניה של AgentDefinition לפרטים.
#בחירה אם Claude ממתין לסוכני משנה
כברירת מחדל, סוכן משנה שהסוכן הראשי יוצר רץ ברקע. לתוצאת כלי Agent יש status: "async_launched", והדוח מגיע בתור מאוחר יותר.
Claude ממתין כשהפעולה הבאה שלו צריכה את הדוח: הקריאה נחסמת ומחזירה status: "completed". כדי לבחור זאת בעצמכם:
- בהנחיה: בקשו מ-Claude להמתין, למשל "run the test-runner agent and wait for it"
- בהגדרה: הגדירו את
backgroundל-trueכדי להשאיר את הסוכן הזה ברקע
#הגדרה מבוססת מערכת קבצים (חלופה)
אפשר גם להגדיר סוכני משנה כקבצי markdown בתיקיות .claude/agents/. ראו את תיעוד סוכני המשנה של Claude Code לפרטים על הגישה הזו. סוכנים שהוגדרו באופן תכנותי גוברים על סוכנים מבוססי מערכת קבצים עם אותו שם.
[!NOTE]
כש-Claude קורא לכלי Agent בלי
subagent_type, הוא מקבל את סוכן המשנה המובנהgeneral-purpose, ש-Claude יכול ליצור גם כשלא מגדירים סוכנים משלכם. הגדרתCLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS=1מסירה את ברירת המחדל הזו, וקריאה כזו נכשלת עםsubagent_type is required.
#מה סוכני משנה יורשים
אלא אם סוכן המשנה הוא fork, חלון ההקשר שלו מתחיל מחדש, בלי שיחת ההורה, אבל הוא לא ריק. התוכן היחיד שמעבירים מההורה לסוכן המשנה הוא מחרוזת ההנחיה של כלי Agent, לכן כללו בהנחיה הזו ישירות כל נתיבי קבצים, הודעות שגיאה או החלטות שסוכן המשנה צריך.
סוכן משנה שיש לו את הכלי SendMessage מתחיל עם רשימה של שאר הסוכנים בעלי השם שרצים בסשן, כך שהוא יודע לאילו שמות הוא יכול לשלוח הודעות. Claude Code מוסיף את הרשימה לתור הראשון של סוכן המשנה באופן אוטומטי. fork לא מקבל את הרשימה כי הוא יורש את שיחת ההורה.
סוכן משנה גם יורש את הגדרת החשיבה המורחבת של הסשן הראשי.
הטבלה למטה מפרטת מה ההקשר של סוכן משנה שאינו fork מכיל ומה הוא משמיט.
| סוכן המשנה מקבל | סוכן המשנה לא מקבל |
|---|---|
הנחיית המערכת שלו (AgentDefinition.prompt) והנחיית כלי Agent | היסטוריית השיחה של ההורה או תוצאות כלים |
CLAUDE.md של הפרויקט (נטען דרך settingSources). סוכן שמגדיר omitClaudeMd מתחיל בלעדיו | תוכן מיומנויות שנטען מראש, אלא אם הוא רשום ב-AgentDefinition.skills |
הגדרות כלים (בירושה מההורה או תת הקבוצה ב-tools, מסוננות להרצות רקע) | הנחיית המערכת של ההורה |
[!NOTE]
ההורה מקבל את הדוח הסופי של סוכן המשנה, אבל עשוי לסכם אותו בתשובה שלו. כדי לשמור את פלט סוכן המשנה כלשונו בתשובה שמוצגת למשתמש, כללו הנחיה לעשות זאת בהנחיה או באפשרות
systemPromptשאתם מעבירים לקריאתquery()הראשית.ב-v2.1.210 ואילך, Claude Code סורק את ההודעה הסופית לאיתור תבניות בצורת הנחיה לפני שההורה קורא אותה. הסריקה מטפלת בשלושה סוגי תבניות באופן שונה:
- חיקוי תגית בקרה: Claude Code מנטרל במקום תגית שרק ה-harness פולט, כמו בלוק
<system-reminder>. הוא מכניס לוכסן הפוך אחרי סוגר הזווית הפותח ולא מוחק דבר.- אזכורי הגדרת הרשאות: Claude Code שומר הפניות להגדרת ההרשאות, כמו
.claude/settings.json,bypassPermissionsאו--dangerously-skip-permissions, כפי שנכתבו.- סמני תור: שורה שמתחילה ב-
Human:אוAssistant:מקבלת לוכסן הפוך לפני הנקודתיים, כך שההודעה לא יכולה לחקות גבול של תור בשיחה.עבור התאמה של תגית בקרה או של הגדרת הרשאות, Claude Code מוסיף בהתחלה שורת סמן
[harness: ...]שמציינת את התבניות שהותאמו. התאמה של סמן תור לא מוסיפה את שורת הסמן. אלה השינויים היחידים שהסריקה עושה: היא אף פעם לא מסירה את הטקסט של סוכן המשנה ולא מנסחת אותו מחדש.
שגיאת API שמסיימת את סוכן המשנה מוקדם, כמו מגבלת קצב, אף פעם לא נמסרת כתוצאה שלו. ראו שגיאות API בסוכני משנה לגבי ההתנהגות בחזית וברקע.
#הפעלת סוכני משנה
#הפעלה אוטומטית
קלוד מחליט אוטומטית מתי להפעיל סוכני משנה לפי המשימה ולפי ה-description של כל סוכן משנה. לדוגמה, אם מגדירים סוכן משנה performance-optimizer עם התיאור "Performance optimization specialist for query tuning", קלוד יפעיל אותו כשהפרומפט מזכיר אופטימיזציה של שאילתות.
כתבו תיאורים ברורים ומדויקים כדי שקלוד יוכל להתאים משימות לסוכן המשנה הנכון.
#הפעלה מפורשת
כדי להבטיח שקלוד ישתמש בסוכן משנה מסוים, ציינו אותו בשמו בפרומפט:
"Use the code-reviewer agent to check the authentication module"זה עוקף את ההתאמה האוטומטית ומפעיל ישירות את סוכן המשנה ששמו צוין.
#סוכני משנה מסועפים
fork הוא סוכן משנה שמעתיק את השיחה עד כה במקום להתחיל מחדש. מ-Claude Code v2.1.302, קלוד יכול להתחיל forks באפליקציה בלי שום הגדרה, באמצעות קריאה לכלי Agent עם subagent_type: "fork".
כדי למנוע מקלוד להתחיל forks, הוסיפו כלל דחייה Agent(fork):
options = ClaudeAgentOptions(
# Deny only the fork subagent type; other subagents still run
disallowed_tools=["Agent(fork)"],
)const options = {
// Deny only the fork subagent type; other subagents still run
disallowedTools: ["Agent(fork)"]
};#הגדרת סוכן דינמית
אפשר ליצור הגדרות סוכן באופן דינמי לפי תנאי זמן ריצה. הדוגמה הזו יוצרת סוקר אבטחה עם רמות קפדנות שונות, ומשתמשת במודל בעל יכולת גבוהה יותר לסקירות קפדניות.
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
# Factory function that returns an AgentDefinition
# This pattern lets you customize agents based on runtime conditions
def create_security_agent(security_level: str) -> AgentDefinition:
is_strict = security_level == "strict"
return AgentDefinition(
description="Security code reviewer",
# Customize the prompt based on strictness level
prompt=f"You are a {'strict' if is_strict else 'balanced'} security reviewer...",
tools=["Read", "Grep", "Glob"],
# Key insight: use a more capable model for high-stakes reviews
model="opus" if is_strict else "sonnet",
)
async def main():
# The agent is created at query time, so each request can use different settings
async for message in query(
prompt="Review this PR for security issues",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Agent"],
agents={
# Call the factory with your desired configuration
"security-reviewer": create_security_agent("strict")
},
),
):
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())import { query, type AgentDefinition } from "@anthropic-ai/claude-agent-sdk";
// Factory function that returns an AgentDefinition
// This pattern lets you customize agents based on runtime conditions
function createSecurityAgent(securityLevel: "basic" | "strict"): AgentDefinition {
const isStrict = securityLevel === "strict";
return {
description: "Security code reviewer",
// Customize the prompt based on strictness level
prompt: `You are a ${isStrict ? "strict" : "balanced"} security reviewer...`,
tools: ["Read", "Grep", "Glob"],
// Key insight: use a more capable model for high-stakes reviews
model: isStrict ? "opus" : "sonnet"
};
}
// The agent is created at query time, so each request can use different settings
for await (const message of query({
prompt: "Review this PR for security issues",
options: {
allowedTools: ["Read", "Grep", "Glob", "Agent"],
agents: {
// Call the factory with your desired configuration
"security-reviewer": createSecurityAgent("strict")
}
}
})) {
if ("result" in message) console.log(message.result);
}#זיהוי הפעלה של סוכן משנה
קלוד מפעיל סוכני משנה דרך הכלי Agent. כדי לזהות מתי סוכן משנה מופעל, חפשו בלוקי tool_use שבהם name הוא "Agent".
הודעות מתוך ההקשר של סוכן משנה כוללות שדה parent_tool_use_id. ב-TypeScript, כל הודעת assistant והודעת user שסוכן משנה מייצר נושאות גם agent_id: ה-task_id של אירועי המשימה של אותו סוכן משנה. agent_id נשאר זהה כשמחדשים את סוכן המשנה. agent_id דורש TypeScript Agent SDK v0.3.292 או גרסה מאוחרת יותר.
[!NOTE]
הכלי מופיע כ-
"Agent"בבלוקיtool_use, אבל כ-"Task"ברשימת הכלים שלsystem:init. לפני Claude Code v2.1.63, גם בלוקיtool_useקראו לו"Task". כדי שהזיהוי ימשיך לעבוד בכל גרסאות ה-SDK, התאימו את שני הערכים ב-block.name.
מבנה ההודעה שונה בין ערכות ה-SDK. ב-Python ניגשים לבלוקי התוכן ישירות דרך message.content. ב-TypeScript, SDKAssistantMessage עוטף את הודעת ה-API של קלוד, ולכן ניגשים לתוכן דרך message.message.content.
הדוגמה הזו עוברת על הודעות בזרם, ורושמת מתי סוכן משנה מופעל ומתי הודעות בהמשך מגיעות מתוך הקשר ההרצה של אותו סוכן משנה. גרסת TypeScript רושמת גם את agent_id של כל הודעת סוכן משנה שנושאת אותו.
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition, ToolUseBlock
async def main():
async for message in query(
prompt="Use the code-reviewer agent to review this codebase",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Glob", "Grep", "Agent"],
agents={
"code-reviewer": AgentDefinition(
description="Expert code reviewer.",
prompt="Analyze code quality and suggest improvements.",
tools=["Read", "Glob", "Grep"],
)
},
),
):
# Check for subagent invocation. Match both names: older SDK
# versions emitted "Task", current versions emit "Agent".
if hasattr(message, "content") and message.content:
for block in message.content:
if isinstance(block, ToolUseBlock) and block.name in (
"Task",
"Agent",
):
print(f"Subagent invoked: {block.input.get('subagent_type')}")
# Check if this message is from within a subagent's context
if hasattr(message, "parent_tool_use_id") and message.parent_tool_use_id:
print(" (running inside subagent)")
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Use the code-reviewer agent to review this codebase",
options: {
allowedTools: ["Read", "Glob", "Grep", "Agent"],
agents: {
"code-reviewer": {
description: "Expert code reviewer.",
prompt: "Analyze code quality and suggest improvements.",
tools: ["Read", "Glob", "Grep"]
}
}
}
})) {
const msg = message as any;
// Check for subagent invocation. Match both names: older SDK versions
// emitted "Task", current versions emit "Agent".
for (const block of msg.message?.content ?? []) {
if (block.type === "tool_use" && (block.name === "Task" || block.name === "Agent")) {
console.log(`Subagent invoked: ${block.input.subagent_type}`);
}
}
// Check if this message is from within a subagent's context
if (msg.parent_tool_use_id) {
console.log(" (running inside subagent)");
// On assistant and user messages, agent_id matches the task_id
// on that subagent's task_started and other task events
if (msg.agent_id) {
console.log(` agent_id: ${msg.agent_id}`);
}
}
if ("result" in message) {
console.log(message.result);
}
}#חידוש סוכני משנה
אפשר לחדש סוכן משנה כדי להמשיך מהמקום שבו הוא עצר, במקום להתחיל מחדש. סוכן משנה מחודש שומר את כל היסטוריית השיחה, כולל כל קריאות הכלים הקודמות, התוצאות והחשיבה.
כשסוכן משנה נעצר במגבלת maxTurns, Claude Code מסמן את הפלט בתוצאת הכלי Agent כחלקי, כדי שקלוד יידע שההרצה לא הסתיימה.
כשסוכן משנה מסיים, תוצאת הכלי Agent כוללת בלוק טקסט שמכיל agentId: <id>. הסוכנים המובנים Explore ו-Plan הם חד-פעמיים ולא מחזירים agentId, ולכן השתמשו בסוכן מותאם אישית או ב-general-purpose כשצריך לחדש. כדי לחדש סוכן משנה באופן תכנותי:
- לכידת מזהה הסשן: חלצו את
session_idמההודעות במהלך השאילתה הראשונה - חילוץ מזהה הסוכן: נתחו את
agentIdמטקסט תוצאת הכלי Agent - חידוש הסשן: העבירו
resume: sessionIdבאפשרויות של השאילתה השנייה, וכללו את מזהה הסוכן בפרומפט. כל קריאה ל-query()מתחילה סשן חדש כברירת מחדל, וחייבים לחדש את אותו הסשן כדי לגשת לתמליל של סוכן המשנה.
[!NOTE]
כשמשתמשים בסוכן מותאם אישית, העבירו את אותה הגדרת סוכן בפרמטר
agentsבשתי השאילתות.
הדוגמה למטה מגדירה סוכן מותאם אישית endpoint-finder. השאילתה הראשונה מריצה אותו ולוכדת את מזהה הסשן ואת מזהה הסוכן מתוצאת הכלי Agent, ואז השאילתה השנייה מחדשת את הסשן כדי לשאול שאלת המשך שדורשת הקשר מהניתוח הראשון.
import asyncio
import re
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition, ToolResultBlock
AGENTS = {
"endpoint-finder": AgentDefinition(
description="Locates and catalogs API endpoints in a codebase.",
prompt="You find and document API endpoints. Report each endpoint's path, method, and handler.",
tools=["Read", "Grep", "Glob"],
)
}
def extract_agent_id(block: ToolResultBlock) -> str | None:
"""Extract agentId from an Agent tool result's text content."""
parts = block.content if isinstance(block.content, list) else [{"text": block.content}]
for part in parts:
if match := re.search(r"agentId:\s*([\w-]+)", part.get("text") or ""):
return match.group(1)
return None
async def main():
agent_id = None
session_id = None
# First invocation - run the endpoint-finder subagent
try:
async for message in query(
prompt="Use the endpoint-finder agent to find all API endpoints in this codebase",
options=ClaudeAgentOptions(allowed_tools=["Read", "Grep", "Glob", "Agent"], agents=AGENTS),
):
# Capture session_id from ResultMessage (needed to resume this session)
if hasattr(message, "session_id"):
session_id = message.session_id
# Search tool results for the agentId trailer
for block in getattr(message, "content", None) or []:
if isinstance(block, ToolResultBlock):
agent_id = extract_agent_id(block) or agent_id
# Print the final result
if hasattr(message, "result"):
print(message.result)
except Exception as error:
# A single-shot query() raises after yielding an error result,
# so session_id and agent_id have already been captured by the loop above.
print(f"Session ended with an error: {error}")
# Second invocation - resume and ask follow-up
if agent_id and session_id:
async for message in query(
prompt=f"Resume agent {agent_id} and list the top 3 most complex endpoints",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Agent"], agents=AGENTS, resume=session_id
),
):
if hasattr(message, "result"):
print(message.result)
else:
print("No agentId found in the first query, so there is no subagent to resume.")
asyncio.run(main())import { query, type SDKMessage } from "@anthropic-ai/claude-agent-sdk";
const agents = {
"endpoint-finder": {
description: "Locates and catalogs API endpoints in a codebase.",
prompt: "You find and document API endpoints. Report each endpoint's path, method, and handler.",
tools: ["Read", "Grep", "Glob"]
}
};
// Stringify content to search for agentId without traversing nested block types
function extractAgentId(message: SDKMessage): string | undefined {
if (message.type !== "assistant" && message.type !== "user") return undefined;
const content = JSON.stringify(message.message.content);
const match = content.match(/agentId:\s*([\w-]+)/);
return match?.[1];
}
let agentId: string | undefined;
let sessionId: string | undefined;
// First invocation - run the endpoint-finder subagent
try {
for await (const message of query({
prompt: "Use the endpoint-finder agent to find all API endpoints in this codebase",
options: { allowedTools: ["Read", "Grep", "Glob", "Agent"], agents }
})) {
// Capture session_id from ResultMessage (needed to resume this session)
if ("session_id" in message) sessionId = message.session_id;
// Search message content for the agentId (appears in Agent tool results)
const extractedId = extractAgentId(message);
if (extractedId) agentId = extractedId;
// Print the final result
if ("result" in message) console.log(message.result);
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// so sessionId and agentId have already been captured by the loop above.
console.error(`Session ended with an error: ${error}`);
}
// Second invocation - resume and ask follow-up
if (agentId && sessionId) {
for await (const message of query({
prompt: `Resume agent ${agentId} and list the top 3 most complex endpoints`,
options: { allowedTools: ["Read", "Grep", "Glob", "Agent"], agents, resume: sessionId }
})) {
if ("result" in message) console.log(message.result);
}
} else {
console.log("No agentId found in the first query, so there is no subagent to resume.");
}תמלילי סוכני משנה נשמרים בקבצים נפרדים ונשארים בנפרד מהשיחה הראשית. ראו חידוש סוכני משנה ב-Claude Code להתנהגות דחיסה ולתקופת הניקוי cleanupPeriodDays.
#הגבלות כלים
השתמשו בשדה tools כדי להגביל את מה שסוכן משנה יכול לעשות:
- השמטת
tools: סוכן המשנה מקבל כל כלי שזמין לסוכני משנה - רשימת כלים: סוכן המשנה מקבל רק אותם. סוקר קוד שאסור לו לערוך קבצים, לדוגמה, מקבל
["Read", "Grep", "Glob"]
כלי שמשמיטים אינו נמצא בסשן של סוכן המשנה בכלל: קלוד עובד בלעדיו, בלי בקשת הרשאה ובלי שגיאה.
הדוגמה הזו יוצרת סוכן ניתוח לקריאה בלבד, שיכול לבחון קוד אבל לא יכול לשנות קבצים או להריץ פקודות.
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
async def main():
async for message in query(
prompt="Analyze the architecture of this codebase",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Agent"],
agents={
"code-analyzer": AgentDefinition(
description="Static code analysis and architecture review",
prompt="""You are a code architecture analyst. Analyze code structure,
identify patterns, and suggest improvements without making changes.""",
# Read-only tools: no Edit, Write, or Bash access
tools=["Read", "Grep", "Glob"],
)
},
),
):
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Analyze the architecture of this codebase",
options: {
allowedTools: ["Read", "Grep", "Glob", "Agent"],
agents: {
"code-analyzer": {
description: "Static code analysis and architecture review",
prompt: `You are a code architecture analyst. Analyze code structure,
identify patterns, and suggest improvements without making changes.`,
// Read-only tools: no Edit, Write, or Bash access
tools: ["Read", "Grep", "Glob"]
}
}
}
})) {
if ("result" in message) console.log(message.result);
}#שילובי כלים נפוצים
| תרחיש שימוש | כלים | תיאור |
|---|---|---|
| ניתוח לקריאה בלבד | Read, Grep, Glob | יכול לבחון קוד אבל לא לשנות או להריץ |
| הרצת בדיקות | Bash, Read, Grep | יכול להריץ פקודות ולנתח פלט |
| שינוי קוד | Read, Edit, Write, Grep, Glob | גישת קריאה וכתיבה מלאה בלי הרצת פקודות |
| גישה מלאה | כל הכלים | יורש את הכלים הזמינים לסוכני משנה (השמטת שדה tools) |
#הגבלת עומק סוכני משנה, מקביליות והוצאה
[!NOTE]
הסעיף הזה מתאר את TypeScript SDK מגרסה v0.3.219 ואת Python SDK מגרסה v0.2.127 ואילך, הגרסאות שמצרפות את Claude Code מגרסה v2.1.219 ואילך. בגרסאות מוקדמות יותר חלק מהמגבלות האלה חסרות או שברירת המחדל שלהן שונה, לכן שדרגו לפני שאתם מסתמכים עליהן כדי לתחום הרצה. הפניה למשתני סביבה ותורות ותקציב מתעדים את גרסת Claude Code שהוסיפה כל משתנה, ואת אכיפת תקרת ההוצאה על סוכני משנה.
קלוד מחליט בעצמו מתי להפעיל סוכן משנה וכמה להפעיל. כל סוכן משנה שולח בקשות API משלו, והן נספרות ב total_cost_usd של השאילתה, וסוכן משנה יכול להפעיל סוכני משנה משלו, כך שפרומפט אחד יכול לגדול לעץ של סוכנים.
אפשר להגביל את הצמיחה הזו בשלוש דרכים: עד כמה סוכני משנה מקוננים, כמה רצים בו זמנית, וכמה כל השאילתה מוציאה. קבעו את מגבלות העומק והמקביליות כמשתני סביבה דרך האפשרות env, ואת מגבלת ההוצאה כאפשרות של השאילתה:
| מגבלה | איך קובעים | ברירת מחדל | מה Claude Code עושה במגבלה |
|---|---|---|---|
| עומק | CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH | 3 שכבות של סוכני משנה מתחת לסוכן הראשי שלכם. 1 עוצר את סוכני המשנה שלכם מלהפעיל סוכנים משלהם | משאיר סוכן משנה בשכבה התחתונה בלי יכולת להפעיל, כך שהוא עושה בעצמו את העבודה שהואצלה לו. ראו סוכני משנה מקוננים |
| מקביליות | CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS | 20 סוכני משנה שרצים בו זמנית, בספירה של כל סוכן משנה שקלוד מפעיל עם הכלי Agent | מסרב להפעיל סוכן משנה נוסף, ומחזיר Concurrent subagent limit reached, עד שמספר הרצים יורד מתחת למגבלה. סשנים שבהם ultracode פעיל לעולם לא נדחים. ראו מגבלת סוכני משנה מקבילים |
| הוצאה | maxBudgetUsd ב TypeScript, max_budget_usd ב Python | אין מגבלה. נספרת ההוצאה של הקריאה עצמה, כולל בקשות של סוכני משנה | אוכף את התקרה בשלוש דרכים: מסרב להפעיל סוכני משנה נוספים ומחזיר Budget limit reached, עוצר סוכני משנה ברקע שעדיין רצים, ומסיים את השאילתה עם תת הסוג error_max_budget_usd של התוצאה. לאופן שבו התקרות מתנהגות לאורך סשן, ראו תורות ותקציב |
שני ה SDK מתייחסים לאפשרות env באופן שונה: TypeScript SDK מחליף באמצעותה את סביבת תהליך המשנה, לכן פרסו לתוכה את process.env כדי לשמור משתנים כמו PATH, ואילו Python SDK ממזג אותה אל תוך הסביבה המורשת. הדוגמה הזו מכבה את הקינון, מתירה לכל היותר חמישה סוכני משנה בו זמנית, ועוצרת את השאילתה ברגע שההוצאה המשוערת מגיעה ל-$5:
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
try:
async for message in query(
prompt="Audit every service in this repo for unhandled promise rejections",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Agent"],
# env is merged on top of the inherited environment
env={
"CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH": "1",
"CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS": "5",
},
max_budget_usd=5.0,
),
):
if isinstance(message, ResultMessage):
print(f"{message.subtype}: ${message.total_cost_usd}")
except Exception as error:
# A single-shot query() raises after yielding an error result,
# so the budget-capped result has already been printed above.
print(f"Session ended with an error: {error}")
asyncio.run(main())import { query } from "@anthropic-ai/claude-agent-sdk";
try {
for await (const message of query({
prompt: "Audit every service in this repo for unhandled promise rejections",
options: {
allowedTools: ["Read", "Grep", "Glob", "Agent"],
// env replaces the subprocess environment, so spread process.env to keep PATH
env: {
...process.env,
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH: "1",
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS: "5",
},
maxBudgetUsd: 5,
},
})) {
if (message.type === "result") {
console.log(`${message.subtype}: $${message.total_cost_usd}`);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// so the budget-capped result has already been logged above.
console.error(`Session ended with an error: ${error}`);
}מה שתראו תלוי באיזו מגבלה, אם בכלל, השאילתה מגיעה אליה:
- מתחת לתקרת ההוצאה: תראו
successואת העלות המשוערת. - בתקרת ההוצאה: תראו
error_max_budget_usdעם עלות של5או מעליה, ואז רץ מטפל השגיאות שלכם. - במגבלת המקביליות: תראו בלוק
tool_resultבזרם ההודעות שנושא אתConcurrent subagent limit reached. קלוד מקבל את אותו בלוק כתוצאה של הכלי Agent.
#הרצת Opus 5 עם סוכני משנה
Claude Opus 5 מאציל לסוכני משנה ביתר קלות ממודלים קודמים, ולכן מגבלות העומק, המקביליות וההוצאה חשובות ביותר בשאילתות שמריצות את Opus 5. במדריך הפרומפטים של Opus 5 יש הוראת האצלה שאפשר להוסיף לכל פרומפט. אם Claude Code מוסיף הוראה משלו תלוי באיזה פרומפט מערכת אתם משתמשים:
- פריסט
claude_code: כשהמודל הוא Opus 5, Claude Code מוסיף שורה לפרומפט המערכת שלו שאומרת לקלוד לא לקרוא לכלי Agent אלא אם הוא מתבקש לכך. הכלי Agent נשאר זמין. - פרומפט מותאם, או בלי
systemPrompt: Claude Code לא בונה את פרומפט המערכת שלו, ולכן השורה הזו חסרה. הוסיפו את הוראת ההאצלה ממדריך הפרומפטים לפרומפט שלכם.
כל אחת משתי ההוראות רק מכוונת את קלוד, לכן קבעו גם את המגבלות. Claude Code אוכף אותן בכל אופן שבו קלוד מחליט להאציל.
#הגדלת קנה מידה עם תהליכי עבודה דינמיים
סוכני משנה מתאימים היטב לכמה משימות מואצלות בכל תור. להרצות שמתאמות עשרות עד מאות סוכנים, השתמשו בכלי Workflow, שמעביר את התזמור אל סקריפט שה runtime מריץ מחוץ להקשר השיחה. ראו תהליכי עבודה דינמיים במה תהליכי עבודה נבדלים מהאצלת סוכני משנה תור אחר תור.
הכלי Workflow זמין ב TypeScript Agent SDK מגרסה v0.3.149 ואילך. כללו את Workflow ב allowedTools כדי לאשר אוטומטית הרצות של תהליכי עבודה. סכמות הקלט והפלט של הכלי מפורטות בהפניה של TypeScript.
#פתרון בעיות
#קלוד לא מאציל לסוכני משנה
אם קלוד משלים משימות ישירות במקום להאציל לסוכן המשנה שלכם:
- השתמשו בפרומפט מפורש: ציינו את סוכן המשנה בשמו בפרומפט, לדוגמה "Use the code-reviewer agent to check the authentication module"
- כתבו תיאור ברור: הסבירו בדיוק מתי להשתמש בסוכן המשנה כדי שקלוד יוכל להתאים משימות כראוי
#סוכנים מבוססי מערכת קבצים לא נטענים
Claude Code עוקב אחרי ~/.claude/agents/ ו .claude/agents/ וקולט קובץ סוכן חדש או ערוך בתוך כמה שניות, בלי צורך בהפעלה מחדש. אם הגדרה לא מופיעה אף פעם, עברו על הסיבות האלה:
- ספריית
agentsחדשה: הצופה מכסה רק ספריות שהיו קיימות כשהסשן התחיל, ולכן הקובץ הראשון בספרייה חדשה דורש הפעלה מחדש של הסשן. זו הסיבה הנפוצה ביותר. - frontmatter לא תקין או
nameכפול: בדקו את ה YAML של הקובץ, ואם סוכן קיים כבר משתמש בname. --disable-slash-commands: סשנים שהתחילו עם הדגל הזה לא עוקבים אחרי הספריות האלה ותמיד צריכים הפעלה מחדש כדי לטעון קבצים חדשים.- קובץ תחת ספרייה שנוספה: Claude Code טוען את
.claude/agents/מספריות שנוספו עם האפשרותadd_dirs(Python) אוadditionalDirectories(TypeScript), או עם--add-dirאו/add-dirשל ה CLI, אבל לא עוקב אחריהן, ולכן קובץ חדש או ערוך שם דורש הפעלה מחדש של הסשן. - סוכן פרוגרמטי עם אותו שם:
agentsשמועבר לquery()דורס סוכן מבוסס מערכת קבצים עם אותו שם.
לפורמט הקובץ, ראו איך לכתוב קבצי סוכן משנה.
#תיעוד קשור
- סוכני משנה של Claude Code: תיעוד סוכני משנה כולל הגדרות מבוססות מערכת קבצים
- תהליכי עבודה דינמיים: תזמור סוכני משנה רבים מסקריפט לעבודות גדולות מדי לשיחה אחת
- סקירת ה SDK: תחילת עבודה עם Claude Agent SDK