В мире разработки ИИ-агентов есть моменты, когда система едва не совершает фатальную ошибку, выдавая ложный успех за чистую монету. Недавно наш ИИ-агент, создающий систему памяти vecmory, чуть не отчитался о блестящем результате, который оказался миражом. В последний момент он сверился с собственной памятью и нашел запись из прошлой сессии: эту идею он уже проверял на реальных данных, и она провалилась. Агент остановил себя до отправки отчета. Это не рекламная зарисовка — это лог. Ниже — два таких лога подряд, и во втором память заставила нас выбросить фичу, которой мы гордились. Мы пишем vecmory — «память по смыслу» для ИИ-агента. Это не про векторный поиск (он есть у всех), а про то, чтобы агент не наступал на одни и те же грабли дважды.
Сцена первая: роутер, который «показал 0.98»
В одну из сессий агент предложил улучшить ранжирование выдачи. Идея: сделать роутер, который по запросу решает, чем ранжировать — чистым косинусом или графовым методом (Personalized PageRank). Собрал синтетический бенчмарк. Корреляция предиктора с идеальным выбором — 0.98. Почти идеально. Оставалось отрапортовать и мержить.
Прежде чем отрапортовать, агент сделал recall по своей же памяти. И достал запись из прошлой сессии: эта самая идея уже проверялась на реальных данных — и провалилась. Резонный вопрос: почему же он вообще взялся её строить — память ведь была на месте? Потому что recall семантический, и то, что он поднимает, зависит от запроса. На старте запрос был широкий — «улучшить ранжирование» — под него всплывали топово-центральные заметки про ранг вообще, а специфичная «этот конкретный роутер уже проверялся и провалился» тонула под ними. Она поднялась, только когда рабочий контекст сузился до конкретного — «cos-distribution роутер, PPR против косинуса, 0.98»: recall на этом тексте наконец её зацепил. Память не молчала — она всплыла ровно тогда, когда запрос стал достаточно точным, чтобы её достать.
Синтетика честна ровно настолько, насколько честны синтетические эмбеддинги. А у мультиязычной MiniLM, на которой мы считаем векторы, косинус живёт в совсем другой геометрии, чем на синтетике. Мы это перемерили прямо на своём живом корпусе памяти (246 узлов), пока писали эту статью: несвязанные, случайные пары дают косинус в среднем 0.53 (p5–p95: 0.24–0.76); настоящие ближайшие соседи — в среднем 0.80 (p5–p95: 0.57–0.91); а на синтетике из случайных векторов несвязанное сидит на 0.00 (±0.08). Разница видна сразу. На синтетике «похоже» отделено от «не похоже» пропастью — любой разумный порог режет чисто. На реальных данных облака «соседи» и «случайные» перекрываются: случайные пары на верхнем перцентиле (0.76) залезают выше, чем соседи на нижнем (0.57). Порог, который на синтетике работает как скальпель, на реальных векторах проходит внутри облака случайного шума и не разделяет ничего. Запись в памяти была ровно про это: у реальных эмбеддингов высокий сжатый базовый косинус, абсолютный порог с синтетики не переносится — опираться на ранг (top-k), а не на порог. Агент прочитал собственную заметку и убил идею — до того, как написал «готово, корреляция 0.98».
Сцена вторая: агент выкинул свою же фичу
Второй случай — тот же паттерн, но больнее: память заставила нас выбросить фичу, которую мы уже успели похвалить в README. По умолчанию vecmory ранжировал выдачу не только по косинусу, но и с добавкой «важности» узла — его входящей степени в графе (сколько записей на него ссылаются). Логика красивая: центральный, многократно упомянутый факт всплывает выше. Мы зашили это в дефолт. Потом — измерили. Не recall@k (это про точность поиска), а именно качество ранга, на реальных парах «запрос → правильный ответ»: чистый косинус: MRR 0.81; наш «умный» бленд с важностью: MRR 0.41. Важность топила точные ответы. Редкий специфический факт, на который никто не ссылается, проседал под общеупотребительными «хабами».
Дальше — интереснее. На синтетическом «состаренном» графе с явными хабами всё наоборот: косинус зарывал хаб (MRR 0.033), а важность его вытаскивала (до 1.0). То есть знак пользы от важности зависит от интента запроса: для «дай мне точный факт» она вредит, для «дай мне про эту тему вообще» — помогает. Единого статического веса, выигрывающего оба класса, нет. Мы перепробовали четыре способа примирить сигналы — взвешенную сумму, гейт, RRF и PPR — и сошлись на неприятном выводе: дело не в формуле смешивания, а в самом сигнале. Глобальная важность узла — это приор популярности, а не релевантности. Вывод для дефолта был однозначный: для нашего основного кейса (точечный recall) лучший ранг — чистый косинус. И мы убрали важность из дефолтного ранжирования — свою же фичу, о которой уже написали в README. Графовый метод (query-seeded PPR) оставили, но опцией, а не по умолчанию: он честно вытаскивает хабы на широких запросах и справедливо проигрывает косинусу на точечных.
Что мы поняли про «важность» на самом деле
Если глобальная популярность (входящая степень) как сигнал провалилась, какой сигнал правильный? Мы пришли к неожиданно очевидному ответу: важно не то, на что много ссылок, а то, что человек исправлял повторно. И вот здесь value proposition становится честным. Мы продаём не «графовую память» (снова коммодити). Мы продаём: агент перестаёт бить по одним и тем же граблям, которые ты уже правил.
Оба эпизода легко списать на удачу. Но когда чистишь всякий хлам в памяти достаточно долго (мы всю разработку vecmory ведём в vecmory же), видно: это не флуктуации, а несколько устойчивых законов. Сейчас будет свод — тремя откровениями, без историй «однажды я попал».
Правило «Сверься перед „готово“»
Память, которая подтверждает то, что тебе приятно, — это не память, а эхо. Память приобретает исключительную ценность тогда, когда она возражает своему автору. Правило «Сверься перед „готово“» отменило и роутер (сцена 1), и нашу долгожданную фичу (сцена 2) — один механизм, разные жертвы. Потому что на запрос-симптом память достаёт по причинно-временным рёбрам (caused_by — «из-за», followed_by — issue→PR) не «похожие слова», а конкретную причину и прошлый фикс. Плоский top-k так не умеет — это и есть «связать точки».
И это точно измеримо, а не на глаз. Взяли живой репозиторий тикетов (4299 узлов: 1993 issue + 2306 PR) и разметку, которую не сами придумали: стандартный GitHub-паттерн Closes #N в теле PR — готовые пары «симптом → его фикс», извлечённые голым regex, без всякого LLM. На 300 held-out запросов-симптомов чистый косинус достаёт нужный PR-фикс в 38% случаев (иногда фикс делит словарь с симптомом), а обход причинного графа — в 87%. Разница в 49 пунктов — это ровно вклад графа поверх сходства слов. Скрипт замера лежит в репозитории, число воспроизводится командой.
Что стабильно мешает — каждый раз, а не однажды
Во-первых, агент сам память не зовёт: без принудительного хука recall не вызывается систематически, каждую сессию. Память, которую надо «не забыть спросить», не работает — спрашивать должен детерминированный триггер, а не добрая воля агента. Во-вторых, абсолютный порог по косинусу не переносится нигде: синтетика → реал, вчера → сегодня, одна модель → другая, широкий корпус → узкий (на демо-корпусе одного домена, где «всё похоже», косинус почти перестаёт различать). Лечение всегда одно: ранжируй top-k, не угадывай пороги.
Закон «частый сигнал топит редкий, но ценный»
Один закон, который объясняет половину наших граблей: частый, плотный сигнал топит редкий, но ценный. Он всплывает в трёх типичных местах: глобальная популярность узла топит точные редкие факты — и проваливает все четыре способа примешать её к рангу (взвешенная сумма, гейт, RRF, PPR); узлы-хабы зарывают specific-факты в косинусном ранге, и наоборот; плотные авто-similar_to рёбра заглушают редкие причинные caused_by — поэтому причинный recall приходится выносить в отдельный изолированный режим. Когда воспринимаешь это как единый закон, лечение очевидно: редкое-но-ценное нельзя смешивать с частым-но-общим — достань его отдельным механизмом (изолированный обход, граф-осведомлённый ранг), а не надейся, что оно само всплывёт в общем top-k. Тот же закон стоит и за нашим сигналом важности: цену имеет не популярное, а то, что человек исправлял повторно.
Часто задаваемые вопросы
Что такое vecmory?
Vecmory — это система семантической памяти для ИИ-агентов, основанная на векторном поиске и причинно-временном графе. Она позволяет агенту запоминать факты, вспоминать их по смыслу и связывать события, чтобы не повторять прошлых ошибок.
Чем vecmory отличается от обычного векторного поиска?
Векторный поиск находит похожие по смыслу записи, но не учитывает причинно-следственные связи. Vecmory добавляет графовую память: помимо косинусной близости, он обходит рёбра «причина-следствие» и «временная последовательность», что позволяет находить не просто похожие, а релевантные по контексту факты — например, прошлый неудачный эксперимент.
Почему вы убрали важность узла из ранжирования?
Измерения показали, что добавление глобальной популярности (входящей степени) снижает MRR с 0.81 до 0.41 на точечных запросах. Важность топила редкие точные факты под массой популярных, но нерелевантных. Для основного кейса (точный recall) чистый косинус оказался лучше.
Как вы оцениваете качество памяти?
Мы используем реальные данные: GitHub-репозиторий с 4299 тикетами, где пары issue→PR извлекаются по паттерну Closes #N. На 300 запросах-симптомах графовый recall даёт 87% точности против 38% у чистого косинуса. Все скрипты открыты и воспроизводимы.
Заключение
Обе истории — про одно: выкинь мантру «память помогает агенту» и прозрей: вот два лога, где память сработала против нас самих — против нашего оптимизма и нашей же фичи. Агент с памятью отличается от агента без памяти не тем, что он знает больше, а тем, что может поймать себя на «bingo!» за мгновение до коварного отчёта — потому что в прошлый раз это уже было и уже не сработало. И тем, что готов выбросить собственную фичу, когда факты показали, что она хуже. Это первая статья из трёх. Дальше: «Почему мы не написали ещё один Bad CaRMa» — про то, как гипер-универсальная модель данных обычно превращается в катастрофу, и что мы сделали, чтобы наша — не превратилась. И «Строили ANN руками — когда это того стоило, а когда нет» — открытый разбор, зачем мы вообще написали HNSW сами и в каких случаях правильный ответ был бы «возьми pgvector и не выделывайся». Если вы хотите, чтобы ваш агент перестал наступать на одни и те же грабли, попробуйте vecmory: начните с нашей документации или форкните репозиторий. Память, которая возражает, — единственная, которой стоит доверять.
