Skip to main content
הדף הזה מיועד לסוכן שכתבת בעצמך, או לפריימוורק שאין לו מתאם ב-Failproof AI. אין כאן פריימוורק שצריך לבצע עליו אינסטרומנטציה: את האירועים אתה פולט בעצמך. זהו אותו API שארבעת מתאמי הפריימוורק קוראים לו מאחורי הקלעים. הם בסך הכול טבלאות תרגום שנבנו מעליו.

התקנה

בלי extras ובלי תלויות.

אינסטרומנטציה

קרא את הקוד מלמעלה למטה, והוא מסביר את עצמו: ומה כל אחד מהם פולט בפועל: כל מה שבתוך ההיקפים יכול להשמיט את session_id ואת agent_id. ההיקפים קושרים את הזהות למשתני הקשר (context variables), וכל קריאה לשיטת אירוע קוראת אותה משם, כך שלעולם לא צריך להעביר מזהים מפונקציה לפונקציה. שלושתם עובדים גם עם async with, ולא רק עם with. קינון סוכנים בונה את העץ. parent_id והעומק מחושבים מתוך המחסנית:

איך היקף נסגר

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

שיטות האירועים

חמש עשרה שיטות בשש משפחות. רובן באות בזוגות — אתה פולט את אירוע הפתיחה ואחריו את אירוע הסגירה, וה-SDK מודד את ה-span שביניהם.
העדף את ההיקפים — agent() ו-tool_call() — בכל מקום שהם מתאימים. הם מבטיחים שאירוע הסגירה ייפלט גם כשגוף הבלוק זורק חריגה. פנה לשיטות האלה ישירות כשזרימת הבקרה שלך לא בנויה בצורה מקוננת, למשל כשהקריאה למודל מתבצעת בתוך פונקציית עזר.
שתי משפחות בני האדם פועלות בכיוונים הפוכים.אף פריימוורק לא מסמן את הזוג השני, ולכן תמיד תצטרך לפלוט אותו בעצמך.
העבר request_id כשקריאות למודל רצות במקביל. בלעדיו, בקשות ותגובות מזווגות לפי סדר ההגעה בכל סוכן — וקריאות מקבילות מזווגות לא נכון, כך שכל תגובה מוצמדת לבקשה הלא נכונה.

דוגמה

לולאת קריאות לכלים מול ה-API של OpenAI, בלי פריימוורק סוכנים:
הקוד הזה מפיק את אותם שישה סוגי אירועים שמתאם היה נותן לך. הגרסה המלאה והניתנת להרצה, כולל הגדרות הכלים, נמצאת במאגר של ה-SDK תחת docs/manual/examples/.

חוטים ו-async

משתני הקשר עוברים אוטומטית למשימות asyncio. הם לא עוברים לחוטים חדשים, כי חוט מתחיל עם הקשר ריק.
בלי propagate(), קריאות האירועים ב-worker זורקות TypeError שמציין את התיקון, במקום שהאירועים ינחתו בלי session. זה מכוון: ה-ingest מדלג על אירוע בלי session ועונה 200, וזה בדיוק הכשל השקט ששכבת הזהות נועדה למנוע.

אינסטרומנטציה לפריימוורק שאין לו מתאם

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

תחום את ההרצה

2

תחום כל כלי

בכל רכיב שהפריימוורק מכנה tool wrapper או middleware.
3

עטוף כל קריאה למודל בזוג אירועים

יש גבול של צומת, שלב או middleware שכדאי לראות? עטוף אותו בזוג hook — hook_triggered / hook_completed — ולא ב-agent() מקונן. agent_id הוא facet בעל קרדינליות נמוכה, ורשומה נפרדת לכל צומת מציפה אותו. ה-spans של hooks מוצגים באותה צורה ונותנים לך latency לכל צומת.
אינסטרומנטציה ידנית ואוטומטית משתלבות. מתאם שרץ בתוך היקף שכתבת ידנית מצטרף ל-session של ההיקף הזה, והסוכן של המתאם נרשם כילד של הסוכן שכתבת ידנית — כך מתקבל עץ אחד ולא שניים. זה שימושי כשאתה מוסיף אינסטרומנטציה ידנית לפריימוורק אחד לצד פריימוורק נתמך.
יש לכך שתי סיבות, ושלוש נקודות החיבור שלמעלה הן התשובה לשתיהן:
  • autogen-core לא מתוחזק מאז ספטמבר 2025.
  • AG2 לא חושף נקודת רישום ברמת התהליך כולו, מקבילה ל-hooks של הפריימוורקים האחרים, ולכן אינסטרומנטציה שלו פירושה לעטוף כל סוכן בכל מקום שבו הוא נוצר.
מיפוי ידני של נקודות החיבור רושם את אותם אירועים, באותה רמת פירוט, שמתאם מובנה היה רושם.

