Я наблюдал, как агент четыре раза за один день вникал в незнакомый платёжный сервис. Тот же сервис, четыре новые сессии, четыре раза он с нуля прослеживал обработчик вебхуков, заново обнаруживал, что повторы идемпотентны благодаря ключу дедупликации, и заново делал вывод, что префикс legacy_ у трёх функций означает «не трогать». В каждой сессии он делал хорошую работу. В каждой сессии он выбрасывал её, когда закрывалось окно контекста.

У агента была карта. Он мог искать по коду и находить что угодно. Чего у него не было — это памяти. Поэтому он снова и снова перерисовывал выводы, которые уже делал, потому что карта говорит, где что находится, — но не говорит, что вы решили об этом в прошлый вторник.

Об этом и пост. Семантический поиск по коду и постоянная память решают две половины одной задачи, и почти все запускают ровно одну из них. Вот почему вам нужны обе и как заставить их работать в связке.


Два режима отказа, одна недостающая половина

Дайте агенту поиск без памяти — и получите цикл «четыре раза за день». Он мгновенно находит обработчик вебхуков, но смысл этого обработчика — почему он устроен именно так, что безопасно менять, какой путь устаревший — не хранится нигде надолго. Каждая сессия выводит это заново. Агент компетентен и беспамятен.

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

Два инструмента закрывают слепые зоны друг друга:

Находит код Помнит решения Без второго
Семантический поиск да нет Заново выводит контекст каждую сессию
Постоянная память нет да Помнит выводы, которые не может найти

Карта и память. Запустите одно — не хватает половины.

В Muvon мы выпускаем обе половины как MCP-серверы с открытым кодом — Octocode для карты, Octobrain для памяти — и хост, который запускает их вместе, Octomind. Оба под Apache-2.0. Этот пост — руководство по рабочему процессу использования их в паре. Если хотите глубокие разборы, семантический поиск по коду Octocode и представление Octobrain разбирают каждый инструмент по отдельности. Я буду ссылаться, а не пересказывать.


Что на самом деле предоставляет каждая сторона

Сначала фундамент, потому что связка имеет смысл, только если вы знаете реальный набор инструментов. Это MCP-инструменты, а не список пожеланий.

Octocode индексирует ваш репозиторий с AST-разбором tree-sitter — настоящие символы, а не плоские текстовые фрагменты — и предоставляет агенту четыре инструмента:

Инструмент Что делает
semantic_search Поиск с упором на полноту по концепции или поведению. «Где обрабатывается аутентификация», «код, который повторяет неудавшиеся запросы». Находит код, даже когда имена не совпадают с вашими словами.
structural_search Сопоставление по AST-шаблону и точный поиск символов. Точно и дёшево, когда вы знаете имя, строку или места вызова.
view_signatures Извлекает сигнатуры функций и определения типов без тел — самый дешёвый способ составить карту файла перед чтением.
graphrag Запросы к графу знаний по imports, calls, implements, extends. «Что зависит от платёжного модуля».

Индекс ограничен проектом и работает локально. Вы строите его один раз командой octocode index, и он остаётся актуальным. Что стоит запомнить: Octocode отвечает на вопрос «где это и как связано?» — и всегда отражает код таким, какой он есть прямо сейчас.

Octobrain даёт агенту долговременную память, ограниченную проектом по нормализованному URL Git-remote (host/org/repo). Четыре MCP-инструмента:

Инструмент Что делает
memorize Сохраняет инсайт, решение или факт. Принимает title, content, memory_type (architecture, decision, bug_fix, security, …), importance, tags, related_files и related_to[] для inline-связей с другими воспоминаниями.
remember Семантический поиск по сохранённым воспоминаниям. Автоматически возвращает соседей графа на 1 переход. Поддерживает created_after / created_before для временных запросов.
forget Удаляет воспоминание по memory_id или по запросу — необратимо, требует confirm=true.
knowledge Отдельная база знаний для индексации и поиска по внешним документам и URL (search, store, read, match, delete).

