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

أربعة قيود
أربعة قيود حكمت كل خيار أدناه.
- يعمل الـ hook في المسار الحرج للأداة المضيفة، فيجب ألا يعطّلها أبدًا.
- قد تحتوي الذاكرة على أسرار، فلا يُخزَّن شيء قبل حذف الأسرار منه.
- على الذاكرة أن تخدم عدة أدوات، لذا المخزن محايد تجاه الأدوات.
- تتلقى كل جلسة جديدة سياقًا في كل مرة، وهذا يكلّف رموزًا (tokens) في كل مرة، فيجب أن يكون السياق صغيرًا ويمكن توقع حجمه.
خط المعالجة في صورة واحدة
يجري الالتقاط في عملية قصيرة العمر هي 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 قصيرة:
- ينتهي دائمًا برمز الخروج 0. أي خطأ داخلي يُبتلع.
- مؤقّت مراقبة (watchdog) ينهي العملية بعد 150 ms (
setTimeout(() => process.exit(0), 150)،bin/aletheia.mjs). هو مؤقّت، فلا يعمل إلا حين تكون حلقة الأحداث حرة: لا يستطيع مقاطعة قراءة متزامنة أو استدعاء SQLite. - لا يجري أي استدعاء للشبكة ولا لنموذج.
- إذا لم يستطع فتح قاعدة البيانات أو الكتابة فيها، يضيف العنصر الذي حُذفت أسراره إلى
spool/hook-spool.jsonlفي مجلد البيانات، بصلاحيات الملف 0600. وأول hook يصل إلى قاعدة البيانات بعد ذلك يعيد تشغيل محتوى الـ spool أولًا.
أثناء انقطاع قاعدة البيانات يكون الأثر تأخيرًا لا فقدانًا للعناصر التي تصل إلى الـ spool: تظهر الذاكرة في البحث بعد أول hook يصل إلى قاعدة بيانات سليمة. أما إذا انطلق الـ watchdog أثناء كتابة جارية فيضيع ذلك العنصر. التوثيق المرجعي يقول ذلك، ولم أُحدث هذه الحالة في اختبار.
الخيار 3: حذف الأسرار في دالة بسيطة قبل الكتابة
حذف الأسرار دالة نقية (src/hub/redact.mjs) تعمل بترتيب ثابت:
- النص بين
<private>و</private>يصبح[private]. - أشكال الأسرار المعروفة تصبح علامة مثل
[redacted:github-token]: مفاتيح Anthropic وبنمط OpenAI وStripe، ورموز GitLab وnpm وGitHub، ورموز JSON Web Token، ومعرّفات مفاتيح AWS، ورموز Slack، ومفاتيح Google API، والمفاتيح الخاصة بصيغة PEM. - كلمات المرور داخل عناوين URL، وقيم
Authorization: Bearerالتي طولها 12 حرفًا أو أكثر، وتعييناتpasswordوpasswdوsecretوtokenوapi_keyالتي قيمتها ست خانات أو أكثر، تصبح[redacted].
المهم هو هذه المجموعات الثلاث. في الشيفرة تعمل قاعدة كلمة المرور داخل URL قبل قاعدتي GitHub وAWS.
اخترت الأنماط على المصنّف لأن النمط متوقع وسريع ويمكن اختباره ضمن ميزانية 150 ms. الثمن هو التغطية. يذكر التوثيق ما يفلت:
- سر تحت مفتاح JSON، لأن علامة الاقتباس تقع بين المفتاح والنقطتين.
- أسماء متغيرات لها بادئة، مثل
MY_API_KEY=أوDB_PASSWORD=. - القيم الأقصر من ست خانات، والبيانات الشخصية، ووسم
<private>غير المغلق.
كذلك لا يعمل حذف الأسرار على كل مسارات الكتابة. تحذف الأسرار: الـ hooks، وaletheia save، وaletheia capture، وPOST /v1/events، وأداة MCP save_memory، واستيراد النصوص المسجلة، وaletheia watch. أما مسار REST POST /v1/memories، وخانة الملاحظة في لوحة التحكم، والتعديلات، واستيراد الحزم، واستيراد claude-mem، فتخزّن النص كما أُرسل تمامًا. في اختبار بتاريخ 2026-10-29 خزّن أول اثنين منها مفتاحًا مزيفًا دون تغيير.
الخيار 4: الضغط لاحقًا، في طابور بعقود تأجير
المطالبات الخام وأحداث الأدوات رخيصة وكثيرة الضجيج. أما تحويل جلسة منتهية إلى حقائق وملخص فأبطأ، لذلك لا يجري أبدًا داخل hook.
- يضيف الـ hook من نوع
Stopالجلسة إلى طابور صغير ولا يستخرج شيئًا. - يعمل العامل في
aletheia-serverكل ثانيتين ويحوّل كل إدخال إلى صف مهمة من النوعextract_session. - يطالب بمهمة واحدة في كل مرة بعقد تأجير مدته 60 ثانية (
LEASE_MS = 60_000). إذا ماتت العملية ينتهي العقد وتصبح المهمة قابلة للمطالبة من جديد. المهمة موجودة في صف، فالعملية التي تُقتل لا تفقد شيئًا. - المهمة الفاشلة تُعاد بتأخير متزايد أسيًّا بحد أقصى خمس محاولات (
MAX_ATTEMPTS = 5). مدد الانتظار هي 1 و2 و4 و8 ثوانٍ. تتوقف الصيغة عند 60 ثانية، وهو حد لا تبلغه خمس محاولات. - تقرأ المهمة ذكريات
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 حرف من النهاية، فالكتلة الطويلة تفقد مفتاح الرموز والتلميح وسطر التكلفة أولًا.
ثلاث تفاصيل في التصميم أهم من الأسقف.
- الكشف التدريجي. الكتلة فهرس بسطر واحد لكل ذاكرة. يعلّم مفتاح الرموز وتلميح الجلب الوكيلَ النمط
searchثمtimelineثمget_observations، فلا يُجلب النص الكامل إلا عند الحاجة. - بيانات لا تعليمات. تقع الكتلة داخل وسمي
<aletheia-memory>مع إشعار بأن المحتوى بيانات. نسخة من الوسم داخل ذاكرة مخزنة تُعطَّل، فلا تستطيع ملاحظة مخزنة إغلاق الكتلة مبكرًا. - سطر تكلفة صادق. يقدّر Aletheia الرموز بعدد الأحرف مقسومًا على أربعة، ويقول ذلك. في العرض التجريبي أعلاه تبلغ الكتلة نحو 194 رمزًا مقابل نحو 132 لنص العناصر المدرجة، أي أنها أكبر. التوفير الحقيقي يحتاج إلى ذكريات ذات سرد طويل.
الخيار 6: البحث بحث بالكلمات مع الحقائق، يُدمج بالترتيب
يتحول الاستعلام إلى كلمات المحتوى. تُسقط أي كلمة توجد في أكثر من 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 ثانية وتُطالَب المهمة من جديد |
| ملاحظة مخزنة تحتوي وسم إغلاق السياق | يُعطَّل الوسم |
| سر لا يطابق أي نمط | يُخزَّن كما كُتب. هذه ثغرة معروفة. |
الأرقام
كل الأرقام قاسها المؤلف وتحمل مصدرها.
- عند 50,000 صف اصطناعي، بلغ p95 للبحث بالكلمات 32.7 ms ضمن مشروع واحد و58.6 ms عبر كل المشاريع (Linux، 12 نواة، 2026-10-27،
bun bench/scale.mjs 50000 20000 --index tuned --queries 200 --seed 1). - على أسئلة LoCoMo، بلغ Recall@5 للأدوار الخام 57.4% عند 3.72 ms للاستعلام. هذا الصف هو البحث الهجين (كلمات مع تضمينات)، لا البحث بالكلمات وحده الموصوف أعلاه. البحث بالكلمات وحده كان 55.3% عند 2.00 ms. يعرض ملف سجل واحد 44.7% للأدوار الخام، ويقول توثيق الاختبار إن السبب لم يُحسم. كل ذلك عن الاسترجاع، لا عن دقة الإجابات.
ما الذي تحققت منه، وما الذي لم أتحقق منه
قرأت الثوابت في هذا المقال داخل الشيفرة بتاريخ 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. دقة الإجابات غير مقيسة. المزامنة بين جهازين حقيقيين غير مختبرة.
ما الذي كنت سأفعله بشكل مختلف
- حذف الأسرار على كل مسارات الكتابة من البداية. اليوم يخزّن مسار REST وخانة الملاحظة في لوحة التحكم والتعديلات والاستيرادات النص كما أُرسل.
- جعل الإعدادات تطابق الشيفرة. تعرض شاشة الإعدادات “Redact before storing” و“Excluded projects”، ولا تقرأ شيفرة الالتقاط أيًّا من القيمتين. حذف الأسرار يعمل دائمًا في مساراته، والاستثناء يعمل عبر
ALETHEIA_EXCLUDED_PROJECTS. كنت سأزيل الخيارين أو أربطهما بالشيفرة، حتى لا تقول الشاشة شيئًا لا تفعله الشيفرة. - دمج النتائج بالطريقة نفسها في كل مكان. يدمج CLI الحقائق والذكريات؛ أما REST وMCP فلا. ترتيب واحد أسهل في الفهم.
المصادر
- توثيق Aletheia، كيف يعمل, جُلب في 2026-10-31
- توثيق Aletheia، الخصوصية وحذف الأسرار, جُلب في 2026-10-31
- توثيق Aletheia، البحث, جُلب في 2026-10-31
- توثيق Aletheia، سياق بدء الجلسة, جُلب في 2026-10-31
- ملف README لـ Aletheia، الأرقام المقيسة والحدود, جُلب في 2026-10-31
- شيفرة Aletheia، طابور المهام, جُلب في 2026-10-31