לעומק

איך ההקלטה עובדת בפועל. שום דבר מזה לא נדרש כדי להתחיל.
לכל הקלטה יש אותו מבנה: span נפתח, העבודה מקוננת בתוכו, ולכל אירוע פתיחה יש אירוע סגירה משלו.הזוג הוא יחידת הבסיס. כל אירוע סגירה נושא משך זמן שה-SDK מודד מאירוע הפתיחה שלו.להלן הרצה אמיתית אחת לכל פריימוורק — כל אחת נלכדה מהדוגמאות שמגיעות עם ה-SDK, ושם המודל עבר נרמול. שים לב כמה מידע מתקבל מקריאה אחת.
14 events
צמתים הופכים לזוגות hook, כך שמתקבל latency לכל צומת בלי שהם יציפו את רשימת הסוכנים.
אין אירוע סיום ל-session. את ה-session לא סוגרים — הוא פשוט קבוצת אירועים שחולקים את אותו session_id.הסטטוס נגזר מהמבנה של ה-trace:כלומר, session מסתיים כשכל הזוגות נסגרו. המתאמים פולטים את agent_end בשבילך, ובזמן הכיבוי הם סוגרים כל מה שעדיין פתוח ומסמנים אותו כלא שלם — הרצה שקרסה מסתיימת בסטטוס done עם פער גלוי, במקום להישאר תלויה.
זו הסיבה ש-session יכול להשתרע על פני שתי קריאות. interrupt() של LangGraph משהה את ההרצה, ה-span השורשי נשאר פתוח בכוונה, והקריאה שממשיכה את ההרצה סוגרת אותו. שתי הקריאות הן session אחד.
session_id ו-agent_id הם אופציונליים בכל שיטות האירועים. אם משמיטים אותם, הם נלקחים מההיקף העוטף:
אפשר עדיין להעביר אותם במפורש, ואז הם גוברים על ההיקף. אם שום דבר לא קשור ושום דבר לא הועבר, הקריאה זורקת TypeError שמציין את התיקון, במקום לפלוט אירוע בלי session — אירוע שה-ingest היה מדלג עליו ובכל זאת עונה 200.ההיקפים קושרים את הזהות למשתני הקשר. אלה עוברים אוטומטית למשימות asyncio, אבל לא לחוטים חדשים — עטוף את ה-worker ב-failproofai_sdk.propagate().

מי מנפיק איזה מזהה

איך מתאמים קובעים את session_id

ההתאמה הראשונה קובעת:
  1. אפשרות session_id מפורשת
  2. מטא-דאטה ברמת הקריאה
  3. היקף session() העוטף
  4. מטא-דאטה של הפריימוורק
  5. מזהה ההרצה של הפריימוורק עצמו
המזהה אף פעם לא מומצא כל עוד אחד מאלה קיים — מזהה סינתטי היה מפצל הרצה אחת על פני כמה sessions.

שמור על קרדינליות נמוכה ב-agent_id

זהו ה-facet העיקרי בכל תצוגה של ה-dashboard, והוא עמודה מסוג LowCardinality(String). ערך שמשתנה בכל הרצה פוגע ביעילות העמודה וממלא את התפריט הנפתח של המסנן ברשומה נפרדת לכל הרצה.המתאמים מגנים על העמודה הזו בשבילך:המזהה האמיתי נשמר ב-fw_agent_id / fw_run_id, שם עדיין אפשר לתשאל אותו בלי שהוא יהיה facet.
ההגנה הזו חלה רק על תוויות שבחר הפריימוורק. agent_id שאתה מעביר בעצמך — ל-event.* או ל-failproofai_sdk.agent(...) — נרשם בדיוק כפי שהועבר. שכתוב שקט של ארגומנט מפורש היה גרוע יותר מבעיית הקרדינליות שהוא בא למנוע, ולכן תן ל-spans שלך שמות מתאימים.
מה כל פריימוורק רושם, לפי מדידה בהרצות שלמעלה:מקף פירושו שאין לפריימוורק מושג כזה. human_pause ו-human_interrupt מתארים אדם שפועל על הסוכן, ואף פריימוורק לא מסמן זאת — את אלה עליך לפלוט בעצמך.
אירוע אף פעם לא מגיע לבד. אירוע אחד פותח span, אחר סוגר אותו, ואירוע הסגירה נושא משך זמן שה-SDK מודד מאירוע הפתיחה.
אירוע פתיחה בלי אירוע סגירה הוא span שלעולם לא מסתיים. ה-session מוצג כאילו הוא עדיין רץ, לנצח, ומשך הפעילות שלו ממשיך לגדול. זה הכשל שצריך להיזהר ממנו כשמבצעים אינסטרומנטציה ידנית.

