דלג לתוכן הראשי
רמה: מתקדם עודכן: אוגוסט 2026

זיכרון בסוכני AI

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

"זיכרון" הוא ארבע בעיות שונות

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

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

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

השאלה שממיינת

האם אני שולף לפי ערך מדויק או לפי משמעות? הראשון — טבלה. השני — ורק השני — מצדיק מסד וקטורי.

החלון אינו זיכרון

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

התיקון האינטואיטיבי — לשלוח את כל התמליל בכל בקשה — עובד לעשר הודעות ונשבר אחר כך, משתי סיבות שאינן קשורות זו לזו:

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

ניהול השיחה: שלוש אסטרטגיות

לניהול מצב השיחה יש שלוש גישות בסיסיות, ולכל אחת כשל אופייני.

מה שעובד בפועל: היברידי

המבנה שמחזיק משלב את שלושתם, וכל רכיב בו עונה על כשל של אחר:

messages = [
    {"role": "system", "content": SYSTEM},

    # 1. עובדות נעוצות — לא נמחקות לעולם
    {"role": "system", "content": render_facts(user_facts)},

    # 2. סיכום של כל מה שלפני החלון
    {"role": "system", "content": rolling_summary},

    # 3. N התורים האחרונים, מילה במילה
    *recent_turns[-8:],

    {"role": "user", "content": new_message},
]

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

וכשמסכמים — תנחה את המסכם במפורש מה לשמור. סיכום חופשי יזרוק את המספרים.

סכם את חלק השיחה הזה בקצרה.

חובה לשמור מילה במילה:
שמות, מספרים, סכומים, תאריכים,
מזהי הזמנה, והחלטות שהתקבלו.

אל תסכם החלטה — צטט אותה.

עובדות: הטבלה מנצחת

כאן נמצא ההבדל הגדול ביותר בין מערכת שעובדת לאחת שמתנהגת מוזר.

עובדה על משתמש היא נתון: plan = "pro", timezone = "Asia/Jerusalem", prefers_whatsapp = true. השליפה שלה היא לפי מפתח, לא לפי דמיון. מסד וקטורי יחזיר לך "את שלוש העובדות הכי דומות לשאלה", וזו לא השאלה — אתה רוצה את המסלול של המשתמש הזה, בוודאות, תמיד.

המבנה המינימלי שעובד:

CREATE TABLE user_facts (
  user_id    TEXT NOT NULL,
  key        TEXT NOT NULL,      -- "plan", "timezone", "prefers_channel"
  value      TEXT NOT NULL,
  source     TEXT,               -- "user_said" | "system" | "inferred"
  updated_at TIMESTAMP NOT NULL,
  PRIMARY KEY (user_id, key)
);

שלוש העמודות האחרונות הן מה שהופך את זה לשימושי ולא רק לאחסון. source מאפשר לתעדף — מה שהמשתמש אמר במפורש גובר על מה שהמודל הסיק. updated_at מאפשר להכריע בסתירות. וה-PRIMARY KEY על הצמד מבטיח שעובדה חדשה דורסת את הישנה במקום להצטבר לצידה — וזו בדיוק הבעיה בסעיף על ריקבון.

כמה עובדות באמת להזריק

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

התשובה הנאיבית — כולן — נשברת משתי סיבות מוכרות: זה עולה, וזה מדלל. ארבעים עובדות שרובן לא רלוונטיות לשאלה הנוכחית מקשות על המודל למצוא את השתיים שכן.

מה שעובד הוא חלוקה לשתי שכבות: גרעין קבוע — חמש-שש עובדות שרלוונטיות תמיד (שם, מסלול, שפה, אזור זמן), שנכנסות בכל בקשה; ושכבה מותנית שנשלפת לפי הנושא. אם השאלה על חיוב, נכנסות העובדות הפיננסיות; אם היא על תמיכה טכנית, נכנסות אלה.

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

מתי כן מסד וקטורי

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

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

הצד הקשה הוא הכתיבה, לא הקריאה

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

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

מהשיחה הזאת, חלץ עובדות על המשתמש.

מותר לכתוב רק את המפתחות הבאים:
plan, timezone, prefers_channel,
company_size, industry

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

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

זיכרון שהתיישן

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

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

שלושה מנגנונים, לפי סדר החשיבות:

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

הסוג הרביעי: מה שנלמד

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

הקושי אינו טכני אלא הגדרתי: אי אפשר לדעת אם משהו היה העדפה או מקרה. משתמש שביקש פעמיים תשובה קצרה — האם הוא מעדיף קצר, או שבמקרה שתי השאלות היו פשוטות? מערכת שמסיקה מהר מדי הופכת מעצבנת, ואחת שמסיקה לאט מדי לא נראית כלומדת בכלל.

מה שכן עובד, ובלי שום קסם:

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

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

מצב המשימה: לא מודל, מכונת מצבים

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

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

הפתרון הוא לא זיכרון טוב יותר אלא להוציא את זה מהמודל לגמרי:

run = {
  "id": "run_8812",
  "steps": {
    "check_stock": {"status": "done", "at": "..."},
    "charge":      {"status": "done", "ref": "ch_44"},
    "notify":      {"status": "pending"},
  },
}

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

עברית: שלושה פרטים

לבנות או להשתמש בספרייה

יש היום ספריות ושירותים שמציעים "זיכרון לסוכנים" כרכיב מוכן. שווה לדעת מה הם באמת נותנים לפני שמכניסים תלות.

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

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

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

ברגע שיש זיכרון, יש מידע אישי

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

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

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

כשהסוכן זוכר משהו מוזר

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

מה שצריך לתעד בכל קריאה כדי שתקלה כזאת תהיה פתירה:

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

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

איך יודעים שהזיכרון עוזר

"עכשיו הוא זוכר" זו לא מדידה, והיא גם לא תחזיק כשתשנה משהו בעוד חודש.

מה שכן אפשר למדוד:

ובנייה של ערכת בדיקה עם שיחות רב-תוריות אמיתיות היא מה שמאפשר להשוות גרסאות — אותה גישה שמפורטת בEvals.

מתי לא צריך זיכרון בכלל

טעויות שחוזרות

הצעד הבא

זיכרון הוא חלק מבניית סוכנים ומניהול הקשר. העמק בשניהם.