حافظه Agent — کوتاه‌مدت و بلندمدت

قسمت ۳ ۱۸ دقیقه

مقدمه: ماهی طلایی با حافظه ۳ ثانیه‌ای

یه شوخی قدیمی هست که می‌گه حافظه ماهی طلایی ۳ ثانیه‌ست. حالا تصور کن یه Agent هم همین حافظه رو داشته باشه. هر بار که باهاش حرف بزنی، همه چیز از اول! نه اسمت رو یادش می‌مونه، نه ترجیحاتت، نه حتی اینکه ۵ دقیقه پیش چی بهش گفتی.

واقعیت اینه که LLM ها ذاتاً بدون حافظه هستن. هر بار که API رو صدا می‌زنی، از صفر شروع می‌شه. اون “حافظه‌ای” که توی ChatGPT یا Claude می‌بینی، توسط سیستم‌های اطراف LLM ساخته شده — نه توسط خود LLM.

توی این اپیزود می‌خوایم بفهمیم سیستم‌های حافظه Agent چطور کار می‌کنن و چطور می‌شه یه حافظه خوب برای Agent ساخت.

چرا حافظه مهمه؟

بدون حافظه:

  • کاربر هر بار باید خودش رو معرفی کنه
  • Agent نمی‌تونه از اشتباهات قبلیش درس بگیره
  • هر مکالمه مستقل و بی‌ارتباط با بقیه‌ست
  • تجربه کاربری افتضاح می‌شه

با حافظه:

  • Agent می‌دونه تو کی هستی و چی دوست داری
  • از تجربه‌های قبلی استفاده می‌کنه
  • مکالمات به هم مرتبط می‌شن
  • کیفیت پاسخ‌ها با زمان بهتر می‌شه

انواع حافظه

مثل مغز انسان، حافظه Agent هم انواع مختلف داره. بیا هر کدوم رو بررسی کنیم:

۱. حافظه کوتاه‌مدت (Short-term / Working Memory)

این همون تاریخچه مکالمه فعلی ه. وقتی داری با Agent حرف می‌زنی، پیام‌های قبلی توی context window قرار می‌گیرن.

class ShortTermMemory:
    def __init__(self, max_messages: int = 50):
        self.messages = []
        self.max_messages = max_messages
    
    def add(self, role: str, content: str):
        self.messages.append({
            "role": role,
            "content": content
        })
        # اگه از حد گذشت، قدیمی‌ها رو حذف کن
        if len(self.messages) > self.max_messages:
            self.messages = self.messages[-self.max_messages:]
    
    def get_context(self) -> list:
        return self.messages.copy()
    
    def clear(self):
        self.messages = []
محدودیت مهم: هر LLM یه context window محدود داره. مثلاً GPT-4o حدود ۱۲۸ هزار توکن و Claude حدود ۲۰۰ هزار توکن. وقتی مکالمه طولانی می‌شه، نمی‌تونی همه رو توی context بذاری.

۲. حافظه بلندمدت (Long-term Memory)

اطلاعاتی که بین session ها باقی می‌مونه. مثل اسم کاربر، ترجیحاتش، تاریخچه سفارشاتش. این اطلاعات معمولاً توی دیتابیس ذخیره می‌شن.

import json
from datetime import datetime

class LongTermMemory:
    def __init__(self, storage_path: str = "memory.json"):
        self.storage_path = storage_path
        self.data = self._load()
    
    def _load(self) -> dict:
        try:
            with open(self.storage_path, 'r') as f:
                return json.load(f)
        except FileNotFoundError:
            return {"facts": [], "preferences": {}, "history": []}
    
    def _save(self):
        with open(self.storage_path, 'w') as f:
            json.dump(self.data, f, ensure_ascii=False, indent=2)
    
    def store_fact(self, fact: str, source: str = "conversation"):
        self.data["facts"].append({
            "fact": fact,
            "source": source,
            "timestamp": datetime.now().isoformat()
        })
        self._save()
    
    def store_preference(self, key: str, value: str):
        self.data["preferences"][key] = value
        self._save()
    
    def recall_facts(self, query: str = None) -> list:
        if query is None:
            return self.data["facts"]
        # جستجوی ساده
        return [f for f in self.data["facts"] 
                if query.lower() in f["fact"].lower()]
    
    def get_preference(self, key: str) -> str:
        return self.data["preferences"].get(key)

