П/ВИН

AI агент для разработки: память через observer Claude-Mem

·9 мин чтения

Закрываешь терминал вечером, открываешь утром - и AI-агент снова тащит ту же гипотезу, которую мы отвергли два часа назад. Я ловлю эту боль каждый раз, когда сажаю ai агент для разработки на длинную задачу: рефакторинг крупного модуля, SEO-аудит сайта на 150+ страниц, отладка с десятком гипотез. Каждая новая сессия начинается с нуля. Агент не помнит, что уже пробовал, какой root cause нашёл и почему именно это решение было выбрано. Контекст-окно конечно, история сессий изолирована, и вся накопленная мудрость испаряется ровно в момент, когда я закрываю терминал.

Именно эту проблему я решал, разворачивая инструмент Claude-Mem — специализированный observer, который в реальном времени смотрит за тем, что делает основная сессия Claude Code, и превращает поток событий в структурированные, searchable наблюдения. Это не транскрипт и не лог — это именно осмысленная память, которая в будущих сессиях помогает агенту мгновенно поднять контекст и не повторять старых ошибок.

Зачем вообще наблюдатель отдельно от основной сессии

Изначально я пробовал решать задачу проще — складывал важные находки в KNOWLEDGE.md файлы внутри проектов и в глобальный MEMORY.md. Этот подход работает, но у него есть фундаментальный недостаток: основной агент во время работы должен сам решать, что важно записать. А он, занятый реальной задачей (фиксит баг, пишет фичу, делает деплой), часто забывает обновить knowledge-файлы или записывает только то, что было критично прямо сейчас. Получается селективная и предвзятая память.

Claude-Mem перевернул эту логику. Вместо того чтобы заставлять основного агента быть архивистом самого себя, я выделил отдельную роль — observer. Это вторая сессия Claude, у которой есть только одна задача: смотреть на события основной сессии (вызовы инструментов, их параметры, результаты) и фиксировать то, что было LEARNED / BUILT / FIXED / DEPLOYED / CONFIGURED. У observer-а нет доступа к инструментам — он не может ни читать файлы, ни запускать команды. Только наблюдает и пишет наблюдения. Это разделение ответственности оказалось ключевым: основной агент работает на полную, а observer параллельно строит память.

Как устроен поток наблюдений

Каждое событие в основной сессии — Bash, Read, Write, Edit — приходит к observer-у в виде структурированного XML-блока:

<observed_from_primary_session>
  <what_happened>Bash</what_happened>
  <occurred_at>2026-05-01T11:49:15.035Z</occurred_at>
  <working_directory>/home/deploy/projects/v0-german</working_directory>
  <parameters>"{...}"</parameters>
  <outcome>"{...}"</outcome>
</observed_from_primary_session>

Задача observer-а — на основе этого потока выдать наблюдение в фиксированном формате:

<observation>
  <type>discovery</type>
  <title>Root Cause Isolated: Category Page Lacks try/catch</title>
  <subtitle>app/blog/page.tsx wraps Supabase calls in try/catch; 
    [category]/page.tsx does not — causing HTTP 500.</subtitle>
  <facts>
    <fact>app/blog/page.tsx (returns static page) wraps all calls</fact>
    <fact>app/blog/[category]/page.tsx throws unhandled errors</fact>
  </facts>
</observation>

Типов наблюдений несколько: discovery (что-то выяснилось), decision (принято решение), feature (создана функциональность), fix (исправлена ошибка), deploy (что-то выкачено в прод). Эта типизация позволяет потом фильтровать память — например, при следующей работе с тем же проектом вытащить все decision за последний месяц и понять, какие архитектурные решения уже приняты.

Реальные кейсы из боевых сессий

Лучше всего полезность Claude-Mem видна на конкретных примерах. Возьму три эпизода из последних сессий разработки.

Кейс 1: Фича lib/profile.ts в проекте draw-collab

Основной агент создавал helpers для localStorage-профилей в проекте collaborative-рисования. Observer зафиксировал не только сам факт создания файла, но и тонкости дизайна: профиль владельца хранится под ключом draw.profile.owner, гостевой — под draw.profile.guest, цветовая палитра жёстко зафиксирована и не должна меняться между релизами (иначе у пользователей внезапно сменятся цвета их курсоров). Это именно та категория знаний, которую через месяц забудешь — а observer её сохранил.