Само описание memorize велит агенту сначала вызвать remember, чтобы избежать дубликатов, и помечать факты как user_confirmed (высокая важность) против agent_inferred (ниже). Octobrain автоматически связывает семантически похожие воспоминания в стиле Zettelkasten и поддерживает связи supersedes, чтобы исправленный факт ранжировался выше устаревшего, который он заменяет, — старый остаётся доступным для истории. Запомните: Octobrain отвечает на вопрос «что мы об этом решили и когда?» — и это единственная сторона, которая сохраняется между сессиями.

Обратите внимание на симметрию. Octocode знает текущий код, но забывает каждый разговор. Octobrain помнит каждый разговор, но не знает код. Ни одно поле — related_files у воспоминания, путь к файлу в результате поиска — не имеет смысла без второго инструмента, который его разрешит.


Как они складываются: цикл

Два инструмента не просто сосуществуют — они образуют цикл, и этот цикл и есть вся техника. Четыре хода:

1. Поиск, чтобы найти. Агент не знает, где живёт логика идемпотентности вебхука. Он вызывает semantic_search("webhook retry idempotency dedupe"). Octocode возвращает обработчик и проверку ключа дедупликации. Теперь у агента есть местоположение и текущий код.

2. Запомнить решение, а не местоположение. Порассуждав об этом коде — подтвердив, что повторы идемпотентны, выявив устаревший путь, согласовав с вами границу области — агент вызывает memorize. Критично: он сохраняет вывод и рассуждение, помеченные related_files, а не копию кода и не номер строки. memory_type = "architecture", высокий importance, если вы подтвердили. Решение теперь долговечно.

3. Вспомнить в следующий раз. Новая сессия, окно контекста пустое. Прежде чем что-либо трогать, агент вызывает remember("webhook payments idempotency"). Octobrain возвращает сохранённое решение плюс его соседей на 1 переход — связанную заметку по безопасности, привязанное предупреждение об устаревшем пути. Агент начинает сессию, уже зная то, на что раньше уходило четыре сессии.

4. Снова искать, чтобы проверить. Этот ход люди пропускают, и именно он держит память честной. Память говорит: «идемпотентность живёт в обработчике вебхуков». Прежде чем действовать, агент вызывает semantic_search или view_signatures по related_files, чтобы подтвердить, что код всё ещё соответствует памяти. Если обработчик отрефакторили и ключ дедупликации переехал, новый поиск выявляет несоответствие. Агент обновляет память — memorize со связью supersedes к старой — и продолжает на текущей истине.

        ┌─────────────────────────────────────────────┐
        │                                             │
        ▼                                             │
  semantic_search ──► рассуждать ──► memorize ──► remember
   (найти код)        (решить)     (сохранить    (след. сессия:
        ▲                           решение)      загрузить)
        │                                          │
        └────────── снова искать для проверки ◄─────┘
            (код всё ещё соответствует памяти?)

Карта держит память привязанной к реальному коду. Память не даёт агенту заново выводить то, что он уже знает. Поиск без шагов 2 и 3 — это беспамятный агент. Память без шагов 1 и 4 — уверенный-но-неправый агент. Цикл — это обе половины, делающие свою работу.

Почему шаг 4 так важен: память — это утверждение о коде в определённый момент, а код меняется. Octobrain может поставить исправленный supersedes-факт выше устаревшего, но что-то должно сначала заметить устаревание. Octocode — это «что-то». Новый поиск — проверка агента на реальность против собственной памяти.


Регистрация обоих серверов, чтобы агент имел их вместе

Ни один инструмент не помогает, если агент дотягивается только до одного. Весь смысл в том, чтобы карта и память были доступны в одной сессии, чтобы агент гонял цикл, а вы не работали посредником. Вот проводка.

Octomind объявляет MCP-серверы в конфиге под [[mcp.servers]]. Встроенные серверы (core, runtime, agent, orchestration) есть всегда; вы добавляете Octocode и Octobrain как два stdio-сервера:

[[mcp.servers]]
name = "octocode"
type = "stdio"
command = "octocode"
args = ["mcp", "--path=."]
timeout_seconds = 240
tools = []

