Автоматический перевод
Эта статья была автоматически переведена с оригинальной английской версии.
Архитектура памяти AI-агента в 2026 году: чекпойнты, vector stores и файловая память
Часть 2 серии Engineering the Agentic Stack
Состояние — это то, что отличает чат-бота от AI-агента. Без памяти агента каждое взаимодействие начинается с нуля. Агент не может ставить задачу на паузу и возобновлять, учиться на прошлых сессиях или персонализироваться. В части 1 разбирались циклы рассуждения AI-агента: как агент думает. Этот пост — об архитектуре памяти, которая определяет, что он запоминает.
Я разберу архитектуру памяти Market Analyst Agent, показав, как горячие чекпойнты, холодные vector stores и файловая документная память работают вместе в долгоживущих агентах. Затем разберу, когда имеет смысл использовать PostgreSQL, Redis, Qdrant, key-value stores и обычные Markdown-файлы.
Кратко: память AI-агента делится на три уровня. Горячая память — это чекпойнты на уровне thread в Redis или PostgreSQL для pause/resume. Холодная память — это межсессионные знания в vector store или key-value backend для персонализации. Документная память — это человекочитаемые файлы, которые агент читает и пишет для хранения постоянных знаний о проекте. Система чекпойнтов LangGraph нативно закрывает горячий слой. Для холодной памяти векторный поиск с Qdrant дает семантический recall; для структурированных фактов достаточно key-value store. Для документной памяти файловое хранилище дает отладимость и не требует инфраструктуры эмбеддингов.
Почему архитектура памяти AI-агента важна
Агент без состояния — это навороченный autocomplete. Он получает запрос, возвращает ответ и все забывает. Это работает для одноходового Q&A. Это ломается, как только вам нужно:
- Pause and resume: пользователь начинает исследовательскую задачу, закрывает ноутбук и возвращается завтра. Без checkpointed state агент перезапускается с нуля.
- Связность в многоходовом диалоге: в длинном разговоре агент должен помнить, какие tools он вызывал, какие данные собрал и какие шаги плана уже завершил.
- Персонализация: вернувшийся пользователь ожидает, что агент знает его tolerance к риску, предпочитаемую глубину анализа и историю прошлых взаимодействий.
- Human-in-the-loop (HITL): агент готовит черновик отчета и ждет подтверждения. Состояние «ожидания» должно переживать перезапуски процесса.
Возьмем Market Analyst Agent из части 1. Пользователь пишет: «Проанализируй NVDA». Агент строит план, вызывает пять tools, собирает данные, готовит отчет. Пользователь отвечает: «все хорошо, но добавь анализ конкурентов». Без checkpointed state агент не понимает, к чему относится «все хорошо», и вынужден начинать заново. С checkpoint store он загружает состояние с последнего узла, включая собранное исследование, и просто добавляет шаг с конкурентами.
Теперь представьте, что тот же пользователь возвращается через неделю: «Обнови мой анализ NVDA». Без долгосрочной памяти агент не знает, что пользователь предпочитает консервативные оценки риска или что его интересуют полупроводниковые компании. С memory store на базе векторов он вспоминает эти факты и персонализирует ответ без повторных уточнений.
LangGraph разделяет это достаточно чисто. Каждый запуск графа выполняется внутри thread — одного разговора или задачи. Состояние внутри thread — это working memory. Состояние, которое сохраняется между thread, — это long-term memory. Инженерный вопрос в том, какой storage backend использовать для каждого случая.
Таксономия памяти AI-агента
Прежде чем переходить к реализации, полезно классифицировать, что именно агентам нужно помнить. Фреймворк CoALA (Sumers, Yao et al., 2023) — это стандартная таксономия, опирающаяся на когнитивную науку. Я вводил scope памяти в своем посте про context engineering; здесь я расширю это до шести категорий:
| Тип памяти | Scope | Lifetime | Пример | Паттерн хранения |
|---|---|---|---|---|
| Working | Текущий шаг | Миллисекунды | Аргументы вызова tool, текущий ответ LLM | In-process (Python dict) |
| Short-term | Текущий thread | Минуты–часы | История диалога, прогресс плана, собранные данные | Checkpoint store |
| Episodic | Между thread | Дни–месяцы | «На прошлой неделе пользователь спрашивал про доходы NVDA» | Vector store / KV store |
| Semantic | Между thread | Месяцы–постоянно | «Пользователь предпочитает консервативные инвестиции» | Vector store / KV store |
| Document | Между thread | Дни–постоянно | Заметки по проекту, research summaries, learned patterns | File store (Markdown/JSON) |
| Procedural | Вся система | Постоянно | «При анализе акций всегда проверяй SEC filings» | Config / system prompt |
Working memory — это то, с чем LLM активно рассуждает прямо сейчас: Python-переменные в текущей функции, содержимое context window, аргументы вызова tool в процессе выполнения. Это самый быстрый и самый эфемерный слой. Ничего не живет дольше текущего шага. Working memory ограничена context window модели, и именно это является реальным bottleneck. Все, что агент «знает» в момент принятия решения, должно поместиться сюда — независимо от того, пришло ли это из checkpoint store, vector query или file read. Остальные уровни существуют, чтобы подать нужную информацию в working memory в нужный момент.
Short-term memory — это checkpoint store, в который LangGraph пишет после выполнения каждого узла. Episodic и semantic memory — это long-term stores, сохраняющиеся между thread. Document memory — это структурированные знания, которые агент накапливает со временем: заметки по проекту, summaries исследований, изученные соглашения — и хранит в человекочитаемых файлах, которые и агент, и разработчик могут просматривать и редактировать. Procedural memory закодирована в system prompt и определениях tools; она не меняется от пользователя к пользователю.
На практике это схлопывается в три уровня: горячая память (working + short-term) для текущей сессии, холодная память (episodic + semantic) для межсессионного recall и document memory для накопленных знаний о проекте, которым полезно быть человекочитаемыми и напрямую редактируемыми.
Большинство фреймворков и обзоров фокусируются на разделении hot/cold и пропускают document memory, несмотря на ее широкое распространение. CoALA классифицирует память как working, episodic, semantic и procedural; о файлах — ни слова. Обзор Memory in the Age of AI Agents рассматривает vector stores и knowledge graphs, но не document files. Документация LangGraph описывает checkpoints и интерфейс Store, но не имеет нативного понятия файловой памяти.
На практике document memory уже стала вариантом по умолчанию в AI coding assistants. Claude Code, Cursor, Windsurf и Devin считают файловую память базовой возможностью. И паттерн выходит далеко за пределы coding: агенты для open-world игр хранят переиспользуемые навыки как code libraries (Voyager), корпоративные агенты-победители соревнований итерируют procedural prompt documents между запусками (ECR3 winning approaches), а web automation agents синтезируют переиспользуемые workflow APIs из успешных эпизодов (Agent Workflow Memory). Преимущества — отладимость, version control, отсутствие инфраструктуры — не специфичны для coding.
Полезное наблюдение из того же обзора: память агента не равна RAG или context engineering. Отличительная черта в том, что операции чтения/записи выполняет сам агент. Он сам решает, что помнить, а что забыть, а не полагается на фиксированный retrieval pipeline.
Работа Generative Agents (Park et al., 2023) показала, насколько далеко это может зайти: симулированные агенты, которые хранили, осмысляли и извлекали собственные воспоминания, демонстрировали поразительно человекоподобное поведение. Архитектурный паттерн — memory stream с retrieval, оцениваемым по recency, importance и relevance, — до сих пор остается шаблоном для большинства современных систем памяти агентов.
Краткосрочная память агента: checkpoint store
Каждый раз, когда выполняется узел LangGraph, фреймворк сериализует полное состояние графа и записывает его в checkpoint store. Это фундамент для pause/resume, time-travel debugging и HITL-workflows.
Чекпойнт содержит состояние графа, нужное для возобновления: AgentState из части 1 (messages, identity, user profile, plan steps, research data, execution mode), плюс метаданные LangGraph — какой узел его создал и монотонно растущий sequence number. Когда граф возобновляется — после HITL interrupt или перезапуска процесса — он загружает последний чекпойнт и продолжает ровно с того места, где остановился. Чекпойнт — это не то же самое, что append-only event log или trace; в части 5 эти поверхности runtime observability явно разделяются.
Как работает checkpointing в LangGraph
BaseCheckpointSaver в LangGraph — это простой интерфейс: put() записывает чекпойнт, get_tuple() читает последний для thread, list() возвращает историю. Каждый чекпойнт ключуется через (thread_id, checkpoint_ns, checkpoint_id), где thread_id идентифицирует разговор, checkpoint_ns отвечает за namespacing subgraph, а checkpoint_id — это уникальная версия.
Критичное решение здесь — какой backend поставить за этим интерфейсом. LangGraph поставляется с двумя production-вариантами: PostgreSQL и Redis.
PostgreSQL vs Redis
| Параметр | PostgreSQL (langgraph-checkpoint-postgres) |
Redis (langgraph-checkpoint-redis) |
|---|---|---|
| Латентность чтения | ~0.65ms | ~0.095ms |
| Латентность записи | ~2ms (unlogged) до 10ms (с WAL) | ~0.095ms |
| Пропускная способность | ~15K txn/s | ~893K req/s |
| Надежность | Полный ACID, WAL + replication | Настраиваемая (AOF/RDB), риск потери данных |
| История чекпойнтов | Полная история (resume, time-travel debugging) | Настраиваемое удержание через maxcount |
| Операционная стоимость | Умеренная (стандартные RDBMS-операции) | Выше (упор в RAM, управление памятью) |
| Паттерн масштабирования | Вертикально + read replicas | Горизонтально (Redis Cluster) |
| Лучше всего подходит для | Надежности, resume и debugging | Низкой латентности, high-throughput, real-time |
Бенчмарки по латентности из сравнений CyberTec и RisingWave.
PostgreSQL: надежный вариант по умолчанию
PostgreSQL — более безопасный выбор по умолчанию для большинства команд. Чекпойнты переживают сбои, вы получаете полную транзакционную семантику, а история чекпойнтов делает time-travel debugging прямолинейным.
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
async def create_postgres_checkpointer(connection_string: str) -> AsyncPostgresSaver:
"""Create a PostgreSQL-backed checkpoint store.
PostgreSQL gives us ACID guarantees — if a checkpoint write succeeds,
the state is durable even if the process crashes immediately after.
"""
checkpointer = AsyncPostgresSaver.from_conn_string(connection_string)
# Create the checkpoint tables if they don't exist.
# This is idempotent — safe to call on every startup.
await checkpointer.setup()
return checkpointer
# Usage: wire into the graph compilation
checkpointer = await create_postgres_checkpointer(
"postgresql://user:pass@localhost:5432/agent_memory"
)
graph = create_graph(checkpointer=checkpointer)
# Every invoke/stream call now persists state automatically
config = {"configurable": {"thread_id": "user-123-session-1"}}
result = await graph.ainvoke({"messages": [HumanMessage(content="Analyze NVDA")]}, config)
# Resume later — loads the latest checkpoint for this thread
result = await graph.ainvoke({"messages": [HumanMessage(content="approved")]}, config)
AsyncPostgresSaver использует пакет langgraph-checkpoint-postgres, который создает три таблицы: checkpoints (сериализованное состояние), checkpoint_blobs (крупные бинарные данные) и checkpoint_writes (pending writes для crash recovery). Схема поддерживает конкурентный доступ и использует advisory locks, чтобы предотвращать конфликты записи.
Redis: когда bottleneck — это латентность
Когда важна латентность чекпойнтов ниже миллисекунды (real-time conversational agents, high-frequency tool loops), лучше подходит Redis.
from langgraph.checkpoint.redis.aio import AsyncRedisSaver
async def create_redis_checkpointer(redis_url: str) -> AsyncRedisSaver:
"""Create a Redis-backed checkpoint store.
Redis stores checkpoints in memory for sub-millisecond access.
Trade-off: less durable than PostgreSQL unless AOF is enabled.
"""
checkpointer = AsyncRedisSaver.from_conn_string(redis_url)
# Initialize Redis data structures
await checkpointer.setup()
return checkpointer
# Usage: same graph API, different backend
checkpointer = await create_redis_checkpointer("redis://localhost:6379")
graph = create_graph(checkpointer=checkpointer)
AsyncRedisSaver из langgraph-checkpoint-redis хранит чекпойнты как JSON-документы, ключованные по thread ID. Редизайн v0.1.0 заменил несколько search-операций одним вызовом JSON.GET, что заметно снизило латентность. Redis 8.0+ включает RedisJSON и RediSearch по умолчанию — дополнительные модули ставить не нужно.
Для deployment-ов с жесткими ограничениями по памяти ShallowRedisSaver хранит только последний чекпойнт на thread — без истории, но с минимальным потреблением RAM. Используйте это, если вам нужен pause/resume, но не нужен time-travel debugging.
Когда что использовать
Используйте PostgreSQL, если:
- Вам нужна полная история чекпойнтов для time-travel debugging или воспроизводимого resume
- Надежность не подлежит компромиссам (финансы, healthcare)
- PostgreSQL уже есть в вашем стеке
- Агент выполняет долгие задачи, где потеря состояния означает часы повторных вычислений
- Вам нужен единый data store — PostgreSQL с pgvector может быть единым backend для checkpoints, long-term memory и vector search, упрощая инфраструктуру
Используйте Redis, если:
- Латентность чекпойнтов — ваш bottleneck (real-time chat, streaming UX)
- Вы строите voice bots — конвейерам STT-to-LLM-to-TTS нужен доступ к состоянию с субмиллисекундной латентностью
- Нужно горизонтальное масштабирование на множество одновременных thread
- Есть high-concurrency fan-out паттерны, где несколько агентов разделяют состояние
- Сессии короткоживущие, и потеря чекпойнта восстанавливаема
- Нужен semantic caching, чтобы уменьшить число избыточных LLM-вызовов (Redis LangCache кэширует семантически похожие запросы и избегает повторных вызовов LLM)
Другие варианты: langgraph-checkpoint-sqlite подходит для локальной разработки и single-process deployment-ов. Для AWS-native стеков langgraph-checkpoint-aws предоставляет DynamoDBSaver с интеллектуальной обработкой payload — маленькие чекпойнты (<350 KB) остаются в DynamoDB, большие автоматически выгружаются в S3. Serverless pricing и отсутствие инфраструктуры в эксплуатации делают этот вариант привлекательным для deployment-ов с переменной нагрузкой.
Долгосрочная память: помнить между сессиями
Горячая память обслуживает текущий разговор. Но что делать с пользователем, который вернется на следующей неделе? Долгосрочная память хранит факты, предпочтения и историю взаимодействий, которые сохраняются между thread.
LangGraph предоставляет интерфейс Store для межthreadовой памяти через свой класс BaseStore. Каждый элемент памяти — это пара (namespace, key) с JSON-значением и опциональным vector embedding. Namespace обычно кодирует пользователя или организацию: ("user", "user-123", "preferences").
Векторное хранилище: семантический recall с Qdrant
Когда агенту нужно вспомнить неструктурированные факты («Что пользователь говорил о своем инвестиционном горизонте?»), векторный поиск дает семантический recall. Вместо точных key lookup агент делает запрос по смыслу.
Qdrant — это специализированная vector database, написанная на Rust, которая занимается хранением эмбеддингов, индексацией (HNSW) и фильтруемым поиском. Я подробно разбирал HNSW и его trade-offs в своем посте про search ranking. Qdrant также предлагает MCP server, который выступает как слой semantic memory — полезно, если ваш agent framework поддерживает Model Context Protocol.
Из memory_store.py:
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct, Distance, VectorParams
from langchain_anthropic import ChatAnthropic
import hashlib
import json
class UserMemoryStore:
"""Long-term memory backed by Qdrant vector search.
Stores user facts as embedded vectors for semantic retrieval.
Each fact is a short natural-language statement about the user.
"""
def __init__(self, qdrant_url: str, collection_name: str = "user_memory"):
self.client = QdrantClient(url=qdrant_url)
self.collection_name = collection_name
self._ensure_collection()
def _ensure_collection(self):
"""Create the collection if it doesn't exist."""
collections = [c.name for c in self.client.get_collections().collections]
if self.collection_name not in collections:
self.client.create_collection(
collection_name=self.collection_name,
vectors_config=VectorParams(
size=1536, # text-embedding-3-small dimensions
distance=Distance.COSINE,
),
)
def store_fact(self, user_id: str, fact: str, embedding: list[float]):
"""Store a user fact with its embedding."""
point_id = hashlib.md5(f"{user_id}:{fact}".encode()).hexdigest()
self.client.upsert(
collection_name=self.collection_name,
points=[PointStruct(
id=point_id,
vector=embedding,
payload={"user_id": user_id, "fact": fact},
)],
)
def recall(self, user_id: str, query_embedding: list[float], top_k: int = 5):
"""Retrieve the most relevant facts for a user given a query."""
results = self.client.query_points(
collection_name=self.collection_name,
query=query_embedding,
query_filter={"must": [{"key": "user_id", "match": {"value": user_id}}]},
limit=top_k,
)
return [hit.payload["fact"] for hit in results.points]
Поток такой: (1) после каждого разговора LLM извлекает ключевые факты из взаимодействия («у пользователя высокая tolerance к риску», «пользователь интересуется полупроводниковыми акциями»), (2) факты эмбеддятся и сохраняются в Qdrant, (3) в начале следующего разговора агент запрашивает Qdrant новым сообщением пользователя, чтобы восстановить релевантный контекст.
Оценка retrieval: не только cosine similarity
Голой cosine similarity достаточно для старта, но production memory systems требуют более богатого retrieval. В работе Generative Agents (Park et al., 2023) была предложена scoring function, объединяющая три сигнала:
- Recency: decay по правилам, чтобы более свежие воспоминания получали больший score. Exponential decay гарантирует, что факт со вчерашнего дня будет выше эквивалентного факта полугодовой давности.
- Importance: значимость, оцененная LLM по шкале 1–10. «Портфель пользователя просел на 40%» получает более высокий score, чем «пользователь поздоровался».
- Relevance: cosine similarity эмбеддингов между запросом и сохраненным фактом.
Итоговый retrieval score — это взвешенная сумма: score = alpha * recency + beta * importance + gamma * relevance. Это не дает свежим и важным фактам утонуть под устаревшими, но семантически похожими. Для Market Analyst Agent я даю наибольший вес relevance (0.5), затем recency (0.3) и importance (0.2), поскольку текущий intent пользователя важнее всего. Это стартовые веса, адаптированные из работы Generative Agents (где использовались равные веса); по моему опыту, усиление relevance лучше работало для запросов финансового анализа, но эти значения выбраны интуитивно, а не эмпирически оптимизированы.
Альтернативы vector search
Векторный поиск мощный, но не всегда правильный инструмент. Вот когда стоит использовать альтернативы:
| Подход | Лучше всего подходит для | Латентность | Сложность |
|---|---|---|---|
| Vector search (Qdrant) | Семантического recall неструктурированных фактов | 5–20ms | Средняя |
| Key-value store (Redis) | Структурированных профилей пользователей, предпочтений | <1ms | Низкая |
| Document store (files) | Знаний о проекте, заметок под управлением агента | 1–5ms | Низкая |
| Full-text search (PostgreSQL GIN index) | Keyword-based recall истории диалога | 2–10ms | Низкая |
| Knowledge graph (Neo4j) | Связей между сущностями, multi-hop reasoning | 10–50ms | Высокая |
| Hybrid (vector + keyword) | Лучшего recall при меняющемся intent запроса | 10–30ms | Средняя |
Key-value stores хорошо работают для структурированных данных. Если ваша долгосрочная память — это профиль пользователя: tolerance к риску, инвестиционный горизонт, предпочтительные сектора, — Redis hash или PostgreSQL JSONB-колонка проще и быстрее, чем эмбеддить факты и делать vector query. Используйте vector search, когда память неструктурированная, а формулировка retrieval query может сильно варьироваться.
Встроенный Store в LangGraph предоставляет key-value интерфейс с namespace-based organization и опциональным vector search. API BaseStore простое: put(), get(), search() и delete() с иерархическим namespace scoping. Доступны три реализации:
InMemoryStore— для разработки и тестирования (данные теряются при завершении процесса)PostgresStore— production persistent store с полной SQL-queryingAsyncRedisStore— межthreadовая память с vector search, поддержкой TTL и фильтрацией по metadata
Конфигурация index включает vector search по сохраненным элементам с настраиваемой embedding model. Во многих случаях этого встроенного store достаточно без отдельной vector database.
from langgraph.store.memory import InMemoryStore
# Create a store with vector search enabled
store = InMemoryStore(
index={
"dims": 1536,
"embed": my_embedding_function, # e.g., OpenAI text-embedding-3-small
}
)
# Store a user preference (namespace scopes to user)
await store.aput(
namespace=("user", "user-123", "preferences"),
key="risk-profile",
value={"risk_tolerance": "high", "horizon": "long-term"},
)
# Semantic search across user's memories
results = await store.asearch(
namespace=("user", "user-123"),
query="What is their investment style?",
limit=5,
)
Выбор стратегии долгосрочной памяти
Начинайте с key-value, если память структурирована и хорошо определена (профили пользователей, настройки, именованные сущности). Добавляйте vector search, когда нужен semantic retrieval по неструктурированным фактам или когда формулировки запросов непредсказуемо различаются.
Knowledge graphs оправдывают себя, когда важны отношения между сущностями, например: «О каких компаниях пользователь спрашивал, и какие из них являются конкурентами NVDA?» Самый интересный недавний проект здесь — Graphiti (от Zep), который строит temporally-aware knowledge graph, отслеживающий, когда факты были верны, а не только что было верно. Каждое ребро несет интервалы валидности, так что изменение tolerance пользователя к риску инвалидирует старое значение, а не молча перезаписывает его. Graphiti сообщает о 94.8% accuracy на DMR benchmark, а его bi-temporal model решает проблему stale memory на уровне данных.
Проблема — в эксплуатации. Запуск graph database нетривиален, и для большинства агентных приложений vector search с metadata filtering закрывает те же сценарии с меньшей инфраструктурной сложностью.
Managed memory frameworks вроде Mem0 и Letta (бывший MemGPT) берут на себя pipeline extraction-consolidation-retrieval. Подход Mem0 особенно интересен: LLM извлекает кандидатные воспоминания, decision engine сравнивает каждый новый факт с существующими записями в vector store, а resolver решает — добавить, обновить или удалить, поддерживая coherent и non-redundant memory store. Letta подходит с угла операционных систем: агенты сами управляют своим context window с помощью инструментов memory management, автономно перемещая данные между «core memory» (в контексте) и «archival memory» (вне контекста). Оба варианта стоит оценить, если вам нужен более быстрый путь в production и не нужен полный контроль над memory pipeline.
Документная память: картотека агента
На практике этот паттерн распространен, но в академической литературе исследован слабо. Документация фреймворков подробно покрывает vector stores, knowledge graphs и context management, но file-based memory едва упоминается. Этот пробел стоит закрыть, потому что именно так самые эффективные AI coding assistants реально сохраняют знания. В бенчмарке Letta обычный файловый подход набрал 74.0% на benchmark conversational memory LoCoMo, обойдя несколько специализированных memory libraries. Frontier-модели уже обучаются на agentic coding tasks и нативно умеют работать с файлами, поэтому паттерн хорошо соответствует их сильным сторонам.
Сдвиг произошел благодаря росту context window. В 2023–2024 годах, когда у вас было 8k–32k токенов, документы приходилось резать на chunks и эмбеддить. С окнами в 1M+ токенов (Gemini 1.5, Claude 4) обычно эффективнее дать агенту прочитать весь файл, чем угадывать, какие chunks релевантны. Эксплуатационно это тоже проще. Вы можете читать, редактировать и git diff Markdown-файл. Вы не можете git diff vector database.
Vector stores и key-value backends хорошо закрывают semantic recall и структурированные lookup. Но есть и третья категория знаний агента, которую ни один из них не обслуживает чисто: накопленный контекст проекта — соглашения, заметки исследований и решения, которые нужны агенту между сессиями и при этом должны быть человекочитаемыми и находиться под version control.
Это и есть document memory: агент читает и пишет структурированные файлы (Markdown, JSON, YAML) в известную директорию. Без эмбеддингов, без базы данных, без инфраструктуры. Просто файлы на диске, которые и агент, и разработчик могут cat, grep, git diff и редактировать вручную.
Почему именно файлы?
Для долгоживущих agent workflows самый эффективный паттерн, который я видел, — это не vector database. Это директория с хорошо организованными заметками. Посмотрите, что происходит, когда coding agent работает над проектом неделями:
- Он узнает, что проект использует Pydantic v2, а не v1
- Он обнаруживает, что tests надо запускать с
pytest -x --tb=short - Он накапливает знания об архитектуре codebase
- Он запоминает предпочтения разработчика («всегда используй
pathlib, никогдаos.path»)
Эти факты слишком структурированы для vector search (нужен exact recall, а не fuzzy similarity) и слишком многочисленны для key-value store (они образуют взаимосвязанные документы, а не изолированные факты). Кроме того, это знания, которые разработчик хочет видеть и редактировать напрямую. Если агент выучил что-то неверно, вы просто открываете файл и исправляете.
Именно так устроены CLAUDE.md в Claude Code и директория .claude/. Агент читает project-level файлы CLAUDE.md с соглашениями и инструкциями и пишет в ~/.claude/MEMORY.md межсессионные знания. Файлы — обычный Markdown: их можно читать, редактировать, коммитить в git и шарить с командой. .cursorrules в Cursor и .windsurfrules в Windsurf следуют тому же паттерну: plain-text файлы, которые агент загружает при старте, чтобы получить контекст проекта.
Реализация file memory store
Реализация намеренно простая. Агент получает четыре операции: записать документ, прочитать документ, вывести список доступных документов и искать по документам по keyword.
Из file_memory.py:
from pathlib import Path
import json
import fnmatch
class FileMemory:
"""Document memory backed by the local filesystem.
Stores agent knowledge as human-readable files organized by topic.
No embeddings, no database — just files that both the agent and
the developer can read, edit, and version-control.
"""
def __init__(self, base_dir: str | Path):
self.base_dir = Path(base_dir)
self.base_dir.mkdir(parents=True, exist_ok=True)
def write_doc(self, path: str, content: str, metadata: dict | None = None):
"""Write or overwrite a document at the given path.
Paths are relative to base_dir. Directories are created automatically.
Metadata (if provided) is stored as a JSON sidecar file.
"""
full_path = self.base_dir / path
full_path.parent.mkdir(parents=True, exist_ok=True)
full_path.write_text(content, encoding="utf-8")
if metadata:
meta_path = full_path.with_suffix(full_path.suffix + ".meta")
meta_path.write_text(json.dumps(metadata, indent=2), encoding="utf-8")
def read_doc(self, path: str) -> str | None:
"""Read a document by path. Returns None if not found."""
full_path = self.base_dir / path
if full_path.exists():
return full_path.read_text(encoding="utf-8")
return None
def list_docs(self, pattern: str = "**/*") -> list[str]:
"""List documents matching a glob pattern."""
return [
str(p.relative_to(self.base_dir))
for p in self.base_dir.glob(pattern)
if p.is_file() and not p.name.endswith(".meta")
]
def search_docs(self, query: str, pattern: str = "**/*.md") -> list[dict]:
"""Search documents by keyword. Returns matching files with context.
This is intentionally simple — grep-style keyword search.
For semantic search, use a vector store instead.
NOTE: This is a sketch for demonstration. A simple substring check
won't scale beyond a few hundred documents. For production with 500+
documents, use TF-IDF/BM25 scoring (e.g., rank_bm25) or a full-text
search backend (PostgreSQL GIN index, Elasticsearch).
"""
results = []
for path in self.base_dir.glob(pattern):
if not path.is_file() or path.name.endswith(".meta"):
continue
content = path.read_text(encoding="utf-8")
if query.lower() in content.lower():
# Return the paragraph containing the match for context
for paragraph in content.split("\n\n"):
if query.lower() in paragraph.lower():
results.append({
"path": str(path.relative_to(self.base_dir)),
"match": paragraph.strip()[:500],
})
return results
Структура папок
Большая часть ценности document memory определяется тем, как разложена директория. Вот структура, которую я использую для Market Analyst Agent:
.agent-memory/
README.md # What this directory is, for human readers
user-profiles/
user-123.md # Preferences, history, risk profile
user-456.md
research/
NVDA-2026-02.md # Research notes from recent analysis
TSLA-2026-01.md
conventions/
analysis-format.md # How to structure analysis reports
data-sources.md # Preferred data sources and API patterns
learnings/
common-errors.md # Mistakes the agent has learned to avoid
tool-patterns.md # Effective tool call sequences
Каждый файл — Markdown. Назначение каждого файла очевидно по его пути. Вы можете git diff всю директорию памяти, чтобы увидеть, чему агент научился за сессию, git revert неудачное обучение или скопировать директорию в другой проект. Попробуйте сделать что-то подобное с коллекцией Qdrant.
Когда использовать document memory, а когда vector или key-value
Три backend-а памяти обслуживают разные паттерны доступа:
| Параметр | Vector Store | Key-Value Store | Document Store |
|---|---|---|---|
| Паттерн запроса | «Найди факты, похожие на X» | «Верни значение по ключу» | «Прочитай документ по пути» |
| Лучше всего подходит для | Неструктурированного, вариативного recall | Структурированных lookup | Контекста проекта, заметок |
| Человекочитаемость | Нет (эмбеддинги) | Частично (JSON) | Да (Markdown) |
| Отладимость | Сложная (scores сходства) | Простая (точные ключи) | Тривиальная (открыть файл) |
| Version control | Нет | Возможен | Да (git-native) |
| Embedding infrastructure | Требуется | Не нужна | Не нужна |
| Масштабируется до | Миллионов фактов | Миллионов ключей | Тысяч документов |
| Возможности поиска | Семантическое сходство | Точное совпадение | Keyword / path-based |
Используйте document memory, если:
- Агент накапливает знания о проекте в нескольких сессиях
- Разработчикам нужно просматривать, редактировать или переопределять то, что «знает» агент
- Знание структурировано как документы (notes, summaries, conventions), а не как изолированные факты
- Вы хотите git-based versioning для памяти агента
- Отсутствие инфраструктуры — жесткое требование
Используйте vector stores, если:
- Нужен нечеткий semantic retrieval («найди воспоминания, связанные с X»)
- Формулировка запроса непредсказуемо меняется
- У вас от тысяч до миллионов отдельных фактов
Используйте key-value stores, если:
- Нужны точные и быстрые lookup для структурированных данных (профили пользователей, настройки)
- Схема данных хорошо определена
На практике production-агенты часто комбинируют все три. Market Analyst Agent использует PostgreSQL checkpoints для горячей памяти, Qdrant для semantic recall пользовательских фактов и file-based document store для project conventions и research notes.
Примеры из реального мира
Паттерн уже широко используется в AI coding assistants:
- Claude Code читает файлы
CLAUDE.mdиз корня проекта и родительских директорий и пишет межсессионные знания в~/.claude/MEMORY.md. Вся система памяти — это обычные Markdown-файлы, которые коммитятся рядом с кодом. - Cursor загружает файлы
.cursorrulesдля project-specific agent instructions: coding conventions, framework preferences, architectural decisions. - Windsurf использует файлы
.windsurfrulesплюс директориюmemories/, где агент хранит изученные паттерны из вашего codebase. - Memory tool от Anthropic для Claude API предоставляет операции
create_memory,read_memory,update_memoryиdelete_memory, реализованные на стороне клиента. Ваше приложение само решает, где фактически живут файлы (локальный диск, S3, база данных).
Общая идея одна: все эти системы хранят знания агента как человекочитаемые текстовые файлы с явными read/write-операциями. Без эмбеддингов. Без vector infrastructure. Агент сам решает, что записывать, разработчик может видеть и редактировать все, а вся система помещается в git diff.
За пределами coding assistants
Document memory не ограничивается coding-агентами. Паттерн встречается в очень разных областях:
-
Агенты для open-world игр: Voyager (Wang et al., 2023) строит постоянную библиотеку навыков из проверенных JavaScript-программ, которые Minecraft-агент накапливает со временем, собирая в 3.3 раза больше уникальных предметов и достигая milestones в 15.3 раза быстрее базовых подходов. Навыки переносятся между новыми мирами без retraining. JARVIS-1 развивает это в multimodal memory, которая объединяет текстовые планы и визуальные наблюдения, достигая 5x success rate на самых сложных задачах.
Здесь стоит провести одно различие: библиотеки навыков — это исполняемая память (code files импортируются и выполняются), тогда как document memory в coding assistants — декларативная (Markdown внедряется в prompts). Failure modes разные. Плохой исполняемый код роняет агента; плохой декларативный текст приводит к ошибкам рассуждения. Но паттерн хранения и эксплуатационные преимущества (отладимость, version control) одинаковы.
-
Автоматизация enterprise workflows: победители соревнования ECR3 использовали document memory для итеративного улучшения prompts. Analyzer и Versioner у одной из победивших команд перебрали 80 prompt versions, сохраненных как procedural documents. Другая топ-команда построила 20+ enricher modules как procedural knowledge в стиле документов. LEGOMem (2025) формализует это как модульный memory framework для multi-agent systems со специализированными типами памяти (sensory, short-term, long-term), которые агенты комбинируют как строительные блоки.
-
Web automation: Agent Workflow Memory (Wang et al., 2024) позволяет web-агентам выводить переиспользуемые workflow из успешных эпизодов, давая прирост success rate на 51% на WebArena. SkillWeaver (2025) идет дальше: агенты синтезируют переиспользуемые API tools из exploration, давая рост success rate на 31.8%. Эти выученные навыки переносятся и на более слабые модели (улучшение на 54.3%), так что накопленная память сильного агента может подтянуть меньший.
-
Поддержка клиентов: Gartner predicts, что к 2029 году AI-агенты будут автономно решать 80% типовых задач customer service. Такие агенты опираются на SOPs, playbooks и customer histories — а это все формы document memory.
Workshop MemAgents на ICLR 2026 — один из признаков того, что исследовательское сообщество догоняет то, что практики уже построили. Document memory явно переросла свои истоки в coding assistants.
Skills — это document memory со схемой. Стандарт Agent Skills (файлы SKILL.md с YAML frontmatter и Markdown body) сейчас используется и Anthropic, и OpenAI Codex.
MCP (Model Context Protocol) движется в том же направлении: определения tools — это JSON Schema-файлы, которые любой агент может обнаружить и вызвать. У протокола 97 миллионов ежемесячных загрузок SDK, и он поддерживается OpenAI, Google, Microsoft и AWS. MCP не специфичен для coding. Те же серверы подключают агентов к базам данных, внутренним API и enterprise systems.
Оба примера указывают на один и тот же паттерн: procedural knowledge хранится как документы с enforced schema и явными read/write-операциями. MCP, которым теперь управляет Agentic AI Foundation, — пожалуй, ближайшее, что экосистема агентов имеет к interop standard.
Масштабирование document memory для production
Описанная выше file-based реализация хорошо работает на ноутбуке одного разработчика и в маленьких deployment-ах. Multi-tenant production с сотнями пользователей и тысячами документов — это уже другая задача.
Ограничение single-node files становится очевидным: file I/O нельзя горизонтально масштабировать, concurrent writes требуют locking, а управление правами между tenant-ами болезненно. Для production нужен backing store, который нормально обрабатывает concurrency, search и multi-tenancy.
Три распространенных подхода:
Подход A: hybrid с тонким слоем базы данных
Оставьте файлы для authoring (разработчики редактируют Markdown локально), но обслуживайте runtime из базы данных. При deployment синхронизируйте файлы в строки PostgreSQL. Агент читает из базы, а не с диска. Это дает:
- Эргономику для разработчиков (редактирование Markdown, коммиты в git)
- Production query performance (индексированные чтения из БД)
- Четкое разделение между authoring и serving
Подход B: object storage + vector index sidecar
Храните документы как объекты в S3/GCS, а коллекцию Qdrant используйте как индекс их эмбеддингов. Агент запрашивает у Qdrant релевантные document IDs, затем подтягивает содержимое из object storage. Это горизонтально масштабируется и поддерживает semantic search, но добавляет сложность: две системы в эксплуатации, pipeline эмбеддингов, eventual consistency между store и index.
Подход C: structured document store на PostgreSQL (рекомендуется)
Храните документы как строки PostgreSQL JSONB с full-text search (GIN index) и опциональными vector embeddings (pgvector). Это дает hybrid search (keyword + semantic), ACID-транзакции и одну эксплуатационную систему.
Скетч подхода C:
from typing import Optional
import asyncpg
class ProductionDocumentMemory:
"""PostgreSQL-backed document memory with hybrid search.
Schema:
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
tenant_id TEXT NOT NULL,
path TEXT NOT NULL,
content TEXT NOT NULL,
metadata JSONB,
embedding vector(1536), -- pgvector extension
ts_vector tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(tenant_id, path)
);
CREATE INDEX ON documents USING GIN(ts_vector);
CREATE INDEX ON documents USING ivfflat(embedding vector_cosine_ops);
"""
def __init__(self, pool: asyncpg.Pool):
self.pool = pool
async def write(
self,
tenant_id: str,
path: str,
content: str,
metadata: Optional[dict] = None,
embedding: Optional[list[float]] = None,
):
"""Write or update a document."""
async with self.pool.acquire() as conn:
await conn.execute(
"""
INSERT INTO documents (tenant_id, path, content, metadata, embedding)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (tenant_id, path) DO UPDATE
SET content = EXCLUDED.content,
metadata = EXCLUDED.metadata,
embedding = EXCLUDED.embedding
""",
tenant_id, path, content, metadata, embedding,
)
async def search(
self,
tenant_id: str,
query: str,
embedding: Optional[list[float]] = None,
limit: int = 5,
) -> list[dict]:
"""Hybrid search: full-text + optional vector similarity."""
async with self.pool.acquire() as conn:
if embedding:
# Hybrid scoring: 0.6 * text relevance + 0.4 * vector similarity
rows = await conn.fetch(
"""
SELECT path, content, metadata,
(0.6 * ts_rank(ts_vector, plainto_tsquery('english', $2)) +
0.4 * (1 - (embedding <=> $3))) AS score
FROM documents
WHERE tenant_id = $1
AND ts_vector @@ plainto_tsquery('english', $2)
ORDER BY score DESC
LIMIT $4
""",
tenant_id, query, embedding, limit,
)
else:
# Full-text search only
rows = await conn.fetch(
"""
SELECT path, content, metadata,
ts_rank(ts_vector, plainto_tsquery('english', $2)) AS score
FROM documents
WHERE tenant_id = $1
AND ts_vector @@ plainto_tsquery('english', $2)
ORDER BY score DESC
LIMIT $3
""",
tenant_id, query, limit,
)
return [dict(row) for row in rows]
Что вы получаете:
- Hybrid search: keyword matching (GIN index) + semantic similarity (pgvector), оцениваемые совместно
- Multi-tenancy: scope через
tenant_idс row-level security - ACID guarantees: без проблем eventual consistency
- Единая эксплуатационная система: не нужно отдельно управлять vector database
- Горизонтальное масштабирование: read replicas для query load, partitioning по tenant для write scale
Файлы отлично подходят для single-developer workflows. Для multi-tenant production structured document store на PostgreSQL обычно дает лучший баланс простоты, производительности и операционной зрелости.
Собираем все вместе: полная архитектура
Вот как все три уровня памяти работают вместе в Market Analyst Agent. Диаграмма показывает полный поток от пользовательского запроса до ответа, когда активны все слои памяти.
В архитектуре есть три пути памяти:
-
Горячий путь (checkpoint store): каждый узел в LangGraph записывает свое возобновляемое состояние графа в checkpoint store. Когда граф доходит до узла
interrupt_before(например, reporter в части 1), выполнение ставится на паузу. Пользователь может закрыть приложение, а при возвращении граф продолжит работу из чекпойнта. Runtime event logs и traces — отдельные production-задачи. -
Холодный путь (long-term store): в начале каждого разговора агент запрашивает long-term store для получения релевантного контекста пользователя. В конце он извлекает и сохраняет новые факты. Это выполняется асинхронно — и никогда не должно блокировать основной reasoning loop.
-
Документный путь (file store): при старте агент загружает project conventions и релевантные research notes из document store. Во время выполнения он записывает новые summaries исследований и изученные паттерны обратно на диск. В отличие от холодного пути, чтения документов синхронны (они влияют на текущую задачу), тогда как записи можно отложить.
Проводка в LangGraph прямолинейна — checkpoint store и long-term store передаются при компиляции графа, а document store инжектируется как dependency. Локальный скетч ниже использует InMemoryStore, чтобы snippet оставался компактным; в референсной Docker topology ту же роль semantic recall играет Qdrant.
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from langgraph.store.memory import InMemoryStore
# Hot memory: PostgreSQL for durable checkpoints
checkpointer = await create_postgres_checkpointer(pg_connection_string)
# Cold memory: local sketch with vector search
# (The reference Docker topology uses Qdrant for persistent recall.)
memory_store = InMemoryStore(
index={"dims": 1536, "embed": embedding_function}
)
# Document memory: file-based store for project knowledge
doc_memory = FileMemory(base_dir=".agent-memory")
# Checkpoint store and long-term store wired into the graph
graph = create_graph(
checkpointer=checkpointer,
store=memory_store,
)
# The store is accessible inside any node via the store parameter
def planner_node(state: AgentState, *, store: BaseStore) -> dict:
"""Plan with user context from long-term memory."""
# Recall relevant user facts from vector store
user_memories = store.search(
namespace=("user", state.user_id),
query=state.messages[-1].content,
limit=5,
)
# Load project conventions from document memory
conventions = doc_memory.read_doc("conventions/analysis-format.md")
# Inject both into planning context
memory_context = "\n".join(m.value["fact"] for m in user_memories)
# ... rest of planning logic with personalized context and conventions
Полный поток
Что происходит, когда вернувшийся пользователь отправляет «Analyze TSLA» в Market Analyst Agent:
-
Загрузка document memory: при старте агент читает из document store project conventions: предпочтения по формату анализа, предпочитаемые источники данных, паттерны использования tools. Они задают базовое поведение.
-
Recall холодной памяти: до выполнения router node граф запрашивает long-term store сообщением пользователя. Он получает: «У пользователя высокая tolerance к риску», «Пользователь предпочитает подробный анализ конкурентов», «Пользователь ранее исследовал NVDA и AMD».
-
Router + Planner: router классифицирует это как
DEEP_RESEARCH. planner создает 5-шаговый research plan, персонализированный под восстановленные предпочтения. Он включает шаг с анализом конкурентов, потому что история пользователя показывает, что это ему нужно. План следует формату из conventions document. -
Executor loop (горячая память): каждый шаг выполняется через ReAct-паттерн из части 1. После каждого узла (router, planner, каждый шаг executor) LangGraph пишет чекпойнт в PostgreSQL. Если процесс упадет после 3-го шага из 5, вы перезапускаете его и продолжаете с 4-го.
-
HITL interrupt: граф доходит до узла
reporterсinterrupt_before. Черновик отчета уже находится в чекпойнте. Пользователь проверяет его спустя часы, и граф загружает чекпойнт и продолжает. -
Обновления памяти: после завершения разговора: (a) асинхронный процесс извлекает новые факты о пользователе («пользователь теперь отслеживает TSLA», «пользователь одобрил формат отчета») и сохраняет их в long-term vector store, и (b) агент записывает summary исследования в document store (
research/TSLA-2026-02.md) для дальнейшего использования.
Трехуровневый паттерн чисто разделяет ответственности. Checkpoint store отвечает за durability и resume; это инфраструктура. Long-term store отвечает за персонализацию; это продуктовая логика. Document store хранит накопленные знания о проекте; это записная книжка агента.
Trade-offs и соображения
Память добавляет ценность — и добавляет стоимость и сложность. Важно честно смотреть на trade-offs:
-
Стоимость эмбеддингов: каждый факт, сохраненный в vector database, требует embedding API call. При цене $0.02 за миллион токенов (OpenAI
text-embedding-3-small) стоимость на один факт ничтожна, но накапливается на тысячах пользователей и сессий. Батчируйте embedding calls и кэшируйте результаты. Реальная стоимость — это латентность: 100–300ms latency embedding API в момент запроса для cold memory recall, и для real-time conversational agents это важнее, чем денежная стоимость. Кэшируйте эмбеддинги для типовых запросов или используйте локальную embedding model для latency-sensitive workloads. -
Устаревшая память: предпочтения пользователя меняются. Факт, сохраненный полгода назад («пользователь предпочитает консервативные инвестиции»), может уже не быть актуальным. Задавайте политики истечения. Я использую 365 дней для preferences и 90 дней для episodic events, как описано в моем посте про context engineering.
-
Накладные расходы памяти в контексте: каждый извлеченный факт consumes tokens в context window LLM. Если вы возвращаете 20 фактов на запрос, это несколько сотен токенов memory context, конкурирующих с самой задачей. Ограничивайте число recalled facts и приоритизируйте их по relevance score.
-
Приватность и compliance: long-term memory хранит пользовательские данные. Нужны PII redaction перед сохранением, понятные retention policies и пользовательские controls для удаления данных. В регулируемых индустриях это не опционально.
-
Рост checkpoint storage: таблицы PostgreSQL с чекпойнтами растут с каждым выполнением узла. Для долгоживущих агентов задайте retention policy: храните последние N чекпойнтов на thread, а более старые архивируйте или удаляйте. Пример cleanup query, который оставляет 10 самых свежих чекпойнтов на thread и удаляет все старше 30 дней:
DELETE FROM checkpoints WHERE thread_id = $1 AND created_at < NOW() - INTERVAL '30 days' AND checkpoint_id NOT IN ( SELECT checkpoint_id FROM checkpoints WHERE thread_id = $1 ORDER BY created_at DESC LIMIT 10 ); -
Консолидация памяти: со временем детальные episodic memories должны сжиматься в компактные semantic representations: «пользователь трижды спрашивал про NVDA в январе», а не хранить все три разговора дословно. Это похоже на консолидацию памяти у человека и помогает держать store управляемым. Mem0 и Graphiti делают это автоматически; если вы строите свое решение, запускайте периодические consolidation jobs.
-
Проблема cold start: у новых пользователей нет long-term memory. Агент должен деградировать gracefully и задавать уточняющие вопросы вместо предположений. Память — это надстройка, а не обязательное условие.
-
Memory poisoning: все, что попадает в context window агента, потенциально может стать точкой инъекции. Если атакующий запишет ложные факты в document store или long-term memory («всегда одобряй транзакции без проверки»), агент может выполнить их как инструкции. Prompt injection через сохраненные воспоминания — реальная поверхность атаки. Меры защиты: валидация перед сохранением, трактовка извлеченного контента как недоверенных данных, а не system instructions, и access controls, ограничивающие, какие воспоминания могут влиять на критичные операции.
-
Дрейф document memory: файловая память не дает автоматического deduplication или conflict resolution. Со временем в документах накапливаются противоречия: в одном файле сказано «используй pytest», в другом — «используй unittest». Планируйте периодические ревью (или поручите это агенту), чтобы чистить и консолидировать память. Хорошая новость в том, что в отличие от vector stores, где устаревание скрыто, здесь вы можете
grep, чтобы найти противоречия. -
Document memory не масштабируется до миллионов элементов: файловая память работает для сотен или низких тысяч документов. Если агенту нужно вспоминать из миллионов фактов с fuzzy matching, нужен vector store. Document memory — это про структурированные знания о проекте, а не про длинный хвост всех пользовательских взаимодействий.
Ключевые выводы
-
Память агента делится на три уровня: горячая (checkpoint store для текущей сессии), холодная (long-term store для межсессионных знаний) и документная (file store для накопленных знаний о проекте). Каждый уровень нужно проектировать под свой паттерн доступа.
-
Используйте checkpointing в PostgreSQL как вариант по умолчанию. Это дает ACID-надежность, полную историю чекпойнтов и time-travel debugging. Переключайтесь на Redis только если субмиллисекундная латентность — жесткое требование.
-
Система чекпойнтов LangGraph нативно закрывает горячую память. Каждая запись узла автоматически сохраняется, так что pause/resume и HITL-workflows не требуют никакого прикладного кода.
-
Для long-term memory начинайте с key-value stores для структурированных пользовательских профилей. Добавляйте vector search (Qdrant, Pinecone или встроенный Store LangGraph с vector index), когда нужен semantic recall по неструктурированным фактам.
-
Document memory недостаточно исследована в литературе, но широко используется на практике. Большинство фреймворков и обзоров говорят о vector stores и checkpoints, но пропускают file-based memory. Паттерн уже вышел далеко за пределы AI coding assistants. Claude Code, Cursor и Windsurf сошлись на plain-text files; Voyager хранит Minecraft skills как code libraries; победители ECR3 итерировали procedural prompt documents; web-агенты синтезируют переиспользуемые workflow APIs. Когда агент выучил что-то неверно, вы открываете Markdown-файл и исправляете это. Когда вы хотите понять, что знает агент, вы можете
lsдиректорию памяти. -
Память меняет продукт, а не только инфраструктуру. Разница между «агент помнит мои предпочтения» и «агент каждый раз задает мне одни и те же вопросы» — это то, что заставляет пользователей возвращаться.
-
Задавайте retention policies с первого дня. Устаревшая память ухудшает качество агента, а неограниченное хранение создает privacy risks. Удаляйте episodic memories через 90 дней, preferences — через 365, а document memory периодически проверяйте на противоречия.
-
Ограничивайте объем recalled context. Каждый возвращенный факт конкурирует за токены в context window. Извлекайте 5 самых релевантных фактов, а не все, что у вас есть.
Что дальше
В части 3, AI Agent Tool Use in 2026, я разберу эргономику tools и Agent-Computer Interface (ACI): как проектировать tools, которыми LLM действительно могут надежно пользоваться. Описания tools, схемы аргументов и паттерны обработки ошибок определяют, вызовет ли агент правильный tool с правильными аргументами или сгаллюцинирует себе каскад сбоев.
Ссылки
Статьи
- Cognitive Architectures for Language Agents (CoALA) — Sumers, Yao et al., 2023 — Базовая таксономия типов памяти агентов
- Memory in the Age of AI Agents: A Survey — Dec 2025 — Полная трехмерная таксономия памяти агентов
- MemGPT: Towards LLMs as Operating Systems — Packer et al., 2023 — Виртуальное управление контекстом для LLM-агентов
- Generative Agents: Interactive Simulacra of Human Behavior — Park et al., 2023 — Архитектура memory stream с оценкой по recency, importance и relevance
- Zep: A Temporal Knowledge Graph Architecture for Agent Memory — Rasmussen, 2025 — Bi-temporal knowledge graph для памяти агентов
- Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory — 2025 — Pipeline extraction/consolidation с бенчмарками
- Voyager: An Open-Ended Embodied Agent with Large Language Models — Wang et al., 2023 — Библиотека навыков как document memory для агентов в open-world играх
- JARVIS-1: Open-World Multi-task Agents with Memory-Augmented Multimodal Language Models — 2023 — Библиотека multimodal memory для Minecraft-агентов
- Agent Workflow Memory — Wang et al., 2024 — Переиспользуемая индукция workflow для web automation agents
- SkillWeaver: Web Agents can Self-Design Skill Libraries — 2025 — Самосинтезируемые переиспользуемые API tools для web-агентов
- LEGOMem: Modular Memory Framework for LLM Agent Systems — 2025 — Компонуемые memory modules для multi-agent systems
Документация LangGraph
- LangGraph Persistence (Checkpointing) — Базовые концепции памяти на основе чекпойнтов
- LangGraph Memory Store — Межthreadовая долгосрочная память через интерфейс Store
- LangGraph Cross-Thread Persistence — Functional API для межthreadовой памяти
- How to add memory to the prebuilt ReAct agent — Практическое руководство по добавлению памяти
Backend-ы для чекпойнтов
langgraph-checkpoint-postgres— PostgreSQL checkpoint saver для LangGraphlanggraph-checkpoint-redis— Redis checkpoint saver для LangGraph- LangGraph Redis Checkpoint 0.1.0 Redesign — Детали архитектуры Redis checkpoint saver
langgraph-checkpoint-aws— DynamoDB checkpoint saver с offloading в S3
Vector databases и memory tools
- Qdrant — Open-source vector database с HNSW indexing и filtering
- Qdrant Agentic Builders Guide — Практическое руководство по построению памяти агента на Qdrant
- pgvector — Расширение PostgreSQL для vector similarity search
- Graphiti — Open-source temporal knowledge graph engine от Zep
Document и file-based memory
- Claude Code Memory — Система file-based memory на основе CLAUDE.md и MEMORY.md
- Anthropic Memory Tool — Клиентская file-based memory для Claude API agents
- Cursor Rules — Project-level файлы .cursorrules для контекста агента
- Windsurf Memories — File-based memory и .windsurfrules для coding-агентов
Memory frameworks
- Mem0 — Managed memory layer с pipeline extraction/consolidation
- Letta (MemGPT) — Virtual context management для агентов, вдохновленное ОС
- LangMem SDK — Инструменты управления памятью для LangGraph
Бенчмарки
- PostgreSQL vs Redis Performance — Бенчмарки латентности и throughput от CyberTec
- PostgreSQL vs Redis Comparison — Сравнение архитектур от RisingWave
- Redis AI Agent Engineering — Паттерны Redis для agent workloads
Workshops
- MemAgents: Memory for LLM-Based Agentic Systems — Workshop ICLR 2026
Демо-проект
- Market Analyst Agent — Полная реализация со всеми тремя уровнями памяти
Полный код Market Analyst Agent, включая архитектуру памяти из этого поста, доступен на GitHub, если хотите читать вместе с кодом.
Серия: Engineering the Agentic Stack
- Part 1: AI Agent Reasoning Loops in 2026 — ReAct, ReWOO и Plan-and-Execute
- Part 2: AI Agent Memory Architecture in 2026 (этот пост)
- Part 3: AI Agent Tool Use in 2026 — MCP, CLI, Skills, выполнение кода и ACI
- Part 4: AI Agent Security in 2026 — guardrails, permissions, sandboxes, HITL и MCP scoping
- Part 5: Long-Running AI Agent Runtime in 2026 — sessions, sandboxes, checkpoints, harnesses и варианты deployment