כללי קורלציה

  • השתמש באותו tool_call_id, hook_id, pause_id או input_id גם באירוע הסיום התואם.
  • ה-SDK מחשב את duration_ms עבור tool_result, hook_completed, agent_resume ו-human_input. העברה שלו לשיטות האלה זורקת ValueError.
  • duration_ms כן מתקבל ב-model_response, כי רק מי שמבצע את הקריאה יודע מה ה-latency האמיתי של הספק. הוא חייב להיות מספר שלם — ערך float זורק ValueError כבר בנקודת הקריאה, כי השרת קורא את העמודה כמספר שלם לא מסומן של 32 סיביות, ועבור כל ערך אחר היה שומר NULL.
  • מפתחות הקורלציה תחומים לפי סוג ולפי session, כך שקריאה לכלי ו-hook יכולים לחלוק מזהה בבטחה, ושני sessions שרצים במקביל יכולים להשתמש באותם מזהים בלי להתנגש. הם לא תחומים לפי סוכן: זוג שנפתח תחת סוכן אחד ונסגר תחת סוכן אחר עדיין מתואם, וזה המצב הרגיל בפריימוורקים מרובי סוכנים.
  • request_id מזווג את model_request עם model_response. בלעדיו, אירועי מודל מזווגים לפי הסדר בכל סוכן, כך שקריאות מקבילות מזווגות לא נכון.
  • זוג שמפוצל בין תהליכים עדיין מתואם בהמשך הצינור, אבל ה-SDK לא יכול לחשב את משך הזמן שלו בתוך התהליך.
  • מפת האירועים הממתינים מחזיקה לכל היותר 10,000 אירועי פתיחה, וכשהיא מתמלאת היא מפנה את הרשומה הישנה ביותר.
התקנת failproofai-sdk מתקינה הכול, כולל ארבעת המתאמים. ה-extras מושכים את הפריימוורק, לא את המתאם.
import failproofai_sdk מחויב חוזית לאפס תלויות. הדבר נאכף על ידי בדיקה שמתקינה את ה-wheel הבנוי עם --no-deps, ועל ידי בדיקה נוספת שמוכיחה שאף פריימוורק לא מגיע ל-sys.modules.
אין מאפיין failproofai_sdk.crewai. המתאמים לא נחשפים ברמה העליונה של החבילה, וזה מכוון: גישה לאחד מהם הייתה מייבאת את הפריימוורק כתופעת לוואי של גישה למאפיין, ושוברת את ההבטחה לאפס תלויות. השתמש ב-instrument().
הזיהוי האוטומטי קורא את sys.modules, ולא את רשימת החבילות המותקנות, כך שפריימוורק שהתקנת אבל אף פעם לא ייבאת לא עובר אינסטרומנטציה, וגם לא ייובא בשמך. כדי לראות מה מחובר:
instrument("crewai") במכונה שאין בה CrewAI לא זורק חריגה. הוא רושם אזהרה בלוג ומחזיר (), כך שפריימוורק אחד חסר לעולם לא מפיל תהליך שמבצע אינסטרומנטציה גם לפריימוורקים אחרים.האזהרה כוללת את ה-ImportError המקורי, וההודעה שלו מציינת את פקודת ההתקנה המדויקת — כך שהתיקון נמצא בלוגים שלך, ולא מוסתר.
הגדר FAILPROOFAI_SDK_STRICT=1 כדי שבמקום זאת תיזרק חריגה. הדגל הזה נקרא פעם אחת ונשמר במטמון, לכן הגדר אותו בסביבה לפני שהתהליך מתחיל, ולא באמצע ההרצה.
הקריאה ל-instrument() חייבת לבוא אחרי ייבוא הפריימוורק. הזיהוי האוטומטי קורא את sys.modules, כך שקריאה בלי ארגומנטים מעל הייבוא לא מוצאת כלום, לא מתקינה כלום ומחזירה ().
אם הסדר שגוי, התהליך רץ עם ה-SDK מיובא, המתאם מותקן לכאורה, ואף אירוע לא נפלט. נרשמת בלוג אזהרה שאומרת בדיוק את זה — לכן כשהרצה לא מתעדת כלום, בדוק קודם את הלוגים.
ה-spool הוא מה שהופך את זה לבטוח: הסוכן שלך אף פעם לא נחסם בהמתנה לרשת, והשבתה של Cloud פירושה תיקייה שהולכת וגדלה, ולא אירועים שאבדו.כל flush כותב קובץ אצווה אחד: קודם .tmp, אחר כך fsync, ולבסוף שינוי שם אטומי:
ה-daemon אוסף רק קבצי .jsonl, כך שלעולם לא יקרא קובץ שנכתב רק בחלקו. שם הקובץ כולל חותמת זמן, מזהה תהליך ומספר רץ, כך ששני תהליכים שמבצעים flush באותה אלפית שנייה לא יכולים להתנגש. התור מוגבל ל-10,000 אירועים; מעבר לכך הוא משליך את הישנים ביותר ורושם זאת בלוג.
ברירת המחדל של collector.redact היא minimal גם עבור אירועי SDK. ה-SDK מבצע השחרה לפני שהוא כותב אצווה לדיסק, וה-daemon מריץ שוב את אותו מעבר דטרמיניסטי לפני ההעלאה, כך שגם אצוות מגרסאות SDK ישנות מוגנות.
ה-daemon קורא כל אצווה ומחיל עליה השחרה בזיכרון לפני ההעלאה. הוא לא משכתב את קובץ ה-spool שקרא.הגדר את collector.redact ל-off רק כשיש דרישה מפורשת לשמור payloads כלשונם; גם ה-SDK וגם ה-daemon מכבדים את ההגדרה הזו. השחרה מינימלית תופסת מפתחות API נפוצים, אסימוני bearer, אסימוני JWT והשמה של סודות למשתנים. היא לא יכולה לזהות מידע רגיש שמנוסח כטקסט חופשי.
אתה שולט ב-payloads כבר במקור, בשני מקומות:
  • כבה את לכידת התוכן במתאם. שם האפשרות שונה ממתאם למתאם, ולאחד המתאמים אין אפשרות כזו בכלל — זה לא מתג אוניברסלי אחד:
    • LangChain / LangGraph, Pydantic AI — capture_content=False
    • LlamaIndex — capture_messages=False
    • CrewAI — אין שום מתג תוכן; session_id היא האפשרות היחידה שהוא קורא, ולכן ההנחיות והתשובות של המודל תמיד נרשמות.
    instrument() משמיט אפשרויות שהמתאם לא קורא, כך שהעברת שם שגוי לא זורקת שום חריגה וגם לא משנה דבר.
  • מלכתחילה, אל תעביר את הסוד ל-input=.
