الانتقال إلى المحتوى الرئيسي

إدارة الجلسات والضغط (شرح متعمق)

يوضح هذا المستند كيف يدير OpenClaw الجلسات من البداية إلى النهاية:
  • توجيه الجلسات (كيف تُطابَق الرسائل الواردة مع sessionKey)
  • مخزن الجلسات (sessions.json) وما الذي يتتبعه
  • استمرار حفظ النصوص التفريغية (*.jsonl) وبنيته
  • نظافة النصوص التفريغية (إصلاحات خاصة بموفر الخدمة قبل التشغيل)
  • حدود السياق (نافذة السياق مقابل الرموز المتتبعة)
  • الضغط (الضغط اليدوي + الضغط التلقائي) وأين يمكنك ربط أعمال ما قبل الضغط
  • الصيانة الصامتة (مثلًا عمليات كتابة الذاكرة التي يجب ألا تنتج مخرجات مرئية للمستخدم)
إذا كنت تريد نظرة عامة أعلى مستوى أولًا، فابدأ من:

مصدر الحقيقة: Gateway

تم تصميم OpenClaw حول عملية Gateway واحدة تملك حالة الجلسة.
  • يجب على واجهات المستخدم (تطبيق macOS، واجهة التحكم على الويب، TUI) الاستعلام من Gateway عن قوائم الجلسات وعدد الرموز.
  • في الوضع البعيد، تكون ملفات الجلسات على المضيف البعيد؛ لذا فإن “التحقق من ملفات Mac المحلية” لن يعكس ما يستخدمه Gateway.

طبقتا الاستمرار

يحفظ OpenClaw الجلسات في طبقتين:
  1. مخزن الجلسات (sessions.json)
    • خريطة مفتاح/قيمة: sessionKey -> SessionEntry
    • صغير، قابل للتغيير، وآمن للتحرير (أو حذف الإدخالات)
    • يتتبع بيانات تعريف الجلسة (معرّف الجلسة الحالي، آخر نشاط، المفاتيح التبديلية، عدادات الرموز، وغير ذلك)
  2. النص التفريغي (<sessionId>.jsonl)
    • نص تفريغي بإضافة فقط مع بنية شجرية (تحتوي الإدخالات على id + parentId)
    • يخزن المحادثة الفعلية + استدعاءات الأدوات + ملخصات الضغط
    • يُستخدم لإعادة بناء سياق النموذج للأدوار المستقبلية

المواقع على القرص

لكل وكيل، على مضيف Gateway:
  • المخزن: ~/.openclaw/agents/<agentId>/sessions/sessions.json
  • النصوص التفريغية: ~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl
    • جلسات موضوعات Telegram: .../<sessionId>-topic-<threadId>.jsonl
يحل OpenClaw هذه عبر src/config/sessions.ts.

صيانة المخزن وعناصر التحكم في القرص

تتضمن استمرارية الجلسات عناصر تحكم صيانة تلقائية (session.maintenance) لملف sessions.json وملفات النصوص التفريغية:
  • mode: ‏warn (الافتراضي) أو enforce
  • pruneAfter: حد عمر الإدخالات القديمة للحذف (الافتراضي 30d)
  • maxEntries: حد أقصى للإدخالات في sessions.json (الافتراضي 500)
  • rotateBytes: تدوير sessions.json عند زيادة حجمه (الافتراضي 10mb)
  • resetArchiveRetention: مدة الاحتفاظ بأرشيفات النصوص التفريغية *.reset.<timestamp> (الافتراضي: نفسها مثل pruneAfter؛ وتعطيل التنظيف باستخدام false)
  • maxDiskBytes: ميزانية اختيارية لدليل الجلسات على القرص
  • highWaterBytes: الهدف الاختياري بعد التنظيف (الافتراضي 80% من maxDiskBytes)
ترتيب التنفيذ لتنظيف ميزانية القرص (mode: "enforce"):
  1. إزالة أقدم ملفات النصوص التفريغية المؤرشفة أو اليتيمة أولًا.
  2. إذا ظل الاستخدام أعلى من الهدف، تُزال أقدم إدخالات الجلسات وملفات النصوص التفريغية الخاصة بها.
  3. الاستمرار حتى يصبح الاستخدام عند highWaterBytes أو أقل.
في mode: "warn"، يبلّغ OpenClaw عن عمليات الإزالة المحتملة لكنه لا يغيّر المخزن أو الملفات. شغّل الصيانة عند الطلب:
openclaw sessions cleanup --dry-run
openclaw sessions cleanup --enforce

جلسات Cron وسجلات التشغيل

