Сессии и память

Управление сессиями

OpenClaw рассматривает одну сессию прямого чата на агента как основную. Прямые чаты сворачиваются в agent:<agentId>:<mainKey> (по умолчанию main), в то время как групповые чаты/каналы получают свои собственные ключи. Значение session.mainKey учитывается. Используйте session.dmScope для управления группировкой личных сообщений:

  • main (по умолчанию): все ЛС используют основную сессию для непрерывности.
  • per-peer: изолировать по идентификатору отправителя между каналами.
  • per-channel-peer: изолировать по каналу + отправителю (рекомендуется для многопользовательских инбоксов).
  • per-account-channel-peer: изолировать по аккаунту + каналу + отправителю (рекомендуется для мультиаккаунтных инбоксов). Используйте session.identityLinks для сопоставления идентификаторов отправителей с префиксом провайдера с канонической идентичностью, чтобы один и тот же человек использовал одну сессию ЛС между каналами при использовании per-peer, per-channel-peer или per-account-channel-peer.

Безопасный режим ЛС (рекомендуется для многопользовательских сценариев)

Предупреждение безопасности: Если ваш агент может получать ЛС от нескольких людей, вам настоятельно рекомендуется включить безопасный режим ЛС. Без него все пользователи используют один и тот же контекст разговора, что может привести к утечке приватной информации между пользователями.

Пример проблемы с настройками по умолчанию:

  • Алиса (<SENDER_A>) пишет вашему агенту на приватную тему (например, о медицинской записи)
  • Боб (<SENDER_B>) пишет вашему агенту с вопросом «О чём мы говорили?»
  • Поскольку оба ЛС используют одну сессию, модель может ответить Бобу, используя предыдущий контекст Алисы.

Решение: Установите dmScope для изоляции сессий по пользователям:

// ~/.openclaw/openclaw.json
{
  session: {
    // Безопасный режим ЛС: изолировать контекст ЛС по каналу + отправителю.
    dmScope: "per-channel-peer",
  },
}

Когда включать эту настройку:

  • У вас есть одобренные подключения более чем от одного отправителя
  • Вы используете разрешительный список ЛС с несколькими записями
  • Вы устанавливаете dmPolicy: "open"
  • Несколько номеров телефонов или аккаунтов могут писать вашему агенту

Примечания:

  • По умолчанию dmScope: "main" для непрерывности (все ЛС используют основную сессию). Это подходит для однопользовательских сценариев.
  • Локальная настройка через CLI записывает session.dmScope: "per-channel-peer" по умолчанию, если значение не задано (существующие явные значения сохраняются).
  • Для мультиаккаунтных инбоксов на одном канале предпочтительнее per-account-channel-peer.
  • Если один и тот же человек связывается с вами на нескольких каналах, используйте session.identityLinks, чтобы объединить его сессии ЛС в одну каноническую идентичность.
  • Вы можете проверить настройки ЛС с помощью openclaw security audit (см. безопасность).

Шлюз — источник истины

Все состояние сессии принадлежит шлюзу («главному» OpenClaw). UI-клиенты (приложение для macOS, WebChat и т.д.) должны запрашивать у шлюза списки сессий и количество токенов, а не читать локальные файлы.

  • В удалённом режиме хранилище сессий, которое вас интересует, находится на удалённом хосте шлюза, а не на вашем Mac.
  • Количество токенов, отображаемое в интерфейсах, берётся из полей хранилища шлюза (inputTokens, outputTokens, totalTokens, contextTokens). Клиенты не парсят JSONL-транскрипты, чтобы «исправить» итоговые значения.

Где хранится состояние

  • На хосте шлюза:
    • Файл хранилища: ~/.openclaw/agents/<agentId>/sessions/sessions.json (на агента).
  • Транскрипты: ~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl (сессии тем Telegram используют .../<SessionId>-topic-<threadId>.jsonl).
  • Хранилище представляет собой карту sessionKey -> { sessionId, updatedAt, ... }. Удаление записей безопасно; они воссоздаются по требованию.
  • Групповые записи могут включать displayName, channel, subject, room и space для маркировки сессий в интерфейсах.
  • Записи сессий включают метаданные origin (метка + подсказки маршрутизации), чтобы интерфейсы могли объяснить, откуда взялась сессия.
  • OpenClaw не читает устаревшие папки сессий Pi/Tau.

