מדריך קלוד קוד בעברית

תיעוד 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

שדהסוגחובהתיאור
descriptionstringכןתיאור בשפה טבעית של מתי להשתמש בסוכן הזה
promptstringכןהנחיית המערכת של הסוכן, שמגדירה את התפקיד וההתנהגות שלו
toolsstring[]לאמערך של שמות כלים מותרים. אם משמיטים אותו, הוא יורש כל כלי שזמין לסוכני משנה
disallowedToolsstring[]לאמערך של שמות כלים להסרה מקבוצת הכלים של הסוכן. מתקבלות גם תבניות ברמת שרת MCP: mcp__server או mcp__server__* מסירים כל כלי מהשרת הזה, ו-mcp__* מסיר כל כלי MCP מכל שרת
modelstringלאדריסת מודל לסוכן הזה. מקבל כינוי כמו 'fable', 'opus', 'sonnet', 'haiku', 'inherit', או מזהה מודל מלא. 'inherit' משתמש במודל הראשי. כשמשמיטים אותו, Claude Code בוחר את המודל לפי סדר מודל סוכן המשנה
skillsstring[]לארשימת שמות מיומנויות לטעינה מראש להקשר של הסוכן בהפעלה. לכל היותר 32 השמות הייחודיים הראשונים ברשימה נטענים מראש. מיומנויות שלא ברשימה נשארות ניתנות להפעלה דרך כלי Skill
memory'user' | 'project' | 'local'לאמקור זיכרון לסוכן הזה
mcpServers(string | object)[]לאשרתי MCP הזמינים לסוכן הזה, לפי שם או הגדרה מוטמעת
initialPromptstringלאנשלח אוטומטית כתור המשתמש הראשון כשהסוכן הזה רץ כסוכן של השרשור הראשי. מתעלמים ממנו כשהסוכן מופעל כסוכן משנה
maxTurnsnumberלאמספר מרבי של תורות סוכניים לפני שהסוכן עוצר. כשהסוכן מגיע למגבלה, Claude Code מחזיר את הפלט שלו מסומן כחלקי, ואפשר לחדש את הסוכן כדי להמשיך. הסימון החלקי דורש Claude Code מגרסה v2.1.246 ואילך
autoCompactWindownumberלאהנמכת חלון הדחיסה האוטומטית לסוכן הזה כשהוא רץ כסוכן משנה, למספר טוקנים מ-100000 עד 1000000. דורש TypeScript Agent SDK מגרסה v0.3.296 ואילך. ל-AgentDefinition של Python SDK אין את השדה הזה
backgroundbooleanלאהרצת הסוכן הזה כמשימת רקע לא חוסמת כשהוא מופעל
omitClaudeMdbooleanלאהשארת קבצי CLAUDE.md של המשתמש, הפרויקט והמקומי מחוץ להקשר ההתחלתי של הסוכן הזה. קבצי מדיניות מנוהלים עדיין נטענים. מתעלמים מזה כשהסוכן רץ כסוכן של השרשור הראשי. דורש TypeScript Agent SDK מגרסה v0.3.271 ואילך. ל-AgentDefinition של Python SDK אין את השדה הזה
effort'low' | 'medium' | 'high' | 'xhigh' | 'max' | numberלארמת מאמץ החשיבה לסוכן הזה
permissionModePermissionModeלאמצב הרשאות להרצת כלים בתוך הסוכן הזה. כללי הירושה של סוכן משנה קובעים מתי זה חל

ב-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 כשצריך לחדש. כדי לחדש סוכן משנה באופן תכנותי:

  1. לכידת מזהה הסשן: חלצו את session_id מההודעות במהלך השאילתה הראשונה
  2. חילוץ מזהה הסוכן: נתחו את agentId מטקסט תוצאת הכלי Agent
  3. חידוש הסשן: העבירו 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_DEPTH3 שכבות של סוכני משנה מתחת לסוכן הראשי שלכם. 1 עוצר את סוכני המשנה שלכם מלהפעיל סוכנים משלהםמשאיר סוכן משנה בשכבה התחתונה בלי יכולת להפעיל, כך שהוא עושה בעצמו את העבודה שהואצלה לו. ראו סוכני משנה מקוננים
מקביליותCLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS20 סוכני משנה שרצים בו זמנית, בספירה של כל סוכן משנה שקלוד מפעיל עם הכלי 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() דורס סוכן מבוסס מערכת קבצים עם אותו שם.

לפורמט הקובץ, ראו איך לכתוב קבצי סוכן משנה.

#תיעוד קשור