تخطَّ إلى المحتوى

الذاكرة والتوليد المعزّز بالاسترجاع (RAG)

خريطة دقيقة وموثّقة بالمراجع المصدرية لأنظمة الذاكرة الفرعية في كاظمة — بما في ذلك ملاحظات الحالة الصادقة حول ما هو موصول فعليًا مقابل ما تصفه الوثائق القديمة.


1. أنظمة الذاكرة الفرعية الثلاثة (اقرأ هذا أولًا)

Section titled “1. أنظمة الذاكرة الفرعية الثلاثة (اقرأ هذا أولًا)”

يحتوي الإطار كاظمة على ثلاثة أنظمة ذاكرة فرعية متمايزة وغير متكاملة. تاريخيًا كانت الوثائق تدمجها في «خط أنابيب من أربع طبقات». يفصل هذا القسم بينها بصدق:

النظام الفرعيالخلفية (Backing)التضمينهل يستخدمه وكيل الدردشة؟الكود
أ. VectorMemory (أدوات RAG)chromadb.PersistentClientall-MiniLM-L6-v2 (384-أبعاد)فقط عندما يستدعي نموذج اللغة memory_search / memory_storekazma_core/memory/vector_store.py
ب. SQLiteMemoryBackend (وكيل self.memory)SQLite + FTS5 (porter unicode61)لا يوجدلا يُستعلم أثناء استرجاع الدردشةkazma_memory/search_backend.py
ج. UnifiedMemoryAdapter (الأربع طبقات)ChromaDB + NetworkX + FTS5 + sqlite-vec، ممزوجة عبر RRFall-MiniLM-L6-v2لا — فقط self_improvement.py + phonebook.pykazma_core/swarm/memory/adapter.py

الخلاصة: في مسار الدردشة الافتراضي، الاسترجاع الوحيد للذاكرة الذي يحدث هو استعلام ChromaDB واحد خلف أداة اختيارية يستدعيها نموذج اللغة. لا يوجد حقن تلقائي للسياق المسترجَع إلى الموجّه (prompt)، ولا توجد عملية دمج من المدى القصير إلى الدائم.


2. النظام الفرعي أ — VectorMemory (أدوات RAG)

Section titled “2. النظام الفرعي أ — VectorMemory (أدوات RAG)”

هذا هو ما تستخدمه أدوات memory_search / memory_store فعليًا.

kazma-ui/kazma_ui/app.py:444-462:

vector_memory = VectorMemory(
path=KAZMA_VECTOR_PATH, # default ~/.kazma/vector_memory
collection_name=KAZMA_VECTOR_COLLECTION, # default "agent_memory"
model_name=KAZMA_VECTOR_MODEL, # default "all-MiniLM-L6-v2"
)
set_vector_memory(vector_memory)

VectorMemory (memory/vector_store.py):

  • التخزين: chromadb.PersistentClient(path=...) (السطر 57).
  • التضمين: SentenceTransformerEmbeddingFunction مرتبط عند إنشاء المجموعة (الأسطر 58-63) — جميع عمليات التضمين تعمل محليًا، دون أي استدعاءات لواجهات برمجية خارجية (سطر وثائق الوحدة 5).
  • اسم المجموعة الافتراضي: agent_memory (السطر 42).
  • الأبعاد: 384 (all-MiniLM-L6-v2). ملاحظة: القيمة storage.vector_dim: 1536 في kazma.yaml لا تتطابق ولا يتم فرضها هنا.

2.2 الاسترجاع (قائم على الأدوات، وليس تلقائيًا)

Section titled “2.2 الاسترجاع (قائم على الأدوات، وليس تلقائيًا)”

يحدث الاسترجاع فقط عندما يختار نموذج اللغة استدعاء memory_search (agent/tool_registry.py:591-605):

async def memory_search(query: str, limit: int = 5) -> str:
mem = get_vector_memory()
results = mem.search(query=query, n_results=limit)
return json.dumps(results, ...)