Обслуживание

OpenClaw применяет обслуживание хранилища сессий, чтобы ограничить размер sessions.json и артефактов транскриптов со временем.

Значения по умолчанию

  • session.maintenance.mode: warn
  • session.maintenance.pruneAfter: 30d
  • session.maintenance.maxEntries: 500
  • session.maintenance.rotateBytes: 10mb
  • session.maintenance.resetArchiveRetention: по умолчанию равен pruneAfter (30d)
  • session.maintenance.maxDiskBytes: не задан (отключён)
  • session.maintenance.highWaterBytes: по умолчанию 80% от maxDiskBytes, когда включено бюджетирование

Как это работает

Обслуживание выполняется во время записи в хранилище сессий, и вы можете запустить его вручную с помощью openclaw sessions cleanup.

  • mode: "warn": сообщает, что было бы удалено, но не изменяет записи/транскрипты.
  • mode: "enforce": применяет очистку в следующем порядке:
    1. удаляет устаревшие записи старше pruneAfter
    2. ограничивает количество записей до maxEntries (сначала самые старые)
    3. архивирует файлы транскриптов для удалённых записей, на которые больше нет ссылок
    4. очищает старые архивы *.deleted.<timestamp> и *.reset.<timestamp> согласно политике хранения
    5. ротирует sessions.json, когда он превышает rotateBytes
    6. если задан maxDiskBytes, применяет дисковый бюджет до highWaterBytes (сначала самые старые артефакты, затем самые старые сессии)

Особенности производительности для больших хранилищ

Большие хранилища сессий часто встречаются в высоконагруженных сценариях. Работа по обслуживанию выполняется на пути записи, поэтому очень большие хранилища могут увеличить задержку записи. Что сильнее всего увеличивает затраты:

  • очень высокие значения session.maintenance.maxEntries
  • длинные окна pruneAfter, которые сохраняют устаревшие записи
  • множество артефактов транскриптов/архивов в ~/.openclaw/agents/<agentId>/sessions/
  • включение дисковых бюджетов (maxDiskBytes) без разумных ограничений на удаление/количество

Что делать:

  • используйте mode: "enforce" в продакшене, чтобы рост автоматически ограничивался
  • устанавливайте и временные, и количественные ограничения (pruneAfter + maxEntries), а не только одно
  • задавайте maxDiskBytes + highWaterBytes для жёстких верхних границ в крупных развёртываниях
  • держите highWaterBytes значительно ниже maxDiskBytes (по умолчанию 80%)
  • запускайте openclaw sessions cleanup --dry-run --json после изменения конфигурации, чтобы проверить предполагаемое влияние перед применением
  • для часто активных сессий передавайте --active-key при ручной очистке

Примеры настройки

Используйте консервативную политику принудительной очистки:

{
  session: {
    maintenance: {
      mode: "enforce",
      pruneAfter: "45d",
      maxEntries: 800,
      rotateBytes: "20mb",
      resetArchiveRetention: "14d",
    },
  },
}

Включите жёсткий дисковый бюджет для директории сессий:

{
  session: {
    maintenance: {
      mode: "enforce",
      maxDiskBytes: "1gb",
      highWaterBytes: "800mb",
    },
  },
}

Настройка для крупных инсталляций (пример):

{
  session: {
    maintenance: {
      mode: "enforce",
      pruneAfter: "14d",
      maxEntries: 2000,
      rotateBytes: "25mb",
      maxDiskBytes: "2gb",
      highWaterBytes: "1.6gb",
    },
  },
}

Предпросмотр или принудительное обслуживание из CLI:

openclaw sessions cleanup --dry-run
openclaw sessions cleanup --enforce

Очистка сессий

По умолчанию OpenClaw обрезает старые результаты инструментов из контекста в памяти непосредственно перед вызовами LLM. Это не перезаписывает историю JSONL. См. /concepts/session-pruning.

