الذاكرة
ذاكرة OpenClaw هي Markdown عادي داخل مساحة عمل الوكيل. الملفات هي مصدر الحقيقة؛ فالنموذج لا «يتذكر» إلا ما يُكتب على القرص. توفّر أدوات البحث في الذاكرة عبر إضافة الذاكرة النشطة (الافتراضية:memory-core). عطّل إضافات الذاكرة باستخدام plugins.slots.memory = "none".
ملفات الذاكرة (Markdown)
يستخدم تخطيط مساحة العمل الافتراضي طبقتين من الذاكرة:memory/YYYY-MM-DD.md- سجل يومي (إلحاق فقط).
- تُقرأ ملاحظات اليوم + الأمس عند بدء الجلسة.
MEMORY.md(اختياري)- ذاكرة طويلة الأمد مُنسَّقة.
- تُحمَّل فقط في الجلسة الرئيسية الخاصة (ولا تُحمَّل أبدًا في سياقات جماعية).
agents.defaults.workspace، الافتراضي
~/.openclaw/workspace). راجع مساحة عمل الوكيل للتخطيط الكامل.
متى تكتب الذاكرة
- القرارات والتفضيلات والحقائق الدائمة تذهب إلى
MEMORY.md. - الملاحظات اليومية والسياق الجاري تذهب إلى
memory/YYYY-MM-DD.md. - إذا قال شخص «تذكّر هذا»، فاكتبه (لا تُبقِه في الذاكرة المؤقتة).
- هذا المجال ما يزال قيد التطور. من المفيد تذكير النموذج بتخزين الذكريات؛ فهو يعرف ما الذي يجب فعله.
- إذا أردت أن يثبت شيء ما، اطلب من البوت كتابته في الذاكرة.
التفريغ التلقائي للذاكرة (تنبيه ما قبل الدمج)
عندما تقترب الجلسة من الدمج التلقائي، يطلق OpenClaw دورًا صامتًا وكيليًا يذكّر النموذج بكتابة الذاكرة الدائمة قبل ضغط السياق. تشير المطالبات الافتراضية صراحةً إلى أن النموذج قد يرد، لكن غالبًا ما تكونNO_REPLY هي الاستجابة الصحيحة بحيث لا يرى المستخدم هذا الدور.
يتم التحكم بذلك عبر agents.defaults.compaction.memoryFlush:
- الحد اللين: يُطلق التفريغ عندما يتجاوز تقدير رموز الجلسة
contextWindow - reserveTokensFloor - softThresholdTokens. - صامت افتراضيًا: تتضمن المطالبات
NO_REPLYبحيث لا يُسلَّم شيء. - مطلبان: مطالبة مستخدم بالإضافة إلى مطالبة نظام تُلحِق التذكير.
- تفريغ واحد لكل دورة دمج (يُتتبَّع في
sessions.json). - يجب أن تكون مساحة العمل قابلة للكتابة: إذا كانت الجلسة تعمل ضمن sandbox مع
workspaceAccess: "ro"أو"none"، فيُتخطّى التفريغ.
البحث المتجهي في الذاكرة
يمكن لـ OpenClaw بناء فهرس متجهي صغير فوقMEMORY.md و memory/*.md بحيث
تتمكن الاستعلامات الدلالية من العثور على ملاحظات ذات صلة حتى عند اختلاف الصياغة.
الافتراضيات:
- مُمكَّن افتراضيًا.
- يراقب ملفات الذاكرة بحثًا عن تغييرات (مع إزالة الارتداد).
- قم بتكوين بحث الذاكرة ضمن
agents.defaults.memorySearch(وليس ضمنmemorySearchعلى المستوى الأعلى). - يستخدم تضمينات بعيدة افتراضيًا. إذا لم يتم تعيين
memorySearch.provider، يختار OpenClaw تلقائيًا:localإذا كانmemorySearch.local.modelPathمُهيّأً والملف موجودًا.openaiإذا أمكن حل مفتاح OpenAI.geminiإذا أمكن حل مفتاح Gemini.voyageإذا أمكن حل مفتاح Voyage.- وإلا يبقى البحث في الذاكرة معطّلًا حتى تتم التهيئة.
- يستخدم الوضع المحلي node-llama-cpp وقد يتطلب
pnpm approve-builds. - يستخدم sqlite-vec (عند توفره) لتسريع البحث المتجهي داخل SQLite.
models.providers.*.apiKey، أو متغيرات
البيئة. لا يغطي Codex OAuth سوى الدردشة/الإكمالات ولا يلبّي
متطلبات التضمينات للبحث في الذاكرة. بالنسبة إلى Gemini، استخدم GEMINI_API_KEY أو
models.providers.google.apiKey. وبالنسبة إلى Voyage، استخدم VOYAGE_API_KEY أو
models.providers.voyage.apiKey. عند استخدام نقطة نهاية مخصّصة متوافقة مع OpenAI،
اضبط memorySearch.remote.apiKey (و memorySearch.remote.headers اختياريًا).
خلفية QMD (تجريبية)
اضبطmemory.backend = "qmd" لاستبدال مفهرس SQLite المدمج بـ
QMD: مكوّن بحث محلي أولًا يجمع
BM25 + المتجهات + إعادة الترتيب. يبقى Markdown مصدر الحقيقة؛ ويستدعي OpenClaw
QMD للاسترجاع. نقاط أساسية:
المتطلبات المسبقة
- معطّل افتراضيًا. اشترك لكل تهيئة (
memory.backend = "qmd"). - ثبّت QMD CLI بشكل منفصل (
bun install -g https://github.com/tobi/qmdأو احصل على إصدار) وتأكد من أن ثنائيةqmdموجودة علىPATHللبوابة. - يحتاج QMD إلى بناء SQLite يسمح بالامتدادات (
brew install sqliteعلى macOS). - يعمل QMD محليًا بالكامل عبر Bun +
node-llama-cppويقوم بتنزيل نماذج GGUF تلقائيًا من HuggingFace عند أول استخدام (لا يلزم وجود خدمة Ollama منفصلة). - تشغّل البوابة QMD ضمن منزل XDG مستقل تحت
~/.openclaw/agents/<agentId>/qmd/عبر تعيينXDG_CONFIG_HOMEوXDG_CACHE_HOME. - دعم أنظمة التشغيل: يعمل macOS وLinux مباشرةً بمجرد تثبيت Bun + SQLite. يُفضَّل دعم Windows عبر WSL2.
- تكتب البوابة منزل QMD مستقلًا تحت
~/.openclaw/agents/<agentId>/qmd/(تهيئة + ذاكرة مؤقتة + قاعدة بيانات sqlite). - تُنشأ المجموعات عبر
qmd collection addمنmemory.qmd.paths(بالإضافة إلى ملفات ذاكرة مساحة العمل الافتراضية)، ثم يعملqmd update+qmd embedعند الإقلاع وعلى فاصل زمني قابل للتهيئة (memory.qmd.update.interval، الافتراضي 5 دقائق). - يقوم gateway الآن بتهيئة مدير QMD عند بدء التشغيل، لذلك يتم تفعيل
مؤقتات التحديث الدورية حتى قبل أول استدعاء
memory_search. - يعمل تحديث الإقلاع الآن في الخلفية افتراضيًا حتى لا يُحجب بدء الدردشة؛
اضبط
memory.qmd.update.waitForBootSync = trueللاحتفاظ بسلوك الحجب السابق. - تُنفَّذ عمليات البحث عبر
qmd query --json. إذا كان الوضع المحدد يرفض العلامات في إصدار QMD لديك، فسيعيد OpenClaw المحاولة باستخدامqmd query. إذا فشل QMD أو كانت الثنائية مفقودة، يعود OpenClaw تلقائيًا إلى مدير SQLite المدمج حتى تستمر أدوات الذاكرة بالعمل. - لا يعرِض OpenClaw اليوم ضبط حجم دفعات التضمين في QMD؛ يتحكم QMD نفسه في سلوك الدُفعات.
- قد يكون البحث الأول بطيئًا: قد يقوم QMD بتنزيل نماذج GGUF المحلية
(إعادة ترتيب/توسيع الاستعلام) عند أول تشغيل لـ
qmd query.-
يضبط OpenClaw
XDG_CONFIG_HOME/XDG_CACHE_HOMEتلقائيًا عند تشغيل QMD. -
إذا أردت تنزيل النماذج مسبقًا يدويًا (وتسخين الفهرس نفسه الذي يستخدمه OpenClaw)،
نفّذ استعلامًا لمرة واحدة باستخدام مجلدات XDG الخاصة بالوكيل.
تعيش حالة QMD الخاصة بـ OpenClaw تحت دليل الحالة لديك (الافتراضي
~/.openclaw). يمكنك توجيهqmdإلى الفهرس نفسه تمامًا عبر تصدير متغيرات XDG نفسها التي يستخدمها OpenClaw:
-
يضبط OpenClaw
memory.qmd.*)
command(الافتراضيqmd): تجاوز مسار التنفيذ.searchMode(الافتراضيsearch): اختر أمر QMD الذي يدعمmemory_search(searchأوvsearchأوquery).includeDefaultMemory(الافتراضيtrue): فهرسة تلقائية لـMEMORY.md+memory/**/*.md.paths[]: إضافة أدلة/ملفات إضافية (path، اختياريpattern، اختياري ثابتname).sessions: الاشتراك في فهرسة JSONL للجلسات (enabled،retentionDays،exportDir).update: يتحكم في وتيرة التحديث وتنفيذ الصيانة: (interval،debounceMs،onBoot،waitForBootSync،embedInterval،commandTimeoutMs،updateTimeoutMs،embedTimeoutMs).limits: تقييد حمولة الاستدعاء (maxResults،maxSnippetChars،maxInjectedChars،timeoutMs).scope: المخطط نفسه كما فيsession.sendPolicy. الافتراضي هو الرسائل المباشرة فقط (DM) (denyالكل،allowالدردشات المباشرة)؛ قم بتوسيعه لإظهار نتائج QMD في المجموعات/القنوات.- يطابق
match.keyPrefixمفتاح الجلسة المُطبَّع (بحروف صغيرة، مع إزالة أي بادئةagent:<id>:). مثال:discord:channel:. - يطابق
match.rawKeyPrefixمفتاح الجلسة الخام (بحروف صغيرة)، بما في ذلكagent:<id>:. مثال:agent:main:discord:. - الإصدار القديم: لا يزال
match.keyPrefix: "agent:..."يُعامل كبادئة مفتاح خام، ولكن يُفضَّل استخدامrawKeyPrefixلزيادة الوضوح.
- يطابق
- طابق على مُرسل E.164 (مثل
+15551234567) باستخدامpeer.kind: "direct". - المقاطع المأخوذة من خارج مساحة العمل تظهر باسم
qmd/<collection>/<relative-path>في نتائجmemory_search؛ ويفهمmemory_getهذا البادئة ويقرأ من جذر مجموعة QMD المُهيّأة. - عند
memory.qmd.sessions.enabled = true، يصدّر OpenClaw سجلات الجلسات المُنقّاة (أدوار المستخدم/المساعد) إلى مجموعة QMD مخصّصة تحت~/.openclaw/agents/<id>/qmd/sessions/، بحيث يمكن لـmemory_searchاستدعاء المحادثات الأخيرة دون لمس فهرس SQLite المدمج. - تتضمن مقاطع
memory_searchالآن تذييلSource: <path#line>عندما تكونmemory.citationsهيauto/on؛ اضبطmemory.citations = "off"للاحتفاظ ببيانات المسار داخلية (لا يزال الوكيل يتلقى المسار لأجلmemory_get، لكن نص المقطع يحذف التذييل ويُحذّر مطلب النظام الوكيل من الاستشهاد به).
- تنطبق
memory.citationsبغضّ النظر عن الخلفية (auto/on/off). - عند تشغيل
qmd، نوسمstatus().backend = "qmd"بحيث تُظهر التشخيصات أي محرّك قدّم النتائج. إذا خرجت عملية QMD الفرعية أو تعذّر تحليل خرج JSON، يسجّل مدير البحث تحذيرًا ويعيد المزوّد المدمج (تضمينات Markdown الحالية) حتى يتعافى QMD.
مسارات ذاكرة إضافية
إذا أردت فهرسة ملفات Markdown خارج تخطيط مساحة العمل الافتراضي، أضف مسارات صريحة:- يمكن أن تكون المسارات مطلقة أو نسبية لمساحة العمل.
- تُفحَص الأدلة تكراريًا بحثًا عن ملفات
.md. - تُفهرس ملفات Markdown فقط.
- يتم تجاهل الروابط الرمزية (ملفات أو أدلة).
تضمينات Gemini (أصلية)
اضبط المزوّد إلىgemini لاستخدام واجهة Gemini للتضمينات مباشرةً:
remote.baseUrlاختياري (الافتراضي عنوان قاعدة واجهة Gemini).- يتيح
remote.headersإضافة ترويسات إضافية عند الحاجة. - النموذج الافتراضي:
gemini-embedding-001.
remote مع مزوّد OpenAI:
memorySearch.provider = "local" أو اضبط
memorySearch.fallback = "none".
الارتداد:
- يمكن أن تكون
memorySearch.fallbackواحدة منopenaiأوgeminiأوlocalأوnone. - يُستخدم المزوّد الاحتياطي فقط عندما يفشل مزوّد التضمين الأساسي.
- مُمكَّنة افتراضيًا لتضمينات OpenAI وGemini. اضبط
agents.defaults.memorySearch.remote.batch.enabled = falseللتعطيل. - ينتظر السلوك الافتراضي اكتمال الدُفعة؛ اضبط
remote.batch.waitوremote.batch.pollIntervalMsوremote.batch.timeoutMinutesإذا لزم. - اضبط
remote.batch.concurrencyللتحكم في عدد مهام الدُفعات المتوازية (الافتراضي: 2). - يُطبَّق وضع الدُفعات عندما تكون
memorySearch.provider = "openai"أو"gemini"وتستخدم مفتاح API الموافق. - تستخدم مهام دُفعات Gemini نقطة نهاية الدُفعات غير المتزامنة للتضمينات وتتطلب توفر واجهة Gemini Batch API.
- لعمليات الإرجاع الكبيرة، تكون OpenAI عادةً الخيار الأسرع الذي ندعمه لأننا نستطيع إرسال العديد من طلبات التضمين في مهمة دُفعة واحدة وترك OpenAI تعالجها بشكل غير متزامن.
- تقدّم OpenAI تسعيرًا مخفّضًا لأحمال عمل Batch API، لذا تكون عمليات الفهرسة الكبيرة عادةً أرخص من إرسال الطلبات نفسها بشكل متزامن.
- راجع مستندات وتسعير OpenAI Batch API للتفاصيل:
memory_search— يعيد مقاطع مع الملف + نطاقات الأسطر.memory_get— قراءة محتوى ملف ذاكرة حسب المسار.
- اضبط
agents.defaults.memorySearch.provider = "local". - قدّم
agents.defaults.memorySearch.local.modelPath(GGUF أو URI لـhf:). - اختياري: اضبط
agents.defaults.memorySearch.fallback = "none"لتجنب الرجوع الاحتياطي البعيد.
كيف تعمل أدوات الذاكرة
- يجري
memory_searchبحثًا دلاليًا في مقاطع Markdown (~هدف 400 رمز، تداخل 80 رمزًا) منMEMORY.md+memory/**/*.md. يعيد نص المقطع (محدود ~700 حرف)، ومسار الملف، ونطاق الأسطر، والدرجة، والمزوّد/النموذج، وما إذا كنا قد رجعنا من تضمينات محلية → بعيدة. لا يُعاد حمولة ملف كاملة. - يقرأ
memory_getملف ذاكرة Markdown محددًا (نسبيًا لمساحة العمل)، اختياريًا من سطر بداية ولمدة N أسطر. تُرفض المسارات خارجMEMORY.md/memory/. - تُفعَّل الأداتان فقط عندما تتحقق
memorySearch.enabledللوكيل.
ما الذي يُفهرس (ومتى)
- نوع الملف: Markdown فقط (
MEMORY.md،memory/**/*.md). - تخزين الفهرس: SQLite لكل وكيل في
~/.openclaw/memory/<agentId>.sqlite(قابل للتهيئة عبرagents.defaults.memorySearch.store.path، ويدعم رمز{agentId}). - الحداثة: يعلّم المراقِب على
MEMORY.md+memory/الفهرس على أنه متّسخ (إزالة ارتداد 1.5 ثانية). تُجدول المزامنة عند بدء الجلسة، أو عند البحث، أو على فاصل زمني وتعمل بشكل غير متزامن. تستخدم سجلات الجلسات عتبات دلتا لإطلاق مزامنة خلفية. - محفزات إعادة الفهرسة: يخزّن الفهرس المزوّد/النموذج + بصمة نقطة النهاية + معاملات التجزئة. إذا تغيّر أيّ منها، يعيد OpenClaw الضبط ويُعيد فهرسة المخزن بالكامل تلقائيًا.
البحث الهجين (BM25 + متجه)
عند التمكين، يجمع OpenClaw بين:- تشابه المتجه (تطابق دلالي، يمكن أن تختلف الصياغة)
- ملاءمة كلمات BM25 (رموز دقيقة مثل المعرّفات ومتغيرات البيئة ورموز الشيفرة)
لماذا الهجين؟
البحث المتجهي ممتاز في «هذا يعني الشيء نفسه»:- «مضيف بوابة Mac Studio» مقابل «الآلة التي تشغّل البوابة»
- «إزالة ارتداد تحديثات الملفات» مقابل «تجنب الفهرسة عند كل كتابة»
- المعرّفات (
a828e60،b3b9895a…) - رموز الشيفرة (
memorySearch.query.hybrid) - سلاسل الأخطاء («sqlite-vec unavailable»)
كيف ندمج النتائج (التصميم الحالي)
مخطط التنفيذ:- استرجاع مجموعة مرشحين من الجانبين:
- المتجه: أعلى
maxResults * candidateMultiplierحسب تشابه جيب التمام. - BM25: أعلى
maxResults * candidateMultiplierحسب ترتيب FTS5 BM25 (الأقل أفضل).
- تحويل ترتيب BM25 إلى درجة تقريبية 0..1:
textScore = 1 / (1 + max(0, bm25Rank))
- توحيد المرشحين حسب معرّف المقطع وحساب درجة موزونة:
finalScore = vectorWeight * vectorScore + textWeight * textScore
- يتم تطبيع
vectorWeight+textWeightإلى 1.0 عند حل التهيئة، لذا تتصرف الأوزان كنِسَب مئوية. - إذا كانت التضمينات غير متاحة (أو أعاد المزوّد متجهًا صفريًا)، ما زلنا نشغّل BM25 ونعيد تطابقات الكلمات المفتاحية.
- إذا تعذّر إنشاء FTS5، نحتفظ بالبحث المتجهي فقط (من دون فشل قاسٍ).
ذاكرة التخزين المؤقت للتضمين
يمكن لـ OpenClaw تخزين تضمينات المقاطع مؤقتًا في SQLite بحيث لا تعيد الفهرسة والتحديثات المتكررة (خاصةً سجلات الجلسات) تضمين النص غير المتغير. التهيئة:البحث في ذاكرة الجلسة (تجريبي)
يمكنك اختياريًا فهرسة سجلات الجلسات وإظهارها عبرmemory_search.
هذا خلف راية تجريبية.
- فهرسة الجلسات اختيارية (موقوفة افتراضيًا).
- تُزال ارتدادات تحديثات الجلسة وتُفهرس بشكل غير متزامن بمجرد تجاوز عتبات الدلتا (بأفضل جهد).
- لا يحجب
memory_searchأبدًا بانتظار الفهرسة؛ قد تكون النتائج قديمة قليلًا حتى تكتمل المزامنة الخلفية. - ما تزال النتائج تتضمن مقاطع فقط؛ ويبقى
memory_getمحدودًا بملفات الذاكرة. - فهرسة الجلسات معزولة لكل وكيل (لا تُفهرس إلا سجلات جلسات ذلك الوكيل).
- تعيش سجلات الجلسات على القرص (
~/.openclaw/agents/<agentId>/sessions/*.jsonl). يمكن لأي عملية/مستخدم لديه وصول لنظام الملفات قراءتها، لذا اعتبر الوصول للقرص حدّ الثقة. لعزل أشد، شغّل الوكلاء تحت مستخدمي نظام تشغيل أو مضيفين منفصلين.
تسريع المتجهات في SQLite (sqlite-vec)
عندما يتوفر امتداد sqlite-vec، يخزّن OpenClaw التضمينات في جدول افتراضي لـ SQLite (vec0) ويجري استعلامات مسافة المتجه
داخل قاعدة البيانات. يحافظ ذلك على سرعة البحث دون تحميل كل تضمين إلى JS.
التهيئة (اختياري):
- الافتراضي لـ
enabledهو true؛ عند التعطيل يعود البحث إلى تشابه جيب التمام داخل العملية على التضمينات المخزّنة. - إذا كان امتداد sqlite-vec مفقودًا أو فشل تحميله، يسجّل OpenClaw الخطأ ويتابع مع الرجوع الاحتياطي لـ JS (من دون جدول متجه).
- يتجاوز
extensionPathمسار sqlite-vec المضمّن (مفيد للبناءات المخصّصة أو مواقع التثبيت غير القياسية).
التنزيل التلقائي للتضمين المحلي
- نموذج التضمين المحلي الافتراضي:
hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf(~0.6 غيغابايت). - عند
memorySearch.provider = "local"، يحلّnode-llama-cppإلىmodelPath؛ إذا كان GGUF مفقودًا فإنه يُنزَّل تلقائيًا إلى الذاكرة المؤقتة (أوlocal.modelCacheDirإذا عُيّن)، ثم يُحمَّل. تُستأنف التنزيلات عند إعادة المحاولة. - متطلب البناء الأصلي: شغّل
pnpm approve-builds، اخترnode-llama-cpp، ثمpnpm rebuild node-llama-cpp. - الرجوع الاحتياطي: إذا فشل الإعداد المحلي وكانت
memorySearch.fallback = "openai"، ننتقل تلقائيًا إلى التضمينات البعيدة (openai/text-embedding-3-smallما لم يُتجاوز) ونسجّل السبب.
مثال نقطة نهاية مخصّصة متوافقة مع OpenAI
- لـ
remote.*أولوية علىmodels.providers.openai.*. - تندمج
remote.headersمع ترويسات OpenAI؛ ويتغلب البعيد عند تعارض المفاتيح. احذفremote.headersلاستخدام افتراضيات OpenAI.