۳. حافظه اپیزودیک (Episodic Memory)

حافظه اپیزودیک مثل خاطرات شخصیه. Agent یادش می‌مونه چه اتفاقاتی افتاده و از اونا درس می‌گیره.

مثال: “دفعه قبل که کاربر درباره پایتون سوال کرد، من یه مثال ساده زدم و خوشش اومد. پس این بار هم مثال ساده بزنم.”

class EpisodicMemory:
    def __init__(self):
        self.episodes = []
    
    def record_episode(self, situation: str, action: str, 
                       outcome: str, feedback: str = None):
        self.episodes.append({
            "situation": situation,
            "action": action,
            "outcome": outcome,
            "feedback": feedback,
            "timestamp": datetime.now().isoformat()
        })
    
    def recall_similar(self, current_situation: str) -> list:
        """پیدا کردن تجربه‌های مشابه"""
        relevant = []
        for ep in self.episodes:
            # اینجا می‌شه از similarity search استفاده کرد
            if self._is_similar(current_situation, ep["situation"]):
                relevant.append(ep)
        return relevant
    
    def _is_similar(self, a: str, b: str) -> bool:
        """مقایسه ساده — در عمل از embedding استفاده می‌شه"""
        common_words = set(a.lower().split()) & set(b.lower().split())
        return len(common_words) > 3

۴. حافظه معنایی (Semantic Memory)

حافظه معنایی شامل دانش کلی و ساختاریافته‌ست. مثل یه دایره‌المعارف شخصی. تفاوتش با حافظه اپیزودیک اینه که به زمان و مکان خاصی گره نخورده.

مثال: “پایتون یه زبان برنامه‌نویسی سطح بالاست” (حافظه معنایی) در مقابل “دیروز به کاربر پایتون یاد دادم” (حافظه اپیزودیک).

Vector Database: قلب حافظه بلندمدت

برای پیاده‌سازی حافظه بلندمدت به شکل موثر، از Vector Database استفاده می‌شه. ایده ساده‌ست:

  1. متن رو تبدیل به embedding (بردار عددی) کن
  2. بردار رو توی دیتابیس ذخیره کن
  3. وقتی چیزی رو می‌خوای پیدا کنی، سوالت رو هم embedding کن و مشابه‌ترین بردارها رو پیدا کن
from openai import OpenAI
import numpy as np

client = OpenAI()

class VectorMemory:
    def __init__(self):
        self.memories = []  # لیست (text, embedding)
    
    def _get_embedding(self, text: str) -> list:
        """تبدیل متن به بردار"""
        response = client.embeddings.create(
            model="text-embedding-3-small",
            input=text
        )
        return response.data[0].embedding
    
    def store(self, text: str, metadata: dict = None):
        """ذخیره یه خاطره"""
        embedding = self._get_embedding(text)
        self.memories.append({
            "text": text,
            "embedding": embedding,
            "metadata": metadata or {}
        })
    
    def recall(self, query: str, top_k: int = 5) -> list:
        """یادآوری مرتبط‌ترین خاطرات"""
        query_embedding = self._get_embedding(query)
        
        # محاسبه شباهت کسینوسی
        scored = []
        for mem in self.memories:
            similarity = self._cosine_similarity(
                query_embedding, mem["embedding"]
            )
            scored.append((similarity, mem["text"]))
        
        # مرتب‌سازی بر اساس شباهت
        scored.sort(reverse=True)
        return [text for _, text in scored[:top_k]]
    
    def _cosine_similarity(self, a: list, b: list) -> float:
        a = np.array(a)
        b = np.array(b)
        return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
در عمل به جای پیاده‌سازی دستی، از ابزارهایی مثل ChromaDB، Pinecone، Weaviate یا Qdrant استفاده می‌شه. اینا بهینه‌تر هستن و قابلیت‌های بیشتری دارن.

مثال با ChromaDB

import chromadb

# ساخت دیتابیس
chroma_client = chromadb.Client()
collection = chroma_client.create_collection(name="agent_memory")