Интересный момент: observer заметил расхождение между дизайн-документом и реализацией. В спеке 2026-05-01-shareable-collab-design.md профиль владельца описан как { name, color } без userId, а в коде Profile тип содержит userId для обоих типов профилей. Observer отметил это как discovery — расхождение спека и кода. Когда через неделю кто-то полезет менять auth-логику, эта запись поможет понять, почему userId нужен (и что спек неактуален).

Кейс 2: SEO-аудит x10seo.ru и 500-е ошибки в sitemap

Здесь основной агент решал классическую SEO-задачу: четыре URL в sitemap возвращали 500 — /blog/pf-osnovy, /blog/tutorials, /blog/cases, /blog/news. Observer проследил весь путь к root cause:

  1. Структура проекта — Next.js App Router с sitemap.ts, динамическими маршрутами app/blog/[category]/[slug]/page.tsx, Supabase в качестве источника данных.
  2. Гипотеза №1 отвергнута — компоненты блога чистые, рендеринговых багов нет.
  3. Гипотеза №2 подтверждена — app/blog/page.tsx оборачивает все Supabase-вызовы в try/catch, а app/blog/[category]/page.tsx — нет. Любая ошибка Supabase летит в Next.js и превращается в HTTP 500.

Каждый из этих шагов был зафиксирован отдельным наблюдением. Если через три месяца появится похожая проблема, агент в следующей сессии быстро найдёт паттерн «динамические category-страницы без try/catch на Supabase-вызовах» и сразу пойдёт проверять то же самое.

Кейс 3: Кириллические слаги в gptweb.ru

Самый тонкий кейс. На сайте gptweb.ru один URL в sitemap возвращал 404: /blog/reviews/лучшие-нейросети-для-написания-статей. Observer пошагово зафиксировал диагностику:

// Проблемный код в app/sitemap.ts
url: `${BASE_URL}/blog/${post.blog_categories?.slug}/${post.slug}`
// post.slug = "лучшие-нейросети-для-написания-статей" — кириллица как есть

Корень проблемы — отсутствие encodeURIComponent при сборке URL. Браузер отдаёт запрос с percent-encoded кириллицей, а Next.js роутер при сравнении со статически сгенерированным sitemap-URL не находит соответствия. Дополнительно observer заметил артефакт аудита: директория 050-gptweb.ru__blog__reviews__ с пустым слагом — аудитор не смог сконвертировать кириллицу в ASCII для файловой системы. Это не баг основного приложения, но важная деталь для будущих SEO-аудитов: инструмент аудита плохо работает с кириллическими слагами, нужно учитывать.

fix-наблюдение в этом случае выглядит примерно так:

// Было
url: `${BASE_URL}/blog/${cat?.slug}/${post.slug}`
 
// Стало
url: `${BASE_URL}/blog/${encodeURIComponent(cat?.slug ?? '')}/` +
     `${encodeURIComponent(post.slug)}`

Технические детали реализации

Observer запускается как отдельная Claude-сессия в режиме «без инструментов». В системном промпте жёстко зафиксировано: «You do not have access to tools. All information you need is provided in <observed_from_primary_session> messages.» Это критично — иначе observer начнёт сам пытаться что-то проверять, тратить токены и шуметь в логах.

Каждое событие основной сессии стримится в observer через лёгкий мост на Node.js. Параметры и результаты вызовов сериализуются в строку, чтобы Claude мог их парсить без специальных инструментов. Observer возвращает наблюдение в формате XML — этот формат выбран потому, что его проще валидировать регуляркой и склеивать в единую базу, чем JSON, который Claude иногда ломает экранированием.

Для долгосрочного хранения наблюдения индексируются в локальную базу (в моём сетапе — на PostgreSQL, но подойдёт и SQLite для маленьких инсталляций). Поля наблюдения — type, title, subtitle, facts[], working_directory, occurred_at — позволяют делать выборки вида «все decision по проекту draw-collab за последний месяц» или «все discovery про Supabase в любых проектах».

Сверху построен тонкий API на Anthropic SDK, который при старте новой сессии получает наиболее релевантные наблюдения и инжектит их в системный промпт через prompt caching — это критично для скорости и стоимости. Без кэширования каждый запуск читал бы 10-20 тысяч токенов памяти заново.

Безопасность и приватность

Очевидный риск — observer видит ВСЁ, что делает основной агент, включая команды с секретами. Поэтому в моём сетапе перед записью наблюдения проходит фильтр, который вычищает паттерны вроде Authorization: Bearer ..., переменные окружения с подстрокой KEY|SECRET|TOKEN, содержимое .env-файлов. Это та же логика, которую я применяю при ведении dev-блога — никаких реальных учётных данных в открытом виде, только архитектурные факты.

