Mohamed Ashraf Elsaed

ذاكرة محلية لوكلاء الذكاء الاصطناعي: تصميم النظام

بقلم Mohamed Ashraf Elsaedنُشر 9 دقيقة قراءة

هذه ترجمة آلية لم تُراجَع بعد. الأصل الإنجليزي هو المرجع. اقرأ الأصل الإنجليزي

Aletheia (حزمة npm باسم aletheia-mem) مخزن ذاكرة محلي أولًا لوكلاء البرمجة بالذكاء الاصطناعي. يشرح هذا المقال تصميمه: ما الذي يعمل وأين، وأي خيارات تمنعه من تعطيل أداتك، وأين يتوقف التصميم قبل أن يغطي كل شيء.

شعار Aletheia: شبكة على شكل دماغ، بداخلها طبقات من الذاكرة فوق بعضها

أربعة قيود

أربعة قيود حكمت كل خيار أدناه.

  1. يعمل الـ hook في المسار الحرج للأداة المضيفة، فيجب ألا يعطّلها أبدًا.
  2. قد تحتوي الذاكرة على أسرار، فلا يُخزَّن شيء قبل حذف الأسرار منه.
  3. على الذاكرة أن تخدم عدة أدوات، لذا المخزن محايد تجاه الأدوات.
  4. تتلقى كل جلسة جديدة سياقًا في كل مرة، وهذا يكلّف رموزًا (tokens) في كل مرة، فيجب أن يكون السياق صغيرًا ويمكن توقع حجمه.

خط المعالجة في صورة واحدة

مخطط خط المعالجة في Aletheia: الالتقاط في hook، ثم حذف الأسرار، ثم التخزين في hub.db مع spool للحالات الفاشلة، ثم الضغط عبر طابور في الخلفية، ثم الاسترجاع عند بدء الجلسة
الالتقاط والضغط والاسترجاع تعمل في ثلاثة أماكن مختلفة. الوسطى وحدها تحتاج إلى الخادم.

يجري الالتقاط في عملية قصيرة العمر هي aletheia hook <tool> <event> تشغّلها الأداة المضيفة. ويجري الضغط في عامل (worker) في الخلفية داخل aletheia-server، أو عند تشغيل aletheia compress. ويجري الاسترجاع في hook من نوع SessionStart، أو في خادم MCP، أو في aletheia context. يفتح الـ hook الملف hub.db بنفسه ولا يستدعي الخادم، لذلك يعمل التقاط الذاكرة والخادم متوقف.

الخيار 1: يكتب الـ hook في SQLite لا في خادم

وجود خادم في مسار الالتقاط يضيف نقطة فشل إلى كل طلب: إن كان الخادم متوقفًا أو بطيئًا أو يبدأ للتو، فعلى الـ hook أن ينتظر أو يفشل. الكتابة المباشرة في SQLite تحذف هذه الخطوة. والثمن أن عدة عمليات قصيرة العمر قد تكتب في ملف واحد في وقت واحد، لذلك ينتظر الـ hook القفل 50 ms على الأكثر. يفتح المخزن نفسه بمهلة انشغال قدرها 3,000 ms (src/hub/store.mjs)، ويخفضها الـ hook إلى 50 ms بعد الفتح (PRAGMA busy_timeout = 50، src/hub/hooks/run.mjs). وإذا لم تتوفر قاعدة البيانات في الوقت المناسب، يرجع الـ hook إلى spool.

الخيار 2: الفشل بأمان ثم spool

قواعد الـ hook قصيرة:

طرفية تعرض hook موجّهًا إلى مسار قاعدة بيانات غير موجود: رمز الخروج 0 ويظهر العنصر في hook-spool.jsonl بصلاحيات الملف 600
hook موجّه إلى قاعدة بيانات غير موجودة. ينتهي برمز 0 ويبقى العنصر في الـ spool. شُغّل مع aletheia-mem 0.1.10 في مجلد مؤقت بتاريخ 2026-10-30؛ ثلاثة إشعارات صلاحيات تظهر مرة واحدة في stderr غير معروضة.