الدالة VectorMemory.search() (vector_store.py:136-180) عبارة عن استدعاء ChromaDB واحد:

self._collection.query(query_texts=[query], n_results=..., where=tenant_filter)

تعود النتيجة إلى نموذج اللغة كملاحظة أداة عادية في حلقة ReAct. لا توجد خطوة FTS، ولا إعادة ترتيب، ولا حقن تلقائي.

2.3 التخزين (قائم على الأدوات)

Section titled “2.3 التخزين (قائم على الأدوات)”

يحدث التخزين فقط عندما يستدعي نموذج اللغة memory_store (tool_registry.py:607-624): mem.add(text, metadata)VectorMemory.add()self._collection.add(documents, metas, ids).

لا توجد استراتيجية تجزئة (chunking). كل استدعاء add() يمثّل مستندًا واحدًا. عمليات البحث عن chunk، وtext_splitter، وchunk_size، وoverlap لم تجد أي تطبيق لتقسيم المستندات.

2.4 النسخة الوحيدة (singleton) المشتركة للتضمين

Section titled “2.4 النسخة الوحيدة (singleton) المشتركة للتضمين”

يعرّض swarm/memory/vector.py:27-46 الدالة get_encoder() التي تحمّل SentenceTransformer("all-MiniLM-L6-v2") مرة واحدة. تعيد الطبقة 4 (sqlite_vec.py:141) استخدام هذه النسخة الوحيدة.


3. النظام الفرعي ب — SQLiteMemoryBackend (FTS5)

Section titled “3. النظام الفرعي ب — SQLiteMemoryBackend (FTS5)”

kazma_memory/search_backend.py — خلفية هجينة FTS5 + متجهات (مقصودة).

  • قاعدة البيانات: kazma-data/memory.db (السطر 32)، توجيهات PRAGMA journal_mode=WAL، synchronous=NORMAL (الأسطر 52-53).
  • الجدول memories (الأسطر 63-75): id, content, content_arabic, metadata, timestamp, source, relevance, embedding BLOB, tenant_id (يُهاجَر المستأجر تلقائيًا، الأسطر 78-81).
  • جدول FTS5 المسمى memories_fts (الأسطر 86-113) يُبقى متزامنًا عبر محفّزات AFTER INSERT/DELETE/UPDATE، بأعمدة memory_id, content, content_arabic.

الدالة search() (الأسطر 170-320) تُشغّل FTS5 عبر MATCH + bm25(memories_fts) أولًا، ثم اختياريًا بحثًا متجهيًا إذا كان semantic_search=True وتوفّر embedding. الدرجة المجمّعة: -bm25*0.7 + relevance*0.3 (الأسطر 310-318).

3.3 ملاحظة الحالة الصادقة: هذه الخلفية لا تُستعلم في الدردشة

Section titled “3.3 ملاحظة الحالة الصادقة: هذه الخلفية لا تُستعلم في الدردشة”
  • موصولة كـ self.memory الخاصة بالوكيل (agent_runner.py:220).
  • لا يستدعي أحدٌ في مسار الدردشة الدالة search() أثناء الاسترجاع.
  • لذا هي فعليًا خاملة للاسترجاع. قد تُكتب فيها بيانات عبر مسارات أخرى، لكنها لا تغذّي حلقة الدردشة.

4. النظام الفرعي ج — المحوّل UnifiedMemoryAdapter رباعي الطبقات

Section titled “4. النظام الفرعي ج — المحوّل UnifiedMemoryAdapter رباعي الطبقات”

«ذاكرة الأربع طبقات» المُشار إليها في README.md وswarm/memory/__init__.py. الطبقات الأربع كلها موجودة ككود تحت kazma_core/swarm/memory/:

الطبقةالملفالخلفية (Backing)
1. المتجهاتvector.pyChromaDB (PersistentClient أو في الذاكرة)؛ المجموعة الافتراضية kazma_global.
2. الرسم البيانيgraph.pynetworkx.MultiDiGraph، يُحفظ بصيغة JSON في kazma-data/knowledge_graph.json. يتتبّع تبعيات الكود، وسلالة العمّال، والمهمة→المُخرجات، والتسليمات.
3. المعجميةfts5.pyغلاف FTS5LexicalStore.
4. sqlite-vecsqlite_vec.pyجداول vec0 الافتراضية (FLOAT[384]WHERE embedding MATCH ? ORDER BY distance. قاعدة البيانات في kazma-data/vector.db.

الدالة UnifiedMemoryAdapter.query() (adapter.py:78) تتوزّع على الطبقات الأربع بالتوازي (asyncio.gather، السطر 116) وتدمجها عبر دمج الرتبة العكسية (Reciprocal Rank Fusion)، حيث _RRF_K = 60 (السطر 26): مساهمة الدرجة 1.0 / (_RRF_K + rank) (السطر 237). تُزال التكرارات بالاعتماد على uid.

مستدعِيان غير اختباريَّين فقط:

  • kazma_core/skills/self_improvement.py:131,194 (get_adapter())
  • kazma_core/swarm/phonebook.py:69 (get_adapter()) — يحقن سياق «PREVIOUS_SUCCESSFUL_STRATEGIES» قبل الإرسال.

لا يُستخدم من قبل agent_runner.py، أو مسار بوابة الدردشة، أو tool_registry.py، أو majlis.py. وكيل الدردشة الافتراضي لا يلمسه إطلاقًا.


kazma-memory/kazma_memory/arabic_tokenizer.py — يغذّي عمود FTS5 المسمى content_arabic.

5.1 خط أنابيب التطبيع (normalize، الأسطر 132-165)

Section titled “5.1 خط أنابيب التطبيع (normalize، الأسطر 132-165)”

تُطبَّق بالترتيب التالي:

  1. إزالة التشكيل (_remove_diacritics، الأسطر 193-204): التعبير النمطي [\u064B-\u065F\u0670] (الحركات + ألف الخنجرية).
  2. تطبيع الألف (_normalize_alef، الأسطر 206-218): أ، إ، آا.
  3. تاء مربوطة ← هاء: ةه (السطر 148).
  4. تطبيع الياء (_normalize_yeh، الأسطر 220-232): ئ، ؤ، إي، ىي.
  5. واو/ياء الهمزة: ؤو، ئي (ملاحظة: هذه تتداخل مع القاعدة 4 — تعارف ثانوي معروف).
  6. إزالة التطويل/الكشيدة: text.replace("ـ", "") (السطر 160). موجودة.
  7. طيّ المسافات البيضاء.

5.2 كلمات الإيقاف (الأسطر 35-102)

Section titled “5.2 كلمات الإيقاف (الأسطر 35-102)”

نحو 40 مدخلًا مكتوبًا برمجيًا: أدوات (في، من، على)، وضمائر (أنا/انا، هو، هي)، وأدوات ربط (و، أو/او، ثم)، إضافة إلى مصطلحات اللهجة الكويتية (يلا، شلون، عشان، مو، ليه، ماكو، فد). أُضيفت تكرارات مطبّعة (مجرّدة من الهمزة) ضمن «إصلاح BUG-023».

5.3 المُجدِّع (Stemmer) (_init_stemmer، الأسطر 104-130)

Section titled “5.3 المُجدِّع (Stemmer) (_init_stemmer، الأسطر 104-130)”

تجريد اللواحق والبادئات قائم على التعبيرات النمطية (اللواحق: ات، ون، ين، ة، ان، نا؛ البادئات: ال، بـ، كـ). تصِف وثائق الدالة عملها بأنه «Basic stemming» — أي تجذيع أساسي وليس مُؤَلِّفًا كاملًا للقاموس (lemmatizer).

  • ArabicTokenizer.tokenize() ← تُعيد سلسلة نصية (الأسطر 167-191).
  • ArabicTantivyTokenizer.tokenize() ← تُعيد قائمة (الأسطر 243-266)، مع تصفية len(word) > 1.

عندما يطول المحادثة نحو حدود نافذة السياق، يقوم ContextAuthority بتلخيصها. هذه هي الوحيدة من بين عمليات إدارة السياق التلقائية التي تعمل في مسار الدردشة.

المكوّنالملفالدور
CompactionEnginecompaction.py (287 سطرًا)المُلخِّص.
ContextAuthorityauthority.py (94 سطرًا)الحارس الذي يقرّر متى يُكثَّف.
TokenCountertoken_counter.pyعدّ الرموز + العتبة.

6.2 الاستراتيجية (compaction.py:63 compact)

Section titled “6.2 الاستراتيجية (compaction.py:63 compact)”
  1. حفظ نقطة تفتيش (الأسطر 87-92) — مقصود؛ انظر ملاحظة الحالة.
  2. تلخيص بنموذج اللغة (السطر 95) — يستخدم _SUMMARY_SYSTEM (الأسطر 20-30): الحفاظ على هدف المهمة، والقرارات الرئيسية، ونتائج الأدوات، وقيود المستخدم؛ بسقف 2000 رمز، وقصّ محرفي عند 8000 (السطر 160).
  3. استرجاع أعلى 5 ذكريات (السطر 98) — مقصود؛ انظر ملاحظة الحالة.
  4. بناء رسالة نظام جديدة تحتوي الملخص + الذكريات (_build_compacted_system، الأسطر 231-259).
  5. إرجاع حالة جديدة مع messages = [رسالة نظام واحدة]، context_tokens = 0.

قيمة مكتوبة برمجيًا int(window * 0.8) (token_counter.py:23). مع القيمة الافتراضية memory.max_context_tokens: 128000، يُطلَق التكثيف عند 102,400 رمز. هذا مقصودٌ ألا يكون قابلًا للضبط من قِبل المستخدم (authority.py:5، compaction.md:178)؛ الإعداد القابل للضبط الوحيد هو النافذة نفسها.

داخل عقدة المشرف (graph_builder.py:167): ينفّذ compacted_state = await authority.check_and_enforce(state_for_check) قبل استدعاء نموذج اللغة. إذا حصل تكثيف، تعود السيطرة إلى SUPERVISOR لإعادة التشغيل (السطر 174).

6.5 الاحتياطي الاستدلالي (Heuristic fallback)

Section titled “6.5 الاحتياطي الاستدلالي (Heuristic fallback)”

إن لم يتوفّر نموذج لغة، تبني الدالة _summarize_heuristic (السطر 168) ملخصًا من عدد الرسائل + آخر رسالة للمستخدم (500 محرف) + آخر 5 مُخرجات أدوات (200 محرف لكل منها).

يستخدم TokenCounter مكتبة tiktoken إن كانت مثبّتة، وإلّا يلجأ إلى تقدير محارف/4 (token_counter.py:44-46). مكتبة tiktoken ليست تبعية مُعلَنة — التقدير الاستدلالي هو الافتراضي ما لم تُثبّتها بنفسك.


7. ملاحظات الحالة الصادقة (اقرأها قبل الاعتماد على ميزات الذاكرة)

Section titled “7. ملاحظات الحالة الصادقة (اقرأها قبل الاعتماد على ميزات الذاكرة)”

هذه هي الفجوات التي كشفها التدقيق. وُثّقت هنا كي لا يعتمد المشغّلون على سلوك غير مُفعّل فعلًا.

  1. دمج المدى القصير→الدائم غير موجود. لم يُعثَر على أيٍّ من consolidat*، أو promote، أو persist.*memory، أو منطق ترقية خلفي. تُكتَب الذاكرة الدائمة فقط عندما يستدعي نموذج اللغة صراحةً memory_store. التكثيف لا يكتب الرسائل المُهمَلة في VectorMemory — هو فقط يحفظ نقطة تفتيش (مقصود) ويُلخِّص.

  2. استرجاع الذكريات وحفظ نقطة التفتيش أثناء التكثيف عمليتان فارغتان في التوصيل الافتراضي. يستدعي agent_runner.py:162-166 الدالة create_authority(...) دون تمرير memory_store أو checkpoint_manager (كلاهما افتراضي None). النتائج:

    • تُعيد retrieve_memories() القائمة [] (compaction.py:220-221) — خطوة «استرجاع أعلى 5 ذكريات» خاملة.
    • تُتجاوز خطوة «حفظ نقطة تفتيش قبل التكثيف».
    • الذي يعمل فعلًا هو تلخيص نموذج اللغة فقط. الوثائق الأقدم (compaction.md، README) التي تصف تكثيفًا غنيًّا بالذاكرة ومُزوَّدًا بنقاط تفتيش تصف التصميم المقصود، لا زمن التشغيل الفعلي.
  3. المحوّل رباعي الطبقات غير موصول في الدردشة. هو موجود ويعمل، لكنه لا يُ reachable إلا من self_improvement.py وphonebook.py.

  4. اكتشاف sqlite-vec في search_backend.py عملية فارغة. تشغّل الأسطر 55-60 الاستعلام SELECT sqlite_version() (ينجح دائمًا) ولا تستدعي load_extension أبدًا، لذا فالقيمة _vec_available غير موثوقة.

  5. يستخدم search_backend.py الدالة _vector_search بصيغة distance(embedding, ?) (الأسطر 343، 354)، وهي ليست دالة sqlite-vec صالحة (يُستعلم عن جداول vec0 عبر MATCH/k، كما هو منفّذ بشكل صحيح في swarm/memory/sqlite_vec.py:215-217). هذا المسار سيُطلق استثناءً عند التشغيل ويُعيد [] من كتلة except. عمليًا لا يُبلَغ إليه لأن المستدعين لا يمرّرون semantic_search=True مع embedding.

  6. sqlite-vec ليست تبعية مُعلَنة. تظهر في uv.lock فقط كتبعية انتقالية لـ langgraph-checkpoint-sqlite. إنها متاحة عند التشغيل كأثر جانبي، لا لأن كاظمة تطلبها.

  7. ثلاثة أنظمة ذاكرة فرعية متباينة تتعايش دون تكامل: (أ) أدوات RAG عبر ChromaDB، (ب) self.memory عبر FTS5 (خامل للاسترجاع)، (ج) محوّل RRF رباعي الطبقات (للتطوير الذاتي/دفتر الهاتف فقط).

  8. خط الأنابيب الموثّق «embed → vector → FTS → re-rank → inject» لا يوجد كتدفّق متّصل. الاسترجاع هو استعلام ChromaDB واحد خلف أداة اختيارية يستدعيها نموذج اللغة.


  • تصحيح نسبة الفرضية: ادعاء «ذاكرة الأربع طبقات» موجود في README.md:313 وswarm/memory/__init__.py:1، وليس في AGENTS.md.
  • فجوة التوصيل في agent_runner.py:162-166 هي أهم نتيجة واحدة لأي من يخطط للاعتماد على RAG أثناء المحادثات الطويلة. حتى تُمرَّر memory_store/checkpoint_manager إلى create_authority()، يبقى التكثيف تلخيصًا فقط.
  • عدم التطابق بين storage.vector_dim: 1536 والأبعاد الفعلية 384 موثّق في الإعدادات (Configuration).
  • عدم وجود تجزئة يعني أن المستندات الطويلة المخزَّنة عبر memory_store تُضمَّن كاملة — يُستحسن التقسيم المسبق في مهارتك/أداتك إن احتجت استرجاعًا أدقّ.