Function Calling לעומק — Tool Calling בפרודקשן

מאת צוות מדיה דיל · 09.08.2026 · AI · 10 דק׳

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

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

איך זה עובד בפועל, שלב אחר שלב

הזרימה הבסיסית של Function Calling כוללת כמה שלבים ברורים: (1) האפליקציה שולחת למודל את הפרומפט יחד עם רשימת כלים זמינים, כל אחד עם שם, תיאור וסכמת פרמטרים; (2) המודל מחליט - על סמך הפרומפט וההקשר - האם צריך לקרוא לכלי כלשהו, ואם כן לאיזה, עם אילו ארגומנטים; (3) המודל לא מריץ את הכלי בעצמו - הוא רק מחזיר בקשה מובנית לקריאה, כולל שם הכלי והפרמטרים; (4) האפליקציה מריצה את הפעולה בפועל (קריאת API, שאילתת DB, חישוב) ומחזירה את התוצאה למודל כהודעת "tool result"; (5) המודל ממשיך לייצר תשובה, לפעמים כזו שכוללת קריאה נוספת לכלי אחר. השרשרת הזו יכולה לחזור על עצמה כמה פעמים באותה תשובה בודדת - זו בדיוק התשתית שעליה נבנים סוכני AI.

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

הבחירה בין לחשוף כלי אחד לבין לפרק לכמה כלים ממוקדים

שאלת עיצוב שחוזרת בכל פרויקט: האם עדיף כלי גנרי אחד עם פרמטר "action" שקובע מה בדיוק לבצע (למשל כלי יחיד "manage_subscription" עם פעולות create/cancel/pause), או כמה כלים נפרדים וממוקדים לכל פעולה. בפועל, הניסיון מראה שכלים ממוקדים כמעט תמיד עדיפים: הם קלים יותר למודל לבחור נכון, קל יותר להעניק להם הרשאות שונות (למשל לאפשר "pause" בלי לאפשר "cancel"), וקל יותר לבדוק ולתחזק בנפרד. כלי גנרי שמאחד כמה פעולות שונות תחת פרמטר בחירה יוצר סיכון כפול: גם סיכוי גבוה יותר שהמודל יבחר בפעולה הלא נכונה בתוך הכלי, וגם קושי לאכוף בקרת הרשאות עדינה - כי מבחינת מערכת ההרשאות, "יש הרשאה לכלי" הופך לגס מדי כשהכלי בעצמו מכיל כמה פעולות שונות בעוצמה שונה.

הכלל המעשי הוא לפרק כלים לפי גבול ההרשאה הטבעי שלהם - אם שתי פעולות תמיד דורשות אותה רמת הרשאה ומתבצעות באותו הקשר, איחוד סביר; אם הן שונות ברמת הסיכון או בהרשאה הנדרשת, עדיף לפרק לכלים נפרדים לגמרי.

עיצוב סכמת כלים - כאן נקבעת ההצלחה

הגורם המשפיע ביותר על אמינות Function Calling הוא לא בחירת המודל, אלא איכות עיצוב הכלים עצמם:

  • שמות וברורים ותיאורים ממוקדים - שם כלי כמו "search" הוא עמום; "search_customer_by_email" ברור ומכוון את המודל להחלטה נכונה. התיאור צריך לפרט מתי להשתמש בכלי ומתי לא, כולל דוגמאות קצה.
  • מספר כלים מוגבל בכל קריאה - ככל שיש יותר כלים דומים זמינים בו-זמנית, כך גדל הסיכוי לבחירה שגויה. מערכות בשלות משתמשות בשכבת סינון או Retrieval שמעבירה למודל רק את הכלים הרלוונטיים להקשר הנוכחי, לא את כל הרשימה המלאה.
  • הימנעות מכלים חופפים - שני כלים ש"עושים כמעט אותו דבר" (למשל "get_user" ו-"fetch_user_details") מבלבלים את המודל יותר ממה שהם עוזרים. עדיף לאחד או להבחין בבירור בתיאור.
  • פרמטרים עם ברירת מחדל וטיפוסים מדויקים - שימוש ב-Enum במקום string חופשי, טווחי ערכים ברורים, ושדות אופציונליים מסומנים כך בבירור - כל זה מקטין את שטח הטעויות האפשריות.

דוגמה לתיאור כלי איכותי

