2 שלב הבסיס

Agent Architecture & Design Patterns — ארכיטקטורת סוכנים ודפוסי עיצוב

איך סוכני AI באמת עובדים מבפנים: לולאת ReAct, שרשרת חשיבה, ארכיטקטורת כלים, ניהול מצב וזיכרון, דפוסי Multi-Agent, טיפול בשגיאות, ומחזור חיי הפיתוח. בסוף הפרק הזה תבנו סוכן מינימלי ב-20 שורות קוד ותבינו בדיוק מה קורה מאחורי הקלעים בכל SDK.

מה יהיה לך בסוף הפרק הזה
מה תוכלו לעשות אחרי הפרק הזה
דרישות קדם
הפרויקט שלך — קו אדום לאורך הקורס

בפרק 1 הבנתם מה סוכנים עושים. בפרק הזה אתם מבינים איך הם עובדים מבפנים. זה הפרק הכי חשוב בחלק הבסיס — כי כל SDK שתלמדו בפרקים 5-10 מבוסס על אותם דפוסים בדיוק.

הסוכן המינימלי שתבנו כאן (20 שורות, ללא SDK) ישמש אתכם כ-benchmark — כשתעבדו עם Claude Agent SDK, OpenAI Agents SDK או Vercel AI SDK, תוכלו תמיד לחזור לכאן ולשאול: "מה ה-SDK הזה עושה בשבילי שאני לא עושה בעצמי?"

הפרק הזה גם מניח את הבסיס לפרויקט הגמר: סוכן תמיכת לקוחות מלא (פרק 15) שישתמש ב-Router Agent, זיכרון ארוך-טווח, ו-Error Handling — כל הדפוסים שנלמד כאן.

מילון מונחים — ארכיטקטורת סוכנים

הפרק הזה עמוס במונחים טכניים. לא צריך לשנן — הם יחזרו שוב ושוב. אבל טוב שיהיה לכם מפתח:

מונח תרגום / הסבר
ReActReason + Act — הדפוס המרכזי בסוכני AI. הסוכן חושב (Reason), מבצע פעולה (Act), מתבונן בתוצאה (Observe), וחוזר על הלולאה עד שמסיים
Chain of Thought (CoT)שרשרת חשיבה — טכניקה שבה המודל "חושב בקול רם" צעד אחרי צעד לפני שנותן תשובה. משפר דיוק ב-20-40%
Tool Use / Function Callingשימוש בכלים — היכולת של מודל לבקש הרצת פונקציה חיצונית (חיפוש, שאילתת DB, שליחת מייל) ולקבל תוצאה
State Machineמכונת מצבים — מודל שמגדיר מצבים אפשריים ומעברים ביניהם. דרך ליצור סוכנים צפויים יותר
Context Windowחלון הקשר — כמות הטקסט שהמודל יכול "לזכור" בשיחה אחת. Claude: 1M טוקנים, GPT-5: 1M טוקנים
System Promptפרומפט מערכת — ההוראות שמגדירות את האישיות, הכללים והמגבלות של הסוכן. "מי אתה ומה מותר/אסור לך"
Short-term Memoryזיכרון קצר-טווח — היסטוריית השיחה שנמצאת בתוך ה-Context Window. נמחק כשהשיחה נגמרת
Long-term Memoryזיכרון ארוך-טווח — מידע שנשמר בין שיחות: Vector Store, מסד נתונים, קבצים. שורד Restart
Episodic Memoryזיכרון אפיזודי — זכרונות של אירועים ספציפיים: "ב-15/3 הלקוח ביקש החזר וקיבל אותו". שימושי לפרסונליזציה
Orchestratorתזמורן — סוכן מנהל שמחלק משימות לסוכני-משנה (Workers) ואוסף תוצאות
Router Agentסוכן ניתוב — סוכן שמנתח את הבקשה ומעביר אותה לסוכן המתמחה הנכון
Handoffהעברה — כשסוכן מעביר את השיחה לסוכן אחר (או לאדם). מנגנון מרכזי ב-Multi-Agent
MCPModel Context Protocol — פרוטוקול סטנדרטי לחיבור סוכנים לכלים. "ה-USB-C של AI Agents"
Guardrailsמעקות בטיחות — בדיקות קלט ופלט שמוודאות שהסוכן לא עושה דברים שהוא לא אמור
Max Iterationsמגבלת איטרציות — מספר מקסימלי של סיבובי לולאה. מונע סוכן שרץ לנצח
Token Budgetתקציב טוקנים — מגבלת עלות לריצה בודדת. מונע חשבונות של $500 על באג בלולאה
Durable Executionהרצה עמידה — סוכן ששורד Crash, Restart, ותהליכים ארוכים. קריטי ל-Production
מתחיל 15 דקות חינם

לולאת ReAct — הדפוס המרכזי

כל סוכן AI בעולם — לא משנה באיזה SDK הוא בנוי, באיזה מודל הוא משתמש, או מה הוא עושה — פועל לפי אותו דפוס בסיסי. הדפוס הזה נקרא ReAct (קיצור של Reason + Act), ואם תבינו אותו לעומק, תבינו כל SDK שתפגשו בקורס הזה.

הרעיון פשוט להפליא: הסוכן מקבל קלט, חושב מה לעשות, מבצע פעולה, מסתכל על התוצאה, וחוזר על הלולאה עד שהוא מסיים. חמישה צעדים, שוב ושוב:

שלב שם באנגלית מה קורה דוגמה
1 Input (קלט) הסוכן מקבל את הבקשה מהמשתמש "הזמן לי טיסה לתל אביב ב-15 באפריל"
2 Reason (חשיבה) המודל חושב מה הצעד הבא שצריך לעשות "אני צריך לחפש טיסות לתל אביב ב-15/4. אשתמש בכלי search_flights"
3 Act (פעולה) הסוכן מבצע פעולה — בדרך כלל קריאה לכלי (Tool) search_flights(destination="TLV", date="2026-04-15")
4 Observe (צפייה) הסוכן מקבל את תוצאת הכלי ומנתח אותה "נמצאו 3 טיסות. הזולה ביותר: $450 ב-Turkish Airlines"
5 Repeat / Stop הסוכן מחליט: האם סיימתי? אם לא — חוזר לשלב 2 "צריך לשאול את המשתמש אם לאשר את ההזמנה" → עצירה

זה הכול. כל סוכן AI שתראו — מ-Claude Code ועד CrewAI עם 10 סוכנים — הוא וריאציה על הלולאה הזו. הבנתם אותה? הבנתם 80% מארכיטקטורת סוכנים.

הבעיה הקריטית: מתי הסוכן עוצר?

הלולאה הזו יכולה לרוץ לנצח. אם הסוכן לא מגיע למצב של "סיימתי", הוא ימשיך לחשוב ולפעול שוב ושוב — ויעלה לכם כסף. תמיד צריך מנגנוני עצירה:

באג שגורם ללולאה אינסופית יכול לייצר חשבון API של $500 בלילה אחד. זה קורה. הגדירו מגבלות לפני שאתם מפעילים סוכן.

עשה עכשיו 2 דקות

חשבו על לולאת ReAct בחיים שלכם. נניח שמישהו מבקש מכם "מצא לי מסעדה טובה ליום שישי". מה הצעדים שלכם?

  1. Reason: "אני צריך לדעת מה הוא אוהב, באיזה אזור, ותקציב"
  2. Act: שואלים אותו (כמו כלי — input מהמשתמש)
  3. Observe: "אוהב סושי, מרכז תל אביב, עד 200 ש"ח לזוג"
  4. Reason: "אחפש מסעדות סושי בתל אביב"
  5. Act: מחפשים ב-Google Maps
  6. Observe: 5 תוצאות, בודקים ביקורות
  7. Stop: "ממליץ על X — 4.7 כוכבים, 180 ש"ח לזוג"

ברכות — אתם סוכן ReAct. עכשיו תבינו מה הקוד עושה.

Pseudocode של לולאת ReAct

לפני שנכתוב קוד אמיתי — הנה הלוגיקה במילים פשוטות:

function agent_loop(user_message, tools, max_iterations=10):
    messages = [system_prompt, user_message]

    for i in range(max_iterations):
        response = llm.call(messages)          // שלב Reason

        if response.has_tool_calls:
            for tool_call in response.tool_calls:
                result = execute_tool(tool_call) // שלב Act
                messages.append(result)          // שלב Observe
        else:
            return response.text                 // עצירה — התשובה מוכנה

    return "הגעתי למגבלת הסיבובים"              // Failsafe

ReAct בפועל — Python עם Streaming ו-Token Tracking

הנה גרסה מתקדמת יותר של לולאת ReAct, עם מעקב אחרי טוקנים ו-streaming — שני דברים קריטיים בפרודקשן:

# Python — ReAct Loop עם Token Tracking
import anthropic
import json
import time

client = anthropic.Anthropic()

def react_loop_with_tracking(
    user_msg: str,
    tools: list,
    tool_fns: dict,
    max_iterations: int = 10,
    max_tokens_budget: int = 50000,
    system_prompt: str = "You are a helpful assistant."
) -> dict:
    """
    ReAct loop מתקדם עם:
    - Token budget tracking (מונע חשבונות גבוהים)
    - Iteration logging (לדיבוג)
    - Timing per step (לזיהוי צווארי בקבוק)
    """
    messages = [{"role": "user", "content": user_msg}]
    total_tokens = 0
    steps_log = []

    for i in range(max_iterations):
        step_start = time.time()

        resp = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=1024,
            system=system_prompt,
            tools=tools,
            messages=messages
        )

        # עדכון תקציב טוקנים
        step_tokens = resp.usage.input_tokens + resp.usage.output_tokens
        total_tokens += step_tokens
        step_time = time.time() - step_start

        steps_log.append({
            "step": i + 1,
            "tokens": step_tokens,
            "time_sec": round(step_time, 2),
            "stop_reason": resp.stop_reason
        })

        # בדיקת תקציב
        if total_tokens > max_tokens_budget:
            return {
                "result": "חרגתי מתקציב הטוקנים",
                "total_tokens": total_tokens,
                "steps": steps_log
            }

        # בדיקת tool calls
        tool_calls = [b for b in resp.content if b.type == "tool_use"]
        if not tool_calls:
            return {
                "result": "".join(
                    b.text for b in resp.content if b.type == "text"
                ),
                "total_tokens": total_tokens,
                "steps": steps_log
            }

        # ביצוע כלים
        messages.append({"role": "assistant", "content": resp.content})
        for tc in tool_calls:
            fn = tool_fns.get(tc.name)
            if fn:
                result = fn(**tc.input)
            else:
                result = f"Error: Unknown tool '{tc.name}'"
            messages.append({
                "role": "user",
                "content": [{
                    "type": "tool_result",
                    "tool_use_id": tc.id,
                    "content": str(result)
                }]
            })

    return {
        "result": "הגעתי למגבלת הסיבובים",
        "total_tokens": total_tokens,
        "steps": steps_log
    }

אותו הדבר ב-TypeScript — עם Streaming

// TypeScript — ReAct Loop עם Token Tracking ו-Streaming
import Anthropic from "@anthropic-ai/sdk";

interface StepLog {
  step: number;
  tokens: number;
  timeSec: number;
  stopReason: string;
}

interface AgentResult {
  result: string;
  totalTokens: number;
  steps: StepLog[];
}

async function reactLoopWithTracking(
  userMsg: string,
  tools: Anthropic.Tool[],
  toolFns: Record<string, (args: any) => Promise<string>>,
  maxIterations = 10,
  maxTokensBudget = 50000,
  systemPrompt = "You are a helpful assistant."
): Promise<AgentResult> {
  const client = new Anthropic();
  const messages: Anthropic.MessageParam[] = [
    { role: "user", content: userMsg }
  ];
  let totalTokens = 0;
  const stepsLog: StepLog[] = [];

  for (let i = 0; i < maxIterations; i++) {
    const stepStart = Date.now();

    const resp = await client.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 1024,
      system: systemPrompt,
      tools,
      messages,
    });

    const stepTokens =
      resp.usage.input_tokens + resp.usage.output_tokens;
    totalTokens += stepTokens;

    stepsLog.push({
      step: i + 1,
      tokens: stepTokens,
      timeSec: Math.round((Date.now() - stepStart) / 100) / 10,
      stopReason: resp.stop_reason ?? "unknown",
    });

    // בדיקת תקציב
    if (totalTokens > maxTokensBudget) {
      return {
        result: "חרגתי מתקציב הטוקנים",
        totalTokens,
        steps: stepsLog,
      };
    }

    const toolCalls = resp.content.filter(
      (b) => b.type === "tool_use"
    );
    if (toolCalls.length === 0) {
      return {
        result: resp.content
          .filter((b) => b.type === "text")
          .map((b) => (b as Anthropic.TextBlock).text)
          .join(""),
        totalTokens,
        steps: stepsLog,
      };
    }

    messages.push({ role: "assistant", content: resp.content });
    for (const tc of toolCalls) {
      const block = tc as Anthropic.ToolUseBlock;
      const fn = toolFns[block.name];
      const result = fn
        ? await fn(block.input as any)
        : `Error: Unknown tool '${block.name}'`;
      messages.push({
        role: "user",
        content: [{
          type: "tool_result",
          tool_use_id: block.id,
          content: result,
        }],
      });
    }
  }

  return {
    result: "הגעתי למגבלת הסיבובים",
    totalTokens,
    steps: stepsLog,
  };
}
למה Token Tracking חשוב?

בלי מעקב אחרי טוקנים, אתם עיוורים. סוכן שנראה "עובד" יכול לצרוך 10x יותר טוקנים ממה שחשבתם — בגלל לולאות מיותרות, context window שגדל, או tool results ארוכים. ה-steps_log שבדוגמה למעלה נותן לכם visibility מלאה: כמה טוקנים כל צעד צרך, כמה זמן הוא לקח, ומה הסיבה שהוא עצר. בפרודקשן — שלחו את הלוג הזה ל-monitoring system.

שימו לב: המודל הוא זה שמחליט מתי לעצור. כשהוא לא מבקש לקרוא לכלי (no tool calls), הלולאה נגמרת והתשובה הטקסטואלית חוזרת למשתמש. זה הדפוס שכל SDK מיישם — רק עם יותר אבטחה, Streaming, ו-Error Handling.

נקודה חשובה: כל SDK שנלמד בקורס — Claude Agent SDK, OpenAI Agents SDK, Vercel AI SDK, LangGraph, CrewAI, Google ADK — מסתיר את הלולאה הזו מאחורי API יפה. אבל כשמבינים את ה-Pseudocode למעלה, מבינים בדיוק מה כל SDK עושה מתחת לפני השטח. ב-LangGraph, למשל, הלולאה הזו מיוצגת כ-graph עם nodes ו-edges. ב-Vercel AI SDK, היא מיוצגת כ-generateText + tools + stopWhen. התוצאה זהה — רק ה-syntax שונה.

עשה עכשיו 3 דקות

שרטטו את לולאת ReAct על דף נייר. ריבוע = שלב. חץ = מעבר. ציירו: Input → Reason → Act → Observe → (חזרה ל-Reason או Stop). סמנו איפה ה-LLM עובד ואיפה הכלים עובדים. שמרו את הציור — תשתמשו בו שוב.

מתחיל 12 דקות חינם

Chain of Thought — שרשרת חשיבה ווריאציות

Chain of Thought (CoT) היא טכניקה שבה המודל "חושב בקול רם" — פורט את תהליך החשיבה שלו צעד אחרי צעד לפני שהוא נותן תשובה סופית. זה לא טריק — זה מנגנון שמשפר דיוק באופן מדיד. מחקרים מראים שיפור של 20-40% בדיוק על משימות מורכבות כשמשתמשים ב-CoT.

למה זה עובד? כי בלי CoT, המודל "קופץ" ישר לתשובה. עם CoT, הוא מפרק את הבעיה ליחידות קטנות. ההבדל:

ללא CoT עם CoT
"כמה עולה להריץ 1,000 שאילתות ב-Claude Sonnet?"
→ "$4.50"
(עשוי לטעות)
"כמה עולה להריץ 1,000 שאילתות ב-Claude Sonnet?"
→ "בוא נחשב: כל שאילתה ~500 טוקנים input, ~200 output. סה"כ: 500K input + 200K output. מחיר: $3/M input = $1.50. $15/M output = $3.00. סה"כ: $4.50"
(מדויק + ניתן לביקורת)

