Контекстное окно — это ловушка. Не потому что его не существует, а потому что «200K токенов» создаёт иллюзию, что управлением памятью займётся кто-то другой. Не займётся. Долгая агентская сессия накапливает выводы инструментов, промежуточные результаты, тупиковые исследования и повторяющиеся статусные проверки. К 30-му ходу контекст на 80% состоит из шума.
Мы построили двухуровневую систему памяти для агентов KB Labs, которая разделяет то, что важно долгосрочно, от того, что важно прямо сейчас.
Уровень 1: Рабочая память (sliding window)
Рабочая память — это последние N раундов разговора: непосредственный контекст, который нужен агенту для продолжения текущей задачи. Реализована как ContextFilterMiddleware, выполняющийся перед каждым LLM-вызовом.
// ContextFilterMiddleware (order: 15, fail-open)
const patch = {
messages: [
...systemMessages, // всегда сохраняются
...lastNRounds(messages, windowSize), // скользящее окно
...truncateToolOutputs(messages, maxOutputLen) // обрезка больших выводов
]
};Скользящее окно хранит недавние ходы; всё старше — отбрасывается. Выводы инструментов свыше порога усекаются — листинг файлов на 50KB не нужно хранить в контексте после того, как агент уже его обработал.
Уровень 2: Постоянная память (факты, переживающие сессии)
Постоянная память — это структурированные данные, которые переживают разговор. Хранится по каждой сессии в .kb/memory/<sessionId>/ и записывается через специальные инструменты:
- memory_finding — фактические открытия («этот сервис использует порт 5050», «тесты требуют запущенного Docker»)
- memory_blocker — препятствия и их статус («заблокировано из-за отсутствующей переменной API_KEY»)
- memory_correction — ошибки агента («предполагал, что файл YAML — оказалось TOML»)
Есть ещё общий слой в .kb/memory/shared/memory.json — предпочтения и ограничения для всех сессий воркспейса («всегда pnpm, не npm», «TypeScript strict mode обязателен»).
Сессионная непрерывность
Когда агент возобновляет сессию (--session-id=X), он загружает последние 16 ходов из turns.json и инжектирует их перед текущей задачей. Это даёт агенту разговорный контекст — что было сделано раньше, что сказал пользователь, какие решения были приняты — без повторного воспроизведения всей истории.
// runner.ts — сессионная непрерывность
const continuityEnabled = !!this.config.sessionId;
if (continuityEnabled) {
const history = await loadConversationMessages(sessionPath);
messages = [...history.slice(-16), ...currentMessages];
}Пайплайн middleware
Управление памятью — часть пятиступенчатого middleware-пайплайна, обрабатывающего каждый ход агента:
- Observability (order: 5) — трассировка спанов, подсчёт токенов
- Budget (order: 10) — контроль бюджета токенов, nudge-сообщения при приближении к лимиту
- ContextFilter (order: 15) — скользящее окно + усечение
- FactSheet (order: 20) — инжекция постоянной памяти
- Progress (order: 50) — обнаружение застреваний, интервенция
Почему не просто использовать длинный контекст?
Три причины:
- Стоимость. Контекст на 200K токенов при $15/M output-токенов дорог, когда большая его часть — устаревшие выводы инструментов.
- Латентность. Больше входных токенов — дольше до первого токена вывода.
- Точность. LLM работают хуже с нерелевантным контекстом. Проблема «иголки в стоге сена» реальна — похоронить критический факт в 100K шума снижает вероятность, что модель им воспользуется.
Двухуровневая память не борется с контекстным окном — она делает его стоящим своего размера, заполняя сигналом вместо шума.