דלג לתוכן
18/30פרק 18 מתוך 30

Tool Calling ופלטים מובנים: החוזה שמחזיק מעמד

24 קריאות, אפס JSON שבור ושני תאריכים שמישים. ואז אותו endpoint עם תיאור טוב יותר — ומה שסכמה לא יכולה לתקן.

בעמוד הזה

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

TEXT
<tool_call>
{"name": "search_flights",
 "arguments": {"from": "Madrid", "to": "Berlin", "date": "3rd October 2026"}}
</tool_call>

ה-JSON תקין. שם הכלי נכון. כל השדות הנדרשים קיימים. והקריאה חסרת תועלת: שום flight API לא מקבל "Madrid" במקום שבו הוא מצפה לקוד שדה תעופה, או "3rd October 2026" במקום שבו הוא מצפה לתאריך.

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

לפני המכניקה, המשפט שמונע הכי הרבה בלבול: קריאת כלי היא בקשה, לא פעולה.

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

כל מה שקשור לאבטחת agent בפרק 30 נובע מהחלוקה הזאת, וכך גם כל מה שקשור לעיצוב agent בפרק 23: המודל מציע והקוד שלכם מחליט, והקוד הוא המקום שבו חיה כל הבטחה.

אז כלי, בלי אוצר המילים, הוא שני דברים:

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

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

אתם שולחים את הסכמות עם הבקשה

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

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

המודל עונה בקריאה במקום בטקסט

קישור למקטע: המודל עונה בקריאה במקום בטקסט

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

הקוד שלכם מריץ אותו — או מסרב

קישור למקטע: הקוד שלכם מריץ אותו — או מסרב

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

אתם שולחים את התוצאה בחזרה כהודעה

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

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

המודל עונה, או מבקש כלי נוסף

קישור למקטע: המודל עונה, או מבקש כלי נוסף

זו הלולאה של פרק 23, והסיבה שבקשה יחידה יכולה להפוך לתריסר סבבים הלוך-חזור.

שום דבר מזה אינו מתהווה מעצמו. כפי שקבע פרק 11, tool calling הוא התנהגות מאומנת:1 במהלך post-training המודל ראה אלפי שיחות שנבנו בדיוק בצורה הזאת. לכן הפורמט תלוי-מודל, לכן האמינות משתנה כל כך בין מודלים בגודל דומה, ולכן מודל יכול לקרוא לכלי שמעולם לא ראה — הצורה אומנה, הכלי הספציפי מגיע מה-prompt שלכם.

הנה הכלי כפי שרוב האנשים כותבים אותו בפעם הראשונה. שימו לב ששום דבר בו לא שגוי; הוא פשוט דק:

tools/badFlights.tsTS
{
  name: "search_flights",
  description: "Search for flights.",
  parameters: {
    type: "object",
    properties: {
      from: { type: "string", description: "Airport." },   
      to:   { type: "string", description: "Airport." },   
      date: { type: "string", description: "The date." },  
    },
    required: ["from", "to", "date"],
  },
}

עשרים וארבע בקשות, שישה זוגות ערים מוצלבים עם ארבע דרכים להביע תאריך ("ה-3 בחודש הבא", "ביום שישי הבא", "15 בדצמבר", "מחר"), greedy decoding כדי שהתוצאות ישתחזרו:

הכלי נקראJSON שבורתאריך ב-ISOשדות תעופה כ-IATAהכול נכון
הסכמה למעלה24/2402/244/241/24

קראו את שתי העמודות הראשונות לפני שלוש האחרונות. המודל קורא לכלי הנכון בכל פעם ומייצר JSON תקין בכל פעם. הכשל כולו נמצא בערכים, והערכים לא שמישים: "Madrid" במקום MAD, "3rd October 2026" במקום 2026-10-03.

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

אותו endpoint. אותו קוד מאחוריו. אותו מודל, אותם prompts, אותו decoding. הדבר היחיד שמשתנה הוא הטקסט בסכמה:

tools/goodFlights.tsTS
{
  name: "search_flights",
  description: "Search scheduled flights between two airports on a given day.",
  parameters: {
    type: "object",
    properties: {
      from: {
        type: "string",
        description: "Departure airport as a three-letter IATA code, e.g. MAD for Madrid. Never a city name.",   
        pattern: "^[A-Z]{3}$",
      },
      to: { /* same */ },
      date: {
        type: "string",
        description: "Departure date as an ISO 8601 calendar date, YYYY-MM-DD. Resolve relative dates against today before calling.",   
        format: "date",
        pattern: "^\\d{4}-\\d{2}-\\d{2}$",
      },
    },
    required: ["from", "to", "date"],
  },
}
פורמט תאריךערך תאריךפורמט שדה תעופהערך שדה תעופה
סכמה דקה2/241/244/244/24
סכמה מתוארת24/2412/2416/248/24

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

עכשיו קראו את העמודה השנייה, שהיא החצי החשוב יותר.

סכמה מגבילה צורה. היא לא יכולה לספק ידע.

קישור למקטע: סכמה מגבילה צורה. היא לא יכולה לספק ידע.

התאריך בפורמט ISO ב-24 פעמים מתוך 24. הוא היום הנכון ב-12 פעמים מתוך 24.

כלומר, חצי מהקריאות נושאות עכשיו תאריך בפורמט מושלם שהוא התאריך הלא נכון. התיאור אמר למודל איזו צורה לייצר, והמודל ייצר אותה ללא רבב — אבל להפוך את "ביום שישי הבא" ל-2026-09-11 דורש לדעת מה התאריך היום ולעשות חשבון לוח שנה, ושום כמות של תיאור לא מספקת את זה. אותו סיפור לגבי שדות תעופה: הפורמט עלה מ-4 ל-16, אבל הערך רק מ-4 ל-8, כי כתיבת MAD דורשת לדעת ששדה התעופה של מדריד הוא MAD.

זו ההבחנה שנושאת את משקל הפרק:

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

השניים צריכים תיקונים שונים, ובלבול ביניהם מבזבז שבועות. כשלי פורמט מתקנים בתיאור או באמצעות constrained decoding, למטה. כשלי ידע מתקנים על ידי הכנסת הידע ל-prompt — התאריך הנוכחי בהודעת system, חיפוש שדה תעופה ככלי שני שהמודל קורא לו קודם, enum בסכמה כשהקבוצה קטנה מספיק כדי למנות אותה. שימו לב מה משותף לשלושתם: הם מזיזים את הבעיה מזיכרון המודל אל הקלט שלו, וזה כל פרק 24.

פלטים מובנים, ומהו בעצם "constrained decoding"

קישור למקטע: פלטים מובנים, ומהו בעצם "constrained decoding"

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

נזכור איך generation עובד: בכל צעד המודל מייצר logit לכל token באוצר המילים, וה-sampler בוחר אחד. Constrained decoding מכניס שלב באמצע. בהינתן דקדוק — שנגזר מה-JSON Schema שלכם — הוא מחשב אילו tokens יכולים לבוא אחר כך באופן חוקי, מגדיר את ה-logits של כל האחרים למינוס אינסוף, ומאפשר ל-sampler לבחור ממה שנשאר.

