Глава 6. Краткосрочная, долгосрочная и профильная память¶
1. Почему одно слово “memory” только мешает¶
Как только в системе появляется память, у команды возникает искушение называть одним словом вообще все, что не помещается в текущий prompt. Это удобная абстракция для разговора, но плохая абстракция для архитектуры.
На практике у тебя почти всегда есть как минимум три разных слоя:
short-term memoryдля текущего run или короткой серии шагов;long-term memoryдля устойчивых знаний, summaries и фактов;profile memoryдля предпочтений, ролей и привычек конкретного пользователя или аккаунта.
Если эти слои не развести, агент начинает:
- возвращать в prompt слишком много старого шума;
- путать предпочтения пользователя с фактами о мире;
- сохранять transient observations как долговременную истину;
- ломать explainability, потому что уже непонятно, откуда взялся тот или иной кусок контекста.
2. Short-term memory это рабочий стол, а не архив¶
Short-term memory удобнее всего представлять как рабочий стол агента. Это не “история навсегда”, а то, что помогает ему не потерять текущую нить.
Обычно сюда попадает:
- промежуточный план;
- результаты последних tool calls;
- временные hypotheses;
- selected context chunks для текущего run;
- state для многошагового workflow.
У хорошей short-term memory есть три свойства:
- она ограничена по размеру;
- она имеет короткое время жизни;
- ее можно без боли потерять после завершения задачи.
Если в short-term memory начинают жить “вечные” записи, это уже знак, что у тебя нет модели данных, а есть просто переполненный buffer.
3. Long-term memory нужна не для всего, а для устойчивого знания¶
Long-term memory нужна там, где ценность записи переживает один диалог или один workflow.
Это может быть:
- подтвержденный факт о бизнес-сущности;
- summary прошлой сессии;
- накопленное знание о ходе долгого кейса;
- извлеченная и нормализованная заметка для будущего retrieval.
Но есть важный фильтр: если запись нельзя разумно использовать позже без полного исходного контекста, скорее всего, ей не место в long-term memory.
Именно здесь команды часто переоценивают полезность “сырых” сохранений. В long-term memory лучше хранить не поток всего подряд, а осмысленные и типизированные записи.
4. Profile memory это не knowledge base¶
Profile memory особенно легко испортить, потому что она звучит безобидно. Кажется, что это просто “предпочтения пользователя”. Но в production profile memory быстро становится чувствительным слоем.
Сюда обычно относятся:
- язык общения;
- формат ответов;
- рабочая роль;
- допустимые каналы действий;
- устойчивые предпочтения интерфейса или взаимодействия.
Важно, что profile memory отвечает на вопрос “как лучше работать с этим человеком”, а не “что истинно в мире”.
Если агент начинает складывать сюда произвольные факты, profile memory превращается в мутную смесь персонализации, слухов и случайных наблюдений.
Разные типы памяти решают разные задачи и не должны сливаться в один storage
flowchart LR
A["Current run"] --> B["Short-term memory"]
A --> C["Long-term memory"]
A --> D["Profile memory"]
B --> E["Planner state"]
B --> F["Recent tool outputs"]
C --> G["Validated facts"]
C --> H["Session summaries"]
D --> I["Preferences"]
D --> J["User constraints"] 5. Хорошая архитектура задает для каждого слоя свой вопрос¶
Это очень помогает при проектировании:
short-term memory: что нужно помнить прямо сейчас, чтобы не потерять задачу?long-term memory: что стоит сохранить, потому что это пригодится позже?profile memory: что про этого пользователя или аккаунт действительно стабильно и полезно?
Если запись не отвечает ни на один из этих вопросов, возможно, ее вообще не нужно сохранять.
6. У каждого слоя должны быть свои правила записи и чтения¶
Самая частая архитектурная ошибка здесь в том, что один и тот же pipeline пишет и читает все типы памяти одинаково. Но это почти всегда неверно.
Например:
- short-term memory можно читать свободнее, но хранить очень недолго;
- long-term memory должна требовать provenance и tenant checks;
- profile memory должна быть особенно строгой к privacy, consent и explainability.
Нормальная система не только знает, что хранит, но и как именно каждая категория попадает в prompt.
memory_classes:
short_term:
ttl: "2h"
read_path: "runtime_only"
write_policy: "immediate"
long_term:
ttl: "90d"
read_path: "retrieval_with_filters"
write_policy: "validated_only"
profile:
ttl: "365d"
read_path: "personalization_only"
write_policy: "explicit_or_high_confidence"
Такой YAML не обязан быть конечной реализацией. Но он заставляет команду принять важное решение: памятью нельзя управлять на уровне “ну это просто текст в базе”.
7. Что обычно должно жить в short-term memory¶
Полезный practical rule: short-term memory должна помогать агенту действовать сейчас, а не становиться источником долгосрочной истины.
Хорошие кандидаты:
- текущий план;
- статус подзадач;
- результаты последних двух-трех инструментальных вызовов;
- рабочие заметки о том, что уже проверено;
- временные candidate summaries.
Плохие кандидаты:
- предпочтения пользователя “навсегда”;
- неочищенные документы;
- большие логи;
- чувствительные данные без TTL;
- неподтвержденные факты, которые потом будут повторяться как истина.
8. Что обычно должно жить в long-term memory¶
Long-term memory нужна для повторного использования знаний, а не для архивации всей активности.
Туда разумно складывать:
- подтвержденные факты;
- аккуратные summaries с provenance;
- состояния долгих кейсов;
- нормализованные knowledge records;
- ссылки на документы, а не сами огромные payloads целиком.
Очень полезный принцип: в long-term memory чаще стоит хранить компактный record и ссылку на источник, чем пытаться превращать хранилище памяти в бессрочный dump всего контента.
9. Что обычно должно жить в profile memory¶
Profile memory хороша тогда, когда помогает агенту быть удобнее, но не начинает принимать решения за пользователя на основе сомнительных догадок.
Хорошие примеры:
- “предпочитает короткие ответы”;
- “обычно работает на русском”;
- “для изменений production-данных требует подтверждение”;
- “получает отчеты в конце дня”.
Плохие примеры:
- выводы о мотивации или характере;
- случайные предположения из одной сессии;
- чувствительные персональные данные без явной причины;
- догадки, которые потом используются как факт.
10. Простой кодовый шаблон для routing memory records¶
Ниже очень простой пример, который показывает саму идею: запись сначала классифицируется, и только потом идет в соответствующий storage.
from dataclasses import dataclass
@dataclass
class MemoryRecord:
kind: str
content: str
confidence: float
def select_memory_bucket(record: MemoryRecord) -> str | None:
if record.kind in {"plan_step", "tool_result", "working_note"}:
return "short_term"
if record.kind in {"validated_fact", "session_summary", "case_state"} and record.confidence >= 0.8:
return "long_term"
if record.kind in {"language_preference", "format_preference", "approval_preference"}:
return "profile"
return None
Этот пример специально очень прямой. На практике rules будут богаче, но сама идея должна остаться такой же: память сначала классифицируется, а не бездумно складывается в общий контейнер.
11. На чем чаще всего ломаются команды¶
Обычно проблемы выглядят так:
- profile memory начинает подменять authorization;
- long-term memory набивается шумом;
- short-term memory становится слишком большой и дорогой;
- retrieval возвращает записи без учета их класса;
- никто не может объяснить, почему в ответе появился именно этот кусок контекста.
Все это не “дефекты модели”. Это архитектурные дефекты memory layer.
12. Практический чеклист¶
Если хочешь быстро проверить свой дизайн, пройди по вопросам:
- Понимаешь ли ты, чем short-term memory отличается от long-term?
- Есть ли у profile memory отдельная семантика, а не просто отдельная таблица?
- Можно ли объяснить TTL для каждого типа записи?
- Ясно ли, какой слой памяти попадает в prompt напрямую, а какой только через retrieval?
- Есть ли provenance у долговременных записей?
- Можно ли безопасно удалить или исправить запись?
Если на эти вопросы сложно ответить, архитектуру памяти стоит упростить и развести по ролям.
13. Что читать дальше¶
Следующий шаг в этой части очень естественный: после типов памяти нужно разобрать, как именно агент вытаскивает нужные куски обратно в prompt и почему compaction иногда важнее, чем “больше retrieval”.