Предварительная очистка памяти перед компрессией

Когда сессия приближается к автоматической компрессии, OpenClaw может выполнить скрытую очистку памяти, которая напоминает модели записать устойчивые заметки на диск. Это выполняется только тогда, когда рабочее пространство доступно для записи. См. Память и Компрессия.

Сопоставление транспортов → ключи сессий

  • Прямые чаты следуют session.dmScope (по умолчанию main).
    • main: agent:<agentId>:<mainKey> (непрерывность между устройствами/каналами).
      • Несколько номеров телефонов и каналов могут быть сопоставлены с одним основным ключом агента; они действуют как транспорты в один разговор.
    • per-peer: agent:<agentId>:dm:<peerId>.
    • per-channel-peer: agent:<agentId>:<channel>:dm:<peerId>.
    • per-account-channel-peer: agent:<agentId>:<channel>:<accountId>:dm:<peerId> (accountId по умолчанию default).
    • Если session.identityLinks совпадает с идентификатором отправителя с префиксом провайдера (например, telegram:123), канонический ключ заменяет <peerId>, чтобы один и тот же человек использовал одну сессию ЛС между каналами.
  • Групповые чаты изолируют состояние: agent:<agentId>:<channel>:group:<id> (комнаты/каналы используют agent:<agentId>:<channel>:channel:<id>).
    • Темы форума Telegram добавляют :topic:<threadId> к идентификатору группы для изоляции.
    • Устаревшие ключи group:<id> всё ещё распознаются для миграции.
  • Входящие контексты могут всё ещё использовать group:<id>; канал выводится из Provider и нормализуется к канонической форме agent:<agentId>:<channel>:group:<id>.
  • Другие источники:
    • Cron-задачи: cron:<job.id>
    • Вебхуки: hook:<uuid> (если явно не задано вебхуком)
    • Запуски узлов: node-<nodeId>

Жизненный цикл

  • Политика сброса: сессии повторно используются до истечения срока, и срок действия оценивается при следующем входящем сообщении.
  • Ежедневный сброс: по умолчанию в 4:00 по местному времени на хосте шлюза. Сессия считается устаревшей, если её последнее обновление произошло раньше времени последнего ежедневного сброса.
  • Сброс по бездействию (опционально): idleMinutes добавляет скользящее окно бездействия. Когда настроены и ежедневный, и сброс по бездействию, тот, который истекает первым, принудительно запускает новую сессию.
  • Устаревший режим только по бездействию: если вы установите session.idleMinutes без какой-либо конфигурации session.reset/resetByType, OpenClaw останется в режиме только по бездействию для обратной совместимости.
  • Переопределения по типу (опционально): resetByType позволяет переопределить политику для сессий direct, group и thread (thread = треды Slack/Discord, темы Telegram, треды Matrix, когда предоставляются коннектором).
  • Переопределения по каналу (опционально): resetByChannel переопределяет политику сброса для канала (применяется ко всем типам сессий для этого канала и имеет приоритет над reset/resetByType).
  • Триггеры сброса: точные команды /new или /reset (плюс любые дополнительные в resetTriggers) запускают новый идентификатор сессии и пропускают остаток сообщения. /new <model> принимает псевдоним модели, provider/model или имя провайдера (нечёткое совпадение) для установки модели новой сессии. Если /new или /reset отправлены отдельно, OpenClaw выполняет короткий «приветственный» ход для подтверждения сброса.
  • Ручной сброс: удалите конкретные ключи из хранилища или удалите JSONL-транскрипт; следующее сообщение воссоздаст их.
  • Изолированные cron-задачи всегда создают новый sessionId на запуск (без повторного использования по бездействию).

Политика отправки (опционально)

Блокируйте доставку для определённых типов сессий без перечисления отдельных идентификаторов.

