الدرس 15 من 23

تصميم أنظمة الوكلاء المتعددين

إدارة الحالة والذاكرة

4 دقيقة للقراءة

الوكلاء يحتاجون الذاكرة للحفاظ على السياق عبر التفاعلات. تصميم إدارة حالة فعالة أمر حاسم لبناء أنظمة ذكاء اصطناعي موثوقة ومتسقة.

أنواع ذاكرة الوكيل

┌─────────────────────────────────────────────────────────────┐
│                    أنواع ذاكرة الوكيل                        │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  قصيرة المدى                طويلة المدى                     │
│  ──────────────────────     ──────────────────────────────  │
│  • المحادثة الحالية         • تفضيلات المستخدم              │
│  • سياق العمل              • ملخص التفاعلات السابقة        │
│  • نتائج استدعاء الأدوات   • الحقائق المتعلمة               │
│  • الحالة الجارية          • المعرفة المستمرة               │
│                                                              │
│  حلقية                      دلالية                          │
│  ──────────────────────     ──────────────────────────────  │
│  • ما حدث                   • ما هو صحيح                    │
│  • أحداث محددة              • معرفة عامة                    │
│  • مؤرخة                    • خالدة                         │
│                                                              │
└─────────────────────────────────────────────────────────────┘

الذاكرة قصيرة المدى

سياق المحادثة ضمن جلسة واحدة:

class ShortTermMemory:
    def __init__(self, max_tokens: int = 4000):
        self.messages = []
        self.max_tokens = max_tokens

    def add(self, role: str, content: str):
        self.messages.append({"role": role, "content": content})
        self._trim_if_needed()

    def get_context(self) -> list:
        return self.messages.copy()

    def _trim_if_needed(self):
        """الاحتفاظ بالذاكرة ضمن حد الرموز."""
        while self._estimate_tokens() > self.max_tokens and len(self.messages) > 2:
            # إزالة الرسائل الأقدم (الاحتفاظ بمطالبة النظام)
            self.messages.pop(1)

    def _estimate_tokens(self) -> int:
        return sum(len(m["content"]) // 4 for m in self.messages)

    def summarize_and_compress(self, llm) -> str:
        """تلخيص السياق القديم لتوفير الرموز."""
        if len(self.messages) < 10:
            return

        # الاحتفاظ بالرسائل الحديثة
        recent = self.messages[-5:]
        old = self.messages[:-5]

        # تلخيص الرسائل القديمة
        summary = llm.complete(
            f"لخّص هذه المحادثة باختصار:\n"
            f"{json.dumps(old, indent=2)}"
        )

        # استبدال القديم بالملخص
        self.messages = [
            {"role": "system", "content": f"السياق السابق: {summary}"}
        ] + recent

الذاكرة طويلة المدى

التخزين المستمر عبر الجلسات:

class LongTermMemory:
    def __init__(self, vector_store, embedding_model):
        self.store = vector_store
        self.embedder = embedding_model

    async def store_memory(
        self,
        user_id: str,
        content: str,
        memory_type: str,  # "fact", "preference", "episode"
        metadata: dict = None
    ):
        """تخزين ذاكرة للاسترجاع لاحقاً."""
        embedding = await self.embedder.embed(content)

        await self.store.insert({
            "embedding": embedding,
            "content": content,
            "user_id": user_id,
            "memory_type": memory_type,
            "timestamp": datetime.utcnow().isoformat(),
            "metadata": metadata or {}
        })

    async def recall(
        self,
        user_id: str,
        query: str,
        memory_types: list = None,
        top_k: int = 5
    ) -> list:
        """استدعاء الذكريات ذات الصلة."""
        query_embedding = await self.embedder.embed(query)

        filters = {"user_id": user_id}
        if memory_types:
            filters["memory_type"] = {"$in": memory_types}

        results = await self.store.search(
            embedding=query_embedding,
            filter=filters,
            top_k=top_k
        )

        return [r["content"] for r in results]

    async def update_memory(self, memory_id: str, new_content: str):
        """تحديث ذاكرة موجودة."""
        new_embedding = await self.embedder.embed(new_content)
        await self.store.update(
            memory_id,
            {"content": new_content, "embedding": new_embedding}
        )

نمط الذاكرة العاملة

دمج قصيرة وطويلة المدى للسياق الكامل:

class WorkingMemory:
    def __init__(self, short_term, long_term, max_context_tokens: int = 6000):
        self.short_term = short_term
        self.long_term = long_term
        self.max_tokens = max_context_tokens

    async def build_context(self, user_id: str, current_query: str) -> list:
        """بناء السياق الكامل لـ LLM."""
        context = []

        # 1. استدعاء الذكريات طويلة المدى ذات الصلة
        memories = await self.long_term.recall(user_id, current_query)
        if memories:
            memory_text = "\n".join(memories)
            context.append({
                "role": "system",
                "content": f"ذكريات ذات صلة عن هذا المستخدم:\n{memory_text}"
            })

        # 2. إضافة سجل المحادثة
        context.extend(self.short_term.get_context())

        # 3. إضافة الاستعلام الحالي
        context.append({"role": "user", "content": current_query})

        return self._fit_to_token_limit(context)

    def _fit_to_token_limit(self, context: list) -> list:
        """تقليم السياق ليناسب حد الرموز."""
        while self._estimate_tokens(context) > self.max_tokens:
            # إزالة أقدم رسالة محادثة (الاحتفاظ بالنظام + الذكريات)
            for i, msg in enumerate(context):
                if msg["role"] != "system":
                    context.pop(i)
                    break
        return context

إدارة الحالة الموزعة

لأنظمة الوكلاء المتعددين مع حالة مشتركة:

class DistributedAgentState:
    def __init__(self, redis_client):
        self.redis = redis_client

    async def get_state(self, agent_id: str, session_id: str) -> dict:
        """الحصول على حالة الوكيل لجلسة."""
        key = f"agent:{agent_id}:session:{session_id}"
        data = await self.redis.get(key)
        return json.loads(data) if data else {}

    async def set_state(self, agent_id: str, session_id: str, state: dict):
        """تحديث حالة الوكيل."""
        key = f"agent:{agent_id}:session:{session_id}"
        await self.redis.setex(key, 3600, json.dumps(state))  # TTL ساعة واحدة

    async def share_state(
        self,
        from_agent: str,
        to_agent: str,
        session_id: str,
        data: dict
    ):
        """مشاركة الحالة بين الوكلاء."""
        key = f"shared:{from_agent}:{to_agent}:{session_id}"
        await self.redis.setex(key, 600, json.dumps(data))  # TTL 10 دقائق

    async def get_shared_state(
        self,
        from_agent: str,
        to_agent: str,
        session_id: str
    ) -> dict:
        """استرجاع الحالة المشتركة."""
        key = f"shared:{from_agent}:{to_agent}:{session_id}"
        data = await self.redis.get(key)
        return json.loads(data) if data else {}

توحيد الذاكرة

تحويل قصيرة المدى إلى طويلة المدى بشكل دوري:

class MemoryConsolidator:
    def __init__(self, short_term, long_term, llm):
        self.short_term = short_term
        self.long_term = long_term
        self.llm = llm

    async def consolidate(self, user_id: str):
        """استخراج الحقائق المهمة وتخزينها طويل المدى."""
        conversation = self.short_term.get_context()

        if len(conversation) < 5:
            return  # لا يكفي للتوحيد

        # استخراج الحقائق باستخدام LLM
        prompt = """من هذه المحادثة، استخرج:
1. تفضيلات المستخدم (أشياء يحبها/لا يحبها)
2. حقائق مهمة عنه
3. قرارات رئيسية اتُخذت

أرجع كـ JSON: {"preferences": [], "facts": [], "decisions": []}

المحادثة:
""" + json.dumps(conversation, indent=2)

        extraction = await self.llm.complete(prompt)
        data = json.loads(extraction)

        # تخزين كل ذاكرة مستخرجة
        for pref in data["preferences"]:
            await self.long_term.store_memory(
                user_id, pref, "preference"
            )

        for fact in data["facts"]:
            await self.long_term.store_memory(
                user_id, fact, "fact"
            )

بعد ذلك، سنستكشف أنماط التواصل بين الوكلاء. :::

اختبار

الوحدة 4: تصميم أنظمة الوكلاء المتعددين

خذ الاختبار