تنشئ تشغيلات Cron المعزولة أيضًا إدخالات جلسات/نصوص تفريغية، ولها عناصر تحكم احتفاظ مخصصة:
  • cron.sessionRetention (الافتراضي 24h) يحذف جلسات تشغيل Cron المعزولة القديمة من مخزن الجلسات (استخدم false للتعطيل).
  • cron.runLog.maxBytes + cron.runLog.keepLines يختصران ملفات ~/.openclaw/cron/runs/<jobId>.jsonl (الافتراضيات: 2_000_000 بايت و2000 سطر).

مفاتيح الجلسات (sessionKey)

يحدّد sessionKey أي حاوية محادثة أنت فيها (التوجيه + العزل). الأنماط الشائعة:
  • الدردشة الرئيسية/المباشرة (لكل وكيل): agent:<agentId>:<mainKey> (الافتراضي main)
  • المجموعة: agent:<agentId>:<channel>:group:<id>
  • الغرفة/القناة (Discord/Slack): ‏agent:<agentId>:<channel>:channel:<id> أو ...:room:<id>
  • Cron: ‏cron:<job.id>
  • Webhook: ‏hook:<uuid> (ما لم يُتجاوز)
القواعد القياسية موثقة في /concepts/session.

معرّفات الجلسات (sessionId)

يشير كل sessionKey إلى sessionId حالي (ملف النص التفريغي الذي يواصل المحادثة). قواعد عامة:
  • إعادة الضبط (/new, /reset) تنشئ sessionId جديدًا لذلك sessionKey.
  • إعادة الضبط اليومية (الافتراضي الساعة 4:00 صباحًا بالتوقيت المحلي على مضيف gateway) تنشئ sessionId جديدًا عند الرسالة التالية بعد حد إعادة الضبط.
  • انتهاء المهلة بسبب الخمول (session.reset.idleMinutes أو session.idleMinutes القديم) ينشئ sessionId جديدًا عندما تصل رسالة بعد نافذة الخمول. عند ضبط اليومي + الخمول معًا، يُطبّق ما ينتهي أولًا.
  • حارس تشعّب الأصل للخيط (session.parentForkMaxTokens, الافتراضي 100000) يتجاوز تشعيب النص التفريغي الأصل عندما تكون جلسة الأصل كبيرة جدًا بالفعل؛ ويبدأ الخيط الجديد من جديد. اضبطه إلى 0 لتعطيله.
تفصيل تنفيذي: يتم اتخاذ القرار في initSessionState() داخل src/auto-reply/reply/session.ts.

مخطط مخزن الجلسات (sessions.json)

نوع قيمة المخزن هو SessionEntry في src/config/sessions.ts. الحقول الأساسية (ليست شاملة):
  • sessionId: معرّف النص التفريغي الحالي (يُشتق اسم الملف من هذا ما لم يتم ضبط sessionFile)
  • updatedAt: الطابع الزمني لآخر نشاط
  • sessionFile: تجاوز اختياري صريح لمسار النص التفريغي
  • chatType: ‏direct | group | room (يساعد واجهات المستخدم وسياسة الإرسال)
  • provider, subject, room, space, displayName: بيانات تعريف لتسمية المجموعات/القنوات
  • المفاتيح التبديلية:
    • thinkingLevel, verboseLevel, reasoningLevel, elevatedLevel
    • sendPolicy (تجاوز خاص بكل جلسة)
  • اختيار النموذج:
    • providerOverride, modelOverride, authProfileOverride
  • عدادات الرموز (على أفضل جهد / وتعتمد على موفر الخدمة):
    • inputTokens, outputTokens, totalTokens, contextTokens
  • compactionCount: عدد مرات اكتمال الضغط التلقائي لهذا مفتاح الجلسة
  • memoryFlushAt: الطابع الزمني لآخر تفريغ ذاكرة قبل الضغط
  • memoryFlushCompactionCount: عدد مرات الضغط وقت آخر تشغيل للتفريغ
المخزن آمن للتحرير، لكن Gateway هو المرجع: قد يعيد كتابة الإدخالات أو يعيد ترطيبها أثناء تشغيل الجلسات.

بنية النص التفريغي (*.jsonl)

تُدار النصوص التفريغية بواسطة SessionManager من @mariozechner/pi-coding-agent. الملف بصيغة JSONL:
  • السطر الأول: رأس الجلسة (type: "session"، ويشمل id, cwd, timestamp, وparentSession اختياريًا)
  • ثم: إدخالات الجلسة مع id + parentId (شجرة)
