Сохранение состояния: история обрезки между сессиями
Чему вы научитесь
- Понимать, как DCP сохраняет состояние обрезки между перезапусками OpenCode
- Знать расположение и формат файлов персистентности
- Освоить логику управления состоянием при переключении сессий и сжатии контекста
- Просматривать накопленную экономию токенов по всем сессиям через
/dcp stats
Ваша текущая проблема
Вы закрыли OpenCode, открыли его снова и обнаружили, что предыдущие записи об обрезке исчезли? Или хотите узнать, откуда берётся «накопленная экономия по всем сессиям» в /dcp stats?
Механизм сохранения состояния DCP автоматически сохраняет историю обрезки и статистику в фоновом режиме, обеспечивая их доступность после перезапуска.
Когда это пригодится
- Когда нужна накопительная статистика экономии токенов между сессиями
- Когда нужно продолжить историю обрезки после перезапуска OpenCode
- При длительном использовании DCP для оценки общего эффекта
Основная концепция
Что такое сохранение состояния
Сохранение состояния — это механизм, при котором DCP записывает историю обрезки и статистику на диск, гарантируя, что эта информация не потеряется после перезапуска OpenCode или переключения сессий.
Зачем нужна персистентность?
Без персистентности при каждом закрытии OpenCode:
- Список ID обрезанных инструментов теряется
- Статистика экономии токенов обнуляется
- ИИ может повторно обрезать один и тот же инструмент
С персистентностью DCP может:
- Помнить, какие инструменты уже обрезаны
- Накапливать экономию токенов по всем сессиям
- Продолжать работу после перезапуска
Два типа сохраняемых данных
Состояние, сохраняемое DCP, включает два типа информации:
| Тип | Содержимое | Назначение |
|---|---|---|
| Состояние обрезки | Список ID обрезанных инструментов | Избежание повторной обрезки, отслеживание между сессиями |
| Статистика | Количество сэкономленных токенов (текущая сессия + накопленное) | Демонстрация эффективности DCP, анализ долгосрочных тенденций |
Эти данные хранятся отдельно для каждой сессии OpenCode — каждая сессия соответствует отдельному JSON-файлу.
Поток данных
graph TD
subgraph "Операции обрезки"
A1[ИИ вызывает discard/extract]
A2[Пользователь выполняет /dcp sweep]
end
subgraph "Состояние в памяти"
B1[SessionState.prune.toolIds]
B2[SessionState.stats]
end
subgraph "Персистентное хранилище"
C1[~/.local/share/opencode/storage/plugin/dcp/]
C2[{sessionId}.json]
end
A1 --> B1
A2 --> B1
B1 -->|Асинхронное сохранение| C1
B2 -->|Асинхронное сохранение| C1
C1 --> C2
C2 -->|Загрузка при переключении сессии| B1
C2 -->|Загрузка при переключении сессии| B2
D[Сообщение summary от OpenCode] -->|Очистка кэша| B1Пошаговое руководство
Шаг 1: Узнайте расположение хранилища
Зачем Зная, где хранятся данные, вы сможете проверить или удалить их вручную (при необходимости)
DCP сохраняет состояние в локальной файловой системе, данные не загружаются в облако.
# Расположение директории персистентности
~/.local/share/opencode/storage/plugin/dcp/
# Каждая сессия — отдельный JSON-файл в формате: {sessionId}.jsonОжидаемый результат: В директории может быть несколько файлов .json, каждый соответствует отдельной сессии OpenCode
Конфиденциальность данных
DCP сохраняет локально только состояние обрезки и статистику, без какой-либо конфиденциальной информации. Файлы персистентности содержат:
- Список ID инструментов (числовые идентификаторы)
- Количество сэкономленных токенов (статистика)
- Время последнего обновления (временная метка)
Содержимое диалогов, вывод инструментов или пользовательский ввод не сохраняются.
Шаг 2: Изучите формат файлов персистентности
Зачем Понимание структуры файлов поможет при ручной проверке или отладке
# Список всех файлов персистентности
ls -la ~/.local/share/opencode/storage/plugin/dcp/
# Просмотр содержимого персистентности конкретной сессии
cat ~/.local/share/opencode/storage/plugin/dcp/{sessionId}.jsonОжидаемый результат: JSON-структура примерно такого вида
{
"sessionName": "Название моей сессии",
"prune": {
"toolIds": ["12345", "12346", "12347"]
},
"stats": {
"pruneTokenCounter": 0,
"totalPruneTokens": 15420
},
"lastUpdated": "2026-01-23T10:30:45.123Z"
}Описание полей:
| Поле | Тип | Значение |
|---|---|---|
sessionName | string (опционально) | Название сессии для удобства идентификации |
prune.toolIds | string[] | Список ID обрезанных инструментов |
stats.pruneTokenCounter | number | Токены, сэкономленные в текущей сессии (не архивированы) |
stats.totalPruneTokens | number | Накопленная историческая экономия токенов |
lastUpdated | string | Время последнего обновления в формате ISO 8601 |
Шаг 3: Просмотр накопленной статистики
Зачем Оценить накопленный эффект по всем сессиям и долгосрочную ценность DCP
# Выполните в OpenCode
/dcp statsОжидаемый результат: Панель статистики
╭───────────────────────────────────────────────────────────╮
│ DCP Statistics │
╰───────────────────────────────────────────────────────────╯
Session:
────────────────────────────────────────────────────────────
Tokens pruned: ~15.4K
Tools pruned: 3
All-time:
────────────────────────────────────────────────────────────
Tokens saved: ~154.2K
Tools pruned: 47
Sessions: 12Значение статистики:
| Показатель | Источник | Описание |
|---|---|---|
| Session | Текущее состояние в памяти | Эффект обрезки в текущей сессии |
| All-time | Все файлы персистентности | Накопленный эффект по всем историческим сессиям |
Как рассчитывается статистика All-time
DCP перебирает все JSON-файлы в директории ~/.local/share/opencode/storage/plugin/dcp/ и суммирует:
totalPruneTokens: Общее количество сэкономленных токенов по всем сессиямtoolIds.length: Общее количество обрезанных инструментов по всем сессиям- Количество файлов: Общее число сессий
Так вы можете видеть общий эффект DCP при длительном использовании.
Шаг 4: Понимание механизма автосохранения
Зачем Знание того, когда DCP сохраняет состояние, поможет избежать случайной потери данных
DCP автоматически сохраняет состояние на диск в следующих случаях:
| Триггер | Сохраняемые данные | Место вызова |
|---|---|---|
После вызова ИИ инструментов discard/extract | Обновлённое состояние обрезки + статистика | lib/strategies/tools.ts:148-150 |
После выполнения пользователем команды /dcp sweep | Обновлённое состояние обрезки + статистика | lib/commands/sweep.ts:234-236 |
| После завершения операции обрезки | Асинхронное сохранение, не блокирует основной поток | saveSessionState() |
Процесс сохранения:
// 1. Обновление состояния в памяти
state.stats.totalPruneTokens += state.stats.pruneTokenCounter
state.stats.pruneTokenCounter = 0
// 2. Асинхронное сохранение на диск
await saveSessionState(state, logger)Преимущества асинхронного сохранения
DCP использует механизм асинхронного сохранения, гарантируя, что операции обрезки не блокируются дисковым вводом-выводом. Даже при неудачном сохранении (например, при нехватке места на диске) это не повлияет на эффект обрезки в текущей сессии.
При неудаче записывается предупреждение в лог ~/.config/opencode/logs/dcp/.
Шаг 5: Понимание механизма автозагрузки
Зачем Знание того, когда DCP загружает персистентное состояние, поможет понять поведение при переключении сессий
DCP автоматически загружает персистентное состояние в следующих случаях:
| Триггер | Загружаемые данные | Место вызова |
|---|---|---|
| При запуске OpenCode или переключении сессии | Историческое состояние обрезки + статистика этой сессии | lib/state/state.ts:104 (внутри функции ensureSessionInitialized) |
Процесс загрузки:
// 1. Обнаружение изменения ID сессии
if (state.sessionId !== lastSessionId) {
await ensureSessionInitialized(client, state, lastSessionId, logger, messages)
}
// 2. Сброс состояния в памяти
resetSessionState(state)
state.sessionId = lastSessionId
// 3. Загрузка персистентного состояния с диска
const persisted = await loadSessionState(sessionId, logger)
if (persisted) {
state.prune = { toolIds: persisted.prune.toolIds }
state.stats = {
pruneTokenCounter: persisted.stats.pruneTokenCounter,
totalPruneTokens: persisted.stats.totalPruneTokens
}
}Ожидаемый результат: После переключения на предыдущую сессию /dcp stats показывает сохранённую историческую статистику
Шаг 6: Понимание очистки состояния при сжатии контекста
Зачем Понять, как DCP обрабатывает состояние при автоматическом сжатии контекста OpenCode
Когда OpenCode обнаруживает слишком длинный диалог, он автоматически генерирует сообщение summary для сжатия контекста. DCP обнаруживает это сжатие и очищает соответствующее состояние.
// Обработка при обнаружении сообщения summary
if (lastCompactionTimestamp > state.lastCompaction) {
state.lastCompaction = lastCompactionTimestamp
state.toolParameters.clear() // Очистка кэша инструментов
state.prune.toolIds = [] // Очистка состояния обрезки
logger.info("Detected compaction from messages - cleared tool cache")
}Зачем нужна очистка?
Сообщение summary от OpenCode сжимает всю историю диалога, при этом:
- Старые вызовы инструментов уже объединены в summary
- Сохранение списка ID инструментов теряет смысл (инструменты больше не существуют)
- Очистка состояния предотвращает ссылки на недействительные ID инструментов
Это проектный компромисс: жертвуем частью истории обрезки ради согласованности состояния.
Контрольные точки ✅
Убедитесь, что вы понимаете следующие ключевые моменты:
- [ ] Файлы персистентности DCP хранятся в
~/.local/share/opencode/storage/plugin/dcp/ - [ ] Каждая сессия соответствует файлу
{sessionId}.json - [ ] Персистентные данные включают состояние обрезки (toolIds) и статистику (totalPruneTokens)
- [ ] Статистика «All-time» в
/dcp stats— это сумма данных из всех файлов персистентности - [ ] После операций обрезки выполняется автоматическое асинхронное сохранение без блокировки основного потока
- [ ] При переключении сессии автоматически загружается историческое состояние этой сессии
- [ ] При обнаружении сообщения summary от OpenCode очищается кэш инструментов и состояние обрезки
Типичные ошибки
❌ Случайное удаление файлов персистентности
Проблема: Вручную удалены файлы в директории ~/.local/share/opencode/storage/plugin/dcp/
Последствия:
- Потеря исторического состояния обрезки
- Обнуление накопленной статистики
- Но функция обрезки в текущей сессии не затрагивается
Решение: Начните использование заново, DCP автоматически создаст новые файлы персистентности
❌ Состояние субагента не видно
Проблема: Инструменты обрезаны в субагенте, но при возврате к главному агенту эти записи не видны
Причина: Субагент имеет собственный sessionId, состояние обрезки сохраняется в отдельный файл. При переключении обратно на главный агент, поскольку его sessionId отличается, персистентное состояние субагента не загружается
Решение: Это проектное поведение. Состояние сессии субагента независимо и не разделяется с главным агентом. Если вы хотите видеть все записи обрезки (включая субагенты), используйте статистику «All-time» в /dcp stats (она суммирует данные из всех файлов персистентности)
❌ Ошибка сохранения из-за нехватки места на диске
Проблема: Статистика «All-time» в /dcp stats не растёт
Причина: Возможно, недостаточно места на диске, сохранение не удалось
Решение: Проверьте файлы логов ~/.config/opencode/logs/dcp/ на наличие ошибки «Failed to save session state»
Итоги урока
Ключевая ценность сохранения состояния:
- Память между сессиями: Запоминает, какие инструменты уже обрезаны, избегая повторной работы
- Накопительная статистика: Долгосрочное отслеживание эффекта экономии токенов DCP
- Восстановление после перезапуска: Продолжение работы после перезапуска OpenCode
Сводка потока данных:
Операция обрезки → Обновление состояния в памяти → Асинхронное сохранение на диск
↑
Переключение сессии → Загрузка с диска → Восстановление состояния в памяти
↑
Сжатие контекста → Очистка состояния в памяти (файлы на диске не удаляются)Ключевые моменты:
- Персистентность — это локальная файловая операция, не влияющая на производительность обрезки
- «All-time» в
/dcp stats— это сумма данных всех исторических сессий - Сессии субагентов не персистируются — это проектное поведение
- При сжатии контекста кэш очищается для обеспечения согласованности состояния
Анонс следующего урока
В следующем уроке мы изучим Влияние на кэширование промптов.
Вы узнаете:
- Как обрезка DCP влияет на Prompt Caching
- Как балансировать между попаданием в кэш и экономией токенов
- Механизм тарификации кэширования Anthropic
Приложение: Справочник по исходному коду
Нажмите, чтобы развернуть расположение исходного кода
Дата обновления: 2026-01-23
| Функция | Путь к файлу | Строки |
|---|---|---|
| Определение интерфейса персистентности | lib/state/persistence.ts | 14-19 |
| Сохранение состояния сессии | lib/state/persistence.ts | 33-66 |
| Загрузка состояния сессии | lib/state/persistence.ts | 68-101 |
| Загрузка статистики всех сессий | lib/state/persistence.ts | 109-146 |
| Константа директории хранилища | lib/state/persistence.ts | 21 |
| Инициализация состояния сессии | lib/state/state.ts | 80-116 |
| Обнаружение сжатия контекста | lib/state/state.ts | 118-126 |
| Обработка команды статистики | lib/commands/stats.ts | 46-67 |
| Сохранение состояния при обрезке | lib/strategies/tools.ts | 144-150 |
Ключевые константы:
STORAGE_DIR = ~/.local/share/opencode/storage/plugin/dcp: Корневая директория хранилища файлов персистентности
Ключевые функции:
saveSessionState(state, logger): Асинхронное сохранение состояния сессии на дискloadSessionState(sessionId, logger): Загрузка состояния указанной сессии с дискаloadAllSessionStats(logger): Агрегация статистики всех сессийensureSessionInitialized(client, state, sessionId, logger, messages): Обеспечение инициализации сессии с загрузкой персистентного состояния
Ключевые интерфейсы:
PersistedSessionState: Определение структуры персистентного состоянияAggregatedStats: Определение структуры агрегированной статистики