וריאציות של Chain of Thought

CoT הוא הבסיס, אבל יש כמה גרסאות שחשוב להכיר:

שם איך עובד מתי להשתמש
Zero-shot CoT מוסיפים "חשוב צעד אחרי צעד" (Think step by step) לפרומפט. המודל עושה את השאר כל משימה מורכבת. הכי פשוט להפעיל
Few-shot CoT נותנים 2-3 דוגמאות של חשיבה מפורטת, והמודל מחקה את הסגנון כשרוצים פורמט מסוים של חשיבה
Tree of Thought (ToT) המודל בודק מספר נתיבי פתרון במקביל ובוחר את הטוב ביותר משימות עם כמה פתרונות אפשריים, כמו תכנון אסטרטגיה
Inner Monologue הסוכן כותב "הערות לעצמו" שהמשתמש לא רואה, לפני כל פעולה סוכנים שצריכים לחשוב בלי להציף את המשתמש
ReAct + CoT שילוב: הסוכן חושב בקול רם (CoT) וגם מבצע פעולות (Act) בתוך אותה לולאה הדפוס הנפוץ ביותר לסוכני AI. ברירת המחדל של רוב ה-SDKs
טיפ מעשי: Temperature לסוכנים

סוכנים צריכים לעבוד עם Temperature נמוך: 0.0-0.3. למה? כי אתם רוצים שהסוכן יקבל את אותה החלטה כל פעם שהוא רואה את אותו מצב. Temperature גבוה (0.7-1.0) מתאים ליצירת תוכן — לא לקבלת החלטות.

חריג: סוכן שמייצר תוכן יצירתי (למשל: כותב פוסטים) יכול להשתמש ב-Temperature גבוה בשלב הכתיבה, אבל Temperature נמוך בשלב קבלת ההחלטות (איזה כלי להפעיל, מתי לעצור).

עשה עכשיו 3 דקות

נסו את ההבדל בעצמכם. פתחו את claude.ai ושאלו: "כמה יעלה להריץ סוכן שעושה 50 קריאות API ביום למשך חודש, עם Claude Sonnet, כשכל קריאה כוללת ~1,000 טוקנים input ו-~500 output?"

פעם ראשונה: שאלו ישר. פעם שנייה: הוסיפו "Think step by step, show all calculations." שימו לב להבדל בדיוק ובביטחון שלכם בתשובה.

בינוני 18 דקות חינם

ארכיטקטורת Tool Calling — הידיים של הסוכן

בלי כלים, סוכן הוא בסך הכול צ'טבוט מתקדם. הכלים (Tools / Functions) הם מה שהופכים אותו לסוכן אמיתי — הם הידיים שמאפשרות לו לפעול בעולם: לחפש באינטרנט, לשלוח מייל, לקרוא קובץ, לשאול בסיס נתונים, או לבצע חישוב.

האנטומיה של כלי (Tool)

כל כלי מורכב מ-5 חלקים:

חלק שם באנגלית תפקיד דוגמה
1 Name שם ייחודי שהמודל משתמש בו search_flights
2 Description הסבר מתי ולמה להשתמש בכלי. הגורם מספר 1 לדיוק הסוכן "Search available flights by destination and date. Use when user wants to book or compare flights."
3 Input Schema הגדרת הפרמטרים שהכלי מקבל (JSON Schema) { destination: string, date: string, max_price?: number }
4 Output מה הכלי מחזיר למודל JSON עם רשימת טיסות, מחירים, וזמנים
5 Execution Function הקוד שבאמת רץ כשהמודל קורא לכלי פונקציית Python/TS שמתשאלת API חיצוני

איך המודל בוחר כלי?

זה עובד ככה: כשאתם שולחים הודעה לסוכן, אתם גם שולחים את רשימת הכלים הזמינים (עם השמות, התיאורים וה-Schemas). המודל קורא את כל זה ומחליט: "בהתחשב בבקשת המשתמש ובכלים שיש לי, הכלי הנכון הוא X עם הפרמטרים Y."

מה זה אומר בפועל? שתיאור הכלי (Description) הוא הדבר הכי חשוב שתכתבו. תיאור גרוע = הסוכן בוחר את הכלי הלא נכון = תוצאה גרועה. תיאור טוב = דיוק של 90%+.

הטעות הנפוצה ביותר: תיאור כלי עמום

ראו את ההבדל:

תיאור גרועתיאור טוב
"Search stuff" "Search the company's internal knowledge base for product documentation, FAQs, and troubleshooting guides. Use when user asks about product features, pricing, or technical issues. Do NOT use for general web search — use web_search for that."

שימו לב: התיאור הטוב מסביר מתי להשתמש, מתי לא, ומה הכלי מחזיר. 3 המשפטים האלה הם ההבדל בין סוכן שעובד לסוכן שמתסכל.

Static Tools מול Dynamic Tools

יש שני סוגים של כלים:

בפרק 3 נעמיק ב-MCP ובכלים דינמיים. כרגע, הדבר החשוב להבין: כלים סטטיים פשוטים יותר ומהירים יותר, כלים דינמיים גמישים יותר ו-reusable.

Tool Calling — Python Example

הנה איך מגדירים כלי ושולחים אותו ל-API של Anthropic (Claude). שימו לב — זה בלי SDK, רק API ישיר:

import anthropic

client = anthropic.Anthropic()

# הגדרת כלי
tools = [{
    "name": "get_weather",
    "description": "Get current weather for a city. Use when user asks "
                   "about weather, temperature, or if they need an umbrella. "
                   "Returns temperature in Celsius and conditions.",
    "input_schema": {
        "type": "object",
        "properties": {
            "city": {
                "type": "string",
                "description": "City name, e.g. 'Tel Aviv', 'Jerusalem'"
            }
        },
        "required": ["city"]
    }
}]

# שליחת בקשה עם כלים
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "מה מזג האוויר בתל אביב?"}]
)

# המודל מחליט לקרוא ל-get_weather
print(response.content)
# [ToolUse(type='tool_use', name='get_weather', input={'city': 'Tel Aviv'})]

Tool Calling — TypeScript Example

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

// הגדרת כלי
const tools = [{
  name: "get_weather",
  description: "Get current weather for a city. Use when user asks " +
               "about weather, temperature, or if they need an umbrella. " +
               "Returns temperature in Celsius and conditions.",
  input_schema: {
    type: "object" as const,
    properties: {
      city: {
        type: "string" as const,
        description: "City name, e.g. 'Tel Aviv', 'Jerusalem'"
      }
    },
    required: ["city"]
  }
}];

// שליחת בקשה עם כלים
const response = await client.messages.create({
  model: "claude-sonnet-4-20250514",
  max_tokens: 1024,
  tools,
  messages: [{ role: "user", content: "מה מזג האוויר בתל אביב?" }]
});

console.log(response.content);
// [{ type: 'tool_use', name: 'get_weather', input: { city: 'Tel Aviv' } }]
עשה עכשיו 5 דקות

כתבו הגדרת כלי (Tool Definition) עבור הכלי הבא: כלי ששולח הודעת WhatsApp ללקוח. חשבו על:

  1. מה השם? (רמז: send_whatsapp_message)
  2. מה ה-Description? (כללו: מתי להשתמש, מתי לא, ומה הכלי מחזיר)
  3. מה ה-Input Schema? (אילו פרמטרים הכלי צריך?)

אל תכתבו קוד — רק את ההגדרה ב-JSON או במילים. זה התרגיל הכי חשוב בפרק הזה.

בינוני 12 דקות חינם

State Management — ניהול מצב

סוכן צריך "לזכור" באיזה שלב הוא נמצא. האם הוא כבר חיפש טיסות? האם המשתמש אישר את ההזמנה? האם הייתה שגיאה שצריך לטפל בה? State Management (ניהול מצב) הוא האופן שבו הסוכן עוקב אחרי ההתקדמות שלו.

שלושה דפוסים של ניהול מצב

