В первой части я рассказывал про agent-memory-mcp версии 0.1.0: MCP-сервер на Go, SQLite-хранилище, четыре типа памяти, semantic search, RAG по документации, PathGuard и transport через stdio/HTTP.
Тогда это был довольно прямой инструмент: агент может сохранить знание, потом найти его по смыслу, а рядом лежит индекс документов проекта.
После этого проект прожил несколько месяцев реального использования. И почти всё интересное произошло не там, где я изначально ожидал.
Оказалось, что главная проблема не в том, чтобы “куда-то записать память” и потом сделать cosine similarity. Это базовый слой. Реальная боль начинается дальше:
-
как не смешать embeddings от разных моделей и не получить уверенно неправильный recall;
-
как не превратить память в свалку через две недели;
-
как понять, что знание устарело;
-
как заставить агента не просто помнить, а поддерживать рабочий контекст проекта;
-
как автоматически забирать полезные выводы из сессий, но не сохранять каждый шумный промежуточный шаг;
-
как сделать локальный инструмент достаточно безопасным и стабильным, чтобы его можно было оставить работать сервисом.
На момент подготовки текста проект дошёл до v0.8.0 (tag от 2026-05-06). Ниже — не changelog, а разбор того, почему архитектура изменилась именно так.
Репозиторий: github.com/ipiton/agent-memory-mcp
Коротко: что было и что стало
В 0.1.0 ядро выглядело так:
|
Было в 0.1.0 |
Зачем |
|---|---|
|
4 типа памяти: |
Не хранить всё как плоские заметки |
|
Semantic search через embeddings |
Искать по смыслу, а не только по словам |
|
RAG-индекс документов |
Отделить память агента от проектной документации |
|
SQLite |
Zero-ops: один бинарник, один файл БД |
|
stdio + HTTP transport |
Работать и как MCP-сервер, и как HTTP-сервис |
|
PathGuard |
Не дать агенту читать произвольные пути |
К 0.8.0 это превратилось в другой класс системы:
|
Стало |
Почему появилось |
|---|---|
|
|
Fallback между embedding-моделями оказался опаснее отказа |
|
Hybrid retrieval + source-aware ranking |
Чистый semantic search слишком шумит на инженерных запросах |
|
Trust/freshness metadata |
Агенту важно знать не только “похоже”, но и “можно ли этому верить” |
|
Session close pipeline |
Полезные знания рождаются в конце работы, а не только в ручных |
|
Claude Code hooks |
Ручная дисциплина не масштабируется даже для одного разработчика |
|
Canonical knowledge + project bank |
Нужен слой поддерживаемых знаний, а не только raw memory |
|
Stewardship layer |
Память стареет, дублируется и конфликтует сама с собой |
|
Sedimentation |
Не все знания должны всплывать одинаково часто |
|
Structure-aware RAG |
Markdown-документы нельзя резать только по символам |
|
Multi-hop recall |
Некоторые ответы требуют цепочки связей, а не одного похожего текста |
|
SQLite/WAL hardening |
“Один файл БД” всё равно требует нормальной эксплуатации |
Главная смена фокуса:
agent-memory-mcpперестал быть просто memory tool. Он стал memory + docs + repo context layer for engineering agents.
Или короче: memory backbone для инженерных агентов.
Урок 1. Time-to-value важнее списка возможностей
Первая версия была инженерно понятной, но не очень дружелюбной на старте. Нужно было разобраться с env-переменными, путями к данным, embedding-провайдерами, настройками MCP-клиента, индексированием документов.
Для меня как автора это нормально: я знаю, где что лежит. Для пользователя — нет.
Поэтому одним из первых направлений стал solo local режим:
.agent-memory/ rag-index/ memory-store/ logs/
Одна директория данных, один ожидаемый layout, один быстрый smoke path:
agent-memory-mcp store -content "Solo local smoke check" -type working -tags "smoke,local"agent-memory-mcp recall "solo local smoke"agent-memory-mcp indexagent-memory-mcp search "agent memory"
Появились:
-
автозагрузка
.envиз текущего проекта; -
configcommand для генерации MCP-конфигов под Claude Desktop, Cursor и Codex; -
Homebrew tap и GoReleaser;
-
make local-smoke; -
документация “start local in 3 minutes” вместо длинной простыни про все режимы сразу.
Это не выглядит как архитектурная фича, но на практике сильно меняет продукт.
Память агента должна быть включена до того, как пользователь устал её настраивать. Если первый запуск требует слишком много решений, человек не дойдёт до момента, где инструмент начинает окупаться.
Урок 2. Fallback embeddings был неправильной отказоустойчивостью
В первой статье я уже писал про проблему embedding model mismatch. В 0.1.0 архитектура выглядела красиво: Jina AI как primary, OpenAI-compatible API и Ollama как fallback.
На схеме это отказоустойчивость.
В реальности это ловушка.
Embeddings от разных моделей живут в разных векторных пространствах. Даже если размерность совпала, cosine similarity между векторами от разных моделей не имеет полезного смысла.
Самый неприятный режим отказа: система не падает. Она возвращает результаты. Просто неправильные.
То есть агент не говорит “не смог найти”. Он уверенно приносит нерелевантный контекст, и дальше весь reasoning строится на мусоре.
После этого в проекте появились три изменения.
local-only режим
Если выставить:
MCP_EMBEDDING_MODE=local-only
то сервер не ходит в Jina и OpenAI-compatible endpoints. Embeddings генерируются только через локальный Ollama.
Это решает сразу две задачи:
-
privacy — текст документов и памяти не уходит наружу;
-
consistency — все вектора живут в одном пространстве.
embedding_model как часть данных
Для memory store и RAG-индекса стало важно явно знать, какой моделью построен embedding.
При recall система больше не смешивает записи из разных embedding spaces. Если запись была построена другой моделью, она не участвует в semantic similarity как равная текущим записям.
Да, это может дать меньше результатов. Но “меньше результатов” лучше, чем “неправильные результаты с высоким score”.
reembed
Смена модели теперь трактуется не как runtime fallback, а как миграция данных.
agent-memory-mcp reembed
Это принципиальная разница:
-
fallback отвечает на вопрос “что делать, если провайдер временно недоступен?”;
-
re-embed отвечает на вопрос “как перевести корпус на новую модель?”.
В первой версии эти две вещи были смешаны. После реального использования стало понятно, что их надо развести.
Урок 3. Semantic search недостаточен для инженерной памяти
Cosine similarity хорошо работает как базовый recall. Но инженерные запросы редко бывают просто “найди похожий текст”.
Агент спрашивает:
-
“какой runbook подходит для этого инцидента?”;
-
“почему мы отключили HPA?”;
-
“что недавно меняли в ingress?”;
-
“какие caveats есть у этой миграции?”;
-
“мы уже пробовали такой подход?”.
Для таких вопросов важно не только смысловое сходство.
Важны:
-
тип источника: ADR, runbook, postmortem, changelog, Helm, Terraform, CI config;
-
свежесть;
-
confidence;
-
owner;
-
last verified date;
-
совпадение ключевых терминов;
-
контекст проекта или сервиса;
-
является ли запись canonical или это сырая заметка из сессии.
Так появился hybrid retrieval.
Source-aware ingestion
RAG-индекс начал классифицировать инженерные артефакты:
-
docs;
-
ADR/RFC;
-
changelog;
-
runbooks;
-
postmortems;
-
CI configs;
-
Helm/Terraform/Kubernetes файлы.
Это нужно не для красивой метаданной. Это меняет ranking.
Если запрос похож на incident response, runbook и postmortem должны иметь другой вес, чем обычный README. Если запрос про миграцию, caveat из прошлой миграции может быть важнее общего architectural overview.
Hybrid ranking
Вместо “только cosine” ranking стал учитывать несколько сигналов:
-
semantic similarity;
-
keyword/BM25-like matching;
-
recency;
-
source type;
-
trust/freshness;
-
importance;
-
lifecycle status.
Это менее элегантно, чем один score из embedding-модели. Зато лучше соответствует реальным вопросам агента.
В инженерном поиске точное слово иногда важнее “похожего смысла”. Если в запросе есть HPA, ingress, billing-api или migration #47, нельзя полностью отдавать ранжирование на откуп embedding-модели.
Explainable retrieval
Ещё один вывод: если retrieval странный, его надо уметь разбирать.
Появился debug mode, который показывает:
-
какие фильтры применились;
-
какие score-компоненты участвовали;
-
какие boosts сработали;
-
почему результат оказался выше или ниже.
Потом появился lightweight retrieval console на /console.
Для обычного пользователя UI не обязателен. Для разработки retrieval — очень полезен. Иначе tuning превращается в гадание: “кажется, стало лучше”.
Eval suite
К v0.7.0 в проекте появился RAG eval harness с fixture corpus и baseline metrics.
Это важный сдвиг: ranking перестал быть набором интуитивных весов, которые можно случайно сломать. Теперь изменения в retrieval можно прогонять через regression gate.
Да, тестовый deterministic embedder не заменяет реальную embedding-модель. Но он фиксирует contract: если поменяли chunking, ranking или source boosts, нужно увидеть, какие вопросы стали хуже искать ожидаемые документы.
Урок 4. Память нельзя только дописывать
Самая большая ошибка в memory-системах — считать, что проблема решается append-only storage.
Агент сохраняет:
-
одно решение;
-
потом уточнение;
-
потом workaround;
-
потом “workaround больше не нужен”;
-
потом похожую запись другими словами;
-
потом session summary, где всё это повторено ещё раз.
Через месяц recall начинает приносить смесь актуальных решений, устаревших фактов и промежуточных мыслей.
В первой версии у памяти были типы. Но типа недостаточно.
Нужен жизненный цикл.
Lifecycle status
Появились состояния вроде:
-
active;
-
outdated;
-
superseded;
-
canonical.
mark_outdated может связать старую запись с новой через supersession chain. Старое знание не удаляется, потому что оно всё ещё полезно для истории: “почему мы тогда так сделали?” Но в обычном recall оно должно ранжироваться ниже.
Отсюда же выросли temporal-запросы:
-
recall_as_of— что было верно на конкретную дату; -
knowledge_timeline— как менялось знание по теме.
Это уже не просто “заметки”. Это маленькая модель эволюции инженерного знания.
Canonical knowledge
Raw memory полезна, но агенту нужен слой “поддерживаемого знания”.
Так появился canonical layer:
-
promote_to_canonical; -
list_canonical_knowledge; -
recall_canonical_knowledge; -
project summaries, которые сначала показывают canonical context, а потом сырые записи.
Идея простая: не все сохранённые записи равны. Некоторые — наблюдения. Некоторые — решения. Некоторые прошли проверку и должны всплывать первыми.
Project bank
Потом стало ясно, что “список memories” — плохой интерфейс для поддерживаемого проекта.
Нужны представления:
-
decisions;
-
runbooks;
-
incidents;
-
caveats;
-
migrations;
-
review queue;
-
canonical overview.
Так появился project_bank_view.
Это хороший пример того, как низкоуровневые memory tools постепенно превращаются в доменные инструменты. Агенту проще спросить “покажи caveats по миграциям”, чем самому фильтровать сырые записи по tags/type/metadata.
Урок 5. Конец сессии важнее ручного store_memory
В первой версии я в основном думал о ручном сохранении:
Нашли важное решение -> вызвали store_memory.
Но реальная работа с агентом устроена иначе.
Во время сессии происходит много шума:
-
гипотезы;
-
неудачные проверки;
-
чтение файлов;
-
промежуточные планы;
-
правки;
-
тесты;
-
откаты;
-
уточнения.
Если сохранять всё подряд, память умирает. Если полагаться на ручную дисциплину, часть знаний теряется.
Лучшее место для извлечения reusable knowledge — конец сессии.
К этому моменту уже понятно:
-
что действительно изменилось;
-
какие решения были приняты;
-
какие подходы не сработали;
-
что надо проверить позже;
-
какие старые записи стали устаревшими.
Так появился session close pipeline.
close_session
close_session анализирует summary законченной работы и строит план:
-
new— создать новую запись; -
update— обновить существующую; -
merge— объединить дубли; -
outdate— пометить старое знание как устаревшее; -
raw_only— сохранить только сырое summary без автоматических выводов.
Главное — он не обязан всё применять сразу.
Каждое действие получает rationale и risk level. Низкорисковые действия можно применить автоматически, спорные уходят в review queue.
Session modes
Потом появился ещё один слой: режим сессии.
Кодинг-сессия и incident-сессия должны закрываться по разным правилам.
Для обычной coding-сессии можно агрессивнее auto-apply low-risk updates. Для incident или migration лучше review-first: там цена неверного вывода выше.
Поэтому session close учитывает mode:
-
coding; -
incident; -
migration; -
research; -
cleanup.
Это не “красивое enum-поле”. Это способ не применять одинаковую политику к разным типам инженерной работы.
Dead ends
Отдельно появился store_dead_end.
Это важная категория памяти, которую легко недооценить. Агенту нужно помнить не только что сработало, но и что уже пробовали и почему отказались.
Пример:
Пробовали ускорить deploy через parallel helm upgrades.Отказались: race в shared CRD migration, два релиза одновременно ломают schema ownership.
Через три месяца похожая идея снова выглядит заманчиво. Хорошая memory-система должна поднять прошлый dead end как warning, а не дать команде повторить тот же круг.
Урок 6. Автоматизация нужна, но только с review boundary
Следующий логичный шаг после close_session — интеграция с реальными агентами.
Для Claude Code появились hooks:
|
Hook |
Что делает |
|---|---|
|
|
вызывает |
|
|
вызывает |
|
|
сохраняет checkpoint перед сжатием контекста |
Это сильно меняет ergonomics.
Раньше агент должен был помнить: “в начале вызови recall, в конце сохрани summary”.
Теперь часть этого становится инфраструктурой.
Но тут есть опасность. Автоматическая память без review boundary быстро становится автоматическим загрязнением.
Поэтому hooks не делают “всё в canonical”. Они сохраняют raw summary, строят план, применяют безопасное и складывают спорное в review queue.
Dedup для checkpoint’ов
Ещё один практический момент: автоматические checkpoint’ы легко дублируются.
Если PreCompact или SessionEnd несколько раз сохраняют близкие summaries, память зарастает одинаковыми “session checkpoint” записями.
Поэтому появился Jaccard dedup:
-
сравниваем checkpoint с недавними записями в том же context;
-
если похожесть выше threshold, запись пропускается;
-
threshold/window/min length настраиваются через
MCP_CHECKPOINT_DEDUP_*.
Это не сложная ML-задача. Это простая санитарная защита, без которой automation быстро портит corpus.
Performance hooks тоже имеет значение
Один из релизов был почти комично практичным: lightweight context-inject.
Хук SessionStart должен быть быстрым. Если для старта сессии поднимать весь Store, инициализировать embeddings, RAG и фоновые компоненты, пользователь будет ждать там, где он хотел просто начать работу.
В итоге context-inject получил read-only fast path: открыть SQLite в WAL mode=ro, взять недавние записи и pending summaries, вывести в stdout.
Вывод: hooks — это не просто “вызвать CLI”. Это часть UX. Они должны быть быстрыми, предсказуемыми и не ломать запуск инструмента.
Урок 7. Stewardship: память должна обслуживать сама себя
После lifecycle и session close стало понятно, что нужна отдельная подсистема обслуживания знаний.
Так появился stewardship layer.
Его задача — не отвечать на запросы агента, а поддерживать качество корпуса.
steward_run делает maintenance cycle:
-
ищет дубли;
-
ищет конфликты;
-
ищет stale entries;
-
предлагает canonical promotion candidates.
Действия делятся на:
-
safe_auto_apply; -
review_required.
Спорные действия попадают в stewardship inbox.
Почему это отдельный слой
Можно было бы засунуть всё в recall_memory: при каждом поиске пытаться понять, что устарело, где дубли, что надо промотировать.
Но это плохая граница ответственности.
Recall должен быстро отвечать на вопрос. Stewardship должен периодически обслуживать базу знаний.
Это разные режимы:
-
recall path — latency-sensitive;
-
stewardship path — batch/maintenance, может делать больше анализа и оставлять audit trail.
Drift detection
Особенно полезным оказался drift_scan.
Память может ссылаться на файл, runbook или архитектурное решение. Но проект меняется. Файл удалили, runbook переписали, конфиг переехал.
Drift scan сравнивает memory against live sources и поднимает случаи вроде:
-
source file изменился после last verification;
-
source path больше не существует;
-
canonical entry давно не проверялась.
Для инженерного агента это критично. Хуже устаревшей документации только устаревшая память, которую агент считает актуальной.
Verification model
Появились:
-
verify_entry; -
verification_candidates; -
canonical health diagnostics.
Это кажется бюрократией, пока не видишь, как агент использует старую запись из прошлого месяца и уверенно предлагает неактуальный rollback path.
Для живого проекта знание без freshness и verification — это просто текст с неизвестной надёжностью.
Урок 8. Sedimentation: не вся память должна всплывать одинаково
Один из экспериментальных слоёв — memory sedimentation.
Идея такая: у памяти есть type, но type отвечает на вопрос “что это за знание?”.
Например:
-
episodic— событие; -
semantic— факт; -
procedural— процедура; -
working— текущий контекст.
Но есть другой вопрос: насколько это знание load-bearing?
Для этого появился sediment_layer:
|
Layer |
Поведение |
|---|---|
|
|
сессионное/задачное, всплывает только при совпадении context |
|
|
участвует в recall с небольшим penalty |
|
|
обычный слой |
|
|
важное знание, которое должно всплывать почти всегда |
Почему не заменить этим type?
Потому что это ортогональные измерения.
Инцидент может остаться episodic по природе, но со временем стать настолько важным, что его надо поднимать как character-level warning. Например: “никогда не запускать эту миграцию параллельно с deploy worker’ов”.
Sedimentation cycle предлагает переходы:
surface -> episodic -> semantic -> character
Тривиальные переходы можно auto-apply. Нетривиальные — через review queue.
Сейчас это экспериментальный слой. Но сама мысль оказалась полезной: relevance — это не только similarity. У памяти есть “геология”: свежие поверхностные записи, рабочие факты, подтверждённые знания и очень важные ограничения, которые должны переживать отдельные сессии.
Урок 9. RAG по Markdown нельзя строить только на char chunks
В первой версии RAG был простым:
-
прочитать документ;
-
разбить на чанки;
-
построить embeddings;
-
искать по cosine similarity.
Это работает, но плохо понимает структуру Markdown.
Большинство инженерных документов уже имеют смысловую структуру:
# Service Deployment## Rollback### Database migration rollback
Если резать документ только по символам, можно потерять связь чанка с разделом. Агент найдёт кусок текста, но не поймёт, где он находится и какой родительский контекст у него был.
В v0.7.0 появился structure-aware Markdown chunking:
-
парсится дерево заголовков;
-
каждый chunk получает breadcrumb;
-
шумные секции вроде TOC/References/Changelog можно отбрасывать;
-
у результата есть
SectionPathиSectionKey; -
ExpandSectionпозволяет достать весь раздел по pointer’у.
Это улучшает не только качество поиска, но и UX результата.
Вместо “вот похожие 800 символов” агент может получить: “это кусок из Deployment > Rollback > Database migration rollback, хочешь развернуть весь раздел?”
Для RAG по инженерным документам это важнее, чем кажется. Документация живёт разделами, а не случайными окнами символов.
Урок 10. Иногда нужен multi-hop recall
Single-hop recall отвечает на вопрос:
какие записи похожи на этот запрос?
Но часть инженерных вопросов устроена иначе:
почему мы выбрали X, какие feedback memories к этому привели, и какие incident/postmortem потом подтвердили или опровергли решение?
Это уже цепочка.
Для этого появился слой memory_triples:
subject --relation--> object
Triple extractor может асинхронно извлекать 3-7 связей из новой memory-записи. Потом recall_multihop делает graph walk по этим связям и возвращает не только найденные memories, но и path, который к ним привёл.
Примерно так:
billing migration -> caused -> index lockindex lock -> mitigated_by -> rollback runbookrollback runbook -> references -> incident 2026-02
Важно: это optional layer. Он требует LLM-backed extractor и backfill через:
agent-memory-mcp index-triples
Я не хочу делать вид, что graph memory бесплатна. Она добавляет complexity, latency, фоновые goroutines и новые failure modes.
Но для вопросов “как одно решение связано с другим” обычный semantic search часто слаб. Он ищет похожий текст, а не объясняет цепочку.
Урок 11. Shared service начинается с неприятных деталей
Первая версия была local-first. Это всё ещё базовая идея: один пользователь, локальная SQLite, stdio transport.
Но постепенно появился shared-service path:
solo local -> team laptop -> shared service
Для этого добавились:
-
Docker Compose recipe;
-
shared env template;
-
nginx reverse proxy example;
-
HTTP auth token;
-
безопасные дефолты для host binding;
-
threat model;
-
backup/restore docs;
-
Homebrew service mode;
-
config hot-reload для RAG-настроек.
HTTP defaults
HTTP mode теперь bind’ится на 127.0.0.1 по умолчанию.
Если хочется слушать 0.0.0.0, нужен MCP_HTTP_AUTH_TOKEN, либо явный небезопасный opt-in:
MCP_HTTP_INSECURE_ALLOW_UNAUTHENTICATED=true
Это тот случай, где лучше раздражать пользователя явной ошибкой, чем случайно открыть память проекта в сеть.
Brew services и hot-reload
Homebrew service preset тоже оказался не просто packaging.
Сервис может стартовать на login, хранить config в predictable path и подхватывать часть изменений без рестарта:
kill -HUP $(pgrep agent-memory-mcp)
Но здесь проявилась ещё одна практическая вещь: Homebrew sandbox не даёт post_install писать в ~/. Поэтому автоматическая настройка Claude hooks через brew install сначала появилась, потом была исправлена, потом убрана из post_install как неправильная граница.
Интеграции с пользовательским home directory должны быть явным agent-memory-mcp setup, а не скрытым side-effect пакетного менеджера.
Урок 12. SQLite всё ещё хороший выбор, но его надо уважать
В первой статье я защищал SQLite: один файл, простой backup, zero-ops, достаточно для локального и небольшого team-сценария.
Я всё ещё считаю это правильным выбором.
Но v0.8.0 появился не из теории, а из неприятного инцидента.
index_documents завис на SQLITE_BUSY примерно на 25 часов. Снаружи это выглядело как постоянная ошибка:
{ "code": -32000, "message": "document indexing failed", "data": "failed to mark index state dirty: ... database is locked"}
Диагноз:
-
внешний процесс БД не держал;
-
lock держал сам
agent-memory-mcp; -
на диске был rollback journal, хотя ожидался WAL;
-
одна write-транзакция не откатилась корректно;
-
без busy timeout любая contention превращалась в мгновенный hard error.
Корневой урок: “мы включили WAL в DSN” не равно “WAL реально включился”.
После этого появился internal/dbutil:
-
открыть SQLite;
-
явно применить
PRAGMA busy_timeout=5000; -
явно применить
PRAGMA journal_mode=WAL; -
проверить возвращённый mode;
-
выставить
synchronous=NORMAL; -
использовать общий helper и для
memories.db, и дляvectors.db.
Плюс аудит транзакций на defer tx.Rollback().
Плюс логирование ошибок index_documents/search в service log, а не только в JSON-RPC response.
Это скучная часть архитектуры. Но именно она отличает “игрушку, которую можно перезапустить” от инструмента, который можно оставить в фоне.
Урок 13. Performance проблемы появляются там, где появляется зрелость
Когда в системе были только store/recall/search, performance был простым.
Потом появились:
-
stewardship scans;
-
session tracker;
-
sediment cycle;
-
archive sweep;
-
multi-hop recall;
-
review queue;
-
background checkpoints.
И сразу всплыли N+1 и лишние roundtrip’ы в SQLite.
Например, один steward regression дал примерно 24x slowdown.
Причина оказалась не в “SQLite медленный”, а в архитектурной мелочи: cache-resident memory не хранил raw Metadata, а steward’у нужны были поля вроде service/type/lifecycle. Поэтому он ходил через Store.List, который тянул full corpus из SQLite.
Фикс:
-
добавить
Metadataв cached memory; -
сделать
ListLightweight(filters)cache-only path; -
переключить
loadActiveMemories.
Benchmark на 2000 memories: примерно 32ms -> 8.6ms/op.
Похожая история была в других местах:
-
RecallMultihopделалGetper result -> заменено на batchgetBatch; -
getBatchполучил chunking по 500 ids, чтобы не упереться в SQLite variable limit; -
sediment cycle перестал перезагружать review queue на каждый candidate;
-
archive sweep стал pre-load’ить existing review items;
-
access stats стали flush’иться в одной транзакции;
-
session checkpoints ушли с hot path в bounded background pool.
Это нормальная эволюция. Сначала делаешь понятную модель. Потом реальные workflows показывают, где она N+1.
Главное — чинить root cause, а не добавлять sleep/retry вокруг симптома.
Что подтвердилось
Несмотря на большое количество изменений, несколько решений из 0.1.0 подтвердились.
Go подходит
Один бинарник, нормальная concurrency-модель, простой deploy, хорошая стандартная библиотека, удобный CLI/service story.
Для локального MCP-инструмента это всё ещё хороший выбор.
SQLite подходит
После WAL/busy-timeout hardening SQLite остаётся правильным default.
Postgres или vector DB не нужны на старте. Они добавили бы operational burden раньше, чем появилась бы реальная необходимость.
Типизированная память лучше плоских заметок
episodic/semantic/procedural/working пережили все изменения.
Поверх них появились lifecycle, trust, sediment layers, engineering taxonomy, но базовая идея осталась: память агента не должна быть одним мешком строк.
MCP как граница интеграции работает
Один memory layer можно подключать к разным агентам и клиентам. Это по-прежнему главная причина существования проекта.
Что пришлось пересмотреть
“Semantic search решает recall”
Нет. Semantic search — только один сигнал.
Для инженерного знания нужен hybrid retrieval, metadata, freshness, source type, keyword matching, lifecycle и sometimes reranking.
“Fallback между embedding-провайдерами повышает надёжность”
Не в такой форме.
Fallback между разными embedding spaces создаёт silent correctness bug. Теперь смена модели — это migration через reembed, а не прозрачное runtime-переключение.
“Пусть агент сам сохраняет важное”
Частично работает, но недостаточно.
Нужны session close, hooks, dedup, review queue и policies. Иначе агент либо забывает сохранить важное, либо сохраняет слишком много.
“Память можно чистить вручную”
На маленьком corpus — можно.
Как только появляются auto-capture, checkpoints, incidents, runbooks, migrations и canonical entries, нужен stewardship.
“SQLite WAL включается одной строкой”
Лучше проверить.
И лучше иметь busy timeout, rollback discipline, logging и shutdown drain.
Что всё ещё не идеально
v0.8.0 не делает проект “завершённым”.
Открытые зоны:
-
testability и architectural splits ещё продолжаются (
T57/T58); -
некоторые файлы всё ещё слишком большие (
tools_registry,rag, config); -
multi-hop layer требует optional LLM extractor и backfill;
-
sedimentation пока experimental и требует больше production data;
-
local neural reranker описан как design, но не реализован;
-
для больших team-corpus всё ещё может понадобиться другой storage/retrieval backend.
Важно другое: теперь эти ограничения видны явно. Они не спрятаны под видом “у нас semantic search, значит всё хорошо”.
Практические выводы, если вы строите похожую систему
1. Записывайте embedding model рядом с embedding.
Не смешивайте вектора разных моделей в одном similarity search. Silent mismatch хуже ошибки.
2. Делайте local-first путь простым.
Один layout данных, один smoke test, один быстрый старт. Memory tool должен начать приносить пользу до того, как пользователь ушёл читать документацию.
3. Не доверяйте одному score.
Для инженерных запросов cosine similarity — только часть ranking. Добавляйте source type, keyword matching, freshness, confidence и domain-specific boosts.
4. Разделяйте raw memory и maintained knowledge.
Сырые session summaries полезны, но агенту нужен canonical/project-bank слой.
5. Любая автоматическая запись должна иметь санитарные границы.
Dedup, review queue, risk levels, mode-aware policy. Иначе automation превращает память в мусор быстрее, чем ручной ввод.
6. Учитывайте старение знания с первого дня.
Outdated/superseded/verified/freshness — это не enterprise bureaucracy, а защита от уверенно неправильного контекста.
7. Эксплуатационные мелочи не вторичны.
WAL, busy timeout, graceful shutdown, service logs, auth defaults — всё это напрямую влияет на доверие к memory layer.
Заключение
Первая версия agent-memory-mcp отвечала на вопрос:
как дать AI-агенту persistent memory?
После нескольких месяцев использования вопрос изменился:
как сделать так, чтобы агент вспоминал правильное, актуальное, проверенное и полезное знание, а не просто похожий текст из прошлого?
Именно поэтому проект вырос в сторону hybrid retrieval, session capture, canonical knowledge, stewardship, sedimentation, multi-hop graph recall и operational hardening.
Самый важный вывод для меня: memory для AI-агентов — это не storage feature. Это жизненный цикл знания.
Знание появляется в сессиях, проходит через review, становится canonical, стареет, конфликтует с новыми фактами, иногда превращается в dead end, иногда становится load-bearing правилом проекта.
Если всё это не моделировать, агент будет “помнить”, но не обязательно будет помнить правильно.
Репозиторий: github.com/ipiton/agent-memory-mcp
ссылка на оригинал статьи https://habr.com/ru/articles/1033388/