Внутреннее устройство компрессии

Подробный разбор управления сессиями

В этом документе объясняется, как OpenClaw управляет сессиями от начала до конца:

  • Маршрутизация сессий (как входящие сообщения сопоставляются с sessionKey)
  • Хранилище сессий (sessions.json) и что оно отслеживает
  • Сохранение транскрипта (*.jsonl) и его структура
  • Гигиена транскрипта (исправления, специфичные для провайдера, перед запусками)
  • Лимиты контекста (контекстное окно vs отслеживаемые токены)
  • Компрессия (ручная + автоматическая) и где подключить работу перед компрессией
  • Фоновая служебная работа (например, запись в память, которая не должна создавать видимый пользователю вывод)

Если вам сначала нужен обзор высокого уровня, начните с:


Единый источник истины: Шлюз

OpenClaw построен вокруг единого процесса Шлюза (Gateway), который владеет состоянием сессий.

  • Пользовательские интерфейсы (приложение для macOS, веб-интерфейс Control UI, 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>
  • Вебхук: 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 (помогает UI и политике отправки)
  • 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: сообщения пользователя/ассистента/toolResult
  • custom_message: сообщения, внедренные расширениями, которые попадают в контекст модели (могут быть скрыты от UI)
  • custom: состояние расширения, которое не попадает в контекст модели
  • compaction: сохраненная сводка компрессии с firstKeptEntryId и tokensBefore
  • branch_summary: сохраненная сводка при навигации по ветке дерева

OpenClaw намеренно не «исправляет» транскрипты; Шлюз использует SessionManager для их чтения/записи.


Контекстные окна vs отслеживаемые токены

Имеют значение две разные концепции:

  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 (бэкенды CLI пропускают ее).
  • Запись пропускается, когда рабочая область сессии доступна только для чтения (workspaceAccess: "ro" или "none").
  • См. Память для структуры файлов рабочей области и шаблонов записи.

Pi также предоставляет хук session_before_compact в API расширений, но логика записи OpenClaw сегодня находится на стороне Шлюза.


Контрольный список для устранения неполадок

  • Неправильный ключ сессии? Начните с /concepts/session и подтвердите sessionKey в /status.
  • Несоответствие между хранилищем и транскриптом? Подтвердите хост Шлюза и путь к хранилищу из openclaw status.
  • Спам компрессии? Проверьте:
    • контекстное окно модели (слишком маленькое)
    • настройки компрессии (слишком высокий reserveTokens для окна модели может вызывать раннюю компрессию)
    • раздувание tool-result: включите/настройте удаление сессий
  • Утечка тихих ходов? Убедитесь, что ответ начинается с NO_REPLY (точный токен) и вы используете сборку, которая включает исправление подавления потоковой передачи.

Node.jsНастройка