الأجزاء الداخلية للضغط

غوص عميق في إدارة الجلسات

يشرح هذا المستند كيفية إدارة OpenClaw للجلسات من البداية إلى النهاية:

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

إذا كنت تريد نظرة عامة عالية المستوى أولاً، ابدأ بـ:


مصدر الحقيقة: البوابة

تم تصميم OpenClaw حول عملية بوابة واحدة تمتلك حالة الجلسة.

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

طبقتا استمرارية

يحافظ OpenClaw على الجلسات في طبقتين:

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

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

لكل وكيل، على مضيف البوابة:

  • المستودع: ~/.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 صباحًا بالتوقيت المحلي على مضيف البوابة) ينشئ 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: عدد الضغط عند تشغيل آخر تفريغ

المستودع آمن للتحرير، لكن البوابة هي السلطة: قد تعيد كتابة أو إعادة تعبئة الإدخالات أثناء تشغيل الجلسات.


هيكل النص (*.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 عمدًا "بإصلاح" النصوص؛ تستخدم البوابة SessionManager لقراءتها/كتابتها.


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

مفهومان مختلفان مهمان:

  1. نافذة سياق النموذج: حد صارم لكل نموذج (الرموز المرئية للنموذج)
  2. عدادات مستودع الجلسات: إحصائيات مستمرة مكتوبة في sessions.json (تُستخدم لـ /status ولوحات التحكم)

إذا كنت تضبط الحدود:

  • تأتي نافذة السياق من كتالوج النموذج (ويمكن تجاوزها عبر التكوين).
  • contextTokens في المستودع هو تقدير/قيمة إبلاغ وقت التشغيل؛ لا تعامله كضمان صارم.

لمزيد من المعلومات، انظر /token-use.


الضغط: ما هو

يُلخص الضغط المحادثة الأقدم في إدخال compaction مستمر في النص ويبقي الرسائل الحديثة سليمة. بعد الضغط، ترى الأدوار المستقبلية:

  • ملخص الضغط
  • الرسائل بعد firstKeptEntryId

الضغط مستمر (على عكس تقليم الجلسات). انظر /concepts/session-pruning.


متى يحدث الضغط الآلي (وقت تشغيل Pi)

في وكيل Pi المضمن، يتم تشغيل الضغط الآلي في حالتين:

  1. استعادة الفيضان: يُرجع النموذج خطأ فيضان السياق → ضغط → إعادة المحاولة.
  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 للإشارة إلى "عدم تسليم رد للمستخدم".
  • يقوم OpenClaw بإزالة/كبح هذا في طبقة التسليم.

اعتبارًا من 2026.1.10، يكبح OpenClaw أيضًا تدفق المسودة/الكتابة عندما تبدأ قطعة جزئية بـ NO_REPLY، حتى لا تتسرب المخرجات الجزئية أثناء الدور الصامت.


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

الهدف: قبل حدوث الضغط الآلي، قم بتشغيل دور وكيلي صامت يكتب حالة دائمة على القرص (مثل memory/YYYY-MM-DD.md في مساحة عمل الوكيل) حتى لا يتمكن الضغط من محو السياق الحرج. يستخدم OpenClaw نهج التفريغ قبل العتبة:

  1. مراقبة استخدام سياق الجلسة.
  2. عندما يتجاوز "عتبة ناعمة" (أقل من عتبة ضغط Pi)، قم بتشغيل توجيه صامت "اكتب الذاكرة الآن" للوكيل.
  3. استخدم NO_REPLY حتى لا يرى المستخدم شيئًا.

التكوين (agents.defaults.compaction.memoryFlush):

  • enabled (الافتراضي: true)
  • softThresholdTokens (الافتراضي: 4000)
  • prompt (رسالة المستخدم لدور التفريغ)
  • systemPrompt (طلب نظام إضافي مرفق لدور التفريغ)

ملاحظات:

  • يتضمن الطلب الافتراضي/طلب النظام الافتراضي تلميح NO_REPLY لكبح التسليم.
  • يعمل التفريغ مرة واحدة لكل دورة ضغط (يتم تعقبه في sessions.json).
  • يعمل التفريغ فقط لجلسات Pi المضمنة (تتخطى واجهات سطر الأوامر الخلفية ذلك).
  • يتم تخطي التفريغ عندما تكون مساحة عمل الجلسة للقراءة فقط (workspaceAccess: "ro" أو "none").
  • انظر الذاكرة لتخطيط ملف مساحة العمل وأنماط الكتابة.

يعرض Pi أيضًا خطاف session_before_compact في واجهة برمجة تطبيقات الامتداد، لكن منطق التفريغ في OpenClaw يعيش على جانب البوابة اليوم.


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

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

Node.jsالإعداد