דפוס איך עובד יתרונות חסרונות מתי להשתמש
Implicit State
(מצב מרומז)
כל ההיסטוריה ב-Context Window. המודל "זוכר" את מה שקרה דרך ההודעות הקודמות פשוט, אין קוד נוסף Context Window מתמלא, אין persistency סוכנים פשוטים, שיחות קצרות
Explicit State
(מצב מפורש)
אובייקט State שעוקב אחרי משתנים: { step: "search_done", results: [...], approved: false } ברור, ניתן ל-debug, ניתן לשמירה צריך לכתוב לוגיקה, מסובך יותר סוכנים עם Workflow מוגדר
State Machine
(מכונת מצבים)
הגדרה מראש של מצבים אפשריים ומעברים ביניהם. כמו תרשים זרימה צפוי מאוד, בטוח, ניתן ל-audit פחות גמיש, לא מתאים למשימות פתוחות Production, Compliance, תהליכים רגולטוריים

השוואה מעשית — מתי להשתמש באיזה דפוס

כל שלושת הדפוסים עובדים. הבחירה תלויה בסוג המשימה:

קריטריון Implicit State Explicit State State Machine
קושי Implementation אפס — ברירת מחדל נמוך-בינוני בינוני-גבוה
Debugging קשה — צריך לקרוא Context בינוני — מדפיסים State קל — רואים בדיוק באיזה מצב
Persistence אין — נמחק בסוף שיחה אפשרי — שומרים לקובץ/DB מובנה — Checkpointing
Auditability נמוכה בינונית גבוהה — כל מעבר מתועד
דוגמה ל-SDK Claude Agent SDK, OpenAI Agents SDK Vercel AI SDK (ToolLoopAgent) LangGraph (StateGraph), Google ADK
מתאים ל Chatbots, שאלות פשוטות Workflows בינוניים Compliance, Finance, Healthcare
כלל אצבע לבחירת State Management

שאלו את עצמכם שלוש שאלות: (1) האם צריך לשחזר את מצב הסוכן אחרי Crash? אם כן — State Machine. (2) האם צריך audit trail (למשל: רגולציה פיננסית)? אם כן — State Machine. (3) האם הסוכן פשוט (2-3 כלים, שיחה קצרה)? אם כן — Implicit State מספיק. ברוב המקרים, Implicit State + כלים טובים הם כל מה שצריך.

State Machine — דוגמה מעשית

נניח שבונים סוכן שמטפל בבקשת החזר כספי. הנה מכונת המצבים:

מצבים אפשריים:
┌──────────────────────────────────────────────────────────┐
│                                                          │
│  [START] ──> [COLLECTING_INFO] ──> [CHECKING_POLICY]    │
│                                        │                 │
│                                   ┌────┴────┐            │
│                              [APPROVED]  [DENIED]        │
│                                   │         │            │
│                           [PROCESSING]  [EXPLAINING]     │
│                                   │         │            │
│                               [DONE]    [ESCALATE]       │
│                                                          │
└──────────────────────────────────────────────────────────┘

מעברים:
  START → COLLECTING_INFO:  כשמגיע בקשה חדשה
  COLLECTING_INFO → CHECKING_POLICY:  כשיש את כל הפרטים
  CHECKING_POLICY → APPROVED:  כשהבקשה עומדת במדיניות
  CHECKING_POLICY → DENIED:  כשהבקשה לא עומדת במדיניות
  APPROVED → PROCESSING:  אוטומטי
  DENIED → EXPLAINING:  מסביר למשתמש
  DENIED → ESCALATE:  אם המשתמש מבקש מנהל
  PROCESSING → DONE:  החזר בוצע

למה זה חשוב? כי ב-State Machine אתם שולטים במה הסוכן יכול לעשות בכל רגע. הוא לא יכול "לקפוץ" ממצב START ישר ל-PROCESSING — הוא חייב לעבור דרך כל השלבים. זה מה שהופך סוכנים ל-Production-ready.

SDKs כמו LangGraph בנויים סביב State Machines — כל ה-Architecture שלהם היא גרף של מצבים ומעברים. Google ADK מציע SequentialAgent, ParallelAgent, ו-LoopAgent שהם State Machines מוכנים. ב-OpenAI Agents SDK וב-Claude Agent SDK, הדפוס הוא יותר Implicit — אבל אפשר לבנות State Machine מעל.

עשה עכשיו 3 דקות

צרו State Machine פשוט לסוכן שמזמין פיצה. רשמו:

  1. 4-6 מצבים (States) — מ-START ועד DELIVERED
  2. המעברים (Transitions) בין המצבים
  3. באיזה מצב הסוכן צריך אישור מהמשתמש? (Human-in-the-Loop)
מתחיל 15 דקות חינם

Memory — זיכרון קצר-טווח, ארוך-טווח ואפיזודי

סוכן ללא זיכרון הוא כמו עוזר שמתחיל כל יום מאפס — לא זוכר שדיברתם אתמול, לא יודע שאתם אלרגיים לבוטנים, לא מכיר את ההעדפות שלכם. זיכרון הוא מה שהופך סוכן ממועיל ל-valuable.

אפשר לחשוב על הזיכרון של סוכן בדיוק כמו הזיכרון של מחשב — בהיררכיה של מהירות, גודל, ועלות:

סוג זיכרון אנלוגיה למחשב בסוכן AI גודל מהירות עלות שורד Restart?
Working Memory Registers / Cache התור הנוכחי — מה הסוכן חושב עכשיו קטן מיידי $0 לא
Short-term Memory RAM Context Window — כל היסטוריית השיחה עד 1M טוקנים מהיר $$ לא
Episodic Memory SSD Cache זכרונות של אירועים: "ב-15/3 הלקוח ביקש החזר" בינוני בינוני $ כן
Long-term Memory Hard Disk Vector Store, מסד נתונים, קבצי זיכרון ללא הגבלה איטי $ כן

Short-term Memory — Context Window

הזיכרון קצר-הטווח של סוכן הוא פשוט כל ההודעות שנשלחו עד כה — ההודעה של המשתמש, התשובה של המודל, קריאות הכלים, והתוצאות שחזרו. הכול נכנס ל-Context Window ונשלח למודל בכל סיבוב.

הבעיה? Context Window מוגבל. Claude: 1M טוקנים (כ-750,000 מילים). GPT-5: 1M טוקנים. Gemini 2.5 Pro: 1M טוקנים. נשמע הרבה, אבל כשסוכן מבצע 50 קריאות כלים ומקבל תוצאות ארוכות — זה מתמלא מהר.

שלוש אסטרטגיות לניהול Context Window:

Episodic Memory — זיכרון אירועים

זיכרון אפיזודי שומר אירועים ספציפיים ומשלב אותם בהמשך. זה הרעיון שמאחורי מה שנקרא Agentic Memory (מגמה חדשה ב-2026) — הסוכן עצמו מחליט מה שווה לזכור ומה לשכוח, במקום שהמפתח יגדיר הכול מראש.

דוגמאות לזיכרון אפיזודי:

Long-term Memory — Vector Store ומעבר

לזיכרון ארוך-טווח יש כמה פתרונות, מהפשוט למורכב:

פתרון איך עובד מתאים ל דוגמה
Key-Value Store שומרים נתונים מובנים: user_prefs = { lang: "he", timezone: "Asia/Jerusalem" } העדפות, הגדרות, נתונים מובנים Redis, Cloudflare KV
Vector Store (RAG) ממירים טקסט ל-Embeddings (וקטורים), שומרים, ומחפשים לפי דמיון סמנטי בסיסי ידע, מסמכים, שיחות קודמות Pinecone, Weaviate, pgvector
File-based Memory קבצי Markdown/JSON שהסוכן קורא וכותב. דפוס CLAUDE.md סוכני פיתוח, זיכרון פרויקטים Claude Code, Cursor
Database מסד נתונים רלציוני עם שאילתות SQL היסטוריית אינטראקציות, לוגים, ניתוח PostgreSQL, SQLite, D1
כלל אצבע לבחירת זיכרון

התחילו בפשוט. רוב הסוכנים לא צריכים Vector Store מהיום הראשון. התחילו עם Context Window (חינם, מיידי). הוסיפו Key-Value Store כשצריך לזכור העדפות בין שיחות. הוסיפו Vector Store רק כשבסיס הידע גדול מדי ל-Context Window (מעל 100K מילים).