أثناء انقطاع قاعدة البيانات يكون الأثر تأخيرًا لا فقدانًا للعناصر التي تصل إلى الـ spool: تظهر الذاكرة في البحث بعد أول hook يصل إلى قاعدة بيانات سليمة. أما إذا انطلق الـ watchdog أثناء كتابة جارية فيضيع ذلك العنصر. التوثيق المرجعي يقول ذلك، ولم أُحدث هذه الحالة في اختبار.

الخيار 3: حذف الأسرار في دالة بسيطة قبل الكتابة

حذف الأسرار دالة نقية (src/hub/redact.mjs) تعمل بترتيب ثابت:

  1. النص بين <private> و</private> يصبح [private].
  2. أشكال الأسرار المعروفة تصبح علامة مثل [redacted:github-token]: مفاتيح Anthropic وبنمط OpenAI وStripe، ورموز GitLab وnpm وGitHub، ورموز JSON Web Token، ومعرّفات مفاتيح AWS، ورموز Slack، ومفاتيح Google API، والمفاتيح الخاصة بصيغة PEM.
  3. كلمات المرور داخل عناوين URL، وقيم Authorization: Bearer التي طولها 12 حرفًا أو أكثر، وتعيينات password وpasswd وsecret وtoken وapi_key التي قيمتها ست خانات أو أكثر، تصبح [redacted].

المهم هو هذه المجموعات الثلاث. في الشيفرة تعمل قاعدة كلمة المرور داخل URL قبل قاعدتي GitHub وAWS.

طرفية تعرض طلبًا فيه مفتاح API مزيف وكتلة خاصة، خُزّنا كعلامة api-key محذوفة وعلامة private
مفتاح مزيف وكتلة خاصة، خُزّنا كعلامات. التشغيل نفسه في الصورة السابقة.

اخترت الأنماط على المصنّف لأن النمط متوقع وسريع ويمكن اختباره ضمن ميزانية 150 ms. الثمن هو التغطية. يذكر التوثيق ما يفلت:

كذلك لا يعمل حذف الأسرار على كل مسارات الكتابة. تحذف الأسرار: الـ hooks، وaletheia save، وaletheia capture، وPOST /v1/events، وأداة MCP save_memory، واستيراد النصوص المسجلة، وaletheia watch. أما مسار REST POST /v1/memories، وخانة الملاحظة في لوحة التحكم، والتعديلات، واستيراد الحزم، واستيراد claude-mem، فتخزّن النص كما أُرسل تمامًا. في اختبار بتاريخ 2026-10-29 خزّن أول اثنين منها مفتاحًا مزيفًا دون تغيير.

الخيار 4: الضغط لاحقًا، في طابور بعقود تأجير

المطالبات الخام وأحداث الأدوات رخيصة وكثيرة الضجيج. أما تحويل جلسة منتهية إلى حقائق وملخص فأبطأ، لذلك لا يجري أبدًا داخل hook.

  1. يضيف الـ hook من نوع Stop الجلسة إلى طابور صغير ولا يستخرج شيئًا.
  2. يعمل العامل في aletheia-server كل ثانيتين ويحوّل كل إدخال إلى صف مهمة من النوع extract_session.
  3. يطالب بمهمة واحدة في كل مرة بعقد تأجير مدته 60 ثانية (LEASE_MS = 60_000). إذا ماتت العملية ينتهي العقد وتصبح المهمة قابلة للمطالبة من جديد. المهمة موجودة في صف، فالعملية التي تُقتل لا تفقد شيئًا.
  4. المهمة الفاشلة تُعاد بتأخير متزايد أسيًّا بحد أقصى خمس محاولات (MAX_ATTEMPTS = 5). مدد الانتظار هي 1 و2 و4 و8 ثوانٍ. تتوقف الصيغة عند 60 ثانية، وهو حد لا تبلغه خمس محاولات.
  5. تقرأ المهمة ذكريات summary وnote التي لم تُستخرج بعد، وتقسمها إلى نوافذ من 20 عنصرًا و12,000 حرف افتراضيًا، وتستخرج الحقائق، وتبني ملخصًا للجلسة وملخصًا للمشروع، وتدمج المتشابهات القريبة عند تشابه 0.92 أو أعلى.

