Memory
תזכורת: זיכרון קצר טווח מול ארוך טווח
בנושא AI Agents ראינו את ההבחנה העקרונית בין זיכרון קצר טווחקצר טווח הוא הקונטקסט של ה-session הנוכחי ונעלם בסיומו; ארוך טווח הוא מה שנכתב במפורש לאחסון חיצוני כדי לשרוד. מתוך AI Agents, Memory — מה שורד מעבר לשיחה. (מה שנשאר בתוך שיחה אחת) לזיכרון ארוך טווח (מה שנשמר גם אחרי שהשיחה נגמרת ומשמש שיחות עתידיות) — ראו פרק ה-Memoryאחסון חיצוני לקונטקסט, כמו קובץ, מסד נתונים או אינדקס וקטורי, שהסוכן כותב אליו וקורא ממנו ב-session חדש. מתוך AI Agents, Memory — מה שורד מעבר לשיחה. שם. בפרק הזה נראה איך LangChainפריימוורק בקוד פתוח לבניית אפליקציות מבוססות LLM ו-agents, שעוטף רכיבים חוזרים בממשק אחיד. מתוך LangChain, מבוא ל-LangChain. ממש את שני הסוגים בקוד.
עד עכשיו כבר השתמשנו בזיכרון קצר טווח בלי לקרוא לו כך: כל thread_id שהעברנו ב-configurable מאז פרק 2 הוא בדיוק זה — היסטוריית ההודעות שנשמרת ומוזרמת חזרה למודל בכל קריאה נוספת לאותו thread.
זיכרון קצר טווח: מעבר להודעות בלבד
זיכרון קצר טווחקצר טווח הוא הקונטקסט של ה-session הנוכחי ונעלם בסיומו; ארוך טווח הוא מה שנכתב במפורש לאחסון חיצוני כדי לשרוד. מתוך AI Agents, Memory — מה שורד מעבר לשיחה. לא חייב להיות רק רשימת הודעות — אפשר להחזיק ב-state של ה-agentLLM שמופעל בלולאה, עם מטרה וסט כלים, ובכל סיבוב מחליט אם להפעיל כלי נוסף או לסיים ולהחזיר תשובה. מתוך AI Agents, Agents והלולאה האגנטית. גם שדות מותאמים אישית משלכם (למשל מונה כמה פעמים כבר הופעל כלי מסוים, או רשימת ישויות שזוהו עד כה בשיחה), שנשמרים ומתעדכנים באותו thread לאורך כל הסיבובים.
המשותף לכל מה שב-state הזה: הוא קשור ל-thread_id הספציפי. ברגע שעוברים ל-thread חדש — גם אם זה אותו משתמש בדיוק — ה-state הזה לא קיים יותר. בשביל מידע שצריך לשרוד מעבר לשיחה בודדת צריך את הסוג השני.
זיכרון ארוך טווח: חנות שחוצה threads
זיכרון ארוך טווחקצר טווח הוא הקונטקסט של ה-session הנוכחי ונעלם בסיומו; ארוך טווח הוא מה שנכתב במפורש לאחסון חיצוני כדי לשרוד. מתוך AI Agents, Memory — מה שורד מעבר לשיחה. נשמר במבנה נפרד — store — שלא קשור לשום thread_id ספציפי. שומרים בו ערכים לפי namespace (למשל מזהה משתמש) ומפתח (למשל "preferences"), וכל agentLLM שמופעל בלולאה, עם מטרה וסט כלים, ובכל סיבוב מחליט אם להפעיל כלי נוסף או לסיים ולהחזיר תשובה. מתוך AI Agents, Agents והלולאה האגנטית. שמקבל גישה לאותו store יכול לקרוא את הערך הזה, בלי קשר לאיזה thread השתמשו בו כדי לכתוב אותו מלכתחילה.
import { InMemoryStore } from "langchain";
const store = new InMemoryStore();
await store.put(["users", "user-42"], "preferences", { unit: "celsius" });
const memory = await store.get(["users", "user-42"], "preferences");
// → { value: { unit: "celsius" }, ... }איך agent קורא וכותב לזיכרון ארוך טווח
מעבירים את ה-store ל-createAgent, ומגדירים כלי שקורא ל-store.put/store.get בתוך הפונקציה שלו — ה-userId מגיע מ-config בדיוק כפי שראינו עם getUserOrders בפרק ה-Tools, כדי שהמודל לא יצטרך (ולא יוכל) להמציא מזהה משתמש בעצמו.
const rememberPreference = tool(
async ({ key, value }: { key: string; value: string }, config) => {
const userId = config?.configurable?.userId;
await store.put(["users", userId], key, value);
return "נשמר";
},
{
name: "remember_preference",
description: "שומר העדפה של המשתמש לשימוש עתידי, גם בשיחות הבאות",
schema: z.object({ key: z.string(), value: z.string() }),
}
);דוגמה מלאה: אותה עובדה נזכרת בשתי שיחות שונות
בדוגמה הבאה יוצרים agentLLM שמופעל בלולאה, עם מטרה וסט כלים, ובכל סיבוב מחליט אם להפעיל כלי נוסף או לסיים ולהחזיר תשובה. מתוך AI Agents, Agents והלולאה האגנטית. עם store משותף, וקוראים לו פעמיים עם שני thread_id שונים לגמרי — כאילו שתי שיחות נפרדות — אבל עם אותו userId. ההעדפה שנשמרה ב-thread הראשון זמינה גם ב-thread השני, כי היא נשמרה בזיכרון הארוך-טווח ולא ב-state של thread מסוים.
const agent = createAgent({
model: "anthropic:claude-sonnet-5",
tools: [getWeather, rememberPreference],
store,
});
// שיחה 1, thread-a
await agent.invoke(
{ messages: [{ role: "user", content: "תזכור שאני מעדיף מעלות צלזיוס" }] },
{ configurable: { thread_id: "thread-a", userId: "user-42" } }
);
// שיחה 2, thread-b — thread שונה לגמרי, אבל אותו משתמש
await agent.invoke(
{ messages: [{ role: "user", content: "מה מזג האוויר בתל אביב?" }] },
{ configurable: { thread_id: "thread-b", userId: "user-42" } }
);
// ה-agent עדיין "זוכר" את ההעדפה מ-thread-a, כי היא הגיעה מה-store ולא מהיסטוריית ה-threadשאלות נפוצות
האם InMemoryStore מתאים ל-production?
לא. כפי שהשם מרמז, הוא שומר את הנתונים בזיכרון של התהליך: כל מה שנשמר נעלם כשהשרת עולה מחדש, והוא לא משותף בין כמה שרתים. הוא מצוין לפיתוח ולדוגמאות, וב-production מחליפים אותו ב-store שמגובה בבסיס נתונים, בלי לשנות את הקוד שקורא ל-put ו-get.
איך מוחקים מידע שהמשתמש ביקש לשכוח?
הזיכרון הארוך-טווח מאורגן לפי namespace ומפתח, ולכן אפשר למחוק רשומה ספציפית או את כל מה שנשמר תחת ה-namespace של משתמש מסוים. כדאי לתכנן את זה מראש ולתת פעולת מחיקה מפורשת, דרך כלי או דרך הממשק, כי בקשות "תשכח את זה" ודרישות פרטיות יגיעו בסוף.
האם כדאי לתת למודל לכתוב לזיכרון כל מפתח שהוא רוצה?
עדיף להגביל. כלי שמקבל רק מפתחות מוגדרים מראש (כמו preferences) וסכימה לערך, במקום מפתח וערך חופשיים, שומר על הזיכרון מסודר. הוא גם מקשה על הזרקה לכתוב הוראות זדוניות שישרדו לשיחות הבאות — ה-memory poisoning שפגשנו בנושא AI Agents.