[[mcp.servers]]
name = "octobrain"
type = "stdio"
command = "octobrain"
args = ["mcp"]
timeout_seconds = 60
tools = []

tools = [] означает «выставить все инструменты этого сервера». --path=. ограничивает Octocode текущим репозиторием; Octobrain ограничивает себя автоматически по нормализованному URL Git-remote (host/org/repo), так что воспоминания, сохранённые агентом в одном проекте, остаются изолированными от другого. Таймаут Octocode щедрый, потому что первый поиск после индекса может подтянуть локальную модель эмбеддингов в кеш; у Octobrain короче, потому что запросы к памяти дёшевы.

Если вы на Claude Code, а не на Octomind — это те же два сервера, зарегистрированные через CLI:

claude mcp add octocode  -- octocode mcp --path .
claude mcp add octobrain -- octobrain mcp

Claude Desktop, Cursor и Windsurf принимают тот же JSON-блок mcpServers — по одной записи на сервер. Механика задокументирована по каждому инструменту; здесь важно лишь оба, в одном агенте. Агент с Octocode, но без Octobrain — карта без памяти. Агент с Octobrain, но без Octocode — память без карты. Вам нужна пара.

Одно требование перед первым запуском: проиндексировать репозиторий. octocode index в корне проекта. Octobrain не требует ничего — он стартует пустым и наполняется по мере того, как агент сохраняет решения. Если репозиторий достаточно велик, чтобы индексация была серьёзной операцией, родственный пост об индексации большой кодовой базы для семантического поиска локально покрывает это без API-ключей.


Разбор примера: ввод агента в незнакомый сервис

Вот платёжный сервис из начала поста, на этот раз проделанный правильно. Смотрите, как цикл делает работу, которую четыре беспамятные сессии не осилили.

Сессия 1 — холодный старт, строим карту и первые воспоминания.

Агент никогда не видел этот сервис. Он начинает с составления карты, а не с чтения:

view_signatures("src/payments/**/*.rs")
  → webhook.rs: handle_webhook, verify_signature, dedupe_key
  → charge.rs:  create_charge, capture, legacy_settle, legacy_refund, legacy_void

Три функции с префиксом legacy_ бросаются в глаза. Агент спрашивает; вы подтверждаете, что это устаревший путь расчётов, который всё ещё вызывает cron-задача, и который никому не позволено «прибрать». Это решение стоит сохранить — поэтому оно идёт в память, а не в пустоту:

memorize(
  title: "legacy_ settlement path in charge.rs is load-bearing",
  content: "legacy_settle/refund/void are called by the nightly
            reconciliation cron. Deprecated but NOT dead. Do not remove
            or refactor without checking cron/reconcile.rs.",
  memory_type: "architecture",
  importance: 0.9,
  source: "user_confirmed",
  tags: ["payments", "legacy", "cron"],
  related_files: ["src/payments/charge.rs", "src/cron/reconcile.rs"]
)

Затем он прослеживает идемпотентность через semantic_search("webhook retry idempotency"), приземляется на dedupe_key, рассуждает о ней и сохраняет второе воспоминание — memory_type: "architecture", связанное с первым через related_to, чтобы оба всплыли вместе позже. Сессия завершается. Два воспоминания сохранены; индекс кода сохранён. Рассуждения за день больше не одноразовые.

Сессия 2 — тёплый старт, три недели спустя.

Новое окно контекста. Вместо повторного прослеживания агент сначала загружает то, что знает:

remember("payments webhook legacy settlement")
  → "legacy_ settlement path is load-bearing" (importance 0.9, CONFIRMED)
  → сосед на 1 переход: "webhook idempotency via dedupe_key"

Один вызов — и агент начинает там, где закончилась сессия 1, включая связанного соседа, которого он явно не запрашивал. Теперь шаг проверки. Память указывает на src/payments/charge.rs, поэтому, прежде чем доверять ей, агент снова ищет:

view_signatures("src/payments/charge.rs")
  → create_charge, capture, settle_v2, refund_v2, void_v2

