Multi-Agent Systems & Deep Agents

תזכורת: טופולוגיות רב-סוכניות ו-Subagents

בנושא AI Agents ראינו שתי טופולוגיות עקרוניות לעבודה עם כמה סוכנים: coordinator שמאציל לעובדים מתמחים ומרכז את התשובות, ו-pipeline שבו סוכנים מעבירים עבודה ביניהם ברצף. גם את Subagents פגשנו שם — האצלת משימה ל"תת-סוכן" עם context נקי, וסיכום קצר בחזרה להורה במקום כל ההיסטוריה הגולמית.

בפרק הזה נראה איך LangChain הופך את שני הרעיונות האלה לקוד: איך סוכן אחד הופך לכלי שסוכן אחר יכול לקרוא לו, ומה זה Deep Agents — הרכבה מוכנה מראש של כל המנגנונים האלה למשימות מורכבות במיוחד.

סוכן כ-tool: הדרך הפשוטה להרכיב agents יחד

הדרך הישירה ביותר ליצור מערכת רב-סוכנית ב-LangChain היא לעטוף agent שלם בתוך tool: הפונקציה שהכלי מריץ פשוט קוראת ל-agent.invoke של הסוכן המתמחה, ומחזירה את התשובה שלו כתוצאת הכלי. מבחינת ה-coordinator שמפעיל את הכלי, אין הבדל בין 'כלי רגיל' לבין 'כלי שהוא בעצם agent שלם' — זה בדיוק העיקרון של Subagent שכבר הכרנו, רק ממומש עם tool.

TypeScript
const weatherAgent = createAgent({
  model: "anthropic:claude-haiku-4-5",
  tools: [getWeather],
  systemPrompt: "אתה מומחה למזג אוויר בלבד.",
});

const weatherTool = tool(
  async ({ question }: { question: string }) => {
    const result = await weatherAgent.invoke({
      messages: [{ role: "user", content: question }],
    });
    return result.messages.at(-1)?.content ?? "";
  },
  {
    name: "ask_weather_agent",
    description: "שואל את סוכן מזג האוויר המתמחה שאלה ומחזיר את תשובתו",
    schema: z.object({ question: z.string() }),
  }
);

Deep Agents: הארנס המורכב מוכן מראש

הרכבת agent-כ-tool ידנית עובדת מצוין לכמה סוכנים מתמחים, אבל למשימות ארוכות-טווח ומורכבות במיוחד (חקר מעמיק, כתיבת קוד רב-שלבית) יש צורך חוזר במגוון middleware יחד: תכנון עם todo lists, האצלה לתת-סוכנים, וגישה לסביבת עבודה (קבצים, sandbox). Deep Agents הוא הרכבה מוכנה מראש בדיוק של הצירוף הזה, כך שלא צריך להרכיב את כל שכבות ה-harness האלה מאפס בכל פעם.

שמות הפונקציות והאפשרויות המדויקות של Deep Agents משתנים בין גרסאות — כדאי לבדוק את התיעוד הרשמי לפרטים העדכניים. העיקרון החשוב לזכור: Deep Agents הוא עדיין createAgent מתחת למכסה המנוע, רק עם harness עשיר יותר מוגדר מראש.

מתי להשתמש במה

agent בודד עם כמה tools מתאים כשהמשימות פשוטות מספיק שסוכן אחד יכול להחזיק את כל ההקשר בראש. סוכן-כ-tool (multi-agent) מתאים כשיש תחומי אחריות ברורים ונפרדים (מזג אוויר מול הזמנות, למשל) שנהנים מ-system prompt וכלים ייעודיים משלהם, ומ-context נקי שלא מתערבב בין התחומים.

Deep Agents שווה לשקול כשהמשימה עצמה ארוכה וכוללת הרבה שלבי תכנון והאצלה מורכבים — לא בתור ברירת מחדל לכל agent, אלא כשההרכבה הידנית של planning + subagents + סביבת עבודה הופכת בעצמה לנטל.

דוגמה מלאה: coordinator עם שני agents מתמחים

בדוגמה הבאה יש שני סוכנים מתמחים — אחד למזג אוויר ואחד להזמנות — עטופים כ-tools, וסוכן coordinator שמחליט לאיזה מהם לפנות בהתאם לשאלה. שימו לב שה-config (כולל thread_id ו-userId) מועבר הלאה מה-tool לסוכן הפנימי, בדיוק כמו שהעברנו אותו לכלים רגילים בפרקים הקודמים.

TypeScript
const ordersAgent = createAgent({
  model: "anthropic:claude-haiku-4-5",
  tools: [getUserOrders],
  systemPrompt: "אתה מומחה להזמנות משתמשים בלבד.",
});

const ordersTool = tool(
  async ({ question }: { question: string }, config) => {
    const result = await ordersAgent.invoke(
      { messages: [{ role: "user", content: question }] },
      config
    );
    return result.messages.at(-1)?.content ?? "";
  },
  {
    name: "ask_orders_agent",
    description: "שואל את סוכן ההזמנות המתמחה שאלה ומחזיר את תשובתו",
    schema: z.object({ question: z.string() }),
  }
);

const coordinator = createAgent({
  model: "anthropic:claude-sonnet-5",
  tools: [weatherTool, ordersTool],
  systemPrompt: "את/ה מתאם/ת שמפנה שאלות לסוכן המתמחה הרלוונטי.",
});

const result = await coordinator.invoke(
  {
    messages: [
      { role: "user", content: "מה מזג האוויר בתל אביב, ומה ההזמנות הפתוחות שלי?" },
    ],
  },
  { configurable: { thread_id: "chat-5", userId: "user-42" } }
);