collector.redact הוא הגנה לעומק, ולא תחליף לאף אחד משני אלה.
תיקיית spool ריקה היא המצב התקין. אל תשתמש בה כדי לבדוק אם האירועים נמסרו.
ה-daemon מוחק כל אצווה תוך אלפיות שנייה מרגע שליחתה, כך ש-ls מתחרה ב-collector ומציג רק חלק קטן ממה שפלטת — ואי אפשר להבחין בין זה לבין SDK שלא תיעד כלום.כדי לוודא שהאירועים באמת הגיעו, בדוק ב-dashboard. כדי לראות את ה-spool מתמלא, עצור קודם את ה-daemon.
כל callback רץ בתוך עוטף שתפקידו היחיד הוא לזרוק חריגות מחדש, כך שהקריאה שלך נמצאת בתוך try אחד בדיוק, וכל מה שה-SDK עושה קורה מחוצה לו.ברירת המחדל נכונה ב-production ושגויה בזמן דיבוג, כי כל מה שהיא יכולה להוכיח הוא ש”זה לא קרס”. הגדר FAILPROOFAI_SDK_STRICT=1 כדי שכשל שהיה נבלע בשקט יתריע בקול רם.

בעיות נפוצות

לאירוע פתיחה אין אירוע סגירה: model_request בלי model_response, או tool_use בלי tool_result. השתמש בהיקפים, שמבטיחים את הזוג גם כשגוף הבלוק זורק חריגה. אם אתה קורא לשיטות האירועים ישירות, השתמש ב-try וב-finally.
הערך נמדד מאירוע הפתיחה התואם, ולכן הוא נדחה ב-tool_result, hook_completed, agent_resume ו-human_input. הוא מתקבל ב-model_response, כי רק אתה יודע מה ה-latency האמיתי של הספק, ושם הוא חייב להיות מספר שלם.
החוט לא ירש את ההקשר. עטוף את ה-callable ב-failproofai_sdk.propagate(). ראה חוטים ו-async.
שדות נוספים ממוזגים אחרונים, כך ששדה ששמו זהה לשדה אמיתי, כמו model או outcome, ידרוס אותו וישנה עמודה שמורה. תן לשדות שלך קידומת ייחודית; המתאמים משתמשים בקידומת fw_.
agent_id הוא facet בעל קרדינליות נמוכה, והכנסת אליו מזהה הרצה. השתמש בשם של תפקיד או של צומת, ושמור את המזהה האמיתי בשדה של ה-payload.

מה הלאה

איך זה עובד

זוגות, מזהים, מחזור החיים של session ומסירה.

קריאת trace

עקוב אחרי שרשרת הסיבתיות ב-session שזה עתה לכדת.

מתאמי פריימוורק

LangGraph, CrewAI, LlamaIndex ו-Pydantic AI.