דפוסים מעשיים לניהול זיכרון

לכל סוג זיכרון יש דפוסים שעובדים בפרודקשן. הנה שלושה דפוסים שתשתמשו בהם שוב ושוב:

דפוס 1: Summarization Buffer — סיכום אוטומטי

כשהשיחה מתארכת, מסכמים את ההיסטוריה ומחליפים אותה בסיכום קומפקטי. חוסך טוקנים, שומר את המידע החשוב:

# Python — Summarization Buffer Pattern
def manage_context_with_summary(
    messages: list,
    model,
    max_messages: int = 20,
    summary_so_far: str = ""
) -> tuple[list, str]:
    """
    כשמספר ההודעות חורג מ-max_messages:
    1. מסכם את ההודעות הישנות
    2. שומר רק את ה-summary + הודעות אחרונות
    """
    if len(messages) <= max_messages:
        return messages, summary_so_far

    # ההודעות שנסכם (החצי הראשון)
    to_summarize = messages[:max_messages // 2]
    to_keep = messages[max_messages // 2:]

    # בניית prompt לסיכום
    summary_prompt = f"""Previous summary: {summary_so_far}

New messages to incorporate:
{format_messages(to_summarize)}

Write a concise summary of the full conversation so far.
Focus on: user preferences, decisions made, pending items."""

    new_summary = model.invoke(summary_prompt).content

    # מחזירים: הודעת summary + הודעות שנשארו
    summary_msg = {"role": "system",
                   "content": f"Conversation summary: {new_summary}"}
    return [summary_msg] + to_keep, new_summary

דפוס 2: User Profile Memory — זיכרון משתמש

// TypeScript — User Profile Pattern
interface UserProfile {
  userId: string;
  name?: string;
  language: "he" | "en" | "auto";
  preferences: Record<string, string>;
  recentTopics: string[];
  lastInteraction: string;
}

class UserMemory {
  private profiles: Map<string, UserProfile> = new Map();

  getSystemPromptAddition(userId: string): string {
    const profile = this.profiles.get(userId);
    if (!profile) return "";

    const parts: string[] = [];
    if (profile.name) parts.push(`User's name: ${profile.name}`);
    if (profile.language !== "auto")
      parts.push(`Preferred language: ${profile.language}`);
    if (Object.keys(profile.preferences).length > 0)
      parts.push(
        `Preferences: ${JSON.stringify(profile.preferences)}`
      );
    if (profile.recentTopics.length > 0)
      parts.push(
        `Recent topics: ${profile.recentTopics.slice(-3).join(", ")}`
      );

    return parts.length > 0
      ? `\n\nKnown user info:\n${parts.join("\n")}`
      : "";
  }

  updateFromConversation(
    userId: string,
    topic: string,
    extractedPrefs: Record<string, string>
  ) {
    const profile = this.profiles.get(userId) ?? {
      userId,
      language: "auto",
      preferences: {},
      recentTopics: [],
      lastInteraction: new Date().toISOString(),
    };
    profile.recentTopics.push(topic);
    if (profile.recentTopics.length > 10)
      profile.recentTopics.shift(); // Sliding window
    Object.assign(profile.preferences, extractedPrefs);
    profile.lastInteraction = new Date().toISOString();
    this.profiles.set(userId, profile);
  }
}

דפוס 3: Tool Result Cache — קאש תוצאות כלים

כשכלי מחזיר תוצאה זהה לאותם פרמטרים, אין טעם לקרוא לו שוב. Cache פשוט חוסך tokens, זמן, ועלויות API חיצוניים:

# Python — Tool Result Cache
import hashlib
import json
from datetime import datetime, timedelta

class ToolCache:
    def __init__(self, ttl_minutes: int = 30):
        self.cache = {}
        self.ttl = timedelta(minutes=ttl_minutes)

    def _key(self, tool_name: str, args: dict) -> str:
        raw = f"{tool_name}:{json.dumps(args, sort_keys=True)}"
        return hashlib.md5(raw.encode()).hexdigest()

    def get(self, tool_name: str, args: dict):
        key = self._key(tool_name, args)
        if key in self.cache:
            result, timestamp = self.cache[key]
            if datetime.now() - timestamp < self.ttl:
                return result  # Cache hit!
        return None  # Cache miss

    def set(self, tool_name: str, args: dict, result):
        key = self._key(tool_name, args)
        self.cache[key] = (result, datetime.now())

# שימוש בלולאת ReAct
cache = ToolCache(ttl_minutes=15)

def execute_tool_cached(tool_name, args, tool_fn):
    cached = cache.get(tool_name, args)
    if cached:
        print(f"  [Cache HIT] {tool_name}")
        return cached
    result = tool_fn(**args)
    cache.set(tool_name, args, result)
    print(f"  [Cache MISS] {tool_name} — called API")
    return result
שלושת הדפוסים ביחד

בפרודקשן, תשתמשו בשלושת הדפוסים במקביל: Summarization Buffer מנהל את גודל ה-Context Window, User Profile Memory שומר העדפות בין שיחות, ו-Tool Cache חוסך קריאות API כפולות. השילוב הזה לבד יכול להוריד עלויות ב-30-50% ולשפר UX משמעותית.

Scratchpad Pattern — פנקס טיוטה

דפוס שימושי: הסוכן כותב "הערות לעצמו" במהלך משימה מורכבת. למשל, סוכן מחקר שקורא 10 מאמרים — הוא כותב סיכום ביניים אחרי כל מאמר, ובסוף משתמש בכל הסיכומים כדי לכתוב דוח אחד.

# Python — Scratchpad Pattern
scratchpad = []

for article in articles:
    summary = agent.summarize(article)
    scratchpad.append({
        "source": article.url,
        "key_points": summary,
        "relevance": agent.rate_relevance(summary, user_query)
    })

# הסוכן משתמש ב-scratchpad כ-input לכתיבת הדוח
final_report = agent.write_report(
    query=user_query,
    notes=scratchpad
)
עשה עכשיו 2 דקות

לאיזה סוג זיכרון מתאים כל מקרה? רשמו ליד כל אחד: Short-term, Episodic, או Long-term.

  1. הסוכן צריך לזכור מה המשתמש אמר לפני 5 הודעות → _______
  2. הסוכן צריך לדעת שהלקוח מעדיף אנגלית → _______
  3. הסוכן צריך גישה ל-500 עמודי תיעוד מוצר → _______
  4. הסוכן צריך לדעת שאתמול הוא ניסה API X וזה נכשל → _______

תשובות: 1=Short-term, 2=Long-term (KV), 3=Long-term (Vector), 4=Episodic

בינוני 15 דקות חינם

Multi-Agent Architecture — דפוסי ריבוי סוכנים

סוכן יחיד עם כלים טובים יכול לעשות הרבה. אבל לפעמים צריך יותר — מספר סוכנים שעובדים יחד, כל אחד עם תפקיד ומומחיות שונה. זה Multi-Agent Architecture.

אזהרה חשובה: אל תקפצו ל-Multi-Agent

אחת הטעויות הנפוצות ביותר: לקפוץ ישר לארכיטקטורת Multi-Agent כי "זה נשמע מגניב". המציאות: סוכן יחיד עם כלים טובים מנצח מערכת Multi-Agent בינונית ברוב המקרים. Multi-Agent קשה יותר ל-debug, יקר יותר להרצה, ומורכב יותר לתחזוקה.

כלל האצבע: "האם הייתם שוכרים צוות של 5 אנשים למשימה הזו, או שעובד אחד טוב מספיק?" אם עובד אחד מספיק — Single Agent.

ארבעת הדפוסים הגדולים

Framework: עץ ההחלטה — בחירת ארכיטקטורה

השתמשו בשאלות הבאות כדי לבחור את הדפוס הנכון:

דפוס מתי להשתמש כמה כלים? כמה דומיינים? דוגמה עלות יחסית
Pattern 1: Single Agent משימה ברורה, תחום אחד < 10 1 סוכן שעונה על שאלות ובודק סטטוס הזמנה $
Pattern 2: Router בקשות מגוונות, כל אחת פשוטה 5-20 3-5 Help Desk שמנתב ל-billing / tech / sales $$
Pattern 3: Orchestrator-Worker משימה מורכבת שמתפרקת לתתי-משימות 10-30 3-10 מחקר שוק: חיפוש + ניתוח + כתיבת דוח $$$
Pattern 4: Collaborative משימות יצירתיות, ביקורת, ולידציה 10+ 2-5 צוות כתיבה: חוקר + כותב + עורך + Fact-checker $$$$

שאלון מהיר:

  1. האם למשימה יש תחום אחד ופשוט? → Single Agent
  2. האם צריך לנתב בקשות לתחומים שונים? → Router
  3. האם המשימה מתפרקת לתתי-משימות עצמאיות? → Orchestrator-Worker
  4. האם צריך "דיון" או ביקורת בין סוכנים? → Collaborative

Pattern 1: Single Agent — סוכן יחיד

סוכן אחד, LLM אחד, כמה כלים. הדפוס הפשוט ביותר:

┌─────────────────────────────┐
│         USER                │
│   "בדוק סטטוס הזמנה 12345" │
└──────────┬──────────────────┘
           │
    ┌──────▼──────┐
    │  SINGLE     │
    │  AGENT      │──── [check_order] ──── [send_email]
    │  (LLM)      │──── [lookup_user] ──── [update_db]
    └──────┬──────┘
           │
    ┌──────▼──────────────────┐
    │  "ההזמנה שלך בדרך!     │
    │   צפי הגעה: מחר 14:00" │
    └─────────────────────────┘

Pattern 2: Router Agent — ניתוב

┌─────────────────────────────┐
│         USER                │
│   "יש לי בעיה עם החיוב"   │
└──────────┬──────────────────┘
           │
    ┌──────▼──────┐
    │  ROUTER     │ ← מודל זול (Haiku/Flash)
    │  AGENT      │   מנתח ומחליט לאן לנתב
    └──┬───┬───┬──┘
       │   │   │
  ┌────▼┐ ┌▼───┐ ┌▼────┐
  │BILL-│ │TECH│ │SALES│  ← סוכנים מתמחים
  │ING  │ │    │ │     │    (מודלים חזקים)
  └─────┘ └────┘ └─────┘

טיפ עלויות: ב-Router Pattern, הסוכן שמנתב (Router) לא צריך מודל חזק — הוא רק מחליט "זה billing או tech?". השתמשו במודל זול כמו Claude Haiku ($0.25/M input) או Gemini Flash ($0.30/M input) לניתוב, ובמודל חזק כמו Claude Sonnet או GPT-5 לסוכנים המתמחים. זה חוסך 80-90% בעלויות.

Pattern 3: Orchestrator-Worker

┌──────────────────────────────────┐
│  USER: "כתוב דוח מחקר שוק      │
│         על שוק ה-AI בישראל"     │
└───────────┬──────────────────────┘
            │
     ┌──────▼──────┐
     │ ORCHESTRATOR│ ← מתכנן, מחלק משימות, אוסף
     │   (מנהל)    │
     └──┬───┬───┬──┘
        │   │   │
   ┌────▼┐ ┌▼───┐ ┌▼─────┐
   │SEARCH│ │DATA│ │WRITE │ ← Workers
   │WORKER│ │    │ │      │    עובדים במקביל
   └──┬───┘ └─┬──┘ └──┬───┘
      │       │       │
   ┌──▼───────▼───────▼──┐
   │   ORCHESTRATOR       │ ← אוסף תוצאות, מרכיב
   │   אוסף ומרכיב       │
   └──────────┬───────────┘
              │
       ┌──────▼──────┐
       │  דוח מחקר   │
       │  מלא ומעוצב │
       └─────────────┘

Pattern 4: Collaborative — שיתופי

    ┌─────────┐     ┌──────────┐
    │ WRITER  │────▶│ EDITOR   │
    │ כותב    │◀────│ עורך     │
    └────┬────┘     └────┬─────┘
         │               │
         ▼               ▼
    ┌─────────────────────────┐
    │     SHARED STATE        │ ← מסמך משותף
    │  (מצב משותף לכל        │    שכולם רואים
    │   הסוכנים)              │    ועובדים עליו
    └─────────────────────────┘
         ▲               ▲
         │               │
    ┌────┴────┐     ┌────┴─────┐
    │RESEARCH │     │FACT-     │
    │חוקר     │     │CHECKER   │
    └─────────┘     └──────────┘

תקשורת בין סוכנים

כשיש מספר סוכנים, הם צריכים דרך לתקשר. ארבע שיטות עיקריות:

שיטה איך עובד SDK שמשתמש
Handoff סוכן A מעביר את השיחה לסוכן B עם Context OpenAI Agents SDK, Claude Agent SDK
Shared State כל הסוכנים קוראים וכותבים ל-State משותף LangGraph, Google ADK
Message Passing סוכנים שולחים הודעות אחד לשני דרך Queue AG2, Microsoft Agent Framework
Event-driven סוכנים מגיבים לאירועים (Events) שסוכנים אחרים מייצרים Mastra, מערכות מותאמות
עשה עכשיו 3 דקות

לאיזה Pattern מתאימה כל משימה?

  1. סוכן שעונה על שאלות על מוצר → _______
  2. Help Desk עם 4 מחלקות → _______
  3. כתיבת בלוג פוסט עם מחקר, כתיבה ועריכה → _______
  4. Code Review עם הצעות שיפור → _______

תשובות: 1=Single, 2=Router, 3=Orchestrator-Worker, 4=Collaborative

בינוני 10 דקות חינם

Error Handling & Retry — טיפול בשגיאות

סוכני AI נכשלים. לא "אולי" — בוודאות. על משימות מורכבות, סוכנים נכשלים ב-10-30% מהמקרים. הבדל בין סוכן חובבני לסוכן Production הוא לא שהסוכן לא נכשל — אלא מה קורה כשהוא נכשל.

סוגי כשלונות

סוג דוגמה תגובה נכונה
Tool Failure API חיצוני מחזיר Error 500 Retry עם Exponential Backoff (1s, 2s, 4s). אחרי 3 ניסיונות — Fallback או הודעה למשתמש
Rate Limit Error 429 — Too Many Requests חכה לפי ה-Retry-After Header ונסה שוב. הפחת קצב
Invalid Output המודל מחזיר JSON שבור או תשובה לא רלוונטית בקש מהמודל לנסות שוב עם הנחיה ברורה יותר
Infinite Loop הסוכן חוזר על אותה פעולה שוב ושוב Max Iterations עוצר אותו. בדקו את ה-prompt
Context Overflow יותר מדי מידע ב-Context Window סכמו את ההיסטוריה, הסירו מידע לא רלוונטי
Hallucination הסוכן "ממציא" מידע שלא קיים Guardrails — בדיקת הפלט. בקשו ציטוטים ומקורות

Retry Strategy — Python

import time
import random

def call_tool_with_retry(tool_fn, args, max_retries=3):
    """
    קורא לכלי עם Exponential Backoff.
    ניסיון 1: מיידי, ניסיון 2: 1-2 שניות, ניסיון 3: 2-4 שניות.
    """
    for attempt in range(max_retries):
        try:
            result = tool_fn(**args)
            return result
        except RateLimitError:
            wait = (2 ** attempt) + random.uniform(0, 1)
            print(f"Rate limited. Waiting {wait:.1f}s (attempt {attempt + 1})")
            time.sleep(wait)
        except ToolError as e:
            if attempt == max_retries - 1:
                return {"error": f"Tool failed after {max_retries} attempts: {e}"}
            time.sleep(1)

    return {"error": "Max retries exceeded"}

Retry Strategy — TypeScript

async function callToolWithRetry(
  toolFn: Function,
  args: Record<string, unknown>,
  maxRetries = 3
): Promise<unknown> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await toolFn(args);
    } catch (error) {
      if (error instanceof RateLimitError) {
        const wait = 2 ** attempt + Math.random();
        console.log(`Rate limited. Waiting ${wait.toFixed(1)}s (attempt ${attempt + 1})`);
        await new Promise(r => setTimeout(r, wait * 1000));
      } else if (attempt === maxRetries - 1) {
        return { error: `Tool failed after ${maxRetries} attempts: ${error}` };
      } else {
        await new Promise(r => setTimeout(r, 1000));
      }
    }
  }
  return { error: "Max retries exceeded" };
}