Второй момент — права доступа. Observer-сессия не должна иметь возможности случайно что-то сделать в проде. Поэтому она запускается с пустым набором инструментов, без доступа к файловой системе и сети. Даже если кто-то попытается prompt-injection-атакой заставить observer-а выполнить команду, у него физически нет интерпретатора.

Что это даёт на практике

По итогам нескольких недель работы с Claude-Mem на реальных проектах я зафиксировал три эффекта.

Первый — резкое падение времени на «вспоминание контекста» в начале новой сессии. Раньше я тратил 10-15 минут на то, чтобы прочитать KNOWLEDGE.md, последние коммиты, открытые issue. Сейчас observer подаёт топ-20 релевантных наблюдений сразу, и я начинаю работать с уже сложившейся картиной. Особенно это заметно при переключении между несколькими проектами — пять минут вместо часа.

Второй — снижение количества повторных ошибок. Когда я в третий раз ловил 500-ку из-за отсутствующего try/catch в Next.js App Router, observer уже знал паттерн и сразу подсказывал проверить аналогичные места. Это не магия — это просто структурированный лог решений, которому agent доверяет.

Третий, неожиданный — observer стал отличным инструментом для ретроспектив. Раз в неделю я делаю запрос «все decision за последние 7 дней» и вижу полный список архитектурных выборов, которые я сделал. Это лучше любого ручного журнала, потому что фиксируется автоматически.

Альтернативы и почему не они

Я рассматривал несколько готовых решений до того, как собрал свой observer. LangGraph и его Memory Saver — мощно, но избыточно для моего случая, я не строю граф агентов, мне нужна плоская память. Anthropic-овская Memory Tool API — отличная вещь, но она работает на уровне одного агента и одной задачи, а мне нужна кросс-сессионная память по проектам. Простые векторные базы вроде Chroma — хорошо для семантического поиска, но плохо передают структуру (тип, время, рабочая директория).

Observer-подход оказался средним путём: достаточно простой, чтобы я мог его поддерживать сам, и достаточно структурированный, чтобы давать конкретные ответы вместо «похожих на запрос» воспоминаний. Если будете повторять — берите за отправную точку обычный pub/sub поверх вашей основной agent-runtime, а не пытайтесь ставить полноценный orchestrator.

Выводы

Главный урок этой работы — память для AI-агента это отдельная инженерная задача, а не побочный эффект логирования. Если вы хотите, чтобы агент через неделю помнил, почему вчера было выбрано конкретное решение, нужно явно проектировать структуру памяти, формат записи и условия извлечения. Просто свалить транскрипты в базу и надеяться на RAG — не работает на сложных проектах с долгой историей.

Второй урок — разделение ролей между «работником» и «наблюдателем» окупается. Основной агент должен думать о задаче, observer — о памяти. Попытка совместить эти роли в одной сессии приводит к тому, что одна из них всегда страдает: либо агент забывает записывать важные находки, либо он слишком увлекается архивированием и тормозит работу. Двухсессионная архитектура снимает этот конфликт.

Третий урок — структурированный формат наблюдений важнее объёма. Двадцать чётких <observation> с типом, заголовком и фактами полезнее, чем тысяча строк raw-лога. Структура позволяет агенту в будущей сессии быстро найти нужное, а не утонуть в шуме. Я начинал с подробных текстовых дампов и быстро понял, что они не дают никакой выгоды по сравнению с лаконичными типизированными записями.

Четвёртый урок — безопасность нельзя добавить потом. Observer должен запускаться без инструментов с самого начала, фильтр секретов нужен на этапе записи, а не извлечения. Любая ретроактивная очистка памяти — это уже компромисс. Если в наблюдении один раз сохранился API-ключ, считайте его утёкшим.

Если подытожить — Claude-Mem решает простую, но критичную задачу: делает работу AI-агента кумулятивной. Каждая сессия не начинается с нуля, а опирается на структурированный опыт предыдущих. Для одиночных задач это излишне, но если вы планируете жить с агентом неделями и месяцами на крупном кодбейзе, без такой системы памяти эффективность быстро упирается в потолок контекстного окна. И этот потолок ниже, чем хочется.

Паша Вин
Паша Вин

AI-инженер, предприниматель, маркетолог. Основатель feberra.com и x10seo.ru. 13 лет в перфоманс-маркетинге, 3 года в системной интеграции AI в бизнес.