# ذخیره خاطرات
collection.add(
    documents=[
        "کاربر اسمش علی هست و برنامه‌نویس پایتون",
        "کاربر ترجیح می‌ده توضیحات با مثال عملی باشه",
        "آخرین بار درباره FastAPI سوال کرد",
        "کاربر تازه‌کار نیست، سطح متوسطه",
    ],
    ids=["fact1", "fact2", "fact3", "fact4"]
)

# یادآوری
results = collection.query(
    query_texts=["کاربر چه سطحی داره؟"],
    n_results=2
)
print(results["documents"])
# [['کاربر تازه‌کار نیست، سطح متوسطه', 
#   'کاربر ترجیح می‌ده توضیحات با مثال عملی باشه']]

خلاصه‌سازی مکالمه (Conversation Summarization)

وقتی مکالمه طولانی می‌شه و از حد context window رد می‌شه، یه راه‌حل خوب خلاصه‌سازی ه. به جای حذف پیام‌های قدیمی، خلاصشون می‌کنی:

class ConversationSummarizer:
    def __init__(self, llm_client, max_messages: int = 20):
        self.client = llm_client
        self.max_messages = max_messages
        self.messages = []
        self.summary = ""
    
    def add_message(self, role: str, content: str):
        self.messages.append({"role": role, "content": content})
        
        if len(self.messages) > self.max_messages:
            self._summarize_old_messages()
    
    def _summarize_old_messages(self):
        """خلاصه‌سازی پیام‌های قدیمی"""
        # نصف اول رو خلاصه کن
        to_summarize = self.messages[:len(self.messages)//2]
        to_keep = self.messages[len(self.messages)//2:]
        
        summary_prompt = f"""خلاصه مکالمه قبلی:
{self.summary}

پیام‌های جدید برای خلاصه‌سازی:
{self._format_messages(to_summarize)}

لطفاً یه خلاصه جامع از کل مکالمه تا اینجا بده. 
نکات مهم، تصمیمات، و اطلاعات کلیدی رو حفظ کن."""

        response = self.client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": summary_prompt}]
        )
        
        self.summary = response.choices[0].message.content
        self.messages = to_keep
    
    def get_context(self) -> list:
        """گرفتن context برای LLM"""
        context = []
        if self.summary:
            context.append({
                "role": "system",
                "content": f"خلاصه مکالمه قبلی:\n{self.summary}"
            })
        context.extend(self.messages)
        return context
    
    def _format_messages(self, messages: list) -> str:
        return "\n".join(
            f"{m['role']}: {m['content']}" for m in messages
        )

الگوهای عملی حافظه

الگوی ۱: حافظه سه‌لایه

یه معماری رایج برای حافظه Agent:

class ThreeLayerMemory:
    def __init__(self):
        # لایه ۱: Context فعلی (حافظه کاری)
        self.working_memory = ShortTermMemory(max_messages=20)
        
        # لایه ۲: خلاصه مکالمات (حافظه میان‌مدت)
        self.conversation_summary = ""
        
        # لایه ۳: دانش پایدار (حافظه بلندمدت)
        self.long_term = VectorMemory()
    
    def build_context(self, user_message: str) -> list:
        """ساختن context کامل برای LLM"""
        
        # از حافظه بلندمدت، مرتبط‌ترین اطلاعات رو بیار
        relevant_memories = self.long_term.recall(
            user_message, top_k=3
        )
        
        context = []
        
        # خلاصه مکالمات قبلی
        if self.conversation_summary:
            context.append({
                "role": "system",
                "content": f"خلاصه تعاملات قبلی:\n"
                          f"{self.conversation_summary}"
            })
        
        # اطلاعات مرتبط از حافظه بلندمدت
        if relevant_memories:
            memories_text = "\n".join(
                f"- {m}" for m in relevant_memories
            )
            context.append({
                "role": "system",
                "content": f"اطلاعات مرتبط:\n{memories_text}"
            })
        
        # مکالمه فعلی
        context.extend(self.working_memory.get_context())
        
        return context

الگوی ۲: استخراج خودکار اطلاعات

Agent می‌تونه خودش از مکالمه اطلاعات مهم رو استخراج و ذخیره کنه:

def extract_and_store(self, conversation: list):
    """استخراج خودکار اطلاعات مهم از مکالمه"""
    
    extraction_prompt = """از مکالمه زیر، اطلاعات مهم رو استخراج کن.
فقط اطلاعات واقعی و مهم رو بنویس، نه نظرات.
فرمت: هر اطلاعات در یک خط.

مکالمه:
{conversation}

اطلاعات استخراج‌شده:"""

    response = self.client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{
            "role": "user",
            "content": extraction_prompt.format(
                conversation=self._format_messages(conversation)
            )
        }]
    )
    
    facts = response.choices[0].message.content.strip().split("\n")
    
    for fact in facts:
        fact = fact.strip("- ").strip()
        if fact:
            self.long_term.store(fact)