Fallback Strategies

כשכלי נכשל אחרי כל הניסיונות, יש כמה אפשרויות:

שלוש טעויות נפוצות בטיפול בשגיאות
  1. Retry בלי Backoff — שולחים 100 בקשות בשנייה, ה-API חוסם אתכם לשעה. תמיד השתמשו ב-Exponential Backoff
  2. Retry לנצח — הגדירו מגבלה (3-5 ניסיונות). אחרי זה — Fallback או הודעה למשתמש
  3. הסתרת שגיאות — הסוכן נכשל בשקט ונותן תשובה שגויה. תמיד תיעדו (Log) שגיאות
מתחיל 10 דקות חינם

The Agent Stack — שכבות ההפשטה

כמו שבפיתוח Web יש את ה-"Tech Stack" (React + Node + PostgreSQL + AWS), גם לסוכני AI יש Stack משלהם. הבנת שבע השכבות תעזור לכם לבחור SDK, לתכנן ארכיטקטורה, ולהבין מה בדיוק כל SDK נותן ומה צריך לבנות בעצמכם.

Framework: The Agent Stack — 7 שכבות
שכבה תפקיד דוגמאות מי בונה?
7. Observability
שקיפות
Logging, Tracing, Monitoring, Cost Tracking LangSmith, Braintrust, Helicone, custom logs אתם + כלי צד שלישי
6. Interface
ממשק
איך המשתמש מתקשר עם הסוכן CLI, Web UI, Slack bot, API endpoint, WhatsApp אתם
5. Orchestration
תזמור
ניהול ריבוי סוכנים, ניתוב, Workflows LangGraph, CrewAI, Google ADK Workflows SDK + אתם
4. Memory & State
זיכרון ומצב
שמירת מידע בין סיבובים ובין שיחות Vector stores, Redis, PostgreSQL, קבצים SDK (חלקי) + אתם
3. Tools & MCP
כלים
פעולות שהסוכן יכול לבצע MCP servers, Custom tools, API integrations SDK + MCP + אתם
2. SDK / Framework הספרייה שמנהלת את הלולאה, הכלים, וה-State Claude Agent SDK, OpenAI Agents SDK, Vercel AI SDK, LangGraph, CrewAI, Google ADK SDK Provider
1. Model Provider
ספק מודל
ה-LLM עצמו — "המוח" Anthropic API, OpenAI API, Google API, DeepSeek, Mistral Model Provider