{
  "name": "cancel_subscription",
  "description": "מבטל מנוי פעיל של משתמש. השתמש רק לאחר שהמשתמש אישר במפורש רצון לבטל. אל תשתמש לבדיקת סטטוס מנוי - לכך יש כלי נפרד get_subscription_status.",
  "parameters": {
    "type": "object",
    "properties": {
      "user_id": { "type": "string" },
      "reason": { "type": "string", "enum": ["price", "not_used", "switching", "other"] },
      "confirmed": { "type": "boolean" }
    },
    "required": ["user_id", "confirmed"]
  }
}

טיפול בשגיאות - השלב שרוב המערכות מזניחות

כלי בפרודקשן נכשל - API חיצוני לא זמין, נתון לא נמצא, הרשאה נדחית. השאלה היא מה קורה אחר כך. מערכת נאיבית מחזירה שגיאת מערכת גולמית ("500 Internal Server Error") כתוצאת הכלי, מה שמבלבל את המודל ולעיתים גורם לו לנסות שוב באופן לא יעיל, או לדווח למשתמש הודעה טכנית לא ברורה. הגישה הנכונה היא להחזיר למודל שגיאה מובנית וברורה בשפה טבעית - "המשתמש לא נמצא במערכת, ייתכן שיש טעות באיות המייל" - כך שהמודל יכול להחליט בעצמו אם לנסות פרמטר אחר, לבקש הבהרה מהמשתמש, או להסלים הלאה.

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

מולטי-שלב: כשקריאה אחת לא מספיקה

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

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

Function Calling מול MCP

שווה להבהיר את מקום ה-Model Context Protocol ביחס ל-Function Calling: MCP הוא פרוטוקול סטנדרטי לחשיפת כלים ומשאבים לסוכן, בעוד Function Calling הוא המנגנון שבו המודל בפועל בוחר ומפעיל כלי. אפשר לחשוב על MCP כעל שכבת "קטלוג וחיבור" אחידה, שמעל הקטלוג הזה עדיין רץ אותו מנגנון בסיסי של Function Calling. שילוב נכון של השניים מאפשר לחבר סוכן למגוון רחב של מערכות בלי לבנות אינטגרציה ייעודית לכל אחת.

אימות פרמטרים לפני ביצוע - שכבת ביטחון נוספת

גם עם Structured Outputs שאוכף טיפוסים תקינים ברמת ה-Schema, ולידציה ברמת הכלי עצמו נשארת הכרחית. מודל יכול להחזיר user_id שהוא מחרוזת תקינה מבחינת טיפוס, אבל לא קיימת בפועל במערכת - אולי כי הוא הבין לא נכון מהקשר השיחה מי המשתמש הרלוונטי. הכלל המעשי: כל כלי שמבצע פעולה בעלת תופעת לוואי צריך לבדוק את הפרמטרים שלו מול המצב האמיתי של המערכת לפני הביצוע, לא רק לסמוך על כך שהמודל "כנראה צדק". זה נכון במיוחד לכלים שמבצעים פעולות כספיות, מוחקות מידע, או שולחות תקשורת החוצה - בכל אחד מהם, פער בין מה שהמודל חשב לבין המציאות יכול להפוך לתקרית ממשית, לא רק לתשובה לא מדויקת.

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

טעויות נפוצות שחוזרות בכל פרויקט

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

בדיקות אוטומטיות להתנהגות בחירת כלים

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

שאלות נפוצות

כמה כלים אפשר לחשוף למודל בקריאה אחת?

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

האם המודל יכול לקרוא לכמה כלים בו-זמנית?

כן, ברוב המודלים המודרניים - זו בדיוק הנקודה שנדונה במאמר על Parallel Tool Calling.

מה קורה אם המודל ממציא ארגומנט שלא קיים בסכמה?

עם Structured Outputs ו-Schema-constrained generation זה כמעט בלתי אפשרי ברמה המבנית - ראו את המאמר על Structured Outputs.

איך יודעים אם בעיה היא במודל או בעיצוב הכלים?

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

האם צריך Sandbox סביב הרצת כלים?

לכלים שמריצים קוד או ניגשים למשאבים רגישים - כן, בהחלט. זה נושא נפרד שמפורט במאמר על Sandboxing.

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

תגיות: Function Calling · Tool Calling · LLM Agents · MCP · Tool Schema · Error Handling

← חזרה לבלוג · צור קשר