مشکلات و چالش‌ها

مشکل ۱: حافظه‌های متناقض

کاربر ممکنه توی مکالمات مختلف چیزهای متناقض بگه. مثلاً یه بار بگه “من برنامه‌نویس جاواام” و بعداً بگه “من فقط پایتون بلدم”. باید سیستمی داشته باشی که اطلاعات جدیدتر رو اولویت بده.

مشکل ۲: حریم خصوصی

حافظه بلندمدت یعنی ذخیره اطلاعات کاربر. این موضوع حریم خصوصی داره. باید به کاربر اجازه بدی حافظه رو ببینه و پاک کنه.

مشکل ۳: مقیاس‌پذیری

وقتی حافظه بزرگ می‌شه، جستجو کندتر می‌شه. Vector database ها اینجا کمک می‌کنن، ولی باید معماری درستی داشته باشی.

مشکل ۴: نویز

همه چیز توی مکالمه مهم نیست. Agent باید بتونه تشخیص بده چه چیزی ارزش ذخیره‌سازی داره.

ترکیب حافظه با Agent

بیا ببینیم یه Agent کامل با حافظه چطوری کار می‌کنه:

class MemoryAgent:
    def __init__(self):
        self.memory = ThreeLayerMemory()
        self.client = OpenAI()
    
    def chat(self, user_message: str) -> str:
        # ذخیره پیام کاربر
        self.memory.working_memory.add("user", user_message)
        
        # ساختن context با حافظه
        context = self.memory.build_context(user_message)
        
        # ارسال به LLM
        response = self.client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", 
                 "content": "تو یه دستیار هوشمند فارسی‌زبان هستی "
                           "که حافظه داره."},
                *context,
            ],
        )
        
        reply = response.choices[0].message.content
        
        # ذخیره پاسخ
        self.memory.working_memory.add("assistant", reply)
        
        # استخراج اطلاعات مهم برای حافظه بلندمدت
        self._maybe_extract_info(user_message, reply)
        
        return reply
    
    def _maybe_extract_info(self, user_msg: str, assistant_msg: str):
        """تشخیص و ذخیره اطلاعات مهم"""
        # هر ۵ پیام، اطلاعات مهم رو استخراج کن
        msgs = self.memory.working_memory.messages
        if len(msgs) % 10 == 0:
            self.memory.extract_and_store(msgs[-10:])

# استفاده
agent = MemoryAgent()
agent.chat("سلام، من رضا هستم")
agent.chat("من دولوپر فرانت‌اند هستم")
# ... چند روز بعد ...
agent.chat("سلام")  
# Agent: "سلام رضا! خوبی؟ بازم سوال فرانت‌اندی داری؟"

جمع‌بندی

  • حافظه کوتاه‌مدت: تاریخچه مکالمه فعلی — ساده ولی محدود به context window
  • حافظه بلندمدت: ذخیره در Vector DB — پایدار و قابل جستجو
  • حافظه اپیزودیک: خاطرات و تجربه‌ها — برای یادگیری از گذشته
  • حافظه معنایی: دانش ساختاریافته — مثل دایره‌المعارف شخصی
  • خلاصه‌سازی: راه‌حل مشکل context window محدود

اپیزود بعدی درباره Planning ه — وقتی Agent قبل از عمل فکر می‌کنه و برنامه‌ریزی می‌کنه. یکی از جذاب‌ترین بخش‌های ساخت Agent!

نظرات

هنوز نظری ثبت نشده. اولین نفر باشید!

نظر خود را بنویسید