المستخرِج الافتراضي يعتمد على قواعد ولا يجري أي استدعاء للشبكة. مزوّد النموذج اختياري. هناك نتيجة سلبية تستحق المعرفة: أول تشغيل محلي للاستخراج بنموذج حجمه 1B أعاد HTTP 500 بعد 4 دقائق و41 ثانية ولم ينتج أي حقائق. أما مستخرج القواعد فأضاف 1,672 حقيقة ونقل Recall@5 من 57.3% إلى 57.7%، بينما أضافت حقائق الاختبار نفسه 7.3 نقطة. تحرك مستخرج القواعد نحو 0.4 نقطة (من 57.3% إلى 57.7% في نص README؛ وجدوله نفسه يعرض خط الأساس 57.4%). المصدر: README، تشغيل 2026-10-26.

الخيار 5: الاسترجاع فهرس محدود وموسوم، لا تفريغ كامل

طرفية تعرض كتلة سياق بدء الجلسة: ذاكرة مثبّتة، وذكريات حديثة مجمعة حسب اليوم، وطلبات الجلسة، والقواعد، ومفتاح رموز، وتلميح للجلب، وسطر تقدير الرموز
الكتلة التي تتلقاها جلسة جديدة، من أربع ذكريات تجريبية وقاعدة واحدة.

لكل قسم سقف: حتى 50 ذاكرة مثبّتة، و20 ذاكرة حديثة افتراضيًا (100 كحد أقصى)، وحتى خمسة طلبات جلسة حديثة، وحتى عشر حقائق نشطة، وكل قاعدة مفعّلة غير مقصورة على أدوات أخرى. افتراضيًا (حين يكون context.facts على auto) تستثني الحقائق المؤهلة الحقائق التي استخرجتها القواعد، وهذه تُخزَّن بثقة 0.45. يُقطع العنوان عند 100 حرف، وتُقطع الكتلة كلها عند 6,000 حرف من النهاية، فالكتلة الطويلة تفقد مفتاح الرموز والتلميح وسطر التكلفة أولًا.

ثلاث تفاصيل في التصميم أهم من الأسقف.

الخيار 6: البحث بحث بالكلمات مع الحقائق، يُدمج بالترتيب

مخطط بحث Aletheia: يتحول الاستعلام إلى كلمات المحتوى، ويجري على فهرس الذكريات وفهرس الحقائق، ويدمج reciprocal rank fusion القائمتين
ترتيب البحث في CLI. مساري REST وMCP لا يدمجان القائمتين؛ يعيد REST كل قائمة في مصفوفة منفصلة.

يتحول الاستعلام إلى كلمات المحتوى. تُسقط أي كلمة توجد في أكثر من 20% من كل الذكريات إذا بقيت كلمة أندر، ويُحتفظ بـ 12 كلمة على الأكثر من أندر الكلمات. يرتّب SQLite FTS5 النتائج بخوارزمية BM25 على العنوان والعنوان الفرعي والسرد والحقائق والمفاهيم، بالمحلّل porter unicode61 remove_diacritics 2، فتطابق “retries” كلمة “retry”. للحقائق فهرسها الخاص وتعود الأحدث أولًا. يدمج CLI القائمتين بـ reciprocal rank fusion، بوزن 1 للحقائق و0.9 للذكريات (FUSION في src/hub/search.mjs، حيث يسمى وزن الذكريات episodeWeight). تُطبَّق المرشحات قبل الترتيب، ولا تغيّر درجات الحداثة والتثبيت والأهمية ترتيبَ الذكريات. البحث الدلالي بنموذج تضمين محلي اختياري، ومعطّل افتراضيًا، ومتاح في CLI فقط.

نموذج البيانات في جدول

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

لكل صف مشروع ونطاق: project افتراضيًا أو global بعد الترقية. الإخفاء (dismiss) يخفي الصف دون حذفه، وaletheia erase يحذف عن قصد ويعطي إيصالًا موقّعًا.

ما الذي يفشل، وماذا يحدث