כל SDK מכסה שכבות אחרות. למשל:

עשה עכשיו 3 דקות

עיצבו Agent Stack לסוכן תמיכת לקוחות. בחרו טכנולוגיה לכל שכבה:

  1. Model Provider: _________ (איזה מודל?)
  2. SDK: _________ (איזה Framework?)
  3. Tools: _________ (אילו כלים? 3 לפחות)
  4. Memory: _________ (איזה סוג?)
  5. Orchestration: _________ (Single/Router/Orchestrator?)
  6. Interface: _________ (Web? Slack? WhatsApp?)
  7. Observability: _________ (מה אתם מנטרים?)
מתחיל 10 דקות חינם

Agent Development Lifecycle — מחזור חיי פיתוח

בניית סוכן AI זה לא "כתוב קוד, Deploy, תשכח." סוכנים הם מערכות שדורשות שיפור מתמיד — כי הם לא דטרמיניסטיים. אותה שאלה יכולה לתת תשובות שונות ביומים שונים. לכן יש מחזור חיים ייחודי לפיתוח סוכנים:

שלב מה עושים כלים זמן מוערך
1. Define
הגדרה
מגדירים 10 תרחישים ספציפיים. "מה הסוכן צריך לעשות? ב-10 מקרים קונקרטיים." מסמך דרישות, User Stories 2-4 שעות
2. Prototype
אב-טיפוס
סוכן מינימלי שעובד על 1-2 מקרים. ללא Production — רק הוכחת היתכנות API ישיר או SDK פשוט 4-8 שעות
3. Evaluate
הערכה
בונים Golden Dataset — 50-100 מקרי בדיקה עם תשובות מצופות. מודדים Pass Rate Braintrust, LangSmith, סקריפט Python 4-8 שעות
4. Iterate
איטרציה
משפרים Prompts, כלים, ו-Logic. מריצים Evals אחרי כל שינוי Git (גרסאות prompts), Eval suite 8-20 שעות
5. Harden
חיזוק
מוסיפים Guardrails, Error Handling, Rate Limits, Cost Caps Code review, Penetration testing 4-8 שעות
6. Deploy
פריסה
Canary deployment — 5% מהתעבורה, ניטור, הרחבה בהדרגה CI/CD, Monitoring, Alerts 2-4 שעות
7. Monitor & Refine
ניטור ושיפור
מנטרים ביצועים בזמן אמת. Prompt Tuning מתמשך Dashboards, Logs, Cost tracking שוטף
טעות: "Deploy and Forget"

סוכנים הם לא תוכנה רגילה. אי אפשר לפרוס ולשכוח. המודלים משתנים, ה-APIs משתנים, והמשתמשים מפתיעים. תכננו מראש זמן שבועי לניטור ושיפור — 1-2 שעות בשבוע מספיקות לסוכן בינוני.

בינוני 60 דקות פרקטי

תרגילים מעשיים

25 דק' תרגיל 1: סוכן מינימלי ב-20 שורות Python

בנו סוכן ReAct מינימלי שיכול לענות על שאלות ולהשתמש בכלי מזג אוויר. ללא SDK — רק API ישיר.

Python:

import anthropic, json

client = anthropic.Anthropic()  # ANTHROPIC_API_KEY from env

tools = [{
    "name": "get_weather",
    "description": "Get weather for a city. Returns temp (C) and conditions.",
    "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
    }
}]

def get_weather(city: str) -> str:
    # בפרויקט אמיתי — קריאה ל-API. כאן: Mock
    data = {"Tel Aviv": "28°C, Sunny", "Jerusalem": "22°C, Partly Cloudy"}
    return data.get(city, f"No data for {city}")

def run_agent(user_msg: str, max_loops: int = 10) -> str:
    msgs = [{"role": "user", "content": user_msg}]

    for _ in range(max_loops):
        resp = client.messages.create(
            model="claude-sonnet-4-20250514", max_tokens=1024,
            system="You are a helpful weather assistant. Answer in Hebrew.",
            tools=tools, messages=msgs
        )

        # בודקים אם יש Tool Use
        tool_calls = [b for b in resp.content if b.type == "tool_use"]
        if not tool_calls:
            # אין קריאות כלים — התשובה מוכנה
            return "".join(b.text for b in resp.content if b.type == "text")

        # מוסיפים את תגובת המודל ומבצעים כלים
        msgs.append({"role": "assistant", "content": resp.content})
        for tc in tool_calls:
            result = get_weather(**tc.input)
            msgs.append({
                "role": "user",
                "content": [{"type": "tool_result", "tool_use_id": tc.id,
                             "content": result}]
            })

    return "הגעתי למגבלת הסיבובים"

# הרצה
print(run_agent("מה מזג האוויר בתל אביב ובירושלים?"))

TypeScript:

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic(); // ANTHROPIC_API_KEY from env

const tools: Anthropic.Tool[] = [{
  name: "get_weather",
  description: "Get weather for a city. Returns temp (C) and conditions.",
  input_schema: {
    type: "object" as const,
    properties: { city: { type: "string" as const } },
    required: ["city"]
  }
}];

function getWeather(city: string): string {
  const data: Record<string, string> = {
    "Tel Aviv": "28°C, Sunny", "Jerusalem": "22°C, Partly Cloudy"
  };
  return data[city] ?? `No data for ${city}`;
}

