يوفر CrewAI نظام ذاكرة موحد — فئة Memory واحدة تستبدل أنواع الذاكرة المنفصلة (قصيرة المدى، طويلة المدى، ذاكرة الكيانات، والخارجية) بواجهة برمجة تطبيقات ذكية واحدة. تستخدم الذاكرة LLM لتحليل المحتوى عند الحفظ (استنتاج النطاق والفئات والأهمية) وتدعم الاسترجاع متعدد العمق مع تسجيل مركب يمزج بين التشابه الدلالي والحداثة والأهمية.يمكنك استخدام الذاكرة بأربع طرق: مستقلة (سكربتات، دفاتر ملاحظات)، مع فرق Crew، مع Agents، أو داخل التدفقات.
from crewai import Memorymemory = Memory()# Store -- the LLM infers scope, categories, and importancememory.remember("We decided to use PostgreSQL for the user database.")# Retrieve -- results ranked by composite score (semantic + recency + importance)matches = memory.recall("What database did we choose?")for m in matches: print(f"[{m.score:.2f}] {m.record.content}")# Tune scoring for a fast-moving projectmemory = Memory(recency_weight=0.5, recency_half_life_days=7)# Forgetmemory.forget(scope="/project/old")# Explore the self-organized scope treeprint(memory.tree())print(memory.info("/"))
استخدم الذاكرة في السكربتات ودفاتر الملاحظات وأدوات سطر الأوامر أو كقاعدة معرفة مستقلة — لا حاجة لوكلاء أو فرق Crew.
from crewai import Memorymemory = Memory()# Build up knowledgememory.remember("The API rate limit is 1000 requests per minute.")memory.remember("Our staging environment uses port 8080.")memory.remember("The team agreed to use feature flags for all new releases.")# Later, recall what you needmatches = memory.recall("What are our API limits?", limit=5)for m in matches: print(f"[{m.score:.2f}] {m.record.content}")# Extract atomic facts from a longer textraw = """Meeting notes: We decided to migrate from MySQL to PostgreSQLnext quarter. The budget is $50k. Sarah will lead the migration."""facts = memory.extract_memories(raw)# ["Migration from MySQL to PostgreSQL planned for next quarter",# "Database migration budget is $50k",# "Sarah will lead the database migration"]for fact in facts: memory.remember(fact)
عند استخدام memory=True، ينشئ الفريق مثيل Memory() افتراضيًا ويمرر إعداد embedder الخاص بالفريق تلقائيًا. يشترك جميع الوكلاء في الفريق في ذاكرة الفريق ما لم يكن لدى الوكيل ذاكرته الخاصة.بعد كل مهمة، يستخرج الفريق تلقائيًا حقائق منفصلة من مخرجات المهمة ويخزّنها. قبل كل مهمة، يسترجع الوكيل السياق ذا الصلة من الذاكرة ويحقنه في موجّه المهمة.
كل تدفق يحتوي على ذاكرة مدمجة. استخدم self.remember() و self.recall() و self.extract_memories() داخل أي دالة تدفق.
from crewai.flow.flow import Flow, listen, startclass ResearchFlow(Flow): @start() def gather_data(self): findings = "PostgreSQL handles 10k concurrent connections. MySQL caps at 5k." self.remember(findings, scope="/research/databases") return findings @listen(gather_data) def write_report(self, findings): # Recall past research to provide context past = self.recall("database performance benchmarks") context = "\n".join(f"- {m.record.content}" for m in past) return f"Report:\nNew findings: {findings}\nPrevious context:\n{context}"
انظر وثائق التدفقات لمزيد من المعلومات حول الذاكرة في التدفقات.
عند استدعاء remember() دون تحديد نطاق، يحلل LLM المحتوى وشجرة النطاقات الحالية، ثم يقترح أفضل موضع. إذا لم يكن هناك نطاق حالي مناسب، ينشئ واحدًا جديدًا. بمرور الوقت، تنمو شجرة النطاقات عضويًا من المحتوى نفسه — لا تحتاج إلى تصميم مخطط مسبقًا.
memory = Memory()# LLM infers scope from contentmemory.remember("We chose PostgreSQL for the user database.")# -> might be placed under /project/decisions or /engineering/database# You can also specify scope explicitlymemory.remember("Sprint velocity is 42 points", scope="/team/metrics")
يقيّد MemoryScope جميع العمليات على فرع من الشجرة. يمكن للوكيل أو الكود الذي يستخدمه الرؤية والكتابة فقط ضمن تلك الشجرة الفرعية.
memory = Memory()# Create a scope for a specific agentagent_memory = memory.scope("/agent/researcher")# Everything is relative to /agent/researcheragent_memory.remember("Found three relevant papers on LLM memory.")# -> stored under /agent/researcheragent_memory.recall("relevant papers")# -> searches only under /agent/researcher# Narrow further with subscopeproject_memory = agent_memory.subscope("project-alpha")# -> /agent/researcher/project-alpha
ابدأ بشكل مسطح، ودع LLM ينظّم. لا تبالغ في هندسة تسلسل النطاقات مسبقًا. ابدأ بـ memory.remember(content) ودع استنتاج النطاق في LLM ينشئ الهيكل مع تراكم المحتوى.
استخدم أنماط /{entity_type}/{identifier}. تنشأ التسلسلات الطبيعية من أنماط مثل /project/alpha و /agent/researcher و /company/engineering و /customer/acme-corp.
حدد النطاق حسب الاهتمام، وليس حسب نوع البيانات. استخدم /project/alpha/decisions بدلاً من /decisions/project/alpha. هذا يبقي المحتوى ذا الصلة معًا.
حافظ على العمق ضحلًا (2-3 مستويات). النطاقات المتداخلة بعمق تصبح متفرقة جدًا. /project/alpha/architecture جيد؛ /project/alpha/architecture/decisions/databases/postgresql عميق جدًا.
استخدم النطاقات الصريحة عندما تعرف، ودع LLM يستنتج عندما لا تعرف. إذا كنت تخزّن قرار مشروع معروف، مرّر scope="/project/alpha/decisions". إذا كنت تخزّن مخرجات وكيل حرة الشكل، اترك النطاق ودع LLM يحدده.
memory = Memory()# Each project gets its own branchmemory.remember("Using microservices architecture", scope="/project/alpha/architecture")memory.remember("GraphQL API for client apps", scope="/project/beta/api")# Recall across all projectsmemory.recall("API design decisions")# Or within a specific projectmemory.recall("API design", scope="/project/beta")
سياق خاص لكل وكيل مع معرفة مشتركة:
memory = Memory()# Researcher has private findingsresearcher_memory = memory.scope("/agent/researcher")# Writer can read from both its own scope and shared company knowledgewriter_view = memory.slice( scopes=["/agent/writer", "/company/knowledge"], read_only=True,)
دعم العملاء (سياق لكل عميل):
memory = Memory()# Each customer gets isolated contextmemory.remember("Prefers email communication", scope="/customer/acme-corp")memory.remember("On enterprise plan, 50 seats", scope="/customer/acme-corp")# Shared product docs are accessible to all agentsmemory.remember("Rate limit is 1000 req/min on enterprise plan", scope="/product/docs")
MemorySlice هو عرض عبر نطاقات متعددة، ربما متباعدة. على عكس النطاق (الذي يقيّد على شجرة فرعية واحدة)، تتيح لك الشريحة الاسترجاع من عدة فروع في وقت واحد.
النمط الأكثر شيوعًا: منح وكيل إمكانية القراءة من فروع متعددة دون السماح له بالكتابة في المناطق المشتركة.
memory = Memory()# Agent can recall from its own scope AND company knowledge,# but cannot write to company knowledgeagent_view = memory.slice( scopes=["/agent/researcher", "/company/knowledge"], read_only=True,)matches = agent_view.recall("company security policies", limit=5)# Searches both /agent/researcher and /company/knowledge, merges and ranks resultsagent_view.remember("new finding") # Raises PermissionError (read-only)
عند الحفظ — عندما تحذف النطاق أو الفئات أو الأهمية، يحلل LLM المحتوى ويقترح النطاق والفئات والأهمية والبيانات الوصفية (الكيانات والتواريخ والموضوعات).
عند الاسترجاع — للاسترجاع العميق/التلقائي، يحلل LLM الاستعلام (الكلمات المفتاحية، تلميحات الوقت، النطاقات المقترحة، التعقيد) لتوجيه الاسترجاع.
استخراج الذكريات — extract_memories(content) يقسم النص الخام (مثل مخرجات المهمة) إلى عبارات ذاكرة منفصلة. يستخدم الوكلاء هذا قبل استدعاء remember() على كل عبارة حتى يتم تخزين حقائق ذرية بدلاً من كتلة كبيرة واحدة.
جميع التحليلات تتدهور بسلاسة عند فشل LLM — انظر سلوك الفشل.
عند حفظ محتوى جديد، يتحقق خط أنابيب الترميز تلقائيًا من وجود سجلات مماثلة في التخزين. إذا كان التشابه أعلى من consolidation_threshold (الافتراضي 0.85)، يقرر LLM ما يجب فعله:
keep — السجل الحالي لا يزال دقيقًا وغير مكرر.
update — يجب تحديث السجل الحالي بمعلومات جديدة (يوفر LLM المحتوى المدمج).
delete — السجل الحالي قديم أو تم استبداله أو تناقضه.
insert_new — ما إذا كان يجب إدراج المحتوى الجديد أيضًا كسجل منفصل.
هذا يمنع تراكم النسخ المكررة. على سبيل المثال، إذا حفظت “CrewAI ensures reliable operation” ثلاث مرات، يتعرف التوحيد على النسخ المكررة ويحتفظ بسجل واحد فقط.
عند استخدام remember_many()، تتم مقارنة العناصر داخل نفس الدفعة مع بعضها البعض قبل الوصول إلى التخزين. إذا كان تشابه جيب التمام >= batch_dedup_threshold (الافتراضي 0.98)، يتم إسقاط العنصر الأحدث بصمت. هذا يلتقط النسخ المكررة الدقيقة أو شبه الدقيقة داخل دفعة واحدة دون أي استدعاءات LLM (رياضيات متجهات خالصة).
# Only 2 records are stored (the third is a near-duplicate of the first)memory.remember_many([ "CrewAI supports complex workflows.", "Python is a great language.", "CrewAI supports complex workflows.", # dropped by intra-batch dedup])
remember_many()غير حاجب — يقدم خط أنابيب الترميز إلى خيط خلفي ويعود فورًا. هذا يعني أن الوكيل يمكنه المتابعة إلى المهمة التالية بينما يتم حفظ الذكريات.
# Returns immediately -- save happens in backgroundmemory.remember_many(["Fact A.", "Fact B.", "Fact C."])# recall() automatically waits for pending saves before searchingmatches = memory.recall("facts") # sees all 3 records
كل استدعاء recall() يستدعي تلقائيًا drain_writes() قبل البحث، مما يضمن أن الاستعلام يرى دائمًا أحدث السجلات المستمرة. هذا شفاف — لا تحتاج أبدًا إلى التفكير فيه.
عند انتهاء الفريق، يستنزف kickoff() جميع عمليات حفظ الذاكرة المعلقة في كتلة finally الخاصة به، لذا لا تُفقد أي عمليات حفظ حتى لو اكتمل الفريق بينما عمليات الحفظ الخلفية قيد التنفيذ.
# Tag memories with their originmemory.remember("User prefers dark mode", source="user:alice")memory.remember("System config updated", source="admin")memory.remember("Agent found a bug", source="agent:debugger")# Recall only memories from a specific sourcematches = memory.recall("user preferences", source="user:alice")
الذكريات الخاصة مرئية فقط للاسترجاع عندما يتطابق source:
# Store a private memorymemory.remember("Alice's API key is sk-...", source="user:alice", private=True)# This recall sees the private memory (source matches)matches = memory.recall("API key", source="user:alice")# This recall does NOT see it (different source)matches = memory.recall("API key", source="user:bob")# Admin access: see all private records regardless of sourcematches = memory.recall("API key", include_private=True)
هذا مفيد بشكل خاص في النشرات متعددة المستخدمين أو المؤسسية حيث يجب عزل ذكريات المستخدمين المختلفين.
depth="shallow" — بحث متجهي مباشر مع تسجيل مركب. سريع (~200 مللي ثانية)، بدون استدعاءات LLM.
depth="deep" (افتراضي) — يشغل RecallFlow متعدد الخطوات: تحليل الاستعلام، اختيار النطاق، بحث متجهي متوازٍ، توجيه قائم على الثقة، واستكشاف متكرر اختياري عندما تكون الثقة منخفضة.
تخطي LLM الذكي: الاستعلامات الأقصر من query_analysis_threshold (الافتراضي 200 حرف) تتخطى تحليل LLM للاستعلام بالكامل، حتى في الوضع العميق. الاستعلامات القصيرة مثل “ما قاعدة البيانات التي نستخدمها؟” هي بالفعل عبارات بحث جيدة — تحليل LLM يضيف قيمة قليلة. هذا يوفر ~1-3 ثوانٍ لكل استرجاع للاستعلامات القصيرة النموذجية. فقط الاستعلامات الأطول (مثل أوصاف المهام الكاملة) تمر عبر تقطير LLM إلى استعلامات فرعية مستهدفة.
# Shallow: pure vector search, no LLMmatches = memory.recall("What did we decide?", limit=10, depth="shallow")# Deep (default): intelligent retrieval with LLM analysis for long queriesmatches = memory.recall( "Summarize all architecture decisions from this quarter", limit=10, depth="deep",)
عتبات الثقة التي تتحكم في موجّه RecallFlow قابلة للإعداد:
memory = Memory( confidence_threshold_high=0.9, # Only synthesize when very confident confidence_threshold_low=0.4, # Explore deeper more aggressively exploration_budget=2, # Allow up to 2 exploration rounds query_analysis_threshold=200, # Skip LLM for queries shorter than this)
# Pass any callable that takes a list of strings and returns a list of vectorsdef my_embedder(texts: list[str]) -> list[list[float]]: # Your embedding logic here return [[0.1, 0.2, ...] for _ in texts]memory = Memory(embedder=my_embedder)
تستخدم الذاكرة LLM لتحليل الحفظ (استنتاج النطاق والفئات والأهمية)، وقرارات التوحيد، وتحليل استعلام الاسترجاع العميق. يمكنك إعداد النموذج المُستخدم.
from crewai import Memory, LLM# Default: gpt-4o-minimemory = Memory()# Use a different OpenAI modelmemory = Memory(llm="gpt-4o")# Use Anthropicmemory = Memory(llm="anthropic/claude-3-haiku-20240307")# Use Ollama for fully local/private analysismemory = Memory(llm="ollama/llama3.2")# Use Google Geminimemory = Memory(llm="gemini/gemini-2.0-flash")# Pass a pre-configured LLM instance with custom settingsllm = LLM(model="gpt-4o", temperature=0)memory = Memory(llm=llm)
يتم تهيئة LLM بشكل كسول — يتم إنشاؤه فقط عند الحاجة لأول مرة. هذا يعني أن Memory() لا يفشل أبدًا في وقت الإنشاء، حتى لو لم تكن مفاتيح API مُعيّنة. تظهر الأخطاء فقط عند استدعاء LLM فعليًا (مثلاً عند الحفظ بدون نطاق/فئات صريحة، أو أثناء الاسترجاع العميق).للتشغيل المحلي/الخاص بالكامل، استخدم نموذجًا محليًا لكل من LLM والمُضمِّن:
الافتراضي: LanceDB، مخزّن تحت ./.crewai/memory (أو $CREWAI_STORAGE_DIR/memory إذا تم تعيين متغير البيئة، أو المسار الذي تمرره كـ storage="path/to/dir").
memory.tree() # Formatted tree of scopes and record countsmemory.tree("/project", max_depth=2) # Subtree viewmemory.info("/project") # ScopeInfo: record_count, categories, oldest/newestmemory.list_scopes("/") # Immediate child scopesmemory.list_categories() # Category names and countsmemory.list_records(scope="/project/alpha", limit=20) # Records in a scope, newest first
يتم إرسال محتوى الذاكرة إلى LLM المُعدّ للتحليل (النطاق/الفئات/الأهمية عند الحفظ، تحليل الاستعلام والاسترجاع العميق الاختياري). للبيانات الحساسة، استخدم LLM محليًا (مثل Ollama) أو تأكد من أن مزودك يلبي متطلبات الامتثال الخاصة بك.
تأكد من أن مسار التخزين قابل للكتابة (الافتراضي ./.crewai/memory). مرّر storage="./your_path" لاستخدام مجلد مختلف، أو عيّن متغير البيئة CREWAI_STORAGE_DIR.
عند استخدام فريق، تأكد من تعيين memory=True أو memory=Memory(...).
الاسترجاع بطيء؟
استخدم depth="shallow" لسياق الوكيل الروتيني. احتفظ بـ depth="deep" للاستعلامات المعقدة.
زد query_analysis_threshold لتخطي تحليل LLM لمزيد من الاستعلامات.
أخطاء تحليل LLM في السجلات؟
لا تزال الذاكرة تحفظ/تسترجع بإعدادات افتراضية آمنة. تحقق من مفاتيح API وحدود المعدل وتوفر النموذج إذا كنت تريد تحليل LLM كاملاً.
أخطاء حفظ خلفية في السجلات؟
عمليات حفظ الذاكرة تعمل في خيط خلفي. تُصدر الأخطاء كـ MemorySaveFailedEvent لكنها لا تعطل الوكيل. تحقق من السجلات للسبب الجذري (عادة مشاكل اتصال LLM أو المُضمِّن).
تعارضات الكتابة المتزامنة؟
عمليات LanceDB مُتسلسلة بقفل مشترك وتُعاد تلقائيًا عند التعارض. هذا يتعامل مع مثيلات Memory المتعددة التي تشير إلى نفس قاعدة البيانات (مثل ذاكرة وكيل + ذاكرة فريق). لا حاجة لإجراء.
تصفح الذاكرة من الطرفية:
crewai memory # Opens the TUI browsercrewai memory --storage-path ./my_memory # Point to a specific directory
إعادة تعيين الذاكرة (مثلاً للاختبارات):
crew.reset_memories(command_type="memory") # Resets unified memory# Or on a Memory instance:memory.reset() # All scopesmemory.reset(scope="/project/old") # Only that subtree