أنواع الإدخالات البارزة:
  • message: رسائل المستخدم/المساعد/نتيجة الأداة
  • custom_message: رسائل تحقنها الإضافات وتدخل في سياق النموذج (ويمكن إخفاؤها من واجهة المستخدم)
  • custom: حالة إضافات لا تدخل في سياق النموذج
  • compaction: ملخص ضغط محفوظ مع firstKeptEntryId وtokensBefore
  • branch_summary: ملخص محفوظ عند التنقل في فرع من الشجرة
لا يقوم OpenClaw عمدًا “بإصلاح” النصوص التفريغية؛ بل يستخدم Gateway SessionManager لقراءتها وكتابتها.

نوافذ السياق مقابل الرموز المتتبعة

هناك مفهومان مختلفان مهمان:
  1. نافذة سياق النموذج: حد أقصى صارم لكل نموذج (الرموز المرئية للنموذج)
  2. عدادات مخزن الجلسات: إحصاءات متدرجة تُكتب في sessions.json (تُستخدم في /status ولوحات المعلومات)
إذا كنت تضبط الحدود:
  • تأتي نافذة السياق من فهرس النماذج (ويمكن تجاوزها عبر الإعدادات).
  • القيمة contextTokens في المخزن هي قيمة تقديرية/للتقارير أثناء التشغيل؛ فلا تتعامل معها على أنها ضمان صارم.
للمزيد، راجع /token-use.

الضغط: ما هو

يلخّص الضغط المحادثة الأقدم في إدخال compaction محفوظ داخل النص التفريغي ويُبقي الرسائل الحديثة كما هي. بعد الضغط، ترى الأدوار المستقبلية:
  • ملخص الضغط
  • الرسائل بعد firstKeptEntryId
الضغط دائم (بخلاف تقليم الجلسات). راجع /concepts/session-pruning.

حدود مقاطع الضغط وإقران الأدوات

عندما يقسم OpenClaw نصًا تفريغيًا طويلًا إلى مقاطع ضغط، فإنه يُبقي استدعاءات أدوات المساعد مقترنة بإدخالات toolResult المطابقة لها.
  • إذا وقع تقسيم حصة الرموز بين استدعاء أداة ونتيجتها، فإن OpenClaw ينقل الحد إلى رسالة استدعاء أداة المساعد بدلًا من فصل الزوج.
  • إذا كانت كتلة نتائج أدوات في النهاية ستدفع المقطع فوق الهدف لولا ذلك، فإن OpenClaw يحتفظ بكتلة الأدوات المعلّقة هذه ويبقي الذيل غير الملخّص كما هو.
  • كتل استدعاءات الأدوات الملغاة/التي تحتوي على أخطاء لا تُبقي تقسيمًا معلّقًا مفتوحًا.

متى يحدث الضغط التلقائي (بيئة تشغيل Pi)

في وكيل Pi المضمّن، يُفعّل الضغط التلقائي في حالتين:
  1. التعافي من التجاوز: يعيد النموذج خطأ تجاوز سياق (request_too_large, context length exceeded, input exceeds the maximum number of tokens, input token count exceeds the maximum number of input tokens, input is too long for the model, ollama error: context length exceeded، ومتغيرات مشابهة بصياغة موفر الخدمة) ← ضغط ← إعادة المحاولة.
  2. الصيانة عند العتبة: بعد دور ناجح، عندما:
contextTokens > contextWindow - reserveTokens حيث:
  • contextWindow هي نافذة سياق النموذج
  • reserveTokens هي مساحة احتياطية مخصصة للمطالبات + مخرجات النموذج التالية
هذه دلالات بيئة تشغيل Pi (يستهلك OpenClaw الأحداث، لكن Pi هو من يقرر متى يضغط).

إعدادات الضغط (reserveTokens, keepRecentTokens)

توجد إعدادات الضغط في Pi داخل إعدادات Pi:
{
  compaction: {
    enabled: true,
    reserveTokens: 16384,
    keepRecentTokens: 20000,
  },
}
يفرض OpenClaw أيضًا حد أمان أدنى للتشغيلات المضمّنة:
  • إذا كانت compaction.reserveTokens < reserveTokensFloor، يرفعها OpenClaw.
  • الحد الأدنى الافتراضي هو 20000 رمز.
  • اضبط agents.defaults.compaction.reserveTokensFloor: 0 لتعطيل الحد الأدنى.
  • إذا كانت أعلى بالفعل، يتركها OpenClaw كما هي.
السبب: ترك مساحة احتياطية كافية لأعمال “الصيانة” متعددة الأدوار (مثل عمليات كتابة الذاكرة) قبل أن يصبح الضغط حتميًا. التنفيذ: ensurePiCompactionReserveTokens() في src/agents/pi-settings.ts (ويُستدعى من src/agents/pi-embedded-runner.ts).

