Долговременная память бота: подробная схема
Это расширенная статья для тех, кто хочет понять внутреннюю механику памяти в Nanobot подробнее. Если вам нужно простое пользовательское объяснение без технических деталей, начните со статьи Память бота: простое объяснение. Этот документ описывает реальную текущую механику памяти в Nanobot. Важно понимать главное:- у бота есть не одна память, а несколько разных слоёв;
- часть данных хранится как сжатые знания;
- часть хранится как поисковые заметки;
- часть хранится как точная история завершённых сессий;
- эти слои решают разные задачи и не заменяют друг друга.
- помнит ли он устойчивые факты о пользователе и проекте;
- может ли он найти старую заметку по смыслу;
- может ли он восстановить точный текст прошлой сессии;
- может ли он связать summary-файл с конкретным JSONL-транскриптом.
Коротко
В Nanobot есть 4 основных слоя памяти:-
memory/MEMORY.mdЭто главная долговременная память с важными устойчивыми фактами. -
memory/YYYY-MM-DD.mdЭто промежуточные «сбросы памяти» перед сжатием длинного контекста. -
memory/YYYY-MM-DD-HHMM-slug.mdЭто summary завершённой сессии, создаваемое при/new. -
sessions/archive/*.jsonl+sessions/_sessions.jsonlЭто точная архивная история завершённых сессий и индекс для доступа к ним.
memory/YYYY-MM-DD-raw-archive.mdЭто страховочный raw archive, который используется, если консолидация вMEMORY.mdнесколько раз подряд не смогла корректно завершиться через LLM tool.
MEMORY.mdотвечает на вопрос «что важно помнить всегда»;- dated memory files отвечают на вопрос «какие были важные разговоры и выводы»;
- archived session JSONL отвечает на вопрос «что именно было сказано слово в слово»;
_sessions.jsonlотвечает на вопрос «как найти нужную завершённую сессию».
Главная идея
Nanobot не пытается бесконечно держать весь текст всех разговоров в активном prompt. Вместо этого он:- держит текущую живую сессию в
sessions/<live>.jsonl; - по мере роста диалога извлекает важные знания в долговременную память;
- при завершении сессии создаёт человекочитаемую summary-память;
- отдельно архивирует точный transcript с уникальным
session_id; - даёт инструменты для поиска по памяти и для точного восстановления истории.
Какие типы данных бот хранит
1. Устойчивые знания
Обычно сюда попадают:- предпочтения пользователя;
- язык ответов;
- формат общения;
- договорённости по проекту;
- архитектурные решения;
- важные ограничения;
- приоритеты;
- роли участников;
- факты, которые будут полезны через много дней или недель.
- «Пользователь предпочитает русский язык».
- «Проект использует Python 3.13».
- «Нельзя делать лишний churn по репозиторию».
- «Для поиска по истории нужна точная расшифровка сессии, а не только summary».
memory/MEMORY.md.
2. Контекст завершённых разговоров
Это не «вечные факты», а сжатые итоги конкретных сессий:- что обсуждали;
- к чему пришли;
- что решили;
- какие были action items;
- какие файлы и темы фигурировали в разговоре.
memory/YYYY-MM-DD-HHMM-slug.md.
3. Точный transcript
Это полный архив сообщений завершённой сессии:- user;
- assistant;
- tool calls;
- tool results;
- timestamps;
- metadata строки в начале JSONL.
sessions/archive/YYYY-MM-DD-HHMM-slug--shortid.jsonl.
4. Метаданные для навигации
Чтобы бот мог не только хранить transcript, но и находить его позже, есть индекс:sessions/_sessions.jsonl
session_idkeyslugcreated_atupdated_atended_atmessage_counttranscript_pathmemory_pathsession_index_path
Какие файлы участвуют в памяти
memory/MEMORY.md
Это основная долговременная память.
Особенности:
- читается как часть system/context prompt;
- обновляется через специальный LLM tool
save_memory; - хранит уже отфильтрованные, нормализованные знания;
- предназначен не для полной истории, а для устойчивых фактов.
memory/YYYY-MM-DD.md
Это файл pre-compaction flush.
Он нужен, когда разговор разрастается и уже близок к лимиту контекста. Перед тем как старую часть истории сжать/архивировать, бот делает отдельный проход и пытается достать:
- важные факты;
- решения;
- предпочтения;
- next steps.
memory_flush.enabled и по умолчанию включён.
memory/YYYY-MM-DD-HHMM-slug.md
Это summary завершённой сессии.
Он создаётся после /new, если сессия была достаточно содержательной.
На практике сейчас summary обычно пропускается для совсем тривиальных сессий, где слишком мало пользовательских сообщений.
Файл содержит:
Session IDSession KeyEnded AtTranscriptSession Index- summary;
- key messages.
session_memory.enabled и по умолчанию включён.
memory/YYYY-MM-DD-HHMM-session-raw.md
Это fallback для завершённой сессии, если SessionMemoryWriter не смог получить нормальные slug + summary от модели.
То есть:
- закрытая сессия всё равно не теряется;
- вместо красивого summary-файла сохраняется raw-конспект последних сообщений;
- дальше эта сессия всё равно архивируется в
sessions/archive/*.jsonl.
memory/YYYY-MM-DD-raw-archive.md:
session-rawотносится к неудачному summary завершённой сессии;raw-archiveотносится к неудачной консолидации кусков истории вMEMORY.md.
sessions/<live-session>.jsonl
Это активная живая сессия текущего чата или треда.
Она нужна для текущего рабочего контекста:
- сюда сохраняются новые сообщения;
- отсюда строится история для следующего запроса к модели;
- здесь же хранится
last_consolidated, чтобы понимать, какая часть уже была сжата в память.
sessions/archive/*.jsonl
Это архив завершённых сессий.
Каждый такой файл:
- хранит точный transcript;
- имеет уникальный
session_id; - содержит slug;
- имеет дату и время окончания сессии до минут;
- связан с summary memory file.
sessions/_sessions.jsonl
Это индекс завершённых сессий.
Без него агенту пришлось бы угадывать нужный JSONL-файл по имени. С ним бот может:
- перечислить завершённые сессии;
- отфильтровать их по slug, key, path;
- выбрать
session_id; - загрузить точный transcript через
session_history.
memory/YYYY-MM-DD-raw-archive.md
Это не основной happy-path, а аварийный fallback внутри MemoryStore.
Он нужен на случай, когда LLM несколько раз подряд не смог:
- вызвать
save_memory; - вернуть корректный payload;
- или вообще упал при консолидации.
- после нескольких подряд неудачных попыток консолидации создаётся/дополняется
memory/YYYY-MM-DD-raw-archive.md; - туда пишется временная метка и сырой формат сообщений;
- это fallback последней линии обороны, а не основной слой обычной памяти.
Что реально попадает в prompt напрямую
Это важная деталь, потому что «хранится на диске» и «автоматически участвует в каждом запросе к модели» — не одно и то же. В каждый обычный запрос напрямую попадают:- system prompt;
- bootstrap-файлы вроде
AGENTS.md,SOUL.md,USER.md,TOOLS.md, если они существуют; - содержимое
memory/MEMORY.md; - текущая живая история активной сессии из
sessions/<live>.jsonl.
memory/YYYY-MM-DD.md;memory/YYYY-MM-DD-HHMM-slug.md;memory/YYYY-MM-DD-raw-archive.md;sessions/archive/*.jsonl;sessions/_sessions.jsonl.
Как память формируется во время жизни сессии
Ниже полный жизненный цикл.Шаг 1. Текущий разговор пишется в live session
Каждое новое сообщение сохраняется в текущий session JSONL. Там живут:- сообщения пользователя;
- ответы ассистента;
- tool calls;
- tool results.
Шаг 2. Если история стала слишком длинной, бот делает pre-compaction flush
Когда оценка prompt-токенов подходит близко к лимиту контекста, Nanobot не сразу режет старую часть истории. Сначала запускается отдельный проходMemoryFlusher, который:
- смотрит на последние user/assistant сообщения;
- просит модель выделить durable knowledge;
- записывает найденное в
memory/YYYY-MM-DD.md.
Шаг 3. Затем старый хвост истории консолидируется в MEMORY.md
После flush старые сообщения могут быть обработаны MemoryConsolidator.
Он:
- выбирает безопасную границу по user-turn;
- вызывает специальный tool
save_memory; - обновляет
memory/MEMORY.md; - двигает
last_consolidated.
- transcript в live session не переписывается как summary;
- бот просто помечает, какая часть уже была «усвоена» в долговременную память.
Шаг 4. Когда пользователь делает /new, живая сессия закрывается
При /new происходит сразу несколько вещей:
- Текущая session-state клонируется.
- Из live session берётся unconsolidated tail для
archive_messages. - Живая сессия очищается и получает новый
session_id. - В фоне запускается архивация закрытой сессии.
/new — это уже разные сущности.
Шаг 5. Для закрытой сессии создаётся summary memory file
Если диалог содержательный,SessionMemoryWriter:
- берёт user/assistant часть последних сообщений;
- просит модель создать
slugи summary; - пишет
memory/YYYY-MM-DD-HHMM-slug.md.
- создаётся raw fallback
memory/YYYY-MM-DD-HHMM-session-raw.md.
Шаг 6. Закрытая сессия архивируется как точный JSONL transcript
После summary создаётся архивный transcript:sessions/archive/YYYY-MM-DD-HHMM-slug--shortid.jsonl
session_idkeyslugcreated_atupdated_atended_atmessage_counttranscript_pathmemory_pathmetadata- все сообщения сессии
Шаг 7. В индекс _sessions.jsonl добавляется запись
Это последний шаг, который делает завершённую сессию discoverable.
С этого момента агент может:
- найти её через
session_list; - получить
session_id; - восстановить transcript через
session_history(session_id=...).
Что именно делает /new
Команда /new не означает «полностью всё забыть».
Она означает:
- начать новый чистый runtime-контекст;
- закрыть предыдущую сессию;
- попытаться извлечь из неё знания;
- сохранить summary;
- сохранить точный transcript;
- создать индексную запись.
/new бот:
- уже не держит старый разговор в active prompt;
- но всё ещё может помнить факты из него;
- всё ещё может найти summary по смыслу;
- всё ещё может восстановить точную историю завершённой сессии.
Чем отличаются MEMORY.md, summary-файлы и transcript
MEMORY.md
Это:
- короткая;
- curated;
- постоянно актуализируемая;
- загружаемая в prompt память.
memory/YYYY-MM-DD-HHMM-slug.md
Это:
- summary одной конкретной завершённой сессии;
- удобный для поиска и чтения человеком файл;
- мост между semantic memory и точным transcript.
- о чём была беседа;
- к чему пришли;
- какие были основные выводы.
sessions/archive/*.jsonl
Это:
- точная история;
- источник истины для полного восстановления сессии;
- единственный слой, где хранятся tool results и вся последовательность сообщений.
- проверить дословный ход разговора;
- найти точный tool output;
- восстановить полную цепочку действий.
Как бот вспоминает прошлое
Сейчас есть три разных режима recall.1. Мгновенный recall из MEMORY.md
Это самый быстрый и дешёвый режим.
Используется для:
- пользовательских предпочтений;
- устойчивых фактов;
- ключевого project context.
2. Поиск по memory-файлам
Если включён vector/semantic memory, бот может искать по:memory/MEMORY.mdmemory/*.md
memory_search.
Важно:
- этот tool регистрируется только при
vector_memory.enabled = true; - по умолчанию vector memory в текущей конфигурационной схеме выключена.
- «что мы раньше решили про API?»;
- «что пользователь предпочитает?»;
- «какая была договорённость по архитектуре?»;
- «в какой сессии обсуждали Telegram/Slack/Redis?».
3. Точный recall по архиву сессий
Если нужна именно полная история прошлой завершённой сессии, используются два инструмента:session_listsession_history
- агент вызывает
session_list - получает список завершённых сессий из
sessions/_sessions.jsonl - выбирает нужный
session_id - вызывает
session_history(session_id=...) - получает точный transcript
Как работают session_list и session_history
session_list
Этот tool читает sessions/_sessions.jsonl и возвращает:
session_idsession keyslugended_at- количество сообщений
- путь к transcript
- путь к memory file
session_idkeyslugmemory_pathtranscript_path
session_history
Этот tool умеет два режима:
- по текущей live session через
session_key - по архивной завершённой сессии через
session_id
- ищет запись в
_sessions.jsonl; - находит
transcript_path; - загружает
sessions/archive/*.jsonl; - возвращает точную историю сообщений.
- «покажи точную историю той сессии»;
- «какой был tool output в прошлый раз?»;
- «что именно сказал пользователь в той беседе?»;
- «какой summary-файл связан с этим transcript?».
Как memory-файл связан с transcript
Теперь связь двусторонняя.Из memory-файла можно перейти к transcript
Вmemory/YYYY-MM-DD-HHMM-slug.md записываются:
Session IDTranscriptSession Index
- к какой сессии он относится;
- где лежит точный JSONL transcript;
- через какой индекс этот transcript находится.
Из transcript/index можно перейти к memory-файлу
В_sessions.jsonl и в metadata архивного JSONL записывается:
memory_path
session_id можно найти не только transcript, но и summary memory file.
Это и есть тот «мост» между summary-памятью и точной историей.
Что бот запоминает хорошо, а что хуже
Хорошо запоминаются:- явные предпочтения;
- стабильные проектные факты;
- архитектурные решения;
- прямые договорённости;
- важные next steps;
- темы завершённых сессий;
- exact transcript завершённых сессий после
/new.
- случайные реплики;
- мелкий шум;
- длинные обсуждения без явных выводов;
- детали, которые никогда не были оформлены как факт, решение или summary.
Как помочь боту запомнить важное
Лучшие формулировки:- «Запомни, что я предпочитаю русский язык».
- «Запомни, что в проекте нельзя делать лишний churn по репозиторию».
- «Считай это важным архитектурным решением».
- «Это новый приоритет проекта».
- «Запомни это как долгосрочный контекст».
- «Больше не считай Redis частью текущей архитектуры».
- «Теперь предпочитаю короткие ответы».
- «Это решение отменено, используем другой подход».
Ограничения и компромиссы
Бот не держит всю историю всегда в prompt
Это осознанное решение ради:- стоимости;
- скорости;
- устойчивости;
- контроля над размером контекста.
MEMORY.md не является полной историей
Это не лог, а curated memory.
Если нужен exact transcript, надо идти в архив сессий.
Summary не равен transcript
Summary-файл может передавать смысл, но не заменяет дословную историю.Архивная точная история появляется после закрытия сессии
Смыслsessions/archive/*.jsonl в том, что это слой завершённых сессий.
Текущая активная беседа живёт отдельно, в live session JSONL.
Semantic search и exact history search — разные вещи
memory_search отвечает на вопрос «что вообще раньше было по этой теме».
session_history(session_id=...) отвечает на вопрос «что точно было сказано в конкретной сессии».
Практические сценарии
Сценарий 1. Пользователь сказал постоянное предпочтение
Пример:- «Отвечай мне по-русски».
memory/MEMORY.md.
Сценарий 2. Длинная рабочая сессия
Когда разговор становится слишком большим:- бот может сбросить durable facts в
memory/YYYY-MM-DD.md; - затем сжать старый хвост диалога.
Сценарий 3. Пользователь делает /new
Тогда:
- live session очищается;
- старая сессия получает summary memory file;
- создаётся архивный transcript;
- в
_sessions.jsonlдобавляется запись; - новой live session назначается новый
session_id.
Сценарий 4. Нужно восстановить точную прошлую беседу
Тогда агент может:- вызвать
session_list - найти нужную запись
- взять
session_id - вызвать
session_history(session_id=...)
Если сформулировать совсем просто
У Nanobot нет одной «магической памяти». У него есть:- короткая рабочая память текущей сессии;
- долговременная curated memory;
- semantic memory-файлы по прошлым разговорам;
- точный архив завершённых сессий;
- индекс, который связывает всё это вместе.
- бот не тащит весь прошлый мир в каждый prompt;
- но и не теряет важные знания;
- а если нужно, может восстановить точную завершённую сессию через
session_id.
Итог
Текущая схема long-term memory в Nanobot устроена так:MEMORY.mdхранит устойчивые знания;memory/YYYY-MM-DD.mdстрахует длинные диалоги перед сжатием;memory/YYYY-MM-DD-HHMM-slug.mdхранит summary завершённой сессии;sessions/archive/*.jsonlхранит точный transcript завершённой сессии;sessions/_sessions.jsonlсвязываетsession_id, transcript и memory file;memory_searchищет по memory-файлам;session_listнаходит завершённые сессии;session_historyдостаёт точную историю поsession_idили текущей live session.
MEMORY.md.
Если нужно понять, о чём был прошлый разговор, — помогают dated memory files и memory_search.
Если нужна точная история, — используются _sessions.jsonl и session_history.