async function runAgent(userMsg: string, maxLoops = 10): Promise<string> {
  const msgs: Anthropic.MessageParam[] = [{ role: "user", content: userMsg }];

  for (let i = 0; i < maxLoops; i++) {
    const resp = await client.messages.create({
      model: "claude-sonnet-4-20250514", max_tokens: 1024,
      system: "You are a helpful weather assistant. Answer in Hebrew.",
      tools, messages: msgs
    });

    const toolCalls = resp.content.filter(b => b.type === "tool_use");
    if (toolCalls.length === 0) {
      return resp.content
        .filter(b => b.type === "text")
        .map(b => (b as Anthropic.TextBlock).text)
        .join("");
    }

    msgs.push({ role: "assistant", content: resp.content });
    for (const tc of toolCalls) {
      const result = getWeather((tc as Anthropic.ToolUseBlock).input.city as string);
      msgs.push({
        role: "user",
        content: [{
          type: "tool_result", tool_use_id: (tc as Anthropic.ToolUseBlock).id,
          content: result
        }]
      });
    }
  }
  return "הגעתי למגבלת הסיבובים";
}

runAgent("מה מזג האוויר בתל אביב ובירושלים?").then(console.log);

מה לשים לב:

20 דק' תרגיל 2: הוספת Memory לסוכן

קחו את הסוכן מתרגיל 1 והוסיפו לו זיכרון פשוט — כך שהוא "יזכור" מידע בין שיחות.

Python — הוספת זיכרון מבוסס קובץ:

import json
from pathlib import Path

MEMORY_FILE = Path("agent_memory.json")

def load_memory() -> dict:
    if MEMORY_FILE.exists():
        return json.loads(MEMORY_FILE.read_text())
    return {"preferences": {}, "history": []}

def save_memory(memory: dict):
    MEMORY_FILE.write_text(json.dumps(memory, ensure_ascii=False, indent=2))

def run_agent_with_memory(user_msg: str) -> str:
    memory = load_memory()

    # הוספת context מהזיכרון ל-System Prompt
    system = "You are a helpful assistant. Answer in Hebrew.\n"
    if memory["preferences"]:
        system += f"User preferences: {json.dumps(memory['preferences'])}\n"
    if memory["history"]:
        system += f"Recent interactions: {json.dumps(memory['history'][-5:])}\n"

    # הרצת הסוכן (כמו קודם, עם system מעודכן)
    result = run_agent_with_system(user_msg, system)

    # שמירת האינטראקציה בזיכרון
    memory["history"].append({
        "query": user_msg,
        "response": result[:200],  # שומרים רק 200 תווים ראשונים
        "timestamp": "2026-03-24"
    })
    save_memory(memory)

    return result

משימות:

  1. הריצו את הסוכן 3 פעמים ברצף ובדקו שה-memory.json גדל
  2. הוסיפו יכולת לשמור העדפות ("אני מעדיף תשובות קצרות" → נשמר ומשפיע)
  3. הוסיפו מגבלה: שמרו רק 20 אינטראקציות אחרונות (Sliding Window)
15 דק' תרגיל 3: כתיבת Tool Design Checklist

כתבו הגדרות כלים (Tool Definitions) עבור סוכן תמיכת לקוחות של חנות אונליין. הסוכן צריך 4 כלים:

  1. lookup_order — חיפוש הזמנה לפי מספר הזמנה
  2. check_inventory — בדיקת מלאי של מוצר
  3. issue_refund — ביצוע החזר כספי (פעולה הרסנית — צריכה אישור!)
  4. send_notification — שליחת הודעה ללקוח

לכל כלי כתבו: name, description (3 משפטים לפחות: מה, מתי, מתי לא), input_schema.

בונוס: כתבו את זה בפורמט JSON Schema תקני. נסו את ההגדרות ב-Claude ובדקו: האם המודל בוחר את הכלי הנכון?

15 דק' תרגיל 4: כתיבת 10 Test Cases וחישוב Pass Rate

קחו את הסוכן מתרגיל 1 (מזג אוויר) וכתבו 10 מקרי בדיקה:

# שאלה תוצאה מצופה Pass/Fail
1"מה הטמפרטורה בתל אביב?"קורא ל-get_weather עם "Tel Aviv"
2"האם צריך מטריה בירושלים?"קורא ל-get_weather עם "Jerusalem"
3"מה מזג האוויר?"שואל באיזו עיר (אין מספיק מידע)
4"מה הבירה של ישראל?"עונה בלי לקרוא לכלי (שאלה לא קשורה)
5"השווה מזג אוויר בת"א וירושלים"קורא ל-get_weather פעמיים
6-10הוסיפו 5 שאלות נוספות בעצמכם — כולל Edge Cases

הריצו את הסוכן על כל 10 המקרים ורשמו Pass/Fail. אם Pass Rate מתחת ל-80% — שפרו את ה-System Prompt או את ה-Tool Description.

מתחיל 8 דקות חינם

טעויות נפוצות — וידוי ארכיטקטורי

# הטעות למה זה בעיה מה לעשות במקום
1 Building a "God Agent" — סוכן שמנסה לעשות הכול נכשל בהכול. יותר מדי כלים = בלבול. יותר מדי תפקידים = דיוק נמוך התחילו עם Single Agent לתפקיד אחד. הוסיפו יכולות בהדרגה
2 No stop conditions — בלי מגבלות עצירה לולאה אינסופית = חשבון API של $500 בלילה תמיד: max_iterations, token_budget, timeout, cost_cap
3 Vague tool descriptions — תיאורי כלים עמומים המודל בוחר את הכלי הלא נכון ב-30-50% מהמקרים 3 משפטים לפחות: מה, מתי, מתי לא. דוגמאות בתיאור
4 Context flooding — שופכים את כל המידע ל-prompt יותר מידע = יותר עלות + ירידה באיכות. המודל "טובע" שלחו רק מידע רלוונטי. השתמשו ב-RAG לבסיסי ידע גדולים
5 Jumping to Multi-Agent — ריבוי סוכנים מהיום הראשון מורכבות x3, עלות x3, debugging x10. בדרך כלל מיותר בדקו: "האם עובד אחד טוב מספיק?" רק אם לא — Multi-Agent
שגרת עבודה — פרק 2
תדירותמשימהזמן
חד-פעמי בנו את הסוכן המינימלי מתרגיל 1 ושמרו אותו כ-reference 30 דק'
חד-פעמי בחרו Architecture Pattern לפרויקט שלכם (Single/Router/Orchestrator) ותעדו 15 דק'
בכל סוכן חדש כתבו 10 Test Cases לפני שכותבים שורת קוד. הגדירו מה "הצלחה" 20 דק'
בכל סוכן חדש מלאו את Agent Stack Template: 7 שכבות עם בחירות טכנולוגיות 10 דק'
שבועי (בסוכן פעיל) בדקו Eval Pass Rate — אם ירד, חקרו למה 15 דק'
שבועי (בסוכן פעיל) בדקו עלויות API ולוגים — חפשו לולאות מיותרות 10 דק'
אם אתם עושים רק דבר אחד מהפרק הזה

בנו את הסוכן המינימלי מתרגיל 1. 20 שורות Python (או TypeScript). ללא SDK. הריצו אותו, ראו את לולאת ReAct בפעולה, ותבינו שכל SDK בעולם — מ-LangGraph עם גרפים מסובכים ועד CrewAI עם צוותים שלמים — מיישם את אותה לולאה בדיוק. הקוד הזה שווה יותר מ-10 שעות קריאת תיעוד.

בדקו את עצמכם — 5 שאלות
  1. מהם 5 השלבים של לולאת ReAct, ומתי הלולאה נעצרת?
  2. למה תיאור הכלי (Tool Description) חשוב יותר מהקוד של הכלי? מה חייב להיות בתיאור טוב?
  3. מה ההבדל בין Router Agent ל-Orchestrator-Worker? תנו דוגמה לכל אחד.
  4. אילו 4 מנגנוני עצירה (Stop Conditions) חייבים להיות בכל סוכן, ולמה?
  5. מה ההבדל בין Episodic Memory ל-Long-term Memory? מתי משתמשים בכל אחד?

ענו על 4 מתוך 5? מצוין — יש לכם את הבסיס הארכיטקטורי. אפשר להמשיך לפרק 3.

סיכום הפרק

בפרק הזה למדנו את כל הבסיס הארכיטקטורי של סוכני AI:

בפרק הבא — The Tool Ecosystem: MCP, Function Calling, APIs — נלמד איך מחברים סוכנים לעולם האמיתי דרך פרוטוקול MCP, נבנה שרת MCP ראשון, ונבין את ההבדל בין כלים סטטיים לדינמיים.

צ'קליסט — סיכום פרק 2