אם הסכמה אומרת שהדבר הבא חייב להיות {, אז לכל token שאינו { יש הסתברות אפס. לא "לא סביר": אפס. המודל לא יכול לפלוט JSON לא תקין כי ה-tokens הלא תקינים הוסרו מההתפלגות לפני הדגימה.

זה מה שנמצא מתחת ל-"structured outputs", "JSON mode" ו-"guided generation", וזה מסביר את שתי התכונות שלהם. הערובה מוחלטת לכל מה שהדקדוק יכול לבטא — טיפוסים, שדות נדרשים, enums, קינון — כי היא נאכפת מכנית ולא מתבקשת בנימוס. והיא לא אומרת שום דבר על התוכן: דקדוק יכול לחייב את "date" להיות מחרוזת שתואמת דפוס תאריך, ולא יכול לחייב אותה להיות היום הנכון. זה אותו קיר כמו בסעיף הקודם, רק מהצד השני.

שתי הערות מעשיות. זה לא בחינם: צריך לחשב את המסכה בכל צעד, ודקדוקים מורכבים עולים latency מדיד. וזה משנה את מה שהמודל עושה — מודל שמוסט מה-token המועדף עליו יכול לייצר תוכן גרוע יותר תוך כדי שהוא מייצר מבנה מושלם, ולכן "לבקש יפה ולאמת" עדיין ברירת מחדל סבירה לצורות פשוטות, ו-constrained decoding מצדיק את העלות שלו כשהצורה מורכבת או כשהצרכן קפדן.

תופעות לוואי, והתכונה היחידה שחשובה

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

פרק 14 מדד timeout ואחריו retry שחייבו שתי פעולות generation עבור תשובה אחת. עם כלים, אותו כשל מחמיר, כי כלי יכול לעשות משהו.

אם הקוד שלכם קורא ל-charge_card, מקבל timeout ומנסה שוב, יש לכם שני חיובים. למודל אין מושג שמשהו מזה קרה; הוא רואה תוצאת כלי אחת. התיקון זהה לכל מערכת מבוזרת והוא לא הבעיה של המודל: להפוך את הפעולה ל-idempotent באמצעות מתן מפתח לקריאה, כך שהביצוע השני יזהה את הראשון ויחזיר את התוצאה שלו במקום לעשות את העבודה שוב.

כלל העיצוב שנובע מכך ראוי להיאמר בפשטות. הפרידו קריאות מקריאות כתיבה בקטלוג הכלים שלכם. קריאה יכולה לעבור retry בחופשיות, לרוץ במקביל ולהישמר ב-cache. כתיבה לא יכולה, והיא צריכה לשאת מפתח, בדיקת הרשאה, ולכל דבר שמשתמש היה רוצה לדעת עליו לפני שהוא קורה — שלב אישור שמציב אדם בין הבקשה לפעולה. שלב האישור הזה אינו אדיבות: הוא אחד הדברים המעטים שעומדים בין prompt injection לבין תוצאה אמיתית — וכפי שפרק 30 מודד, החלש שבהם.

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

כלים טעוניםprompt tokensבחר search_flightsתאריך ב-ISO
135324/2424/24
573024/2424/24
101,19321/2421/24
202,11924/2424/24

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

זו תוצאה שלילית וצריך לדווח עליה ככזו: במשימה הזאת, עם הכלים האלה, "יותר מדי כלים" לא הייתה הבעיה. מה שכן גדל, באופן מונוטוני ובפי שישה, הוא ה-prompt: מ-353 tokens ל-2,119, בתשלום על כל בקשה בשיחה, לנצח, בין אם משתמשים בכלי כלשהו ובין אם לא.

אז הגרסה הכנה של הפולקלור עוסקת בעלות והקשר, לא בדיוק. עשרים כלים הם מס קבוע על כל הודעה, ופרק 16 כבר הראה מה קידומת קבועה עושה לחשבון לאורך ארבעים תורות. כשאנשים מדווחים שכלים רבים פוגעים באיכות, המנגנון הוא בדרך כלל שההגדרות דחקו החוצה את ההקשר שהיה חשוב — וזו בעיה של פרק 24 בתחפושת של פרק 18. כלים שהם באמת כמעט כפילויות זה של זה הם גם בעיה אמיתית, והתיקון שלהם אינו פחות כלים אלא תיאורים ו-namespaces טובים יותר: הוסיפו להם קידומת לפי מערכת (crm.search_customer, billing.search_customer), כדי ששני קטלוגים שמוזגו משני צוותים לא יתנגשו, וכדי שלמודל יהיה על בסיס מה להבחין.

שלושה סוגי כלים, והאחד שפותח את החלק הבא

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

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

כלי נתונים קוראים: חיפוש, שליפה, שאילתה. אפשר לעשות להם retry, להריץ במקביל, לשמור ב-cache. הם נכשלים בכך שהם לא מחזירים שום דבר שימושי, והסיכון המרכזי שלהם הוא שהם מכניסים טקסט לא מהימן אל ההקשר — וזה כל משטח התקיפה של פרק 30.

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

כלי תזמור קוראים למודלים אחרים. כלי שהיישום שלו הוא agent אחר, עם prompt משלו, כלים משלו ולולאה משלו — ולמודל הקורא הוא נראה בדיוק כמו שני האחרים, כי סכמה ו-endpoint הם כל מה שהוא רואה אי פעם.

הסוג השלישי אינו קוריוז. הוא המנגנון מאחורי חצי ה-agent-as-a-tool של פרק 25 — הטופולוגיה האחרת, ה-handoff, מוסרת את השיחה ולא מקבלת אותה בחזרה — והוא עובד בדיוק מפני שהממשק בפרק הזה צר מספיק כדי ש-agent שלם ייכנס מאחוריו.

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

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

פרק 19 הוא retrieval, והוא הפרק שבו תשובה שגויה מפסיקה להיות קוריוז ומתחילה להיות אחריות משפטית.


המדידות בפרק הזה מגיעות מ-Qwen/Qwen2.5-0.5B-Instruct עם greedy decoding, על פני 24 בקשות שנוצרו משילוב של שישה זוגות ערים עם ארבע ניסוחי תאריך, תוך שימוש ב-chat template של המודל עצמו להגדרות כלים. הן משתשחזרות בדיוק, וזה מודל קטן: קראו את ההפרדה בין פורמט לערך כהדגמה של המנגנון ולא כ-benchmark למה שמודלים נוכחיים עושים. מודל frontier פותר את "ביום שישי הבא" נכון הרבה יותר לעיתים קרובות — ועדיין אי אפשר לגרום לו לעשות זאת באמצעות סכמה, וזה החלק שמכליל.

אוצר המילים של JSON Schema ששימש למעלה (type, properties, required, pattern, format, enum) מוגדר בטיוטת JSON Schema שהתיעוד של הספק שלכם נוקב בשמה; התת-קבוצה השימושית קטנה וזהה בין ספקים, וההבדלים שכן קיימים — אילו keywords נאכפים באמצעות constrained decoding ולא רק מועברים למודל — שווים קריאה במדריך structured-output של הספק במקום להניח אותם.

לטכניקה של constrained decoding, ספריות בסגנון guidance ופרויקט outlines מתעדים את הבנייה מדקדוק ל-logit-mask באופן שמתמפה ישירות ל-sampler של פרק 17. ולגבי הסבב עצמו, המפרט הברור ביותר אינו מדריך אלא פרוטוקול: פרק 26 קורא אותו שורה אחר שורה.

  1. Ouyang, L. et al. Training language models to follow instructions with human feedback. arXiv:2203.02155 (2022). המאמר שהפך את מתכון ה-post-training לסטנדרט; הצורה של קריאת כלי נלמדת שם, מדוגמאות, בדיוק כמו הצורה של תשובה.


נוצר על ידי

David Vicente Campos

מייסד NeuraLIA Labs ושותף-מייסד MyRealFood

אני מהנדס מחשבים, בוגר אוניברסיטת לאון. הייתי שותף בהקמת MyRealFood, שם, כסמנכ״ל טכנולוגיות, בניתי את האפליקציה שמיליוני אנשים השתמשו בה כדי לאכול בריא יותר, והקמתי את NeuraLIA Labs, שם אני בונה מוצרי בינה מלאכותית. כאן אני כותב על מה שהייתי צריך להבין לאורך הדרך, כפי שהייתי רוצה שמישהו היה מסביר לי בזמנו.

עוד על המחבר

פורסם על ידי NeuraLIA Labs.

פוסטים חדשים ישירות לתיבת הדואר

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

תוכן הקורס

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev10 דקות קריאה

מודל ה-AI Jev נבנה להחלטות, לא לפרוזה

Jev של TypeSafe AI מושך תשומת לב כי הוא מתייחס לאינטליגנציית תוכנה כאל בעיית הסתברות: לבחור את ההסתעפות הנכונה, להצמיד ביטחון, ולהימנע מתשלום ל-LLM כדי שיכתוב טקסט כשהקוד צריך החלטה.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering10 דקות קריאה

הנדסת הקשר לסוכני AI ארוכי־טווח

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

מוכנים לתת ל-LIA לבחור?

בנו עם כל מודלי ה-AI במקום אחד — התחילו בחינם עוד היום.