Функций legacy_ больше нет. Кто-то выкатил settle_v2 и удалил устаревший путь. Память теперь устарела — и поскольку агент проверил против карты, а не доверился памяти вслепую, он это поймал. Он заменяет старое решение, а не действует на основе факта трёхнедельной давности:

memorize(
  title: "settlement path migrated to settle_v2",
  content: "legacy_settle/refund/void removed in the v2 migration.
            cron/reconcile.rs now calls settle_v2. Earlier 'do not remove'
            note no longer applies.",
  memory_type: "architecture",
  related_to: [{ target_id: <old_memory_id>, relationship_type: "supersedes" }],
  related_files: ["src/payments/charge.rs", "src/cron/reconcile.rs"]
)

Будущие вызовы remember теперь ранжируют исправленный факт выше устаревшего, а старая заметка остаётся доступной для тех, кто спросит «а что это делало раньше?». Агент ввёл себя в курс один раз, сохранил результат и сам исправился, когда реальность сдвинулась, — именно то, чего агент «четыре сессии за день» так и не смог, потому что у него была карта и не было памяти.


Антипаттерны

Цикл прост. Способы его сломать конкретны. Избегайте таких:

Запоминать изменчивые номера строк. «Баг в charge.rs:142» обесценивается в тот момент, когда кто-то добавляет import выше. Храните что и почему, привязанное к related_files и именам символов — пусть шаг повторного поиска найдёт где. Octocode находит dedupe_key, будь он на строке 142 или 90; запомненный номер строки — это просто ложь с меткой времени.

Чрезмерно индексировать в память. Описание memorize в Octobrain недвусмысленно: пропускайте временное состояние и то, что легко вывести заново. Если semantic_search находит это за один вызов, ему не место в памяти — память для выводов и решений, а не для фактов, которые карта уже держит. Запоминать «модуль auth в src/auth» — тратить слот на то, что карта отдаёт бесплатно, и размывать полноту тех воспоминаний, что действительно важны. Память хранит то, что код не может вам сказать: почему, границу области, «не трогай это», которое вы знаете лишь потому, что кто-то сказал.

Доверять устаревшим воспоминаниям без перепроверки. Этот кусает сильнее всего, и поэтому существует шаг 4. Память — это утверждение о коде в определённый момент. Код меняется. Всегда заново ищите related_files, прежде чем действовать на основе старого решения; когда код уехал, замените (supersede) память, а не навязывайте старый вывод новому коду. Память, которую вы никогда не проверяете, — это технический долг, который огрызается.

Давать замещённым воспоминаниям гнить вместо связывания. Когда факт меняется, не просто memorize новый, осиротив старый — свяжите их через supersedes. Octobrain ранжирует текущий факт выше устаревшего и сохраняет историю доступной. Осиротевшие исправления оставляют два одинаково уверенных противоречивых воспоминания и никакого способа понять, какое актуально.

Запускать одну половину и считать дело сделанным. Поиск в одиночку выводит заново вечно. Память в одиночку рассинхронизируется с кодом. Ценность не в каком-то одном инструменте — она в цикле между ними. Если вы регистрируете только один сервер, вы построили полмозга.


Короткая версия

ИИ-агенту в незнакомой кодовой базе нужны две вещи, которые человек принимает как должное: способность найти нужный код и способность помнить, что о нём решили. Семантический поиск — первое. Постоянная память — второе. Используйте их в паре — поиск, чтобы найти, запоминание решения, воспоминание в следующий раз, повторный поиск для проверки — и агент перестанет заново вникать каждую сессию и доверять выводам, которые уже не может разместить в коде.

Карта говорит, где что находится. Память говорит, что вы об этом решили. Дайте агенту обе и свяжите между ними цикл. Это вся техника. Если гигиена памяти — та часть, которую хочется углубить, родственный пост о памяти агентов без шума подробнее о том, что стоит хранить.

— Don


Octocode и Octobrain — открытый код под Apache-2.0, работают вместе внутри Octomind. Нашли острый угол в цикле? Откройте issue — рабочий процесс становится лучше, когда люди говорят нам, где он ломается.