الفشل النتيجة
قاعدة البيانات مشغولة أو غير موجودة أثناء hook يذهب العنصر المحذوفة أسراره إلى الـ spool؛ ويعيد الـ hook التالي تشغيله
الحمولة فارغة أو ليست JSON صالحًا لا تُخزَّن ذاكرة وينتهي الـ hook برمز 0. ومع ذلك يضع Stop أو SessionEnd إدخالًا في الطابور للمشروع default.
ينطلق الـ watchdog أثناء كتابة جارية يضيع ذلك العنصر (موثّق، غير مختبر)
أخطاء مزوّد الاستخراج تُعاد المهمة بتأخير متزايد، بحد أقصى خمس محاولات
قتل عملية العامل في منتصف مهمة ينتهي العقد بعد 60 ثانية وتُطالَب المهمة من جديد
ملاحظة مخزنة تحتوي وسم إغلاق السياق يُعطَّل الوسم
سر لا يطابق أي نمط يُخزَّن كما كُتب. هذه ثغرة معروفة.

الأرقام

كل الأرقام قاسها المؤلف وتحمل مصدرها.

ما الذي تحققت منه، وما الذي لم أتحقق منه

قرأت الثوابت في هذا المقال داخل الشيفرة بتاريخ 2026-10-31: الـ watchdog بـ 150 ms في bin/aletheia.mjs، ومهلة الانشغال 50 ms في src/hub/hooks/run.mjs، وعقد التأجير وحد المحاولات والتأخير في src/hub/queue.mjs، وعتبة الدمج 0.92 في src/hub/dedupe.mjs، وأوزان الدمج في src/hub/search.mjs، والقيمة الافتراضية 6,000 حرف في src/hub/store.mjs. شُغّلت مخرجات الطرفية مع aletheia-mem 0.1.10 في مجلد مؤقت بتاريخ 2026-10-30.

لم أُحدث انطلاق الـ watchdog في منتصف كتابة، فعبارة “يضيع ذلك العنصر” هي قول التوثيق لا قولي. عملت الـ hooks في جلسات حقيقية مع Claude Code وCommand Code وCodex CLI، على جهاز واحد ولمستخدم واحد؛ ولم يعمل أي hook داخل Cursor أو Gemini CLI أو Windsurf أو OpenCode. دقة الإجابات غير مقيسة. المزامنة بين جهازين حقيقيين غير مختبرة.

ما الذي كنت سأفعله بشكل مختلف

  1. حذف الأسرار على كل مسارات الكتابة من البداية. اليوم يخزّن مسار REST وخانة الملاحظة في لوحة التحكم والتعديلات والاستيرادات النص كما أُرسل.
  2. جعل الإعدادات تطابق الشيفرة. تعرض شاشة الإعدادات “Redact before storing” و“Excluded projects”، ولا تقرأ شيفرة الالتقاط أيًّا من القيمتين. حذف الأسرار يعمل دائمًا في مساراته، والاستثناء يعمل عبر ALETHEIA_EXCLUDED_PROJECTS. كنت سأزيل الخيارين أو أربطهما بالشيفرة، حتى لا تقول الشاشة شيئًا لا تفعله الشيفرة.
  3. دمج النتائج بالطريقة نفسها في كل مكان. يدمج CLI الحقائق والذكريات؛ أما REST وMCP فلا. ترتيب واحد أسهل في الفهم.

المصادر

كُتبت مسودة هذه المقالة بمساعدة الذكاء الاصطناعي. راجعت كل ادعاء ونفذت الأوامر المعروضة.

عن المشروع: Aletheia.

Mohamed Ashraf Elsaed
Mohamed Ashraf Elsaed
قائد تقني في Convertedin، القاهرة. أبني أدوات لوكلاء البرمجة بالذكاء الاصطناعي وأكتب عن لارافيل وNode والهندسة.

ما يقدمه aletheia-mem وما لا يقدمه

فوائد Aletheia بأرقامها ومصادرها: مخزن محلي واحد لعدة أدوات، وقواعد تُكتب مرة واحدة، وخصوصية افتراضية، واسترجاع مقيس، مع الحدود.