SSE סטרימינג AI: מדוע התשובות מגיעות בשלמותן ולא תוך כדי יצירה
כאשר משתמש שולח בקשה לפונקציית AI באתר, הדפדפן פותח חיבור וממתין. השרת באותו רגע פונה למודל, מקבל את היצירה בשלמותה — ורק לאחר מכן מעביר אותה בגוש אחד. כל הזמן הזה הממשק תלוי ללא שום סימן חיים: לא המילה הראשונה, לא מחוון התקדמות.
השהיית האסימון הראשון נמתחת על כל זמן היצירה, וזו עבור תשובה ארוכה בקלות עוברת את עש десяток השניות.
הסיבה כמעט תמיד זהה — באפרינג של תשובת המודל בצד השרת. שכבת ביניים (פרוקסי, מאזן עומסים, עטיפת האפליקציה) צוברת את גוף התשובה לפני שהיא שולחת אותו ללקוח, והעברה בזרימה של תשובות לדפדפן פשוט לא מגיעה.
לעיתים לכך מתווסף גם טיימאאוט חיבור: בזמן שהשרת שותק וצובר נתונים, צומת הביניים מחליט שהחיבור מת וקוטע אותו. המשתמש רואה либо טעינה אינסופית, либо שגיאה — ועוזב מבלי שהמתין לתשובה.
SSE סטרימינג AI מסיר את הבעיה הזו ברמת התעבורה. השרת מעביר את התשובה תוך כדי היצירה: ברגע שהמודל הפיק את הקטע הראשון, הוא מיד יוצא לדפדפן, והטקסט מתחיל להופיע על המסך. השהיית האסימון הראשון יורדת מעשרות שניות לשברירי שנייה — האדם רואה שהמערכת עובדת ונשאר להמתין לתשובה המלאה.
בהמשך נפרק כיצד זה נסגר: היכן בדיוק מכבים את הבאפרינג, כיצד מגדירים את טיימאאוט החיבור ליצירה ארוכה ומה יש להקדים כדי שהזרימה לא תיקטע באמצע.
מה נותנת הזרמת תשובות: האפקט במספרים
הזרמה משנה לא את מהירות הייצור, אלא את הרגע שבו המשתמש רואה את התו הראשון. בבקשה רגילה הדפדפן ממתין לתשובה כולה: השרת שותק, החיבור תלוי, והאדם כבר מוכן לסגור את הלשונית.
עם הזרמה, השבר הראשון מגיע בשברירי שנייה לאחר תחילת הייצור, והממשק מתחיל "להקליד" את התשובה בזמן שהמודל עוד מחשב את השאר.
זמן עד לטוקן הראשון
המדד המרכזי כאן הוא הזמן עד לתו הנראה הראשון. הוא נקבע לא לפי זמן הייצור הכולל, אלא לפי ההשהיה עד לתחילת הזרם: ברגע שהמודל מסר את המנה הראשונה, היא מיד יוצאת לדפדפן. בתשובות ארוכות הזמן הכולל כמעט אינו משתנה, אך המהירות הנתפסת עולה פי כמה — המשתמש רואה התקדמות במקום מסך ריק.
שיעור הבקשות שנקטעו
הסיבה העיקרית לבקשות נטושות היא לא תשובה איטית, אלא היעדר משוב. כאשר הטקסט מופיע בהדרגה, האדם קורא את ההתחלה ומחליט להמתין לסוף, במקום לרענן את הדף. בפועל שיעור הבקשות שנקטעו יורד באופן מורגש, ושליחות חוזרות של אותה שאלה נעלמות יחד איתו.
יציבותן של תשובות ארוכות
ייצור ארוך הוא סוג נפרד של כשל: החיבור נקרע באמצע, והמשתמש מאבד הכל. הזרמה שומרת על הערוץ פתוח ומוסרת נתונים בהתאם למוכנות, ולכן הקטיעה נראית מיד ומטופלת כראוי — עם השלמה או הודעה ברורה.
אנחנו בונים פתרונות כאלה על Server-Sent Events: ערוץ חד-כיווני מעל HTTP רגיל, שעובר דרך פרוקסים ומאזני עומסים ללא תשתית נפרדת ומתחבר מחדש כראוי בעת ניתוק.
השורה התחתונה במספרים: הזמן עד לתו הראשון מתקצר לשברירי שנייה, שיעור הבקשות שנקטעו יורד, ותשובות ארוכות מפסיקות להיות סיכון. מפרקים כיצד זה בנוי בצד השרת והלקוח.
אפשרויות מימוש סטרימינג: השוואת גישות
את התעבורה בוחרים לא לפי טעם, אלא לפי שלושה נתוני בסיס: מי יוזם את זרם הנתונים, האם נדרש ערוץ חזרה מהלקוח לשרת, וכיצד מתנהגת התשתית ביניהם. להלן חלופות מעשיות שביניהן באמת בוחרים בעת סטרימינג של תשובות המודל.
| אפשרות | מתי מתאימה | מגבלות |
|---|---|---|
| סקר סטטוס | התשובה נוצרת זמן רב ובמנות, אסימוני ביניים אינם נדרשים — חשוב רק התוצאה | הלקוח סוקר את השרת לפי טיימר: בקשות מיותרות ועיכוב בין מנות; בסקר תכוף גדל העומס על ה-API |
| בקשה ארוכה עם המתנה | תרחיש פשוט, התשובה מגיעה בבלוק אחד, פלט ביניים אינו מוצג | המשתמש רואה מסך ריק עד סוף היצירה; פסקי זמן של פרוקסי מנתקים את החיבור בתשובות ארוכות |
| אירועים מהשרת | נדרש זרם חד-כיווני של אסימונים מהשרת לדפדפן: הדפסת התשובה תוך כדי יצירתה | הערוץ חד-כיווני — נתונים מהלקוח עוברים בבקשה נפרדת; נדרש keep-alive תקין מול פרוקסי מתווכים |
| ערוץ דו-כיווני | דיאלוג בזמן אמת, קטיעת יצירה, עריכה משותפת | מורכב יותר בתפעול: מצב החיבור, התחברות מחדש, איזון עומסים; מיותר אם זרם החזרה אינו נדרש |
בפועל, עבור סטרימינג של תשובות המודל לרוב לוקחים אירועים מהשרת: הם מתאימים ל-HTTP רגיל, אינם דורשים פרוטוקול נפרד ועומדים בהתחברות מחדש של הלקוח. ערוץ דו-כיווני מוצדק כאשר המשתמש צריך להיות מסוגל לקטוע את היצירה או לנהל דיאלוג מקביל — ואז זרם חד-כיווני אינו מספיק.
את המגבלות יש להביא בחשבון כבר בהתחלה: באפרינג בפרוקסי וב-CDN מדביק מנות לבלוק אחד, ופסקי זמן במאזן העומסים קוטעים חיבורים ארוכים. לכן את התעבורה בוחרים יחד עם הגדרות התשתית, ולא אחריהן.
כיצד אנו מיישמים העברה בזרימה: שלבי העבודה
את העבודה מתחילים בביקורת: בודקים כיצד האפליקציה מחזירה תשובה כעת — בשלמותה לאחר הייצור או עם הגעת הטוקנים, היכן ממוקם ה-proxy וכיצד הוא מבצע buffering לתעבורה.
בשלב זה מתעדים את צווארי הבקבוק: timeouts של החיבור, היעדר headers תקינים להעברה בזרימה, שכבות ביניים שצוברות את התשובה בשלמותה. בלי זה כל תיקון הוא בגדר ניחוש.
- ביקורת אופן החזרת התשובות הנוכחי. מודדים את הזמן עד לטוקן הראשון ועד לטוקן האחרון, בודקים היכן התשובה מתעכבת — בצד המודל, האפליקציה או התעבורה.
- תכנון סכמת הזרימה. קובעים כיצד האירועים עוברים מהמקור לדפדפן: פורמט ה-chunks, חלוקה לפי גבולות, טיפול בניתוק ובחיבור מחדש.
- הגדרת ה-proxy וה-load balancer. מכבים buffering של התשובה, מגדירים timeouts ארוכים, בודקים ששכבות הביניים אינן קוטעות את החיבור באמצע הדרך.
- מימוש בצד הלקוח. מחברים קליטת אירועים, מטפלים ב-chunks חלקיים, סוגרים את החיבור בזהירות ומציגים התקדמות למשתמש.
- בדיקה תחת עומס. מריצים תרחישים עם sessions מקבילים, תשובות ארוכות וניתוקי רשת — בודקים אם הזרימה מחזיקה מעמד וכיצד מתנהג השחזור.
- טיפול בכשלים. מגדירים את ההתנהגות בעת ניתוק: חיבור מחדש, השלמת החסר, הודעה ברורה במקום מסך "תקוע".
- מסירה לתפעול. מוסרים את הסכמה המוגדרת, את המדדים ואת ההוראות לצוות, כדי שניתן יהיה לתחזק את ההעברה בזרימה בלעדינו.
כל שלב מסתיים בתוצאה ניתנת לבדיקה, ולא בדוח. סכמת הזרימה והגדרות ה-proxy מתועדים בתיעוד — בשינוי הבא של התשתית הצוות רואה אילו פרמטרים אחראים על ההעברה בזרימה ומה אסור לגעת בו.
מה הלקוח מקבל לידיו לאחר ההטמעה
הטמעת SSE-סטרימינג של תשובות AI אינה מסתיימת באב-טיפוס עובד. לידיכם תקבלו סט ארטיפקטים שלפיהם הצוות שלכם יוכל להפעיל, לאבחן ולפתח את זרימת התשובות בלעדינו — מהגדרות השרת ועד לנהלי טיפול בתקלות.
- הגדרות שרת — פרמטרים של פרוקסי ומאזן עומסים עם ביטול באפרינג של התשובה, הגדרות keep-alive ומגבלות על חיבורים בו-זמניים, כדי שזרימת הטוקנים תגיע ללקוח ללא השהיות וניתוקים.
- תרשים זרימת נתונים — דיאגרמה של מסלול הבקשה מהדפדפן דרך השער אל המודל ובחזרה, עם נקודות שבהן התשובה מפוצלת לחלקים והיכן שעלולות להיות נפילות חיבור.
- מדיניות טיימאאוטים — כללים לקריאה, לזמן המתנה ולמשך הכולל של הסטרים, התנהגות בעת ניתוק בצד הלקוח והמודל, אסטרטגיית התחברות מחדש ללא שכפול של מקטעים שכבר נשלחו.
- נהלי אבחון — פירוק שלב-אחר-שלב של תקלות אופייניות: התשובה מגיעה בשלמותה במקום כזרימה, החיבור נשבר באמצע, חלקים הולכים לאיבוד בעת דחיסה — עם ציון איזה שכבה יש לבדוק ראשונה.
- ניטור ומטריקות — סט מדדים: זמן עד למקטע הראשון, שיעור סטרימים שנקטעו, משך תשובה ממוצע; ספים שבהם מופעלת התראה.
- תיעוד — תיאור ההגדרות עם הערות, הוראות לשחזור מקומי של הזרימה וצ'ק-ליסט קבלה עבור המהנדסים שלכם.
- העברת ידע — פירוק הארכיטקטורה עם הצוות, מענה לנקודות שנויות במחלוקת ואיש קשר לשאלות בתקופת הליווי.
כל ארטיפקט קשור לתרחיש הפעלה קונקרטי, ולא מונח "למראית עין". תארו את המשימה שלכם — נחזור עם הערכה ותוכנית מה בדיוק ייכלל בהעברה בפרויקט שלכם.
מקרה בוחן: כיצד שליחה בזרימה הסירה קטיעות של תשובות ארוכות
הלקוח — שירות לבחירת ציוד עם יועץ מקוון המבוסס על מודל שפה. תשובות לשאילתות מורכבות נוצרו במשך 20-40 שניות, וכל אותו זמן המשתמש ראה מחוון טעינה. לאחר 8-10 שניות של המתנה החלו שליחות חוזרות של הטופס, וחלק מהשאילתות נקטעו בצד ה-proxy: החיבור נסגר לפני שהגיעה התשובה המלאה.
העברנו את הפלט לשליחה בזרימה של אירועים: השרת שולח את התשובה במנות תוך כדי יצירה, והחיבור נשאר פתוח עד הטוקן האחרון.
ב-proxy העלינו את timeout הקריאה בהתאם לאורך היצירה בפועל והשבתנו את ה-buffering של התשובה — קודם לכן שכבת הביניים צברה את גוף התשובה בשלמותו והעבירה אותו בגוש אחד, וזה מה שיצר את הקטיעות.
מה השתנה במספרים
| מדד | לפני | אחרי |
|---|---|---|
| שיעור החיבורים הקטועים | 11-14% | פחות מ-1% |
| זמן עד לטקסט הראשון הנראה | 8-10 ש' | 0,6-0,9 ש' |
| שליחות חוזרות של הבקשה | 1 ל-6 סשנים | נעלמו כמעט לחלוטין |
| עומס שיא על השרת | גדל עם אורך התשובה | נשאר יציב |
הטוקן הראשון מגיע כמעט מיד, ולכן המשתמש רואה את תחילת התשובה ולא מסך ריק, ומפסיק ללחוץ על הכפתור שוב. העומס התאזן: השרת כבר לא מחזיק בקשות כבדות בתור ליצירה בשלמותן — מעביר נתונים לפי מוכנות ומשחרר workers מוקדם יותר. בנוסף הוספנו הערות heartbeat כל 15 שניות, כדי שצמתי ביניים לא ינתקו חיבור לא פעיל.
בנפרד סגרנו את ה-rollback: אם הזרימה נקטעת אצל הלקוח, ה-frontend מתחבר מחדש ומשלים את התשובה מהמיקום האחרון שהתקבל, ולא מתחיל את היצירה מחדש.
לפי הניסיון שלנו על SSE מעבר כזה מחזיר את ההשקעה כבר בגל התעבורה הראשון — הקטיעות נעלמות, והשרת מפסיק לבזבז משאבים על יצירה חוזרת של בקשות נטושות.
במה נבדלים אירועי שרת מסקר שרת מתמשך
בסקר מתמשך הלקוח עצמו יוזם כל בקשה: פעם בשנייה, פעם בשתיים — לא משנה, השרת חייב להשיב, גם אם אין נתונים חדשים. במאה לשוניות פתוחות מדובר במאה בקשות בשנייה, שמתוכן מועילות בודדות.
העומס העיקרי אינו נופל על יצירת התשובה, אלא על הקמת חיבורים, ניתוח כותרות ועבודה עם מאגר עובדים — היישום מבזבז משאבים על אישור שדבר לא השתנה.
אירועי שרת הופכים את הסכימה: הלקוח פותח פעם אחת חיבור ארוך-טווח, והשרת עצמו שולח נתונים עם היווצרם. החיבור נשאר פתוח, אך אינו תופס תהליכון לכל משך ההמתנה — במודל אסינכרוני הוא תלוי בלולאת האירועים ומתעורר רק לצורך שליחה.
מכאן ההבדל בעומס: במקום אלפי בקשות קצרות — מאות ערוצים פתוחים, ועלות התחזוקה של ערוץ אחד נמוכה בסדר גודל.
גם השהיית האספקה מתנהגת אחרת. בסקר הטוקן מגיע לא כשמוכן, אלא כשהתזמון הבא פעל — בממוצע מחצית ממרווח ההמתנה מתבזבזת לריק, ולעין זה נראה כטקסט שמופיע בפרצים. אירועי שרת מוסרים את הטוקן ברגע היצירה, ולכן התשובה מודפסת בצורה חלקה, ללא הפסקות בין מנות.
מורכבות התמיכה אינה בתחבורה עצמה, אלא במה שמקיף אותה. סקר מתרחב בקלות אופקית: הבקשות קצרות, מאזן העומס מפזר אותן בין המופעים ללא תלות בתהליך מסוים. חיבורים ארוכים דורשים שמאזן העומס לא יקרע את הערוץ בגלל פסק זמן, ושהפרוקסי לא יבצע חוצץ על הזרם.
אם מאחורי מאזן העומס ישנם כמה מופעים, יש להחליט כיצד אירוע שנוצר באחד מגיע ללקוח המחובר לאחר — בדרך כלל דרך אפיק משותף.
מסקנה מעשית: סקר מתמשך קל יותר להפעלה, אך הוא מתייקר באופן ליניארי עם גידול הקהל ופוגע בחלקות התשובה. אירועי שרת מספקים עומס קטן יותר והשהיה הוגנת, אך דורשים תשתית מוגדרת — פסקי זמן, חוצץ מבוטל, מנגנון הפצה בין מופעים. הבחירה תלויה בשאלה אם אתם מוכנים להשקיע פעם אחת בתשתית כדי לא לשלם על סקר עבור כל משתמש חדש.
ספקות טכניים נפוצים לפני הטמעה
סטרימינג של תשובות המודל הוא חיבור HTTP ארוך-טווח, וכמעט כל הספקות סביבו מצטמצמים לשאלה כיצד ישרדו אותו הצמתים המתווכים: פרוקסי הפוך, מאזן עומסים, CDN. להלן ניתוח של מצבים אופייניים שצוותים נתקלים בהם בהטמעה הראשונה.
האם פרוקסי ומאזן עומסים לא מבצעים חוצצות לזרם?
כברירת מחדל, פרוקסי הפוך רבים צוברים את התשובה כולה לפני שהם מוסרים אותה ללקוח — והסטרימינג הופך לבקשה ארוכה רגילה. הפתרון הוא השבתת החוצצות ברמת המסלול וכותרת תגובה נכונות. בנפרד בודקים את זמני הקצוב לקריאה: אם הם קצרים מזמן היצירה, החיבור נקטע באמצע התשובה. מגדירים מסלול נפרד לזרם בנפרד משאר ה-API.
מה קורה בעת ניתוק החיבור?
הלקוח מקבל את החלק שכבר נוצר של הטקסט, והשרת מזהה את הניתוק ומפסיק את עבודת המודל — אחרת הבקשה ממשיכה לשרוף משאבים לשווא. בצד הממשק מציגים מצב ברור וכפתור ליצירה מחדש. אם התשובה נכתבת להיסטוריה, שומרים את התוצאה החלקית כדי שהמשתמש לא יאבד את הקשר השיחה.
האם ניתן ליצור מחדש תשובה ללא כפילויות בהיסטוריה?
כן. יצירה מחדש היא בקשה נפרדת עם אותו הקשר, ולא המשך של זו שנקטעה. בשרת מסמנים את הניסיון הקודם כמבוטל ולא כותבים אותו להיסטוריה כתשובה מלאה. כך נשארת בשיחה גרסה אחת עדכנית, והביניים לא מזהמים את ההקשר של הבקשות הבאות.
כיצד זה עובד מאחורי CDN וברשתות סלולריות?
CDN כברירת מחדל מיועד לתשובות קצרות ועלול לסגור חיבור ארוך. עבור הזרם מגדירים התנהגות מטמון נפרדת ומאריכים את זמן החיים המותר של החיבור. ברשתות סלולריות מוסיפים אותות heartbeat כדי שהצמתים המתווכים לא יחשבו שהחיבור בטל ויקטעו אותו לפני סיום היצירה.
אם נותרו שאלות לגבי אחד מהסעיפים בהתאם לקונטור שלכם — תארו את המשימה, ונחזור עם הערכה ותוכנית.
נדון במשימה שלכם ונחזור עם תוכנית
כדי לחזור עם תוכנית הטמעה ולא עם מילים כלליות, אנחנו צריכים נתוני מקור על המערכת שלכם. סטרימינג של תשובות נוגע בצד הלקוח, בשכבת השרת ובתשתית ההפצה — ככל שהנתונים המדויקים יותר, כך יש פחות הנחות בהערכת לוחות הזמנים וכך מהר יותר נוכל להציג לוח זמנים ריאלי.
מה שמזרז את הכנת התוכנית:
- תרחיש שימוש: היכן בדיוק מופיע הזרם — צ'אט תמיכה, עוזר באזור האישי, השלמות בחיפוש בקטלוג.
- מקור התשובות: מודל עצמאי, ספק חיצוני או כמה בו-זמנית — מכך נגזרת סכמת הפרוקסי והבאפרינג.
- הסטאק הנוכחי: שפה ופריימוורק של הצד הלקוח, פלטפורמת השרת, האם יש פרוקסי הפוך ומאזן עומסים.
- תשתית: היכן האפליקציה נפרסת, האם יש מגבלות על חיבורים יוצאים וזמני פסק זמן בהיקף.
- עומס: מספר משוער של דיאלוגים בו-זמנית בשיא ואורך תשובה ממוצע.
- דרישות אמינות: האם נדרש מעבר לתשובה רגילה בעת ניתוק החיבור, כיצד להתנהג בעת אתחול מחדש של השירות.
- הרשאות ותהליכים: מי בצד שלכם מקבל החלטות לגבי שחרורים, האם יש סביבת בדיקות, כיצד בנוי תהליך ההפצה.
אם חלק מהנתונים עדיין אין — זה לא חסם. אנחנו נתעד הנחות ישירות בתוכנית, נסמן מה נבהיר בשלב הבדיקה, ונראה כיצד כל פרמטר לא ידוע משפיע על לוחות הזמנים.
תארו את המשימה בצורה חופשית — נחזור עם הערכה, שלבים ותוכנית הטמעה.







