الذاكرة
ذاكرة OpenClaw هي ملفات Markdown عادية في مساحة عمل الوكيل. الملفات هي مصدر الحقيقة؛ النموذج "يتذكر" فقط ما يتم كتابته على القرص. يتم توفير أدوات البحث في الذاكرة بواسطة إضافة الذاكرة النشطة (الافتراضي: memory-core). يمكن تعطيل إضافات الذاكرة باستخدام plugins.slots.memory = "none".
ملفات الذاكرة (Markdown)
يستخدم تخطيط مساحة العمل الافتراضي طبقتين للذاكرة:
memory/YYYY-MM-DD.md- سجل يومي (إلحاق فقط).
- يُقرأ اليوم + الأمس عند بدء الجلسة.
MEMORY.md(اختياري)- ذاكرة طويلة المدى مُنتقاة.
- يتم تحميلها فقط في الجلسة الرئيسية الخاصة (أبدًا في سياقات المجموعة).
تعيش هذه الملفات تحت مساحة العمل (agents.defaults.workspace، الافتراضي ~/.openclaw/workspace). راجع مساحة عمل الوكيل للحصول على التخطيط الكامل.
أدوات الذاكرة
يقدم OpenClaw أداتين موجهتين للوكيل لهذه الملفات Markdown:
memory_search— استرجاع دلالي عبر المقاطع المفهرسة.memory_get— قراءة مستهدفة لملف Markdown محدد/نطاق أسطر.
أداة memory_get الآن تتراجع بلطف عندما لا يوجد الملف (على سبيل المثال، السجل اليومي قبل الكتابة الأولى). كل من المدير المدمج وخلفية QMD يُرجعان { text: "", path } بدلاً من إلقاء خطأ ENOENT، حتى يتمكن الوكلاء من التعامل مع "لم يتم تسجيل شيء بعد" ومتابعة سير عملهم دون تغليف استدعاء الأداة في منطق try/catch.
متى تكتب في الذاكرة
- القرارات، التفضيلات، والحقائق الدائمة تذهب إلى
MEMORY.md. - الملاحظات اليومية والسياق الجاري يذهبان إلى
memory/YYYY-MM-DD.md. - إذا قال شخص ما "تذكر هذا"، اكتبه (لا تحتفظ به في ذاكرة الوصول العشوائي).
- هذه المنطقة لا تزال تتطور. من المفيد تذكير النموذج بتخزين الذكريات؛ سيعرف ما يجب فعله.
- إذا أردت أن يثبت شيء ما، اطلب من البوت كتابته في الذاكرة.
إفراغ الذاكرة التلقائي (تنبيه ما قبل الضغط)
عندما تكون الجلسة قريبة من الضغط التلقائي، يُطلق OpenClaw دورة وكيلية صامتة تذكر النموذج بكتابة ذاكرة دائمة قبل ضغط السياق. المطالبات الافتراضية تنص صراحةً على أن النموذج قد يرد، ولكن عادةً ما يكون NO_REPLY هو الرد الصحيح حتى لا يرى المستخدم هذه الدورة أبدًا. يتم التحكم في هذا بواسطة agents.defaults.compaction.memoryFlush:
{
agents: {
defaults: {
compaction: {
reserveTokensFloor: 20000,
memoryFlush: {
enabled: true,
softThresholdTokens: 4000,
systemPrompt: "Session nearing compaction. Store durable memories now.",
prompt: "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store.",
},
},
},
},
}
التفاصيل:
- عتبة لينة: يتم تشغيل الإفراغ عندما يتجاوز تقدير رموز الجلسة
contextWindow - reserveTokensFloor - softThresholdTokens. - صامت افتراضيًا: تتضمن المطالبات
NO_REPLYحتى لا يتم تسليم أي شيء. - مطلبين: مطلب مستخدم بالإضافة إلى مطلب نظام يلحق التذكير.
- إفراغ واحد لكل دورة ضغط (يتم تتبعه في
sessions.json). - يجب أن تكون مساحة العمل قابلة للكتابة: إذا كانت الجلسة تعمل في بيئة معزولة مع
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.mistralإذا كان يمكن حل مفتاح Mistral.- وإلا يظل بحث الذاكرة معطلاً حتى يتم تكوينه.
- الوضع المحلي يستخدم node-llama-cpp وقد يتطلب
pnpm approve-builds. - يستخدم sqlite-vec (عند التوفر) لتسريع البحث المتجهي داخل SQLite.
memorySearch.provider = "ollama"مدعوم أيضًا للتضمينات المحلية/المستضافة ذاتيًا لـ Ollama (/api/embeddings)، لكنه لا يتم اختياره تلقائيًا.
تتطلب التضمينات عن بعد بالضرورة مفتاح API لمزود التضمين. يحل OpenClaw المفاتيح من ملفات تعريف المصادقة، أو models.providers.*.apiKey، أو متغيرات البيئة. لا يغطي Codex OAuth سوى الدردشة/الإكمالات ولا يلبي التضمينات لبحث الذاكرة. بالنسبة لـ Gemini، استخدم GEMINI_API_KEY أو models.providers.google.apiKey. بالنسبة لـ Voyage، استخدم VOYAGE_API_KEY أو models.providers.voyage.apiKey. بالنسبة لـ Mistral، استخدم MISTRAL_API_KEY أو models.providers.mistral.apiKey. عادةً لا يتطلب Ollama مفتاح API حقيقي (مكاني مثل OLLAMA_API_KEY=ollama-local يكفي عند الحاجة بواسطة سياسة محلية). عند استخدام نقطة نهاية متوافقة مع OpenAI مخصصة، عيّن memorySearch.remote.apiKey (واختياريًا memorySearch.remote.headers).
خلفية QMD (تجريبية)
عيّن memory.backend = "qmd" لاستبدال المفهرس المدمج SQLite بـ QMD: مساعد بحث محلي أول يجمع بين BM25 + المتجهات + إعادة التصنيف. تظل Markdown مصدر الحقيقة؛ يستدعي OpenClaw QMD للاسترجاع. النقاط الرئيسية: المتطلبات المسبقة
- معطل افتراضيًا. اشترك لكل تكوين (
memory.backend = "qmd"). - قم بتثبيت واجهة سطر أوامر QMD بشكل منفصل (
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 دقائق). - تقوم البوابة الآن بتهيئة مدير QMD عند بدء التشغيل، لذا يتم تفعيل مؤقتات التحديث الدورية حتى قبل أول استدعاء لـ
memory_search. - تحديث بدء التشغيل يعمل الآن في الخلفية افتراضيًا حتى لا يتم حظر بدء الدردشة؛ عيّن
memory.qmd.update.waitForBootSync = trueللحفاظ على سلوك الحظر السابق. - تعمل عمليات البحث عبر
memory.qmd.searchMode(الافتراضيqmd search --json؛ يدعم أيضًاvsearchوquery). إذا رفض الوضع المحدد الأعلام في بناء 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 STATE_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}" export XDG_CONFIG_HOME="$STATE_DIR/agents/main/qmd/xdg-config" export XDG_CACHE_HOME="$STATE_DIR/agents/main/qmd/xdg-cache" # (اختياري) فرض تحديث الفهرس + التضمينات qmd update qmd embed # تسخين / تشغيل تنزيلات النموذج لأول مرة qmd query "test" -c memory-root --json >/dev/null 2>&1
-
سطح التكوين (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. الافتراضي هو المباشر فقط (denyللجميع،allowللمحادثات المباشرة)؛ قم بتخفيفه لعرض نتائج QMD في المجموعات/القنوات.match.keyPrefixيطابق مفتاح الجلسة المُطبع (أحرف صغيرة، مع إزالة أي بادئةagent:<id>:). مثال:discord:channel:.match.rawKeyPrefixيطابق مفتاح الجلسة الخام (أحرف صغيرة)، بما في ذلكagent:<id>:. مثال:agent:main:discord:.- قديم: لا يزال التعامل مع
match.keyPrefix: "agent:..."كبادئة مفتاح خام، لكن يُفضلrawKeyPrefixللوضوح.
- عندما يرفض
scopeبحثًا، يسجل OpenClaw تحذيرًا معchannel/chatTypeالمُشتق حتى تكون النتائج الفارغة أسهل في التصحيح. - تظهر المقاطع المستخرجة خارج مساحة العمل كـ
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الآن تذييلالمصدر: <path#line>عندما يكونmemory.citationsهوauto/on؛ عيّنmemory.citations = "off"للحفاظ على بيانات المسار داخلية (لا يزال الوكيل يتلقى المسار لـmemory_get، لكن نص المقطع يحذف التذييل ويحذر مطلب النظام الوكيل من الاستشهاد به).
مثال
memory: {
backend: "qmd",
citations: "auto",
qmd: {
includeDefaultMemory: true,
update: { interval: "5m", debounceMs: 15000 },
limits: { maxResults: 6, timeoutMs: 4000 },
scope: {
default: "deny",
rules: [
{ action: "allow", match: { chatType: "direct" } },
// بادئة مفتاح جلسة مُطبع (يزيل `agent:<id>:`).
{ action: "deny", match: { keyPrefix: "discord:channel:" } },
// بادئة مفتاح جلسة خام (تتضمن `agent:<id>:`).
{ action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
]
},
paths: [
{ name: "docs", path: "~/notes", pattern: "**/*.md" }
]
}
}
الاستشهادات والتراجع
memory.citationsينطبق بغض النظر عن الخلفية (auto/on/off).- عند تشغيل
qmd، نضع علامةstatus().backend = "qmd"حتى تظهر التشخيصات أي محرك قدم النتائج. إذا خرجت العملية الفرعية QMD أو تعذر تحليل إخراج JSON، يسجل مدير البحث تحذيرًا ويعيد مزود الخلفية (التضمينات المدمجة لـ Markdown) حتى يتعافى QMD.
مسارات ذاكرة إضافية
إذا كنت تريد فهرسة ملفات Markdown خارج تخطيط مساحة العمل الافتراضي، أضف مسارات صريحة:
agents: {
defaults: {
memorySearch: {
extraPaths: ["../team-docs", "/srv/shared-notes/overview.md"]
}
}
}
ملاحظات:
- يمكن أن تكون المسارات مطلقة أو نسبية لمساحة العمل.
- يتم فحص الأدلة بشكل متكرر للعثور على ملفات
.md. - يتم فهرسة ملفات Markdown فقط.
- يتم تجاهل الروابط الرمزية (ملفات أو أدلة).
تضمينات Gemini (أصلية)
عيّن المزود إلى gemini لاستخدام واجهة برمجة تطبيقات تضمينات Gemini مباشرة:
agents: {
defaults: {
memorySearch: {
provider: "gemini",
model: "gemini-embedding-001",
remote: {
apiKey: "YOUR_GEMINI_API_KEY"
}
}
}
}
ملاحظات:
remote.baseUrlاختياري (الافتراضي هو عنوان URL الأساسي لواجهة برمجة تطبيقات Gemini).remote.headersيسمح لك بإضافة رؤوس إضافية إذا لزم الأمر.- النموذج الافتراضي:
gemini-embedding-001.
إذا كنت تريد استخدام نقطة نهاية مخصصة متوافقة مع OpenAI (OpenRouter، vLLM، أو وكيل)، يمكنك استخدام تكوين remote مع مزود OpenAI:
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
remote: {
baseUrl: "https://api.example.com/v1/",
apiKey: "YOUR_OPENAI_COMPAT_API_KEY",
headers: { "X-Custom-Header": "value" }
}
}
}
}
إذا كنت لا تريد تعيين مفتاح API، استخدم memorySearch.provider = "local" أو عيّن memorySearch.fallback = "none". التراجعات:
- يمكن أن يكون
memorySearch.fallbackهوopenai، أوgemini، أوvoyage، أوmistral، أوollama، أوlocal، أوnone. - يتم استخدام مزود التراجع فقط عندما يفشل مزود التضمين الأساسي.
الفهرسة الدفعية (OpenAI + Gemini + Voyage):
- معطل افتراضيًا. عيّن
agents.defaults.memorySearch.remote.batch.enabled = trueلتمكين فهرسة المجموعات الكبيرة (OpenAI، Gemini، و Voyage). - السلوك الافتراضي ينتظر اكتمال الدفعة؛ اضبط
remote.batch.wait، وremote.batch.pollIntervalMs، وremote.batch.timeoutMinutesإذا لزم الأمر. - عيّن
remote.batch.concurrencyللتحكم في عدد وظائف الدفعة التي نقدمها بالتوازي (الافتراضي: 2). - ينطبق وضع الدفعة عندما يكون
memorySearch.provider = "openai"أو"gemini"ويستخدم مفتاح API المقابل. - تستخدم وظائف الدفعة Gemini نقطة نهاية دفعة التضمينات غير المتزامنة وتتطلب توفر واجهة برمجة تطبيقات الدفعة Gemini.
لماذا تكون دفعة OpenAI سريعة + رخيصة:
- بالنسبة للعمليات الخلفية الكبيرة، تعتبر OpenAI عادةً أسرع خيار ندعمه لأنه يمكننا تقديم العديد من طلبات التضمين في وظيفة دفعة واحدة والسماح لـ OpenAI بمعالجتها بشكل غير متزامن.
- تقدم OpenAI تسعيرًا مخفضًا لأحمال عمل واجهة برمجة تطبيقات الدفعة، لذا تكون عمليات الفهرسة الكبيرة عادةً أرخص من إرسال نفس الطلبات بشكل متزامن.
- راجع وثائق واجهة برمجة تطبيقات الدفعة لـ OpenAI والتسعير للحصول على التفاصيل:
مثال التكوين:
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
fallback: "openai",
remote: {
batch: { enabled: true, concurrency: 2 }
},
sync: { watch: true }
}
}
}
الأدوات:
memory_search— يُرجع مقاطع مع ملف + نطاقات أسطر.memory_get— قراءة محتوى ملف الذاكرة حسب المسار.
الوضع المحلي:
- عيّن
agents.defaults.memorySearch.provider = "local". - وفر
agents.defaults.memorySearch.local.modelPath(GGUF أوhf:URI). - اختياري: عيّن
agents.defaults.memorySearch.fallback = "none"لتجنب التراجع عن بعد.
كيف تعمل أدوات الذاكرة
memory_searchيبحث دلاليًا في أجزاء Markdown (~400 رمز مستهدف، تداخل 80 رمز) منMEMORY.md+memory/**/*.md. يُرجع نص المقطع (محدود ~700 حرف)، مسار الملف، نطاق الأسطر، النتيجة، المزود/النموذج، وما إذا كنا قد تراجعنا من تضمينات محلية → عن بعد. لا يتم إرجاع حمولة الملف الكامل.memory_getيقرأ ملف Markdown محدد للذاكرة (نسبي لمساحة العمل)، اختياريًا من سطر بداية ولعدد N من الأسطر. يتم رفض المسارات خارجMEMORY.md/memory/.- يتم تمكين كلتا الأداتين فقط عندما يكون
memorySearch.enabledمحددًا كـ true للوكيل.
ما الذي يتم فهرسته (ومتى)
- نوع الملف: 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 (الرموز الدقيقة مثل المعرفات، متغيرات البيئة، رموز الكود)
إذا كان البحث النصي الكامل غير متاح على نظامك، يتراجع OpenClaw إلى البحث المتجهي فقط.
لماذا هجين؟
البحث المتجهي رائع في "هذا يعني نفس الشيء":
- "جهاز Mac Studio المضيف للبوابة" مقابل "الجهاز الذي يشغل البوابة"
- "إزالة الارتداد لتحديثات الملفات" مقابل "تجنب الفهرسة عند كل كتابة"
لكنه قد يكون ضعيفًا في الرموز الدقيقة عالية الإشارة:
- المعرفات (
a828e60,b3b9895a…) - رموز الكود (
memorySearch.query.hybrid) - سلاسل الأخطاء ("sqlite-vec غير متاح")
BM25 (نص كامل) هو العكس: قوي في الرموز الدقيقة، أضعف في إعادة الصياغة. البحث الهجين هو أرضية وسط عملية: استخدم إشارات الاسترجاع معًا حتى تحصل على نتائج جيدة لكل من استعلامات "اللغة الطبيعية" واستعلامات "إبرة في كومة قش".
كيف ندمج النتائج (التصميم الحالي)
رسم تنفيذي:
- استرجاع مجموعة مرشحة من الجانبين:
- المتجهي: أعلى
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، نحتفظ بالبحث المتجهي فقط (لا فشل قاسي).
هذا ليس "مثاليًا من حيث نظرية استرجاع المعلومات"، لكنه بسيط وسريع ويميل إلى تحسين الاستدعاء/الدقة على الملاحظات الحقيقية. إذا أردنا أن نصبح أكثر تطورًا لاحقًا، فإن الخطوات التالية الشائعة هي اندماج الرتبة المتبادلة (RRF) أو تطبيع النتيجة (الحد الأدنى/الأقصى أو z-score) قبل الخلط.
خطوة المعالجة اللاحقة
بعد دمج النتائج المتجهية والكلمات الرئيسية، تقوم مرحلتان اختياريتان من المعالجة اللاحقة بتنقية قائمة النتائج قبل أن تصل إلى الوكيل:
المتجهي + الكلمات الرئيسية → دمج مرجح → تضاؤل زمني → ترتيب → MMR → أفضل النتائج K
كلتا المرحلتين معطلتان افتراضيًا ويمكن تمكينهما بشكل مستقل.
إعادة التصنيف MMR (التنوع)
عندما يُرجع البحث الهجين نتائج، قد تحتوي مقاطع متعددة على محتوى متشابه أو متداخل. على سبيل المثال، قد يُرجع البحث عن "إعداد شبكة المنزل" خمسة مقاطع متطابقة تقريبًا من ملاحظات يومية مختلفة تذكر جميعها تكوين الموجه نفسه. تعيد MMR (الملاءمة الهامشية القصوى) ترتيب النتائج لتحقيق التوازن بين الملاءمة والتنوع، مما يضمن أن تغطي النتائج الأولى جوانب مختلفة من الاستعلام بدلاً من تكرار نفس المعلومات. كيف تعمل:
- يتم تسجيل النتائج حسب ملاءمتها الأصلية (النتيجة المرجحة للمتجه + BM25).
- تختار MMR النتائج بشكل تكراري لتعظيم:
λ × relevance − (1−λ) × max_similarity_to_selected. - يتم قياس التشابه بين النتائج باستخدام تشابه نص Jaccard على المحتوى المُجزأ.
تتحكم المعلمة lambda في المقايضة:
lambda = 1.0→ ملاءمة نقية (لا عقوبة تنوع)lambda = 0.0→ أقصى تنوع (يتجاهل الملاءمة)- الافتراضي:
0.7(متوازن، تحيز طفيف للملاءمة)
مثال — استعلام: "إعداد شبكة المنزل" بالنظر إلى ملفات الذاكرة هذه:
memory/2026-02-10.md → "تم تكوين موجه Omada، تعيين VLAN 10 لأجهزة إنترنت الأشياء"
memory/2026-02-08.md → "تم تكوين موجه Omada، نقل إنترنت الأشياء إلى VLAN 10"
memory/2026-02-05.md → "إعداد AdGuard DNS على 192.168.10.2"
memory/network.md → "الموجه: Omada ER605، AdGuard: 192.168.10.2، VLAN 10: إنترنت الأشياء"
بدون MMR — أفضل 3 نتائج:
1. memory/2026-02-10.md (النتيجة: 0.92) ← موجه + VLAN
2. memory/2026-02-08.md (النتيجة: 0.89) ← موجه + VLAN (شبه مكرر!)
3. memory/network.md (النتيجة: 0.85) ← وثيقة مرجعية
مع MMR (λ=0.7) — أفضل 3 نتائج:
1. memory/2026-02-10.md (النتيجة: 0.92) ← موجه + VLAN
2. memory/network.md (النتيجة: 0.85) ← وثيقة مرجعية (متنوعة!)
3. memory/2026-02-05.md (النتيجة: 0.78) ← AdGuard DNS (متنوعة!)
يسقط شبه المكرر من 8 فبراير، ويحصل الوكيل على ثلاث قطع مميزة من المعلومات. متى يتم التمكين: إذا لاحظت أن memory_search يُرجع مقاطع زائدة أو شبه مكررة، خاصة مع الملاحظات اليومية التي غالبًا ما تكرر معلومات مماثلة عبر الأيام.
التضاؤل الزمني (تعزيز الحداثة)
الوكلاء الذين لديهم ملاحظات يومية يتراكم لديهم مئات الملفات المؤرخة بمرور الوقت. بدون تضاؤل، يمكن لملاحظة جيدة الصياغة من ستة أشهر مضت أن تتفوق على تحديث الأمس لنفس الموضوع. يطبق التضاؤل الزمني مضاعفًا أسيًا على النتائج بناءً على عمر كل نتيجة، بحيث تحتل الذكريات الحديثة مرتبة أعلى بشكل طبيعي بينما تتلاشى القديمة:
decayedScore = score × e^(-λ × ageInDays)
حيث λ = ln(2) / halfLifeDays. مع نصف العمر الافتراضي البالغ 30 يومًا:
- ملاحظات اليوم: 100% من النتيجة الأصلية
- منذ 7 أيام: ~84%
- منذ 30 يومًا: 50%
- منذ 90 يومًا: 12.5%
- منذ 180 يومًا: ~1.6%
لا يتم تضاؤل الملفات الدائمة أبدًا:
MEMORY.md(ملف الذاكرة الجذر)- الملفات غير المؤرخة في
memory/(مثلmemory/projects.md,memory/network.md) - تحتوي هذه على معلومات مرجعية دائمة يجب أن تحتل مرتبة طبيعية دائمًا.
الملفات اليومية المؤرخة (memory/YYYY-MM-DD.md) تستخدم التاريخ المستخرج من اسم الملف. تعتمد المصادر الأخرى (مثل نصوص الجلسات) على وقت تعديل الملف (mtime). مثال — استعلام: "ما هو جدول عمل رود؟" بالنظر إلى ملفات الذاكرة هذه (اليوم هو 10 فبراير):
memory/2025-09-15.md → "رود يعمل من الاثنين إلى الجمعة،