מדוע OpenAI Structured Outputs נכשלים על נתונים אמיתיים
בעמדת הדמו הכול מתחבר: המודל מחזיר אובייקט, המנתח קורא שדות, הבדיקות ירוקות. בנתונים אמיתיים המצב המחמיר של OpenAI Structured Outputs שומר על צורת התשובה, אך לא מציל מחוסר סנכרון: את המבנה שנשלח בבקשה ואת המבנה שלפיו האפליקציה מאמתת את התוצאה מנהלים ומגהבים בנפרד. אי-ההתאמה מתגלה לא בבקשה הראשונה, אלא בסט נדיר של נתוני קלט.
כשל טיפוסי בניתוח JSON נראה כך: התשובה נחתכת עקב מגבלת אורך באמצע האובייקט, והניתוח קורס על סוגר לא סגור. או שבמקום נתונים מגיע שדה מערכת של סירוב, והקוד מצפה למילון מלא.
מקור נוסף — מפתחות חסרים: אם הסכימה מתירה היעדר שדה, המנתח מקבל ערך ריק במקום שבו הוא מצפה למחרוזת, וקורס כבר גבוה יותר במחסנית, בלוגיקה העסקית.
מקרי קצה בתשובות כמעט לא נכנסים לדוגמאות בדיקה: רשימה ריקה במקום קבוצה צפויה, מספר מגיע כמחרוזת, תאריך — בפורמט חופשי, בחירה מקוננת מבין כמה אפשרויות מופעלת על ענף שהאפליקציה אינה מטפלת בו.
תשובה לא חוקית של המודל כאן אינה תקלה, אלא התנהגות תקינה על נתונים נדירים, ובקשה חוזרת בלי בדיקת אידמפוטנטיות רק מכפילה תופעות לוואי.
נסביר כיצד זה נסגר: הסכימה הופכת לחוזה אחיד לבקשה ולבדיקה, התשובה מאומתת לפני הלוגיקה העסקית, ולא אחריה, ולכל סוג כשל מופיעה התנהגות צפויה במקום קריסה.
כיצד סכימות מחמירות מסירות סוג שלם של שגיאות ניתוח
המנגנון Structured Outputs של OpenAI מעביר את ניתוח התשובה מתחום הסתברותי לתחום דטרמיניסטי: המודל מחזיר נתונים התואמים למבנה שדות שהוגדר מראש, ולא טקסט דמוי אובייקט.
זה מסיר סוג שלם של כשלים — מבנים לא סגורים, טקסט הסבר סביב האובייקט, שדות מקוננים שאבדו — שבגללם בעבר עטפו את החילוץ בביטויים רגולריים ובבקשות חוזרות.
שיעור הניתוחים המוצלחים
בפורמט חופשי התשובה עוברת ללא התערבות ברוב המקרים, אך זנב ניכר נשלח לפסילה: שדה הגיע כמחרוזת במקום מספר, enum החזיר ערך מחוץ לרשימה. עם סכימה מחמירה שיעור הניתוחים המוצלחים נשמר בסמוך לגבול העליון, מפני שסוגים, שדות חובה וערכים מותרים נבדקים לפני שהתשובה מגיעה לקוד העיבוד.
עריכות ידניות ופניות חוזרות
כל ניתוח כושל הוא או עריכה ידנית של מפעיל, או קריאה חוזרת למודל עם הבהרה, ושתי התסריטים מוסיפים השהיה. כאשר המבנה מובטח, היקף העריכות הידניות יורד למקרים בודדים, ופניות חוזרות נדרשות רק להבהרות תוכן, ולא לשאלות חוזרות לשם עיצוב תקין.
אמינות חילוץ שדות בזרימה
בהיקף גדול של מסמכים חשובה לא הדיוק הממוצע, אלא היעדר זנב ארוך של כשלים. סכימות מחמירות הופכות את ההתנהגות לצפויה: סט השדות המחולצים זהה לכל מסמך, ענף הניתוח מפסיק להיות צוואר בקבוק, והלוגיקה של המוצר עובדת עם נתונים שכבר נבדקו.
סכימת תשובה, JSON חופשי או בדיקה בצד שלך
חילוץ נתונים מתשובת המודל הוא פשרה בין חופש הניסוח לבין צפיות המבנה. בפועל בפרויקטים מתקבעים שלושה מצבי עבודה: סכימת תשובה מחמירה, JSON חופשי לפי הנחיה בבקשה וטקסט רגיל עם ניתוח בצד האפליקציה. להלן — מה כל אחד נותן והיכן מסתיים אזור התחולה שלו.
| אפשרות | מתי מתאים | היכן הגבול |
|---|---|---|
| סכימת תשובה מחמירה | שדות וסוגים ידועים מראש: כרטיס מוצר, בקשה, פרטי חשבון, enum עם סט ערכים סגור | את הסכימה צריך לתאר ולתחזק; חלק מההגבלות על ערכי שדות וקינון עמוק לא ניתן לבטא כך, ואת רשימת הערכים אפשר להרחיב רק על ידי עריכת הסכימה עצמה |
| JSON חופשי לפי הנחיה | ניתוח ראשוני, משימות מחקר, שדות אופציונליים רבים | המודל שוב ושוב מוסיף טקסט סביב האובייקט, משנה רישיות של מפתחות, מחזיר מחרוזת היכן שצפוי מספר — נדרשים בדיקה ובקשות חוזרות |
| טקסט עם ניתוח ובדיקה בצד האפליקציה | פורמטים לא סטנדרטיים, נתונים ממקורות חיצוניים, כאשר תשובת המודל היא שכבת ביניים | כללי הניתוח חיים בקוד, ולא בתיאור התשובה: קשה יותר לשנות אותם ולכסות אותם בבדיקות, ושיעור התשובות הנפסלות עולה |
המצבים אינם סותרים זה את זה, ואנחנו בוחרים לא את «האמין ביותר», אלא את המתאים לפי החוזה. לכתיבה למסד נתונים ניקח סכימה מחמירה: התשובה מגיעה בצורה מוסכמת, והקוד עובד איתה בלי הסתעפויות לבדיקת סוגים.
לשלבי ביניים נשאיר פורמט חופשי ונוסיף בדיקה בצד האפליקציה — תשובה לא חוקית נפסלת ומבוקשת מחדש, ולא נכנסת לאחסון. אם התשובה נדרשת להצגה לאדם, ולא לעיבוד, די בטקסט: המבנה כאן לא משפר דבר.
סדר ההטמעה: מביקורת תשובות ועד מסירה
סדר השלבים זהה: תחילה אנו מודדים מה המודל מפיק כעת, אחר כך מתארים את מבנה התשובה הרצוי, ורק לאחר מכן מפעילים את המצב המחמיר. כך בכל שלב יש עם מה להשוות את התוצאה — שיעור הניתוחים המוצלחים לפני ואחרי, ולא תחושה ש«נהיה טוב יותר».
- ביקורת תשובות המודל. מייצאים בקשות ותשובות אמיתיות מהתקופה, מנתחים כשלי ניתוח: אי-התאמה לסכימה, סוג שדה שגוי, טקסט מיותר סביב JSON. התוצאה — שיעור הכשלים והסיבות שלהם.
- תכנון הסכימה. מתארים שדות חובה, סוגים, enum, קינון וכללים לערכים ריקים; בנפרד — קידוד תאריכים וערכים מספריים.
- הגדרת המצב המחמיר. מעבירים קריאות ל-OpenAI Structured Outputs — מצב שבו התשובה חייבת להתאים לסכימה — ומקבעים את ההתנהגות בעת דחייה.
- בדיקה על נתונים היסטוריים. מריצים את הקורפוס השמור דרך התצורה החדשה ומשווים את שיעור הניתוחים המוצלחים לזה המקורי: גם לפי מבנה וגם לפי ערכים בשדות.
- טיפול בכשלים. מגדירים כללים לתשובות לא חוקיות: ניסיון חוזר עם הבהרה, תור לניתוח ידני, התראה כששיעור הכשלים עולה.
- מסירת הפתרון לצוות. מוסרים סכימות, תצורות קריאה, סט ניתוחים לדוגמה והוראות לעריכת הסכימה בלי לשבור תרחישים פעילים.
לאחר המסירה הצוות משנה את הסכימה ומריץ את סט הבדיקה בעצמו — בלי מעורבות שלנו.
ארטיפקטים: סכימות, כללי בדיקה, נהלים
המסירה היא לא רק שירות עובד, אלא גם סט מסמכים שלפיהם אפשר לפתח אותו בלי שנצטרך להיות מעורבים. סכימות, כללי בדיקה ונהלים נמצאים במאגר לצד הקוד: هنگام עריכת הלוגיקה הם מתעדכנים במסגרת אותה בדיקת שינויים, ולא «מתישהו אחר כך». להלן — מה בדיוק נשאר אצל צוות הלקוח.
- תיאור סכימת התשובה — שדות, סוגים, חובה, קינון, ערכים מותרים. סכימת OpenAI Structured Outputs כאן היא מקור האמת היחיד: לפיה נבנית גם הבקשה למודל וגם ניתוח התשובה בצד האפליקציה.
- כללי אימות שדות — טווחים, פורמטים של תאריכים ומזהים, enum. הבדיקה מתבצעת לפני הכתיבה למסד, ולכן ערך לא חוקי לא מגיע ללוגיקה העסקית.
- כללי פסילה וניסיונות חוזרים — מה לעשות עם תשובה שלא עברה בדיקה: בקשת חוזרת, צמצום הקשר או תור לניתוח ידני.
- יומן שינויי סכימה — גרסה, תאריך, מחבר וסיבת העריכה. גרסאות קודמות נשארות זמינות כדי לשחזר את התוצאה של ריצה ישנה.
- הוראות למפתחים — כיצד להקים סביבה, להוסיף שדה, להריץ דוגמאות ייחוס לפני פריסה.
- סט דוגמאות ייחוס — זוגות «קלט / תוצאה צפויה», שעליהם נתפסת רגרסיה לאחר עריכת הסכימה או ניסוח הבקשה.
- נוהל תחזוקה — מי מבצע שינויים, כיצד מתבצע אישור, באיזה סדר מוחזרת גרסה כושלת.
את המסמכים אנו כותבים כך שמהנדס חדש בצוות הלקוח יבין בלי להתכתב איתנו: תיאור קצר, דוגמה וגבולות תחולה.
מקרה בוחן: כיצד הסרנו כשלי ניתוח בדוחות
הדיווח נאסף מתשובות של המודל שהיה צריך לפרק לפי שדות: ערכים מספריים, תקופות, קטגוריות, קישורים למסמכי מקור. הפרומפט ביקש «החזר JSON», אך המודל הוסיף הסברים, עטף את התשובה בטקסט והחזיר מספרים כמחרוזת.
הניתוח נכשל ב-15–20% מהמסמכים, כל כשל מהנדס טיפל ביד, ולא סמכו על הייצוא: בדקו אותו שורה-שורה לפני שליחה ללקוח.
מה שינינו
את סכימת התשובה הוצאנו לתיאור נפרד והעברנו למודל כחוזה מחייב, ולא כבקשה בטקסט של הפרומפט — דרך OpenAI Structured Outputs. שדה מסוג «מספר» כבר לא יכול לחזור כמחרוזת, אי אפשר להחסיר מפתחות חובה, ומיותרים נפסלים לפני הכתיבה למסד.
בצד שלנו שכפלנו את הבדיקה לפי אותם כללים והפעלנו הסגר: תשובה שלא עברה את הסכימה לא שוברת את הזרימה, אלא עוברת לתור לניתוח.
מדדים לפני ואחרי
| מדד | לפני | אחרי |
|---|---|---|
| מסמכים עם כשל ניתוח | 15–20% | פחות מ-0,5% |
| ניתוח ידני ביום | 2–3 שעות של מהנדס | מקרים בודדים בשבוע |
| שדות הדורשים עריכת מנתח | כל שדה חדש | נערכת רק הסכימה |
| בדיקת ייצוא לפני שליחה | שורה-שורה | דגימה |
יציבות חילוץ הנתונים הפסיקה להיות תלויה בניסוחי הבקשה: סוג מסמך חדש מתואר בסכימה תוך שעה, ולא על ידי כתיבה מחדש של קוד הניתוח. הדוח נשלח ללקוח בלי בדיקה ידנית, מפני שמבנה התשובה מובטח בכניסה, וסוג הכשל «שדה הגיע בסוג שגוי» פשוט נעלם.
מה לעשות אם בפרויקט כבר יש OpenAI בלי סכימות מחמירות
כן, זהו תרחיש תקין: סכימה מחמירה היא מאפיין של קריאה מסוימת, ולא מצב של הפרויקט כולו. בקוד הניתוב מופיע מתג: חלק מהפניות עוברות למצב המחמיר, שבו התשובה מנותחת לפי החוזה, והשאר ממשיכות לעבוד כמו קודם, בפורמט חופשי. מתג כזה חי ברמת העטיפה מעל הקריאה, ולכן אין צורך לשנות את לוגיקת האפליקציה כולה.
ראשונים למצב המחמיר יש смысл להעביר קריאות עם מחיר גבוה לטעות ניתוח: אלו שתוצאתן נכנסת למסד, לחישוב או להתראה למשתמש. שם כשל פענוח עולה ביוקר, ושם בדיוק הסכימה נותנת רווח. תשובות שבכל מקרה אדם קורא בעיניים אפשר להשאיר במצב חופשי — החמרת החוזה לא מוסיפה להן דבר.
תאימות של תשובות ישנות נפתרת באמצעות סימן מפורש של פורמט: בכל רשומה נשמרת הערה באיזה מצב היא התקבלה, והמנתח יודע לקרוא את שני הענפים. אין צורך לכתוב מחדש את ההיסטוריה — היא נשארת תקפה, וההתנהגות החדשה מופעלת רק עבור נתונים חדשים. זה גם מסיר את הבעיה של תורים ומטמונים שבהם מונחות תשובות שנעשו לפני המעבר.
הסיכון העיקרי במיגרציה — החמרת החוזה חוסמת חלק מהתשובות שעברו קודם «בניחוש». מודל שמחויב למלא שדה לפעמים מחזיר ערך ריק או סירוב, ולכך נדרש תוכנית מוגדרת מראש: ניסיון חוזר עם הבהרה, ירידה למצב חופשי או תור לניתוח ידני. מסלול עוקף דרך פורמט חופשי צריך להישאר פעיל, אחרת קריאה אחת כושלת תעצור את כל הזרימה.
סדר מעשי — להפעיל את המצב המחמיר בנתיב אחד בכל פעם, תוך השוואת שיעור הניתוחים המוצלחים לפני ואחרי. המתג מאפשר להחזיר את ההתנהגות הקודמת בלי לשחרר גרסה חדשה, אם שיעור הכשלים עלה. כך המעבר מתבצע בהדרגה, והזרימה הפעילה נשארת כשירה לכל אורכו.
ספקות טכניים: סכימות, ניסיונות חוזרים, חזרות לאחור
סכימת התשובה היא חוזה בין המודל למערכת שלך, ולא בקשה לגבי איכות הטקסט. המצב המחמיר ב-OpenAI Structured Outputs מבטיח התאמה של המבנה לתיאור השדות, אך מסביב נותרות שאלות הנדסיות: התנהגות בעת סטייה, השהיה, שינוי שדות ואחריות לאחר המסירה.
מה יקרה אם התשובה לא תואמת את הסכימה?
במצב מחמיר תשובה כזו נחסמת לפני הלוגיקה העסקית: אי-ההתאמה לא עוברת אימות, ולמטפל מגיעה שגיאה מפורשת, ולא JSON חצי-גולמי. לאחר מכן — מספר מוגבל של ניסיונות חוזרים, ולאחריו הבקשה עם נתוני המקור נכנסת לתור הניתוח.
עד כמה הסכימה מא�יטה את התשובה?
חלק הארי של ההשהיה הוא עצם היצירה, ולא בדיקת המבנה. הקריאה הראשונה עם סכימה חדשה מעט יקרה יותר: היא מקומפלת. בהמשך זמן התשובה נשמר בטווח הרגיל של המודל — אנחנו מודדים אותו על סט ייחוס ומסתכלים על בקשות איטיות, ולא על הממוצע.
מה אם יהיה צורך לשנות שדות בסכימה?
הסכימה מנוהלת בגרסאות: הוספת שדות לא חובה לא שוברת צרכנים ישנים, ושינוי שם עובר דרך תקופה של כתיבה כפולה. לפני פריסה מריצים את סט הייחוס ומוודאים ששיעור הניתוחים המוצלחים לא ירד.
מי אחראי לפתרון לאחר המסירה?
אנחנו מוסרים סכימות, בדיקות על סטים של ייחוס, תיאור שדות ונוהל חזרה לתצורה הקודמת — הצוות יכול לשנות שדות והנחיות בעצמו. בהמשך — תחזוקה לפי נוהל מוסכם: עריכות סכימות, ניתוח כשלים, עדכון בדיקות.
נדון במשימה: ננתח את הזרימה שלכם ונחזור עם תוכנית
כדי שההערכה תהיה עניינית, אנחנו צריכים נתוני קלט, ולא סיכום של המשימה: היקף העבודה נקבע לא לפי מספר השדות בסכימת התשובה, אלא לפי מידת השונות של התשובות שמגיעות כעת ומה קורה כשהתשובה לא תואמת את הפורמט. לכן במקום תיאור מלא די בפרוסה של הזרימה הנוכחית — לפיה אנו מחשבים את ההיקף ומציגים תוכנית הטמעה.
מה לשלוח להערכה:
- 15–20 בקשות ותשובות אמיתיות של המודל: מהן רואים את פיזור הניסוחים והיכן באמת נדרש פורמט מחמיר;
- סכימת התשובה הרצויה: שדות חובה, סוגים, ערכים מותרים, קינון;
- דוגמאות לכשלי ניתוח: תשובות שבגללן נכשל הניתוח או שהנתונים השתבשו;
- לאן הולכת התוצאה — מסד נתונים, תור, מערכת פנימית, דוח: מכך תלויות הבדיקות ביציאה;
- דרישות להתנהגות בעת אי-התאמה לסכימה: בקשה חוזרת, סירוב, ניתוח ידני;
- אומדני עומס והיקף: כמה בקשות ביממה ומה השהיית התשובה המותרת.
בהתחלה אנחנו מקבעים את הסכימה ואת כללי הבדיקה, מתארים את הטיפול בתשובות לא שגרתיות ומחלקים את העבודה לשלבים עם נקודות בקרה. השלב הבא — תארו את המשימה שלכם וצרפו את מה שיש מהרשימה: נחזור עם הערכת היקף העבודה ותוכנית הטמעה.