{
  session: {
    sendPolicy: {
      rules: [
        { action: "deny", match: { channel: "discord", chatType: "group" } },
        { action: "deny", match: { keyPrefix: "cron:" } },
        // Совпадение с сырым ключом сессии (включая префикс `agent:<id>:`).
        { action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
      ],
      default: "allow",
    },
  },
}

Переопределение во время выполнения (только для владельца):

  • /send on → разрешить для этой сессии
  • /send off → запретить для этой сессии
  • /send inherit → очистить переопределение и использовать правила конфигурации Отправляйте эти команды как отдельные сообщения, чтобы они зарегистрировались.

Конфигурация (пример опционального переименования)

// ~/.openclaw/openclaw.json
{
  session: {
    scope: "per-sender", // держать групповые ключи отдельно
    dmScope: "main", // непрерывность ЛС (установите per-channel-peer/per-account-channel-peer для общих инбоксов)
    identityLinks: {
      alice: ["telegram:123456789", "discord:987654321012345678"],
    },
    reset: {
      // По умолчанию: mode=daily, atHour=4 (местное время хоста шлюза).
      // Если вы также установите idleMinutes, побеждает тот, который истекает первым.
      mode: "daily",
      atHour: 4,
      idleMinutes: 120,
    },
    resetByType: {
      thread: { mode: "daily", atHour: 4 },
      direct: { mode: "idle", idleMinutes: 240 },
      group: { mode: "idle", idleMinutes: 120 },
    },
    resetByChannel: {
      discord: { mode: "idle", idleMinutes: 10080 },
    },
    resetTriggers: ["/new", "/reset"],
    store: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
    mainKey: "main",
  },
}

Инспекция

  • openclaw status — показывает путь к хранилищу и недавние сессии.
  • openclaw sessions --json — выводит все записи (фильтр с --active <minutes>).
  • openclaw gateway call sessions.list --params '{}' — получить сессии из запущенного шлюза (используйте --url/--token для доступа к удалённому шлюзу).
  • Отправьте /status как отдельное сообщение в чате, чтобы увидеть, доступен ли агент, сколько контекста сессии использовано, текущие переключатели мышления/подробного вывода и когда ваши учётные данные WhatsApp web были последний раз обновлены (помогает обнаружить необходимость перепривязки).
  • Отправьте /context list или /context detail, чтобы увидеть, что находится в системном промпте и внедрённых файлах рабочего пространства (и основных вкладчиков в контекст).
  • Отправьте /stop (или отдельные фразы прерывания, такие как stop, stop action, stop run, stop openclaw), чтобы прервать текущий запуск, очистить запланированные последующие действия для этой сессии и остановить любые запуски под-агентов, порождённые из неё (в ответе будет указано количество остановленных).
  • Отправьте /compact (опциональные инструкции) как отдельное сообщение, чтобы суммировать старый контекст и освободить место в окне. См. /concepts/compaction.
  • JSONL-транскрипты можно открывать напрямую для просмотра полных ходов.

Советы

  • Держите основной ключ предназначенным для трафика 1:1; позвольте группам иметь свои собственные ключи.
  • При автоматизации очистки удаляйте отдельные ключи вместо всего хранилища, чтобы сохранить контекст в других местах.

Метаданные происхождения сессии

Каждая запись сессии записывает (по возможности) своё происхождение в origin:

  • label: человекочитаемая метка (разрешённая из метки разговора + темы группы/канала)
  • provider: нормализованный идентификатор канала (включая расширения)
  • from/to: сырые идентификаторы маршрутизации из входящего конверта
  • accountId: идентификатор аккаунта провайдера (при мультиаккаунтности)
  • threadId: идентификатор треда/темы, когда канал поддерживает это Поля происхождения заполняются для личных сообщений, каналов и групп. Если коннектор только обновляет маршрутизацию доставки (например, чтобы поддерживать основную сессию ЛС свежей), он всё равно должен предоставлять входящий контекст, чтобы сессия сохраняла свои объясняющие метаданные. Расширения могут делать это, отправляя ConversationLabel, GroupSubject, GroupChannel, GroupSpace и SenderName во входящем контексте и вызывая recordSessionMetaFromInbound (или передавая тот же контекст в updateLastRoute).

Начальная настройкаОчистка сессий