Structured Memory — זיכרון מובנה במקום היסטוריית צ׳אט אינסופית

מאת צוות מדיה דיל · 12.08.2026 · Context Engineering · 5 דק׳

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

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

הבעיה המבנית עם "היסטוריה כזיכרון"

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

מה זה זיכרון מובנה בפועל

במקום טרנסקריפט, המערכת שומרת רשומות מובנות, לרוב בפורמט שדומה לזה:

{
  "entity": "user_42",
  "key": "preferred_language",
  "value": "he",
  "confidence": 0.95,
  "source_turn": "conv_881#12",
  "updated_at": "2026-08-01T10:00:00Z"
}

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

סכמה: לא כל עובדה נכתבת אותו דבר

עיצוב סכמה טוב לזיכרון מובנה מבחין בין כמה סוגי רשומות: עובדות יציבות (שם, תפקיד, העדפות) עם confidence גבוה ותוקף ארוך; מצב זמני (משימה פעילה כרגע, שלב בתהליך) עם תוקף קצוב שפג אוטומטית; ואירועים (מה קרה, מתי) שדומים לרשומות log אבל מקושרים לישויות ספציפיות ולא מפוזרים בטרנסקריפט. ההבחנה הזו חשובה כי היא קובעת מה נטען אוטומטית בכל שיחה חדשה (העובדות היציבות) לעומת מה שנשלף רק לפי דרישה. חלוקה נוספת רלוונטית לתחום זה מפורטת במאמר על Working Memory מול Long-Term Memory, שדן בהבדל בין מה שפעיל "כרגע" למה ששמור לטווח ארוך.

קונפליקטים: מה קורה כשמידע חדש סותר ישן

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

מתי טרנסקריפט גולמי עדיין עדיף

זיכרון מובנה לא מייתר לגמרי את הצורך בטרנסקריפט — הוא רק מפסיק להיות מנגנון הזיכרון הראשי. לצרכי audit, debugging, או שחזור הקשר מדויק ("מה בדיוק נאמר בתור הזה"), הלוג הגולמי עדיין הכרחי, ואמור להישמר בנפרד כ-source of truth היסטורי. ההבדל הוא שהמודל בזמן ריצה לא ניזון ישירות מהלוג — הוא ניזון מהתמצית המובנית שנגזרת ממנו, מה שהופך את השאלה "מה המודל יודע על המשתמש הזה כרגע" לתשובה חד-משמעית וניתנת לבדיקה, במקום פונקציה מסובכת של עומק היסטוריה וסדר הופעה.

מי אחראי על חילוץ העובדות

שאלה מעשית שכל צוות שמיישם זיכרון מובנה נתקל בה: מי, בפועל, אחראי להפוך שיחה חופשית לרשומות מובנות? שלוש גישות נפוצות, עם פשרות שונות. הגישה הראשונה היא חילוץ בזמן אמת — בסוף כל תור, קריאה נוספת קטנה למודל שמזהה אם נאמר משהו ששווה לשמור, ומחזירה אותו כ-JSON מובנה; זריז אבל מוסיף עלות וזמן תגובה לכל תור, גם כשלא נאמר שום דבר חדש לשמירה. הגישה השנייה היא חילוץ אסינכרוני — השיחה ממשיכה בלי עיכוב, וג'וב נפרד, שרץ ברקע כמה שניות או דקות אחרי, סורק את התור ומחלץ ממנו רשומות; זה מסיר את העלות מנתיב התגובה למשתמש אבל יוצר חלון זמן קצר שבו הרשומה עוד לא זמינה אם המשתמש ישאל עליה מיד. הגישה השלישית, הכי פשוטה לתחזוקה אבל הכי פחות מדויקת, מבוססת כללים קבועים (רגקסים, דפוסי משפט מוכרים) בלי מודל בכלל — עובדת טוב למקרים חוזרים וצפויים ("קבע לי תזכורת ל-") אבל מפספסת ניסוחים לא צפויים. בפרודקשן נפוץ שילוב: כללים קבועים לתבניות ברורות וחוזרות, וחילוץ אסינכרוני מבוסס מודל לכל השאר.

ניקוי: זיכרון מובנה גם צריך פינוי

נטייה נפוצה היא להתייחס לזיכרון מובנה כמשהו שרק גדל — כל עובדה חדשה נשמרת, ואף אחת לא נמחקת, מתוך תחושה שגויה ש"יותר מידע תמיד עדיף". בפועל, רשומות שאבד להן תוקף (משימה שהסתיימה, העדפה זמנית שכבר לא רלוונטית) שנשארות במאגר בלי סימון תפוגה מתחילות להתנהג בדיוק כמו הבעיה שהזיכרון המובנה נועד לפתור מלכתחילה — הן מצטברות, חלקן סותרות זו את זו, וקשה יותר ויותר לדעת מה עדיין תקף. לכל סוג רשומה צריך להיות TTL (time-to-live) מפורש או תנאי תפוגה ברור: העדפת שפה כמעט לעולם לא פגה, אבל "המשימה הפעילה כרגע" צריכה לפוג ברגע שהמשימה מסתיימת, ופרט זמני כמו "המשתמש מחכה לתשובה מספק חיצוני" צריך לפוג תוך שעות, לא להישאר לנצח כרשומה יתומה שמבלבלת שאילתות עתידיות.

תגיות: Structured Memory · Agent Memory · Data Schema · Conflict Resolution · LLM Agents

← חזרה לבלוג · צור קשר