כל המאגר בכל פרומפט
המנוע הראשון היה Lambda ששלחה את כל המאגר המכווץ עם כל בקשה: כ־245 אלף תווים של נתוני שירותים, ב־cache אבל נשלחים שוב בכל סבב, וגדלים עם כל שירות חדש.
השארנו אותו כבסיס עובד ועברנו לתכנון מבוסס retrieval על בסיס ידע מנוהל.
תושבים של אזור שנפגע ממשבר בדרום הארץ מתארים במילים שלהם מה הם צריכים, באחת מחמש שפות. הצ׳אט שואל רק את מה שחייבים לדעת (איפה הם גרים, בני כמה הם ואיזה סוג עזרה הם מחפשים), ומחזיר עד שלושה שירותים אמיתיים מתוך מאגר של 354. הסינון לפי מיקום וגיל נעשה בקוד ולא על ידי המודל.
אחרי משבר אזורי, עשרות ארגונים פתחו לתושבים תוכניות של טיפול, שיקום, ייעוץ משפטי, סיוע כלכלי ופעילות קהילתית. לכל תוכנית כללים משלה: חלקן משרתות רשות מקומית אחת בלבד, חלקן את כל האזור וחלקן את כל הארץ, ולרבות יש מגבלות גיל. תושבת שחיפשה פיזיותרפיה לעצמה, או תמיכה רגשית לבן מתבגר, הייתה צריכה לדעת לאיזה ארגון לפנות והאם היא בכלל זכאית.
הלקוח רצה מקום אחד שאפשר לשאול בו. הוא היה צריך לענות בשפה של התושב, לא להמציא אף שירות, ולהישאר מדויק גם כשהארגונים ממשיכים להוסיף ולשנות תוכניות.
התרשים רחב מהמסך: החליקו הצידה לצפייה בכולו →
מערכת הניהול היא מקור האמת היחיד. כל שינוי בשירות כותב שורה ל־outbox. שלב הסנכרון בונה מחדש את המסמך של אותו שירות, ואחרי reindex מלא או ייבוא CSV כותב snapshot אחד של כל המאגר, שהסוכן טוען מחדש עם TTL של 5 דקות. המנוע הקודם נשאר מאחורי מתג קונפיגורציה, כך שהצוות יכול היה להשוות בין שני המסלולים ולחזור אחורה בלי דיפלוי.
הסוכן המנוהל החליט לבד כמה פעמים לחפש בבסיס הידע, בדרך כלל 3 פעמים או יותר בכל סבב. ה־runtime החדש מריץ רצף קבוע: תשאול, סינון, דירוג. שום דבר לא נשלף לפני שידועים היישוב, הגיל והצורך. המחיר: שירות Python לצד backend ב־.NET, ותחזוקת התזמור עלינו.
לאיזו רשות שייך יישוב, האם שירות הוא מקומי, אזורי או ארצי, ומה טווח הגילים שלו, כל אלה נבדקים בסינון דטרמיניסטי. המודל רק מדרג לפי התחום המקצועי. המחיר: את הטבלה של יישוב לרשות צריך לתחזק ידנית.
במאגר של 354 רשומות קצרות ומובנות, הסינון מצמצם את הרשימה לחציון של כ־70 מועמדים עוד לפני שהמודל רואה משהו. קובץ JSON אחד ב־S3 חסך chunking, משימות ingestion ובעיות של citation tokens. המחיר: זה עובד כל עוד המאגר נכנס לזיכרון; מאגר גדול בהרבה יחזיר את ה־retrieval.
קריאת הדירוג יכולה לבחור רק מתוך רשימת המועמדים שקיבלה. כל מזהה מחוץ לרשימה נזרק לפני שה־backend טוען את כרטיסי השירות. אם לא נשאר אף מזהה תקין, המשתמש מקבל תשובה כנה ש”אין התאמה באזור שלך” והצעה להמשך.
כפתור החירום עוקף את הסוכן ומחזיר רשימה מוגדרת מראש של שירותי חירום בשפת המשתמש. התשובה הדחופה ביותר אף פעם לא מחכה למודל ולא תלויה בו.
רוב הבעיות הקשות צצו כשההתאמה רצה על נתוני מאגר אמיתיים ושאלות אמיתיות. מבחר:
המנוע הראשון היה Lambda ששלחה את כל המאגר המכווץ עם כל בקשה: כ־245 אלף תווים של נתוני שירותים, ב־cache אבל נשלחים שוב בכל סבב, וגדלים עם כל שירות חדש.
השארנו אותו כבסיס עובד ועברנו לתכנון מבוסס retrieval על בסיס ידע מנוהל.
ה־chunking שבברירת המחדל הפריד בין שורת המזהה של כל שירות לבין הטקסט שלו. המודל החזיר את ה־citation tokens של בסיס הידע במקום מזהים אמיתיים, ה־backend לא הצליח לפענח אותם, והתאמות נכונות נעלמו.
יצרנו מחדש את מקור הנתונים בלי chunking, העשרנו כל מסמך בסוג טיפול, מגזר, היקף וגיל, והוספנו לפרומפט כלל של מזהים מספריים.
הסוכן רץ עם temperature 1. התשובות חרגו מחוזה ה־JSON, חזרו על שירותים שכבר הוצגו, ולפעמים הציגו למשתמש {"answer": …} גולמי.
הורדנו את ה־temperature ל־0, הוספנו parser מוקשח ב־backend, ורשימת המזהים שכבר הוצגו נשלחת בכל סבב כמקור האמת.
תושבים של מועצה אזורית אחת קיבלו שירותים שמשרתים רק מועצה שכנה. המודל הסיק את המיקום מטבלת חיפוש שהייתה כתובה בתוך הפרומפט.
העברנו את המיקום לסינון דטרמיניסטי: יישוב ← רשות, ואז היקף השירות. המודל לא רואה בכלל שירותים מחוץ לאזור.
ה־traces הראו כמה איטרציות אוטונומיות על בסיס הידע, ועוד pre- ו־post-processing בכל סבב, ומעל זה throttling וניסיונות חוזרים.
החלפנו את הסוכן המנוהל בסוכן מבוקר על AgentCore Runtime: קריאה אחת לתשאול, אחת לדירוג, system prompt ב־cache, ושאלות של ספירה או חיפוש לפי שם נענות מהמאגר בלי retrieval.
כשלא נשארו מועמדים, הסוכן שאל שוב על היישוב, והשיחה נכנסה ללולאה.
שתי סגירות מפורשות: ”היישוב שלך מחוץ לאזור שאנחנו מכסים” או ”הצגתי לך את כל מה שרלוונטי באזור שלך”, ולכל אחת צעד המשך.
המדרג רואה לכל היותר 60 מועמדים. במיון לפי מזהה, תוכניות ארציות יכלו למלא את החלון ולדחוק החוצה שירות מקומי שמתאים יותר.
ממיינים קודם מקומיים, אחר כך לפי התאמת מילות מפתח לצורך, ורק אז חותכים. הוספנו כלל ”לא ממלאים את הרשימה”: עדיף פחות תוצאות, או אפס, מאשר תוצאה מתחום אחר.
ה־runtime יצא עם קובץ CSV מובנה, כך שכל עדכון במאגר חייב דיפלוי מחדש של הסוכן.
ה־backend כותב snapshot של המאגר ל־S3 בכל reindex וייבוא CSV. הסוכן טוען אותו מחדש כל 5 דקות, וה־CSV המובנה נשאר רק כגיבוי קר.
שחזור מפושט של בקשה אחת: ”פיזיותרפיה לאישה בת 30 ביישוב באזור”. השלבים להמחשה בלבד. המספרים מגיעים מהקוד וממאגר של 354 שירותים. לא מוצגים נתוני לקוח.
| תחום | לפני | אחרי | שינוי | מקור |
|---|---|---|---|---|
| הקשר בכל קריאת דירוג | כל המאגר, כ־245 אלף תווים | עד 60 מועמדים מסוננים, כ־17 אלף תווים | −93% | נגזר מהקוד |
| קריאות מודל בסבב חיפוש | 3 איטרציות אוטונומיות או יותר | 2 (תשאול + דירוג), 1 לשאלת המשך | ירידה של 33%+ | נגזר מהקוד |
| זמן תשובה טיפוסי | דקות | כ־10 שניות | פי 10 ויותר | הערכה |
| מזהי שירות לא תקינים | citation tokens, התאמות נזרקו | רק מזהים מרשימת המועמדים | 0 בתכנון | נגזר מהקוד |
| משינוי במאגר ועד הצ׳אט | דיפלוי של הסוכן | טעינה מחדש של snapshot כל 300 שניות | עד 5 דקות | נמדד |
| תוצאות מחוץ לאזור | המודל מסיק את המיקום | סינון דטרמיניסטי לפי רשות והיקף | 0 בבדיקות | נגזר מהקוד |
| טעינת המאגר | הזנה שירות אחר שירות | ייבוא CSV אחד (30 שדות) + reindex אוטומטי | מימים לדקות | הערכה |
| תשובת חירום | דרך המודל | רשימה מוגדרת, בלי קריאת מודל | 0 קריאות מודל | נגזר מהקוד |
שיחת אבחון חינם · 30 דקות · ללא התחייבות
כתבו בכמה שורות: איזה תהליך או מערכת, מה כואב היום (זמן, טעויות, עלויות או פיילוט AI שנתקע) ומי עובד עם זה. אחרי שיחה קצרה מקבלים תוך כ־3 ימים תוכנית כתובה והצעת מחיר סגורה.
נפתח מייל מוכן מראש · מענה תוך יום עסקים
ה־AI קורא. הקוד סופר.
כל הפרויקטים