الأسطح المرئية للمستخدم

يمكنك ملاحظة الضغط وحالة الجلسة عبر:
  • /status (في أي جلسة دردشة)
  • openclaw status (CLI)
  • openclaw sessions / sessions --json
  • الوضع المطوّل: 🧹 Auto-compaction complete + عدد مرات الضغط

الصيانة الصامتة (NO_REPLY)

يدعم OpenClaw الأدوار “الصامتة” للمهام الخلفية حيث يجب ألا يرى المستخدم مخرجات وسيطة. الاصطلاح:
  • يبدأ المساعد مخرجاته بالرمز الصامت الدقيق NO_REPLY / no_reply للإشارة إلى “لا تُرسل ردًا إلى المستخدم”.
  • يزيل OpenClaw هذا أو يمنعه في طبقة التسليم.
  • يكون منع الرمز الصامت الدقيق غير حساس لحالة الأحرف، لذا فإن NO_REPLY و no_reply كلاهما يُحسبان عندما تكون الحمولة بالكامل هي الرمز الصامت فقط.
  • هذا مخصص فقط للأدوار الخلفية الحقيقية/التي لا تتطلب تسليمًا؛ وليس اختصارًا لطلبات المستخدم العادية القابلة للتنفيذ.
اعتبارًا من 2026.1.10، يمنع OpenClaw أيضًا البث المتدفق للمسودات/الكتابة عندما يبدأ مقطع جزئي بـ NO_REPLY، بحيث لا تُسرّب العمليات الصامتة مخرجات جزئية أثناء الدور.

”تفريغ الذاكرة” قبل الضغط (مطبّق)

الهدف: قبل حدوث الضغط التلقائي، تشغيل دور وكيل صامت يكتب حالة دائمة إلى القرص (مثل memory/YYYY-MM-DD.md في مساحة عمل الوكيل) حتى لا يمحو الضغط السياق الحرج. يستخدم OpenClaw نهج التفريغ قبل العتبة:
  1. مراقبة استخدام سياق الجلسة.
  2. عندما يتجاوز “عتبة مرنة” (أقل من عتبة الضغط في Pi)، شغّل توجيهًا صامتًا إلى الوكيل يقول “اكتب الذاكرة الآن”.
  3. استخدم الرمز الصامت الدقيق NO_REPLY / no_reply حتى لا يرى المستخدم أي شيء.
الإعدادات (agents.defaults.compaction.memoryFlush):
  • enabled (الافتراضي: true)
  • softThresholdTokens (الافتراضي: 4000)
  • prompt (رسالة المستخدم لدور التفريغ)
  • systemPrompt (مطالبة نظام إضافية تُلحق بدور التفريغ)
ملاحظات:
  • تتضمن المطالبة الافتراضية/مطالبة النظام تلميح NO_REPLY لمنع التسليم.
  • يُشغَّل التفريغ مرة واحدة لكل دورة ضغط (ويُتتبَّع ذلك في sessions.json).
  • يعمل التفريغ فقط لجلسات Pi المضمّنة (تتجاوزها الواجهات الخلفية لـ CLI).
  • يُتجاوز التفريغ عندما تكون مساحة عمل الجلسة للقراءة فقط (workspaceAccess: "ro" أو "none").
  • راجع Memory لتخطيط ملفات مساحة العمل وأنماط الكتابة.
يعرّض Pi أيضًا hook باسم session_before_compact في API الخاصة بالإضافات، لكن منطق التفريغ في OpenClaw يوجد اليوم في جهة Gateway.

قائمة التحقق لاستكشاف الأخطاء وإصلاحها

  • مفتاح الجلسة غير صحيح؟ ابدأ من /concepts/session وأكّد sessionKey في /status.
  • عدم تطابق بين المخزن والنص التفريغي؟ أكّد مضيف Gateway ومسار المخزن من openclaw status.
  • ضغط متكرر بشكل مزعج؟ تحقّق من:
    • نافذة سياق النموذج (صغيرة جدًا)
    • إعدادات الضغط (قد يؤدي ارتفاع reserveTokens جدًا بالنسبة إلى نافذة النموذج إلى ضغط أبكر)
    • تضخم نتائج الأدوات: فعّل/اضبط تقليم الجلسات
  • تسرّب الأدوار الصامتة؟ أكّد أن الرد يبدأ بـ NO_REPLY (رمز دقيق غير حساس لحالة الأحرف) وأنك تستخدم إصدارًا يتضمن إصلاح منع البث المتدفق.