- סוכן Claude Agent SDK עובד שמקבל שאלות ומחזיר תשובות
- 3 כלים מותאמים אישית (מחשבון, קורא קבצים, חיפוש) מחוברים לסוכן
- מערכת sub-agents עם handoffs — סוכן מנתב שמעביר לסוכנים מומחים
- Guardrails לזיהוי PII ופילטור תוכן
- Streaming output שמציג תשובות בזמן אמת
- Tracing מופעל לכל הרצה עם debugging מלא
- סוכן production-ready מלא עם כל הרכיבים משולבים
- תוכלו להתקין ולהגדיר את Claude Agent SDK ב-Python וב-TypeScript
- תוכלו לבנות סוכן עם כלים מותאמים אישית — כולל type hints, docstrings ו-error handling
- תוכלו לבנות מערכת multi-agent עם sub-agents ו-handoffs
- תוכלו להוסיף guardrails לקלט ולפלט — PII detection, content filtering, cost limits
- תוכלו להפעיל streaming ולבנות חוויית משתמש בזמן אמת
- תוכלו לחבר MCP servers לסוכן ולהשתמש בכלים חיצוניים
- תוכלו לנתח traces ולעשות debug לסוכנים שמתנהגים לא כצפוי
- פרקים קודמים: פרק 1 (מה זה סוכן AI ולולאת ReAct), פרק 2 (ארכיטקטורה ודפוסי עיצוב), פרק 3 (כלים ו-MCP), פרק 4 (סביבת פיתוח מוגדרת)
- מה תצטרכו: Python 3.11+ עם venv, Node.js 20+ עם TypeScript (אופציונלי), מפתח API של Anthropic (
ANTHROPIC_API_KEY) - ניסיון נדרש: ניסיון בסיסי ב-Python (functions, decorators, async/await), היכרות עם terminal
- זמן משוער: 4-5 שעות (כולל כל התרגילים)
- עלות: $5-15 (בהתאם לכמות ההרצות והמודל שנבחר)
בפרקים 1-4 למדת את הבסיס: מה זה סוכן, איך הוא בנוי, איך הוא מתחבר לכלים, ובנית סביבת פיתוח מלאה. עכשיו אנחנו בונים את הסוכן הראשון שלך. Claude Agent SDK הוא ה-SDK הרשמי של Anthropic — אותה טכנולוגיה שמפעילה את Claude Code. הסוכן שתבנה כאן ישמש בסיס להשוואה עם Vercel AI SDK (פרק 6), OpenAI Agents SDK (פרק 7), ו-Google ADK (פרק 8). מומלץ לבנות את אותה פונקציונליות בכל SDK כדי להשוות.
| מונח (English) | עברית | הסבר |
|---|---|---|
| Claude Agent SDK | ערכת פיתוח סוכני Claude | ה-SDK הרשמי של Anthropic לבניית סוכני AI. שכבה דקה מעל ה-API שמוסיפה agent loop, כלים, handoffs ו-guardrails |
| ClaudeSDKClient | לקוח SDK | המחלקה הראשית ב-SDK — מחברת לקוד שלכם את כל יכולות הסוכן של Claude |
| Tool (כלי) | כלי | פונקציית Python/TypeScript שהסוכן יכול להפעיל — מחשבון, קריאת קובץ, חיפוש ברשת, שליחת API |
| Handoff | העברה | מנגנון שמאפשר לסוכן אחד להעביר את השיחה לסוכן אחר — למשל מנתב שמעביר לסוכן טכני |
| Guardrail | מעקה בטיחות | בדיקת אימות שרצה על קלט או פלט — חוסמת תוכן לא רצוי, PII, או תשובות לא בטוחות |
| Streaming | הזרמה | קבלת תשובות טוקן-אחרי-טוקן בזמן אמת, במקום לחכות שכל התשובה תהיה מוכנה |
| RunContext | הקשר הרצה | אובייקט שנושא state משותף — חיבורי DB, העדפות משתמש, נתוני session — וזמין לכל הכלים והסוכנים |
| Trace | עקבה | רישום מפורט של כל הרצת סוכן: קריאות LLM, הפעלות כלים, handoffs, זמנים ועלויות |
| MCP Server | שרת MCP | תהליך שחושף כלים דרך פרוטוקול MCP — כל כלי ב-Claude Agent SDK רץ כ-MCP server פנימי |
| Agent Loop | לולאת סוכן | הלולאה הבסיסית: קבל הנחיה → חשוב → בחר כלי → הפעל → חשוב שוב → החזר תשובה (או כלי נוסף) |
| Extended Thinking | חשיבה מורחבת | יכולת ייחודית של Claude לחשוב "בקול" לפני שעונה — משפרת דיוק במשימות מורכבות |
| Tool Annotations | הערות כלי | מטא-דאטה על כלי: readOnlyHint, destructiveHint, idempotentHint — עוזרות ל-SDK להחליט על הרשאות |
סקירה ופילוסופיה — למה Claude Agent SDK
Claude Agent SDK (שנקרא בעבר Claude Code SDK) הוא ה-SDK הרשמי של Anthropic לבניית סוכני AI. זה לא עוד framework — זה אותו קוד שמפעיל את Claude Code עצמו, הכלי שמאות אלפי מפתחים משתמשים בו יום-יום. כשאתם בונים עם Claude Agent SDK, אתם מקבלים את אותה רמה של agent capabilities.
ה-SDK זמין ב-Python (גרסה 0.1.48 ומעלה) וב-TypeScript (גרסה 0.2.71 ומעלה) — שניהם production-ready.
הפילוסופיה של ה-SDK
ארבעה עקרונות מנחים את העיצוב:
- Safety-First (בטיחות קודם): כל הרצה בתוך sandbox, כלים עם הרשאות מוגדרות, guardrails מובנים
- Minimal Abstraction (מינימום הפשטה): שכבה דקה מעל ה-API — לא מסתיר את מה שקורה מאחורי הקלעים
- Composable Primitives (אבני בניין מורכבות): agents, tools, handoffs, guardrails — כל אחד עומד בפני עצמו ומשתלב עם האחרים
- Sandboxed Execution (הרצה מבודדת): קוד רץ בסביבה מבודדת — לא יכול לגרום נזק למערכת
מתי להשתמש ב-Claude Agent SDK
כן — השתמשו ב-Claude Agent SDK כשאתם:
- רוצים גישה ל-Claude Code toolset המלא (Read, Write, Edit, Bash, Glob, Grep, WebSearch)
- בונים סוכנים שעורכים קוד או מנהלים קבצים
- צריכים extended thinking לבעיות מורכבות
- צריכים computer use (שליטה בדפדפן/מסך)
- בונים מערכות security-critical שדורשות sandbox
לא — השתמשו בכלי אחר כשאתם:
- צריכים תמיכה ב-multi-provider (Claude + GPT + Gemini) — העדיפו Vercel AI SDK (פרק 6)
- רוצים הפשטות high-level של multi-agent orchestration — העדיפו LangGraph (פרק 8)
- צריכים רק API call פשוט בלי agent loop — השתמשו ב-Anthropic API ישירות
| תרחיש | כלי מתאים | למה |
|---|---|---|
| שאלה-תשובה פשוטה | Anthropic API | אין צורך ב-agent loop — API call אחד מספיק |
| סוכן עם 2-5 כלים | Claude Agent SDK | ה-SDK מנהל את הלולאה, בחירת כלים, error handling |
| סוכן שעורך קבצים | Claude Agent SDK | גישה ל-Claude Code toolset (Read, Write, Edit, Bash) |
| Multi-provider (Claude + GPT) | Vercel AI SDK | תמיכה ב-providers מרובים מובנית |
| סוכן עם extended thinking | Claude Agent SDK | תמיכה native ב-extended thinking של Claude |
| Orchestration מורכב של 10+ סוכנים | LangGraph | הפשטות graph-based למערכות מורכבות |
| Batch processing של 1000 בקשות | Anthropic Batch API | 50% הנחה, אין צורך ב-agent loop |
ארכיטקטורת ה-SDK
ה-SDK בנוי משלושה שכבות:
- ClaudeSDKClient — המחלקה הראשית. מתחברת ל-Anthropic API, מנהלת sessions, מריצה את ה-agent loop
- Tools כ-MCP Servers — כל כלי מותאם שאתם מגדירים רץ כ-in-process MCP server. אותו פרוטוקול מפרק 3, אבל מובנה ב-SDK
- Hooks System — מנגנון להתערבות בהתנהגות הסוכן: לפני/אחרי tool calls, לפני/אחרי תשובות LLM, ועוד
מה שמבדיל את ה-SDK מ-frameworks אחרים:
- Claude Code Toolset מלא: Read, Write, Edit, Bash, Glob, Grep, WebSearch — אותם כלים ש-Claude Code משתמש בהם
- Session Forking:
forkSession()— יוצרים עותק של session קיים ומריצים ניסויים בלי להשפיע על המקור - Structured Outputs עם Schema Validation: מגדירים JSON schema ומקבלים פלט מאומת
- MCP Tool Annotations:
readOnlyHint,destructiveHint,idempotentHint,openWorldHint— מטא-דאטה על מה שכלי עושה - Allowed/Disallowed Tools: שליטה גרנולרית באילו כלים הסוכן יכול להשתמש
- אין System Prompt דיפולטי: שליטה מלאה — אתם מחליטים בדיוק מה הסוכן יודע ומה לא
בדוק שה-SDK מותקן ומוכן:
# Python
pip install claude-agent-sdk
python -c "from claude_agent_sdk import ClaudeSDKClient; print('Claude Agent SDK installed!')"
# TypeScript (אופציונלי)
npm install @anthropic-ai/claude-agent-sdk
npx ts-node -e "import { ClaudeSDKClient } from '@anthropic-ai/claude-agent-sdk'; console.log('SDK ready!')"
אם קיבלת שגיאה — חזור לפרק 4, סעיף "התקנת SDKs". ודא ש-ANTHROPIC_API_KEY מוגדר ב-.env.
הסוכן הראשון שלך — Hello World
בואו נבנה את הסוכן הראשון. 5 שורות קוד — וכבר יש לנו סוכן Claude עובד.
Python — הסוכן הבסיסי
# first_agent.py
from claude_agent_sdk import ClaudeSDKClient
# יוצרים לקוח — קורא ANTHROPIC_API_KEY מ-env אוטומטית
client = ClaudeSDKClient()
# שולחים שאילתה — הסוכן חושב ועונה
result = client.query(
prompt="What is the capital of Israel?",
model="claude-sonnet-4-20250514" # אפשר גם claude-opus-4-20250514
)
# מדפיסים את התשובה
print(result.text)
# Output: The capital of Israel is Jerusalem.
# מידע על השימוש
print(f"Tokens used: {result.usage.input_tokens} in, {result.usage.output_tokens} out")
print(f"Cost: ~${(result.usage.input_tokens * 3 + result.usage.output_tokens * 15) / 1_000_000:.4f}")
מה קורה כאן:
ClaudeSDKClient()— יוצר חיבור ל-API. קורא אתANTHROPIC_API_KEYמ-environment variables באופן אוטומטיclient.query()— שולח prompt לסוכן, מריץ את ה-agent loop, מחזיר תוצאהresult.text— הטקסט הסופי שהסוכן מחזירresult.usage— מידע על צריכת טוקנים — חשוב לניטור עלויות
TypeScript — אותו סוכן
// first_agent.ts
import { ClaudeSDKClient } from '@anthropic-ai/claude-agent-sdk';
const client = new ClaudeSDKClient();
async function main() {
const result = await client.query({
prompt: "What is the capital of Israel?",
model: "claude-sonnet-4-20250514",
});
console.log(result.text);
console.log(`Tokens: ${result.usage.inputTokens} in, ${result.usage.outputTokens} out`);
}
main();
השגיאה הכי נפוצה: AuthenticationError: Missing API key. ה-SDK מחפש את המפתח ב-environment variable ANTHROPIC_API_KEY. ודאו שהוא מוגדר בקובץ .env ושאתם טוענים אותו (Python: python-dotenv, TypeScript: dotenv). אל תשימו את המפתח ישירות בקוד — לעולם!
הגדרת System Prompt
בניגוד ל-Claude Code, ב-SDK אין system prompt דיפולטי. אתם מגדירים בדיוק מה הסוכן יודע:
# agent_with_system_prompt.py
from claude_agent_sdk import ClaudeSDKClient
client = ClaudeSDKClient()
result = client.query(
prompt="מה המרחק בין תל אביב לירושלים?",
system_prompt="""אתה עוזר ישראלי ידידותי.
ענה תמיד בעברית.
תן תשובות קצרות ומדויקות.
אם אינך בטוח — אמור שאתה לא בטוח.""",
model="claude-sonnet-4-20250514"
)
print(result.text)
# Output: המרחק בין תל אביב לירושלים הוא כ-60 ק"מ בכביש (כ-70 דקות נסיעה ברכב).
בנה את הסוכן הראשון שלך:
- צור קובץ
first_agent.py(או.ts) - הדבק את הקוד למעלה
- הרץ:
python first_agent.py - אם קיבלת תשובה — מזל טוב! יש לך סוכן Claude עובד
- שנה את ה-prompt ל-3 שאלות שונות וראה את התשובות
עלות: ~$0.02 לכל 3 שאילתות.
Task Progress Events — מעקב בזמן אמת
כשסוכן רץ ברקע (למשל כ-sub-agent), אפשר לקבל עדכוני התקדמות:
# progress_events.py
from claude_agent_sdk import ClaudeSDKClient
client = ClaudeSDKClient()
# הרצה עם callback להתקדמות
def on_progress(event):
print(f"[{event.type}] {event.message}")
if event.usage:
print(f" Tokens so far: {event.usage.input_tokens} in, {event.usage.output_tokens} out")
result = client.query(
prompt="Analyze the pros and cons of microservices vs monoliths",
model="claude-sonnet-4-20250514",
on_progress=on_progress
)
הגדרת כלים — Tool Use
סוכן בלי כלים הוא בסך הכל chatbot. הכוח האמיתי של סוכנים הוא היכולת לעשות דברים — לחשב, לחפש, לקרוא קבצים, לקרוא ל-APIs. ב-Claude Agent SDK, כל כלי הוא פונקציית Python (או TypeScript) עם decorator פשוט.
כלי בסיסי — מחשבון
# tools_basic.py
from claude_agent_sdk import ClaudeSDKClient, tool
@tool
def calculator(expression: str) -> str:
"""
Evaluates a mathematical expression and returns the result.
Use this for any calculation the user needs.
Args:
expression: A mathematical expression like "2 + 2" or "sqrt(16) * 3"
"""
import math
# Whitelist of allowed operations for safety
allowed_names = {
k: v for k, v in math.__dict__.items()
if not k.startswith("__")
}
allowed_names.update({"abs": abs, "round": round})
try:
result = eval(expression, {"__builtins__": {}}, allowed_names)
return f"Result: {result}"
except Exception as e:
return f"Error calculating '{expression}': {str(e)}"
client = ClaudeSDKClient()
result = client.query(
prompt="What is 47 * 89 + sqrt(144)?",
tools=[calculator],
model="claude-sonnet-4-20250514"
)
print(result.text)
# The agent will call calculator("47 * 89 + sqrt(144)") -> "Result: 4195.0"
# Then respond: "47 * 89 + sqrt(144) = 4,195"
# בדיקה: אילו כלים הסוכן השתמש בהם
for call in result.tool_calls:
print(f"Tool: {call.name}, Input: {call.input}, Output: {call.output}")
מה שקורה מאחורי הקלעים:
- ה-
@tooldecorator קורא את ה-type hints ואת ה-docstring ויוצר JSON schema אוטומטי - ה-schema נשלח ל-Claude כחלק מה-API call
- Claude מחליט אם ומתי להפעיל את הכלי
- ה-SDK מפעיל את הפונקציה עם הפרמטרים ש-Claude בחר
- התוצאה חוזרת ל-Claude שמנסח תשובה סופית
ה-docstring הוא ההוראות ש-Claude מקבל על הכלי. בלי docstring טוב, Claude לא ידע מתי להשתמש בכלי. כתבו docstring שמסביר: (1) מה הכלי עושה, (2) מתי להשתמש בו, (3) מה כל פרמטר מצפה לקבל. docstring גרוע = סוכן שמשתמש בכלי הלא נכון.
כלי עם מספר פרמטרים
# tools_advanced.py
from claude_agent_sdk import ClaudeSDKClient, tool
from typing import Optional
from enum import Enum
class TemperatureUnit(str, Enum):
CELSIUS = "celsius"
FAHRENHEIT = "fahrenheit"
@tool
def convert_temperature(
value: float,
from_unit: TemperatureUnit,
to_unit: TemperatureUnit,
round_to: Optional[int] = 2
) -> str:
"""
Converts temperature between Celsius and Fahrenheit.
Args:
value: The temperature value to convert
from_unit: The unit to convert from (celsius or fahrenheit)
to_unit: The unit to convert to (celsius or fahrenheit)
round_to: Number of decimal places (default: 2)
"""
if from_unit == to_unit:
return f"{value} {from_unit.value} = {value} {to_unit.value} (same unit)"
if from_unit == TemperatureUnit.CELSIUS:
result = (value * 9/5) + 32
else:
result = (value - 32) * 5/9
result = round(result, round_to)
return f"{value}° {from_unit.value} = {result}° {to_unit.value}"
שימו לב: Optional[int] = 2 מייצר פרמטר אופציונלי עם default ב-schema. ו-Enum מייצר רשימת ערכים קבועה ש-Claude חייב לבחור מתוכה.
מספר כלים על סוכן אחד
# multi_tools_agent.py
from claude_agent_sdk import ClaudeSDKClient, tool
import json
import os
@tool
def calculator(expression: str) -> str:
"""Evaluates a mathematical expression. Use for calculations."""
import math
allowed = {k: v for k, v in math.__dict__.items() if not k.startswith("__")}
try:
return f"Result: {eval(expression, {'__builtins__': {}}, allowed)}"
except Exception as e:
return f"Error: {str(e)}"
@tool
def read_file(file_path: str) -> str:
"""
Reads and returns the contents of a file.
Use when the user asks to read, view, or analyze a file.
Args:
file_path: Path to the file to read
"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
if len(content) > 10000:
return content[:10000] + f"\n... (truncated, total {len(content)} chars)"
return content
except FileNotFoundError:
return f"Error: File '{file_path}' not found"
except Exception as e:
return f"Error reading file: {str(e)}"
@tool
def list_directory(path: str = ".") -> str:
"""
Lists files and directories in the given path.
Use when the user wants to see what files exist.
Args:
path: Directory path to list (default: current directory)
"""
try:
entries = os.listdir(path)
dirs = [f"📁 {e}" for e in entries if os.path.isdir(os.path.join(path, e))]
files = [f"📄 {e}" for e in entries if os.path.isfile(os.path.join(path, e))]
return "\n".join(dirs + files) or "Empty directory"
except Exception as e:
return f"Error: {str(e)}"
# סוכן עם 3 כלים — Claude בוחר איזה להפעיל
client = ClaudeSDKClient()
result = client.query(
prompt="List the files in the current directory, then read package.json if it exists",
tools=[calculator, read_file, list_directory],
model="claude-sonnet-4-20250514"
)
print(result.text)
# Claude will:
# 1. Call list_directory(".") to see what files exist
# 2. If package.json exists, call read_file("package.json")
# 3. Summarize the contents
Error Handling בכלים
שתי אסטרטגיות להתמודדות עם שגיאות:
# error_handling.py
from claude_agent_sdk import tool
# אסטרטגיה 1: החזרת string עם הודעת שגיאה
# Claude מקבל את ההודעה ויכול להסביר למשתמש
@tool
def safe_divide(a: float, b: float) -> str:
"""Divides a by b. Returns the result."""
if b == 0:
return "Error: Cannot divide by zero. Please provide a non-zero divisor."
return f"Result: {a / b}"
# אסטרטגיה 2: זריקת exception
# ה-SDK תופס את ה-exception ומעביר ל-Claude כשגיאה
@tool
def strict_divide(a: float, b: float) -> str:
"""Divides a by b. Raises error if b is zero."""
if b == 0:
raise ValueError("Division by zero is not allowed")
return f"Result: {a / b}"
מתי להשתמש במה? אסטרטגיה 1 (החזרת string) עדיפה ברוב המקרים — Claude מקבל מידע מלא ויכול להסביר למשתמש מה קרה. אסטרטגיה 2 (exception) מתאימה כשהשגיאה קריטית ואתם רוצים שהיא תופיע גם ב-traces.
הוסף כלי חדש לסוכן שלך:
- כתוב פונקציה
@toolשמחפשת מילה במילון (תחזיר הגדרה כלשהי) - הוסף אותה לרשימת ה-tools ב-
client.query() - שאל את הסוכן "What does 'ephemeral' mean and how many letters does it have?"
- בדוק ש-Claude משתמש גם במילון וגם במחשבון
- צור קובץ
multi_tool_agent.py - הגדר 3 כלים: (1)
calculator— חישובים מתמטיים, (2)read_file— קריאת קבצים, (3)web_search— מחזיר תוצאת חיפוש מדומה (mock) - חבר את שלושת הכלים לסוכן אחד
- שלח 3 שאילתות שונות: אחת שדורשת מחשבון, אחת שדורשת קריאת קובץ, ואחת שדורשת חיפוש
- הדפס אילו כלים הסוכן בחר בכל שאילתה
- הוסף error handling — מה קורה כשהקובץ לא קיים?
הצלחה: 3 שאילתות מצליחות, כל אחת משתמשת בכלי הנכון, ושגיאות מטופלות בצורה חלקה.
עלות: ~$0.20
Streaming ופלט בזמן אמת
בלי streaming, המשתמש מחכה 10-30 שניות לתשובה מלאה. עם streaming, הוא רואה את התשובה נכתבת בזמן אמת — חוויה הרבה יותר טובה.
Streaming בסיסי
# streaming_basic.py
from claude_agent_sdk import ClaudeSDKClient
client = ClaudeSDKClient()
# במקום query() שמחזיר תוצאה מלאה — stream() שמחזיר events
for event in client.stream(
prompt="Explain microservices architecture in 5 bullet points",
model="claude-sonnet-4-20250514"
):
if event.type == "text_delta":
print(event.text, end="", flush=True)
elif event.type == "tool_use":
print(f"\n[Using tool: {event.tool_name}]")
elif event.type == "done":
print(f"\n\n--- Done. Tokens: {event.usage.input_tokens} in, {event.usage.output_tokens} out ---")
Event Types — מה מגיע ב-stream
| Event Type | מתי | מה מכיל |
|---|---|---|
text_delta |
כל כמה מילים | event.text — טקסט חדש |
tool_use |
כשהסוכן מפעיל כלי | event.tool_name, event.tool_input |
tool_result |
כשכלי מחזיר תוצאה | event.tool_name, event.output |
thinking |
Extended thinking (אם מופעל) | event.text — מחשבות פנימיות |
done |
סוף ההרצה | event.usage — סיכום טוקנים |
TypeScript — Streaming
// streaming.ts
import { ClaudeSDKClient } from '@anthropic-ai/claude-agent-sdk';
const client = new ClaudeSDKClient();
async function streamAgent() {
const stream = client.stream({
prompt: "Explain the Israeli tech ecosystem",
model: "claude-sonnet-4-20250514",
});
for await (const event of stream) {
switch (event.type) {
case "text_delta":
process.stdout.write(event.text);
break;
case "tool_use":
console.log(`\n[Tool: ${event.toolName}]`);
break;
case "done":
console.log(`\n\nTokens: ${event.usage.inputTokens}/${event.usage.outputTokens}`);
break;
}
}
}
streamAgent();
Streaming עם כלים — Web UI
דוגמה לשרת FastAPI שמזרים תשובות סוכן ללקוח ב-Server-Sent Events:
# streaming_api.py
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from claude_agent_sdk import ClaudeSDKClient, tool
import json
app = FastAPI()
client = ClaudeSDKClient()
@tool
def get_weather(city: str) -> str:
"""Returns current weather for a city."""
# בפרודקשן — תחליפו ב-API אמיתי
return f"Weather in {city}: 25°C, sunny, humidity 45%"
@app.get("/chat")
async def chat(prompt: str):
async def event_stream():
for event in client.stream(
prompt=prompt,
tools=[get_weather],
model="claude-sonnet-4-20250514"
):
if event.type == "text_delta":
yield f"data: {json.dumps({'type': 'text', 'content': event.text})}\n\n"
elif event.type == "tool_use":
yield f"data: {json.dumps({'type': 'tool', 'name': event.tool_name})}\n\n"
elif event.type == "done":
yield f"data: {json.dumps({'type': 'done', 'usage': {'input': event.usage.input_tokens, 'output': event.usage.output_tokens}})}\n\n"
return StreamingResponse(event_stream(), media_type="text/event-stream")
הפוך את הסוכן שלך ל-streaming:
- שנה את
client.query()ל-client.stream() - הוסף loop שמדפיס כל
text_deltaevent - הרץ ושאל שאלה ארוכה — ראה את התשובה נכתבת בזמן אמת
Sub-Agents ו-Handoffs — הפיצ'ר הרוצח
Sub-agents הם אחד הפיצ'רים החזקים ביותר ב-Claude Agent SDK. במקום סוכן אחד ענק שיודע הכל — בונים סוכנים מומחים ומנתב שמפנה כל בקשה לסוכן הנכון. זה בדיוק הדפוס שלמדנו בפרק 2 (Orchestrator Pattern).
Handoff בסיסי
# handoff_basic.py
from claude_agent_sdk import ClaudeSDKClient, tool, Agent, Handoff
# סוכן מומחה לחיוב
billing_agent = Agent(
name="billing_agent",
system_prompt="""You are a billing specialist. You help with:
- Invoice questions
- Payment methods
- Refund requests
- Pricing information
Answer in a professional, helpful tone.""",
model="claude-sonnet-4-20250514"
)
# סוכן מומחה לתמיכה טכנית
tech_agent = Agent(
name="tech_agent",
system_prompt="""You are a technical support specialist. You help with:
- Bug reports
- Feature questions
- API integration issues
- Performance problems
Include code examples when relevant.""",
model="claude-sonnet-4-20250514"
)
# סוכן כללי — ברירת מחדל
general_agent = Agent(
name="general_agent",
system_prompt="""You are a friendly general support agent. You help with:
- General questions about the product
- Account settings
- Getting started guides
If the question is about billing or technical issues, let the router handle it.""",
model="claude-sonnet-4-20250514"
)
# סוכן מנתב — הוא מחליט לאן להפנות
router_agent = Agent(
name="router",
system_prompt="""You are a customer service router. Analyze the user's message and
hand off to the appropriate specialist:
- billing_agent: for payment, invoice, refund, pricing questions
- tech_agent: for bugs, API, code, technical issues
- general_agent: for everything else
Always hand off — never answer directly.""",
model="claude-sonnet-4-20250514",
handoffs=[
Handoff(target=billing_agent, description="Billing and payment questions"),
Handoff(target=tech_agent, description="Technical support and bug reports"),
Handoff(target=general_agent, description="General questions"),
]
)
client = ClaudeSDKClient()
# שאילתה שתועבר לסוכן חיוב
result = client.run(
agent=router_agent,
prompt="I was charged twice for my subscription last month. Can I get a refund?"
)
print(f"Handled by: {result.agent_name}")
print(f"Response: {result.text}")
# Handled by: billing_agent
# Response: I'd be happy to help with your double charge...
Handoff עם Filters — שליטה במה שעובר
לפעמים אתם לא רוצים שה-sub-agent יקבל את כל ההיסטוריה. Handoff filters מאפשרים לסנן:
# handoff_filters.py
from claude_agent_sdk import Agent, Handoff, HandoffFilter
# סוכן שמטפל ב-PII — מקבל רק את ההודעה האחרונה
pii_handler = Agent(
name="pii_processor",
system_prompt="Process PII requests securely. Never log or repeat PII data.",
model="claude-sonnet-4-20250514"
)
router = Agent(
name="router",
system_prompt="Route messages to specialists.",
model="claude-sonnet-4-20250514",
handoffs=[
Handoff(
target=pii_handler,
description="Handles PII-sensitive requests",
# filter: רק ההודעה האחרונה עוברת — לא כל ההיסטוריה
input_filter=HandoffFilter.LAST_MESSAGE_ONLY
),
]
)
אם אתם נותנים לסוכן המנתב 10 אפשרויות handoff — הוא יתבלבל. כלל אצבע: עד 5 handoffs לסוכן מנתב. אם צריך יותר — בנו עץ: מנתב ראשי שמפנה ל-3 מנתבים משניים, כל אחד עם 3-4 סוכנים מומחים.
בנה מערכת mini customer service:
- צור 2 סוכנים מומחים (billing + tech)
- צור סוכן מנתב עם handoffs
- שלח 3 שאלות: אחת על תשלום, אחת טכנית, ואחת מעורבת
- בדוק שהמנתב בוחר נכון בכל פעם
- צור קובץ
customer_service.py - הגדר 3 סוכנים מומחים: billing (כולל כלי
lookup_invoice), tech (כולל כליcheck_status), general - הגדר סוכן מנתב עם handoffs לשלושת הסוכנים
- כתוב 5 שאילתות בדיקה: 2 billing, 2 tech, 1 general
- הרץ את כל 5 ובדוק שהניתוב נכון לפחות ב-4 מתוך 5
- הוסף handoff filter ל-billing agent שמעביר רק את ההודעה האחרונה
הצלחה: 4/5 ניתובים נכונים, כלים פועלים ב-sub-agents, filter עובד.
עלות: ~$0.50
Guardrails — בטיחות ו-Validation
Guardrails הם "מעקות בטיחות" שבודקים את הקלט והפלט של הסוכן. בלעדיהם, סוכן יכול: לענות על שאלות שאסור, לחשוף PII, לייצר תוכן פוגעני, או לצרוך יותר מדי טוקנים. Guardrails מונעים את כל אלה.
Input Guardrail — בדיקת קלט
# guardrails_input.py
from claude_agent_sdk import ClaudeSDKClient, Agent, InputGuardrail, GuardrailResult
import re
# Guardrail: זיהוי PII (מספרי טלפון, אימיילים, תעודת זהות)
def pii_detector(input_text: str) -> GuardrailResult:
"""Detects PII in user input and blocks the request if found."""
patterns = {
"email": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
"phone_il": r'\b0[2-9]\d{7,8}\b',
"israeli_id": r'\b\d{9}\b',
"credit_card": r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b',
}
found = []
for pii_type, pattern in patterns.items():
if re.search(pattern, input_text):
found.append(pii_type)
if found:
return GuardrailResult(
blocked=True,
message=f"PII detected ({', '.join(found)}). Please remove personal information before sending."
)
return GuardrailResult(blocked=False)
agent = Agent(
name="safe_agent",
system_prompt="You are a helpful assistant.",
model="claude-sonnet-4-20250514",
input_guardrails=[
InputGuardrail(fn=pii_detector, description="Blocks PII in input")
]
)
client = ClaudeSDKClient()
# ניסיון עם PII — ייחסם
result = client.run(
agent=agent,
prompt="My phone number is 0541234567, please save it"
)
print(result.blocked) # True
print(result.guardrail_message) # "PII detected (phone_il)..."
# ניסיון בלי PII — יעבור
result = client.run(
agent=agent,
prompt="What's the weather like in Tel Aviv?"
)
print(result.blocked) # False
print(result.text) # Normal response
Output Guardrail — בדיקת פלט
# guardrails_output.py
from claude_agent_sdk import Agent, OutputGuardrail, GuardrailResult
# Guardrail: וידוא שהתשובה בעברית (אם נדרש)
def hebrew_only_check(output_text: str) -> GuardrailResult:
"""Ensures the response contains Hebrew text."""
hebrew_chars = sum(1 for c in output_text if '\u0590' <= c <= '\u05FF')
total_alpha = sum(1 for c in output_text if c.isalpha())
if total_alpha > 0 and hebrew_chars / total_alpha < 0.3:
return GuardrailResult(
blocked=True,
message="Response must be primarily in Hebrew. Regenerating..."
)
return GuardrailResult(blocked=False)
# Guardrail: הגבלת אורך תשובה
def max_length_check(output_text: str) -> GuardrailResult:
"""Blocks responses that are too long."""
if len(output_text) > 5000:
return GuardrailResult(
blocked=True,
message=f"Response too long ({len(output_text)} chars). Max: 5000."
)
return GuardrailResult(blocked=False)
agent = Agent(
name="hebrew_agent",
system_prompt="אתה עוזר ישראלי. ענה תמיד בעברית.",
model="claude-sonnet-4-20250514",
output_guardrails=[
OutputGuardrail(fn=hebrew_only_check, description="Ensures Hebrew response"),
OutputGuardrail(fn=max_length_check, description="Limits response length"),
]
)
Tripwire Guardrails — עצירה מיידית
Tripwire guardrail עוצר את ההרצה מיד — בלי לתת לסוכן להמשיך:
# guardrails_tripwire.py
from claude_agent_sdk import Agent, InputGuardrail, GuardrailResult
# Tripwire: חוסם הזרקות prompt (prompt injection)
def prompt_injection_detector(input_text: str) -> GuardrailResult:
"""Detects common prompt injection patterns."""
injection_patterns = [
"ignore previous instructions",
"ignore all instructions",
"you are now",
"pretend you are",
"act as if you have no restrictions",
"disregard your system prompt",
"override your instructions",
]
lower_input = input_text.lower()
for pattern in injection_patterns:
if pattern in lower_input:
return GuardrailResult(
blocked=True,
tripwire=True, # עצירה מיידית — לא ממשיכים
message="Potential prompt injection detected. Request blocked."
)
return GuardrailResult(blocked=False)
agent = Agent(
name="secure_agent",
system_prompt="You are a helpful assistant.",
model="claude-sonnet-4-20250514",
input_guardrails=[
InputGuardrail(
fn=prompt_injection_detector,
description="Blocks prompt injection attempts",
tripwire=True # סימון כ-tripwire — עוצר הכל מיד
)
]
)
Guardrails הם שכבה אחת בלבד. הם לא מחליפים system prompt טוב, ולא מחליפים validation ברמת ה-application. השתמשו ב-defense in depth: (1) system prompt ברור, (2) input guardrails, (3) output guardrails, (4) application-level validation. שום שכבה אחת לא מספיקה לבד.
הוסף guardrail PII לסוכן שלך:
- העתק את
pii_detectorמהדוגמה - הוסף אותו כ-
InputGuardrailלסוכן - בדוק עם הודעה שמכילה מספר טלפון — צריך להיחסם
- בדוק עם הודעה רגילה — צריך לעבור
ניהול Context ו-State
סוכנים אמיתיים צריכים state — חיבור ל-database, העדפות משתמש, עגלת קניות, session ID. ה-RunContext נושא את כל המידע הזה ומעביר אותו לכל כלי וסוכן.
RunContext — State משותף
# context_basic.py
from claude_agent_sdk import ClaudeSDKClient, Agent, tool, RunContext
from dataclasses import dataclass
@dataclass
class AppContext:
"""State that's shared across all tools and agents."""
user_id: str
user_name: str
cart: list # עגלת קניות
language: str = "he"
total_spent: float = 0.0
@tool
def add_to_cart(ctx: RunContext[AppContext], item: str, price: float) -> str:
"""
Adds an item to the shopping cart.
Args:
item: Name of the item to add
price: Price of the item in ILS
"""
ctx.state.cart.append({"item": item, "price": price})
ctx.state.total_spent += price
return f"Added '{item}' (₪{price}) to cart. Total: ₪{ctx.state.total_spent}"
@tool
def view_cart(ctx: RunContext[AppContext]) -> str:
"""Shows the current shopping cart contents."""
if not ctx.state.cart:
return "Cart is empty"
lines = [f"Cart for {ctx.state.user_name}:"]
for item in ctx.state.cart:
lines.append(f" - {item['item']}: ₪{item['price']}")
lines.append(f"Total: ₪{ctx.state.total_spent}")
return "\n".join(lines)
@tool
def checkout(ctx: RunContext[AppContext]) -> str:
"""Processes the checkout and clears the cart."""
if not ctx.state.cart:
return "Cart is empty — nothing to checkout"
total = ctx.state.total_spent
items_count = len(ctx.state.cart)
ctx.state.cart.clear()
ctx.state.total_spent = 0.0
return f"Order placed! {items_count} items, total ₪{total}. Thank you, {ctx.state.user_name}!"
# יוצרים context עם state ראשוני
app_context = AppContext(
user_id="user_123",
user_name="Yossi",
cart=[]
)
client = ClaudeSDKClient()
agent = Agent(
name="shopping_agent",
system_prompt="""You are a shopping assistant for an Israeli online store.
Help users add items, view cart, and checkout.
Speak in Hebrew when the user writes in Hebrew.""",
model="claude-sonnet-4-20250514",
tools=[add_to_cart, view_cart, checkout]
)
# שיחה רב-תורנית — ה-state נשמר בין הודעות
result1 = client.run(agent=agent, prompt="Add hummus for 15 shekels", context=app_context)
print(result1.text)
result2 = client.run(agent=agent, prompt="Also add pita for 8 shekels", context=app_context)
print(result2.text)
result3 = client.run(agent=agent, prompt="Show my cart", context=app_context)
print(result3.text)
result4 = client.run(agent=agent, prompt="Checkout please", context=app_context)
print(result4.text)
Lifecycle Hooks — התערבות בכל שלב
# lifecycle_hooks.py
from claude_agent_sdk import Agent, Hooks
import time
class MonitoringHooks(Hooks):
def on_start(self, context, agent):
self.start_time = time.time()
print(f"[START] Agent '{agent.name}' started")
def on_tool_start(self, context, agent, tool_name, tool_input):
print(f"[TOOL] Calling '{tool_name}' with {tool_input}")
def on_tool_end(self, context, agent, tool_name, tool_output):
print(f"[TOOL] '{tool_name}' returned: {tool_output[:100]}")
def on_end(self, context, agent, result):
elapsed = time.time() - self.start_time
print(f"[END] Agent '{agent.name}' finished in {elapsed:.2f}s")
print(f"[END] Tokens: {result.usage.input_tokens} in, {result.usage.output_tokens} out")
agent = Agent(
name="monitored_agent",
system_prompt="You are a helpful assistant with tools.",
model="claude-sonnet-4-20250514",
hooks=MonitoringHooks()
)
הוסף RunContext לסוכן שלך:
- הגדר
dataclassעם שדהuser_nameו-request_count - בכל tool call, הגדל את
request_count - אחרי 3 הרצות, הדפס "User X made Y requests"
אינטגרציה עם MCP
בפרק 3 למדנו על MCP — הפרוטוקול שמחבר סוכנים לכלים חיצוניים. ב-Claude Agent SDK, אפשר לחבר MCP servers ישירות לסוכן. הכלים מהשרת הופכים אוטומטית לכלים של הסוכן.
חיבור MCP Server
# mcp_integration.py
from claude_agent_sdk import ClaudeSDKClient, Agent, MCPServer
# חיבור filesystem MCP server
filesystem_server = MCPServer(
name="filesystem",
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "/home/user/documents"]
)
# חיבור GitHub MCP server
github_server = MCPServer(
name="github",
command="npx",
args=["-y", "@modelcontextprotocol/server-github"],
env={"GITHUB_TOKEN": "your-github-token"}
)
agent = Agent(
name="dev_assistant",
system_prompt="""You are a development assistant. You can:
- Read and write files using the filesystem tools
- Interact with GitHub repos using the GitHub tools
Help developers with code review, file management, and GitHub operations.""",
model="claude-sonnet-4-20250514",
mcp_servers=[filesystem_server, github_server]
)
client = ClaudeSDKClient()
# הסוכן יכול עכשיו לקרוא קבצים ולעבוד עם GitHub
result = client.run(
agent=agent,
prompt="List the files in my documents folder and then check my latest GitHub notifications"
)
print(result.text)
Dynamic Tool Loading
כלים מ-MCP servers נטענים דינמית בזמן ריצה. הסוכן מגלה אותם אוטומטית:
# mcp_dynamic.py
from claude_agent_sdk import Agent, MCPServer
# שרת שחושף כלים דינמיים
db_server = MCPServer(
name="database",
command="python",
args=["my_db_mcp_server.py"],
# כלים יטענו אוטומטית בעת חיבור
)
agent = Agent(
name="data_analyst",
system_prompt="You analyze data. Use the database tools to query and report.",
model="claude-sonnet-4-20250514",
mcp_servers=[db_server],
# אפשר להגביל אילו כלים מ-MCP הסוכן רשאי להשתמש
allowed_tools=["database:query", "database:list_tables"],
disallowed_tools=["database:drop_table", "database:delete"]
)
MCP server יכול לחשוף כלים מסוכנים — כמו delete_file או drop_table. תמיד השתמשו ב-allowed_tools או disallowed_tools כדי להגביל את מה שהסוכן יכול לעשות. ב-production, עדיף whitelist (allowed_tools) על blacklist (disallowed_tools).
חבר filesystem MCP server לסוכן:
- הגדר
MCPServerעם@modelcontextprotocol/server-filesystem - הגבל את הגישה לתיקייה אחת בלבד
- שאל את הסוכן "List the files and read the first one"
- הוסף
disallowed_tools=["write_file", "delete_file"]— read-only access
Tracing ו-Debugging
כל הרצת סוכן ב-Claude Agent SDK מייצרת trace — רישום מפורט של כל מה שקרה: קריאות LLM, הפעלות כלים, handoffs, זמנים, שגיאות. Tracing הוא הכלי הכי חשוב שלכם כשסוכן לא עושה מה שציפיתם.
הפעלת Tracing
# tracing_basic.py
from claude_agent_sdk import ClaudeSDKClient, Agent, tool, Tracing
# הפעלת tracing עם כתיבה לקונסולה
Tracing.enable(output="console")
client = ClaudeSDKClient()
@tool
def lookup_product(product_id: str) -> str:
"""Looks up a product by ID."""
products = {"P001": "Hummus - ₪15", "P002": "Tahini - ₪22", "P003": "Falafel - ₪12"}
return products.get(product_id, f"Product {product_id} not found")
agent = Agent(
name="product_agent",
system_prompt="Help users find products. Use the lookup tool with product IDs.",
model="claude-sonnet-4-20250514",
tools=[lookup_product]
)
result = client.run(agent=agent, prompt="What is product P001?")
# ה-trace יודפס אוטומטית לקונסולה:
# [TRACE] Agent 'product_agent' started
# [TRACE] LLM call: 150 input tokens
# [TRACE] Tool call: lookup_product(product_id="P001")
# [TRACE] Tool result: "Hummus - ₪15"
# [TRACE] LLM call: 200 input tokens
# [TRACE] Agent 'product_agent' finished in 2.3s
# [TRACE] Total: 350 input + 45 output tokens, 1 tool call
Custom Trace Processor
# tracing_custom.py
from claude_agent_sdk import Tracing, TraceProcessor
import json
class JSONTraceProcessor(TraceProcessor):
"""Saves traces as JSON files for later analysis."""
def __init__(self, output_dir: str = "./traces"):
self.output_dir = output_dir
os.makedirs(output_dir, exist_ok=True)
def process(self, trace):
filename = f"{self.output_dir}/trace_{trace.id}_{trace.timestamp}.json"
with open(filename, 'w') as f:
json.dump({
"id": trace.id,
"agent": trace.agent_name,
"duration_ms": trace.duration_ms,
"llm_calls": trace.llm_call_count,
"tool_calls": [
{"name": tc.name, "input": tc.input, "output": tc.output, "duration_ms": tc.duration_ms}
for tc in trace.tool_calls
],
"tokens": {"input": trace.total_input_tokens, "output": trace.total_output_tokens},
"cost_estimate": trace.cost_estimate,
}, f, indent=2)
# הפעלת tracing עם processor מותאם
Tracing.enable(processor=JSONTraceProcessor("./my_traces"))
Debugging בעיות נפוצות
| בעיה | מה לבדוק ב-trace | פתרון |
|---|---|---|
| סוכן בלולאה אינסופית | Tool calls חוזרים על אותו כלי | הוסף max_turns ושפר את ה-system prompt |
| סוכן בוחר כלי לא נכון | ה-tool input לא תואם את הכוונה | שפר docstrings של הכלים — יותר ברורים ומפורטים |
| סוכן מתעלם מהוראות | ה-system prompt לא ברור | כתוב system prompt יותר מפורש, עם דוגמאות |
| תשובות ארוכות מדי | Output tokens גבוה | הוסף הנחיה "Keep responses under 200 words" ל-system prompt |
| עלויות גבוהות | הרבה LLM calls בהרצה אחת | הקטן max_turns, השתמש ב-claude-haiku למשימות פשוטות |
הפעל tracing וראה מה קורה:
- הוסף
Tracing.enable(output="console")בתחילת הקוד - הרץ את הסוכן עם כלים
- בדוק: כמה LLM calls? כמה tool calls? כמה שניות?
תבניות מתקדמות — Production Patterns
עד עכשיו בנינו סוכנים בסיסיים. בואו נעבור לדפוסים שתראו ב-production אמיתי.
Multi-Turn Conversations — שיחה ממשית
# multi_turn.py
from claude_agent_sdk import ClaudeSDKClient, Agent, Conversation
client = ClaudeSDKClient()
agent = Agent(
name="assistant",
system_prompt="You are a helpful Hebrew-speaking assistant. Remember context from previous messages.",
model="claude-sonnet-4-20250514"
)
# יוצרים שיחה שנשמרת
conversation = Conversation()
# תור 1
result = client.run(agent=agent, prompt="שמי יוסי ואני גר בתל אביב", conversation=conversation)
print(f"Turn 1: {result.text}")
# תור 2 — הסוכן זוכר את השם והעיר
result = client.run(agent=agent, prompt="מה השם שלי ואיפה אני גר?", conversation=conversation)
print(f"Turn 2: {result.text}")
# Output: "שמך יוסי ואתה גר בתל אביב."
# תור 3 — שאלה המשך
result = client.run(agent=agent, prompt="מה מזג האוויר שם בדרך כלל?", conversation=conversation)
print(f"Turn 3: {result.text}")
# Claude understands "שם" = Tel Aviv from context
Session Forking — ניסויים בלי סיכון
# session_fork.py
from claude_agent_sdk import ClaudeSDKClient, Agent, Conversation
client = ClaudeSDKClient()
agent = Agent(name="assistant", system_prompt="Helpful assistant.", model="claude-sonnet-4-20250514")
# שיחה ראשית
conversation = Conversation()
client.run(agent=agent, prompt="Let's plan a trip to Japan", conversation=conversation)
# Fork — יוצרים עותק של השיחה
fork1 = conversation.fork()
fork2 = conversation.fork()
# כל fork ממשיך בכיוון אחר — בלי להשפיע על המקור
r1 = client.run(agent=agent, prompt="Focus on Tokyo only", conversation=fork1)
r2 = client.run(agent=agent, prompt="Focus on Kyoto only", conversation=fork2)
# השיחה המקורית לא השתנתה
r_original = client.run(agent=agent, prompt="What were we planning?", conversation=conversation)
# Still talking about general Japan trip
Structured Output — פלט מובנה
# structured_output.py
from claude_agent_sdk import ClaudeSDKClient, Agent
from pydantic import BaseModel
from typing import List
class ProductReview(BaseModel):
product_name: str
rating: float # 1-5
pros: List[str]
cons: List[str]
summary: str
recommended: bool
client = ClaudeSDKClient()
agent = Agent(
name="reviewer",
system_prompt="You analyze products and return structured reviews.",
model="claude-sonnet-4-20250514",
output_schema=ProductReview # מגדיר schema מובנה
)
result = client.run(
agent=agent,
prompt="Review the MacBook Air M3 for a developer"
)
# result.parsed — אובייקט ProductReview מאומת
review: ProductReview = result.parsed
print(f"Product: {review.product_name}")
print(f"Rating: {review.rating}/5")
print(f"Recommended: {review.recommended}")
for pro in review.pros:
print(f" + {pro}")
for con in review.cons:
print(f" - {con}")
TypeScript — Structured Output
// structured_output.ts
import { ClaudeSDKClient, Agent } from '@anthropic-ai/claude-agent-sdk';
import { z } from 'zod';
const ProductReview = z.object({
productName: z.string(),
rating: z.number().min(1).max(5),
pros: z.array(z.string()),
cons: z.array(z.string()),
summary: z.string(),
recommended: z.boolean(),
});
type ProductReview = z.infer<typeof ProductReview>;
const client = new ClaudeSDKClient();
const agent = new Agent({
name: "reviewer",
systemPrompt: "You analyze products and return structured reviews.",
model: "claude-sonnet-4-20250514",
outputSchema: ProductReview,
});
async function main() {
const result = await client.run({
agent,
prompt: "Review the MacBook Air M3 for a developer",
});
const review = result.parsed as ProductReview;
console.log(`${review.productName}: ${review.rating}/5`);
console.log(`Recommended: ${review.recommended}`);
}
main();
Extended Thinking — חשיבה מעמיקה
# extended_thinking.py
from claude_agent_sdk import ClaudeSDKClient, Agent
client = ClaudeSDKClient()
agent = Agent(
name="deep_thinker",
system_prompt="You solve complex problems. Think step by step.",
model="claude-sonnet-4-20250514",
extended_thinking=True, # מפעיל חשיבה מורחבת
thinking_budget=10000 # עד 10K טוקנים לחשיבה
)
result = client.run(
agent=agent,
prompt="A company has 3 products. Product A costs $10, Product B costs $20, Product C costs $30. "
"If they sell A:B:C in ratio 5:3:2, and total revenue is $150,000, "
"how many units of each product were sold?"
)
# אפשר לראות את תהליך החשיבה
if result.thinking:
print("=== Thinking Process ===")
print(result.thinking)
print("========================")
print(result.text)
Max Turns ו-Cost Control
# cost_control.py
from claude_agent_sdk import ClaudeSDKClient, Agent
client = ClaudeSDKClient()
agent = Agent(
name="controlled_agent",
system_prompt="Help the user efficiently.",
model="claude-sonnet-4-20250514",
max_turns=5, # מקסימום 5 tool calls לפני עצירה
max_tokens=1000, # מקסימום 1000 טוקנים בתשובה
)
result = client.run(
agent=agent,
prompt="Analyze this codebase thoroughly"
)
if result.truncated:
print("Warning: Agent reached max_turns limit")
print(f"Turns used: {result.turns_used}/{agent.max_turns}")
print(f"Cost: ~${result.cost_estimate:.4f}")
- הגדר
BaseModel(Pydantic) עם 5 שדות לתיאור מסעדה: שם, דירוג, כתובת, סוג אוכל, טווח מחירים - צור סוכן עם
output_schemaשמחזירRestaurantReview - שלח 3 שאלות על מסעדות שונות
- ודא שכל תשובה מפורסרת כ-
RestaurantReviewתקין - הדפס את הנתונים בפורמט טבלה
הצלחה: 3 reviews מפורסרים בהצלחה, כל השדות מלאים, אין שגיאות validation.
עלות: ~$0.15
שלבו את הכל — סוכן production-ready מלא:
- צור קובץ
production_agent.py - הגדר 3 כלים: calculator, file reader, product lookup
- הגדר 2 sub-agents: technical + billing, עם router agent
- הוסף 2 guardrails: PII detection (input) + max length (output)
- הפעל streaming — הצג תשובות בזמן אמת
- הפעל tracing — שמור traces כ-JSON
- השתמש ב-RunContext לניהול state (user info, request count)
- הגדר max_turns=10 ו-cost limit
- הרץ 5 שאילתות מגוונות ובדוק שהכל עובד
הצלחה: סוכן מלא שמשלב tools + sub-agents + guardrails + streaming + tracing + context. כל 5 השאילתות מצליחות.
עלות: ~$2-5
Claude Agent SDK מול Raw API — מתי להשתמש במה
עכשיו שאתם מכירים את ה-SDK, חשוב להבין מתי הוא הבחירה הנכונה ומתי עדיף להשתמש ב-Anthropic API ישירות.
מה ה-SDK מוסיף מעל ה-API
| יכולת | Raw API | Claude Agent SDK |
|---|---|---|
| Agent Loop | צריך לבנות ידנית | מובנה ומנוהל |
| Tool Orchestration | ידני: parse → execute → send back | אוטומטי עם @tool decorator |
| Handoffs | לא קיים | מובנה עם Handoff class |
| Guardrails | צריך לבנות ידנית | Input/Output guardrails מובנים |
| Tracing | צריך לבנות ידנית | מובנה עם trace processor |
| Streaming | SSE parsing ידני | Event types מובנים |
| Context/State | ידני | RunContext מובנה |
| Overhead | 0 | ~5-10ms per loop iteration |
כלל אצבע: אם אתם כותבים agent loop ידנית — עדיף להשתמש ב-SDK. אם אתם עושים API call אחד בלי לולאה — השתמשו ב-API ישירות.
| מורכבות | דוגמה | כלי | עלות/בקשה |
|---|---|---|---|
| 1 — API Call יחיד | תרגום טקסט, סיכום מאמר | Anthropic API | $0.01-0.05 |
| 2 — Agent עם כלים | עוזר שיכול לחשב ולחפש | Claude Agent SDK | $0.05-0.30 |
| 3 — Multi-Agent | מערכת שירות לקוחות | Claude Agent SDK + Handoffs | $0.30-2.00 |
| 4 — Multi-Provider | מערכת שמשתמשת ב-Claude + GPT | Vercel AI SDK (פרק 6) | $0.10-1.00 |
| 5 — Orchestration מורכב | pipeline עם 10+ שלבים ותנאים | LangGraph (פרק 8) | $1.00-10.00 |
Migration Path — מ-SDK ל-API ובחזרה
היתרון של Claude Agent SDK: הוא שכבה דקה. אם מחר תצטרכו יותר שליטה — קל לחלץ את הלוגיקה ל-raw API calls. ואם התחלתם עם raw API ומרגישים שאתם בונים מחדש את הגלגל — ה-SDK נמצא שם.
# migration_example.py
# === RAW API (manual agent loop) ===
import anthropic
client = anthropic.Anthropic()
messages = [{"role": "user", "content": "What is 2+2?"}]
tools = [{"name": "calculator", "description": "...", "input_schema": {...}}]
while True:
response = client.messages.create(
model="claude-sonnet-4-20250514",
messages=messages,
tools=tools,
max_tokens=1000
)
if response.stop_reason == "end_turn":
break
# Manual tool execution...
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input) # You build this
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": [{"type": "tool_result", "tool_use_id": block.id, "content": result}]})
# === CLAUDE AGENT SDK (same thing, 5 lines) ===
from claude_agent_sdk import ClaudeSDKClient, tool
@tool
def calculator(expression: str) -> str:
"""Evaluates math expressions."""
return str(eval(expression))
client = ClaudeSDKClient()
result = client.query(prompt="What is 2+2?", tools=[calculator])
ה-SDK חוסך 20-30 שורות קוד boilerplate בכל סוכן. בפרויקט עם 5 סוכנים — זה 100-150 שורות שלא צריך לתחזק.
טעויות נפוצות — סיכום
סוכן בלי max_turns יכול להיכנס ללולאה אינסופית — כלי שנכשל, הסוכן מנסה שוב, נכשל שוב, וכך עד שנגמר ה-budget. תמיד הגדירו max_turns — 5-10 לסוכנים פשוטים, 15-20 למורכבים. בלי זה, שגיאה אחת יכולה לעלות $50.
"You are a helpful assistant" הוא system prompt גרוע. Claude חזק — אבל הוא צריך הוראות ברורות. כתבו: (1) מה הסוכן עושה, (2) מה הוא לא עושה, (3) באיזה טון לדבר, (4) באיזו שפה, (5) מגבלות ספציפיות. system prompt ספציפי = סוכן שמתנהג כצפוי.
Claude Opus עולה פי 30 מ-Haiku. אם הסוכן שלכם עושה ניתוב פשוט או עונה על FAQ — השתמשו ב-Haiku. שמרו את Opus למשימות שדורשות חשיבה עמוקה. כלל אצבע: התחילו עם Haiku, שדרגו ל-Sonnet אם לא מספיק, Opus רק כשבאמת צריך.
| תדירות | משימה | זמן |
|---|---|---|
| יומי | בדוק traces של הסוכנים שלך — חפש לולאות, עלויות חריגות, tool failures | 5 דק' |
| שבועי | סקור guardrails — בדוק שה-PII detector תופס patterns חדשים | 10 דק' |
| שבועי | בדוק עדכוני Claude Agent SDK — changelog ב-GitHub | 5 דק' |
| חודשי | ניתוח עלויות: tokens per query, cost per user, model distribution | 20 דק' |
| חודשי | A/B testing: נסה model חדש (למשל Haiku במקום Sonnet) על סוכן ספציפי | 30 דק' |
| רבעוני | סקירת ארכיטקטורה: האם ה-handoff patterns עדיין נכונים? | 60 דק' |
בנו סוכן עם כלי אחד. זה הכל. קובץ אחד, 20 שורות קוד: ClaudeSDKClient, פונקציה עם @tool, ו-client.query(). ברגע שראיתם את Claude בוחר להפעיל את הכלי שלכם ומשתמש בתוצאה — הבנתם את העיקרון הכי חשוב של סוכני AI. כל השאר (handoffs, guardrails, streaming, tracing) הם שיפורים על הבסיס הזה. אבל הבסיס — סוכן + כלי — הוא הכל.
- מה ההבדל בין
client.query()ל-client.stream()? (רמז: synchronous vs real-time events) - למה ה-docstring של כלי כל כך חשוב? (רמז: זה מה ש-Claude רואה כשהוא מחליט אם להשתמש בכלי)
- מה ההבדל בין Input Guardrail ל-Tripwire Guardrail? (רמז: עצירה מיידית)
- מתי עדיף להשתמש ב-Handoffs ומתי בסוכן אחד עם הרבה כלים? (רמז: 5+ כלים, תחומי ידע שונים)
- למה חייבים להגדיר
max_turnsב-production? (רמז: לולאות אינסופיות, $$$)
עברתם 4 מתוך 5? מצוין — אתם מוכנים לפרק 6 (Vercel AI SDK).
בפרק הזה בנינו את הסוכן הראשון שלנו עם Claude Agent SDK — ה-SDK הרשמי של Anthropic. התחלנו עם סוכן בסיסי (5 שורות קוד), הוספנו כלים מותאמים אישית עם @tool decorator, למדנו streaming לפלט בזמן אמת, ובנינו מערכת multi-agent עם handoffs בין סוכנים מומחים.
הוספנו guardrails לבטיחות — PII detection, content filtering, ו-tripwire guardrails לעצירה מיידית. למדנו לנהל state עם RunContext, חיברנו MCP servers לכלים חיצוניים, והפעלנו tracing לדיבאגינג. לסיום, ראינו production patterns: multi-turn conversations, structured output, extended thinking, ו-cost control.
בפרק הבא (פרק 6) נכיר את Vercel AI SDK — ה-SDK שתומך ב-providers מרובים (Claude, GPT, Gemini) ומתמחה בבניית UI עם React. נשווה אותו ל-Claude Agent SDK ונבין מתי להשתמש בכל אחד.
צ'קליסט — סיכום פרק 5
- Claude Agent SDK מותקן ומוכן (Python ו/או TypeScript)
- סוכן Hello World רץ ומחזיר תשובות
- כלי אחד לפחות מוגדר עם
@toolו-docstring ברור - סוכן עם 3 כלים רץ ובוחר כלי נכון לפי השאלה
- Streaming מופעל ומציג תשובות בזמן אמת
- מערכת sub-agents עם router ו-2+ סוכנים מומחים
- Input guardrail (PII detection) מוגדר ופועל
- Output guardrail (אורך/שפה) מוגדר ופועל
- RunContext מגדיר state משותף לכלים
- MCP server מחובר לסוכן (filesystem או אחר)
- Tracing מופעל ו-traces נשמרים
- max_turns מוגדר בכל סוכן
- Structured output עם Pydantic/Zod עובד
- סוכן production-ready מלא (תרגיל 4) מוכן
- הבנתם מתי SDK ומתי raw API — לפי "מדרגת המורכבות"