У нас восемь агентов с постоянной памятью, и на всех вместе больше 360 файлов, около 1,4 МБ текста. Векторной базы нет ни одной — эмбеддинги мы не считаем вообще.
И это не «руки не дошли». Схема работает с мая 2026 года: сначала на одном агенте, с июня — централизованно на всех восьми. За это время вопрос про вектор-БД мы поднимали трижды, каждый раз с конкретным кандидатом на внедрение, и каждый раз закрывали его отказом. Ниже — как это устроено, какими числами живёт и в какой момент перестанет работать.
Рефлекс, от которого мы отказались
Когда заходит разговор про долгую память агента, у всех в голове примерно одна картинка: накрошить знания на чанки, посчитать эмбеддинги, сложить в Qdrant или pgvector и на каждом запросе доставать топ-k по косинусу. RAG стал ответом по умолчанию — даже когда вопроса ещё никто не задавал.
У этой схемы есть точка окупаемости, и лежит она где-то в районе десяти тысяч документов, а не сотни. Пока вы ниже неё, вы платите за инфраструктуру, за эмбеддинг-модель и за рассинхрон индекса, а взамен не получаете ничего, чего не дал бы обычный текст.
Два уровня, и только они
Память устроена в два слоя, и это вся конструкция.
Оглавление — один файл, одна строка на воспоминание: заголовок, ссылка на файл, короткий хук из нескольких слов. Грузится в контекст целиком, каждую сессию, без исключений.
Тела — отдельный markdown-файл на каждый факт. В отличие от оглавления, они подтягиваются не всегда, а по релевантности к текущей задаче: возникла задача — поднялись нужные файлы, и только они.
Числа одного из агентов, замер на 11 августа 2026 года:
| Файлов | Объём | Когда грузится | |
|---|---|---|---|
| Оглавление | 1 | 21 КБ | всегда |
| Тела | 135 | 719 КБ | по требованию |
Соотношение — один к тридцати четырём. Оглавление обязано быть маленьким, потому что это налог на каждый старт сессии: его читают всегда. Тела этим налогом не облагаются, поэтому растут свободно, их берут выборочно.
Отсюда недоразумение, которое стоит развеять сразу. «У агента всего двадцать килобайт памяти» — неправда. Двадцать килобайт это потолок оглавления, а не памяти вообще: путать одно с другим — примерно как судить о содержимом склада по описи на входной двери. Сама память на два порядка больше, и потолка у неё нет.
Разброс между агентами при этом большой: у одного 11 файлов памяти, у другого — 60 файлов и 212 КБ. Второй просто дольше в работе и собрал больше боевых граблей.
Как выглядит одна запись
Одна запись — это frontmatter плюс тело:
| |
Всё держится на поле description, и это не заголовок и не украшение. Именно оно, и только оно, участвует в подборе. Поэтому пишем его не как название главы, а как поисковый запрос, по которому запись должна всплыть. Разница между «архитектура памяти» и «почему мы не берём вектор-БД для памяти агента, порог пересмотра» — это разница между «запись не нашлась» и «нашлась».
Типов записей четыре: кто такой владелец, обратная связь и правила работы, состояние проектов, ссылки на внешние ресурсы. Больше заводить пробовали, не прижилось.
Семантический поиск здесь уже есть
Тут легко пропустить главное: подбор нужных записей делает сама модель, сопоставляя текущую задачу с их описаниями. Это и есть семантический поиск — просто выполняет его LLM по компактному оглавлению, а не косинусная мера по эмбеддингам.
И на нашем масштабе, на сотне с небольшим записей, это работает точнее вектора. Модель понимает нюанс формулировки: что решение залочено и переоткрывать его можно только со сверкой, что правило про проверку цен относится к каталогу, а не к метрикам. Эмбеддинги ловят похожее по словам, а на маленьком корпусе разница между «похоже по словам» и «то самое» видна невооружённым глазом.
Где болит
Идеальной схема не бывает, и про три свои больные точки мы знаем.
Налог на старте. Оглавление грузится всегда, а кириллица в UTF-8 стоит два байта на символ — то есть русскоязычный индекс вдвое дороже английского при том же смысле. Когда файл начинает подпирать бюджет, правильная реакция — консолидировать, а не дописывать дальше.
Как это выглядит на практике. 8 июля 2026 года индекс дорос до 30,4 КБ на 118 записях, и мы прогнали два прохода подряд: сначала ужали распухшие строки, вынеся детали в тела, потом слили настоящие дубли. Вышло 26,5 КБ на 116 записях. Дубль-кластер при этом нашёлся ровно один — и это, по закону жанра, было правило о том, как единообразно писать названия продуктов. Само записанное трижды.
Вывод из прохода неприятный, но полезный: дублей почти нет. Оглавление набито разными фактами, а не повторами, поэтому сжимать в нём особо нечего, и растёт файл по делу. Строки мы потом ужали ещё сильнее, и сейчас 135 записей укладываются в 21 КБ, около 160 байт на строку против прежних 234.
И вот здесь самая честная часть. Файл растёт монотонно: с 30 июля по 11 августа прибавилось 11 записей, примерно по одной в день, и остановиться этому неоткуда — каждый разобранный промах превращается в правило. Консолидация выкупает время, но тренд не отменяет. Дублей нет, значит сжимать нечего, а два прохода подряд дали в сумме меньше, чем один месяц роста. То есть налог на старте сессии медленно, но верно дорожает, и это долг, который придётся отдавать. Как именно — ниже, в разделе про порог.
Один кластер мы намеренно не слили. Два правила про проверку фактов выглядят похоже: одно общее, второе про конкретный тип данных. Второе — родительское, на него ссылаются семь других записей. Слияние сэкономило бы полторы сотни байт и размыло бы точность подбора. Не тот размен.
Силосность. Восемь памятей не видят друг друга, и урок, который агент вынес из своих граблей, у соседа сам собой не появится. Перенос ручной: агент поднимает заметку с уроком наверх, там решают, годится ли она в общее правило для всех, и если да — правило расходится по остальным при следующем хендоффе. Автоматизировать это мы не собираемся — не потому что лень, а потому что половина уроков специфична для стека конкретного агента, и автоперенос быстро превратит чужую память в свалку.
Подбор вероятностный. Гарантии, что нужная запись подтянется именно сейчас, нет. Поэтому гардрейлы, то есть то, что обязано срабатывать всегда, живут не в памяти, а в инструкциях проекта, которые грузятся принудительно. Память хороша для фактов и контекста, а для «никогда не делай X» она ненадёжна.
Почему всё-таки не вектор
Причин три, по убыванию веса.
Первая — арифметика. 135 записей против точки окупаемости в десять тысяч: мы на два порядка ниже, и на этом масштабе вектор-стор просто оверхед.
Вторая — инфраструктура. Эмбеддинг-поиск тянет за собой эмбеддинг-модель, а это внешний вызов и расход токенов на каждый запрос. Сам стор — ещё один сервис, который надо поднять, бэкапить и чинить. Портфель ведут одни руки, и каждый постоянно работающий сервис стоит в нём дороже, чем кажется на этапе docker compose up.
Третья — архитектура. Подбор записей делает харнесс, а не наш код. Вкрутить туда вектор-БД значит построить рядом вторую систему памяти, которая будет конкурировать с первой за право быть источником правды. Ровно этот сценарий и заставил нас в своё время отклонить готовые решения.
Что смотрели и не взяли
За три подхода к вопросу мы разобрали три конкретных решения.
agentmemory — прямой аналог нашей схемы. Миграция ради миграции, то самое шило на мыло.
FluxMem — память как эволюционирующий граф. Разобрали её примитивы по одному и обнаружили, что все они у нас уже есть, только руками: связи между записями через [[ссылки]], продвижение урока снизу вверх, периодическая консолидация, чистка протухших записей. Единственная реальная добавка — LLM-автоматизация эволюции графа, то есть постоянный фоновый расход токенов. Так что ценность этой работы оказалась для нас не в коде, а в подтверждении, что направление выбрано верно.
memoir — самый интересный случай. Это git-based память для агентов: ветки, blame, откат, иерархические пути вида profile.skills.python. И вот что важно: memoir тоже отказался от векторной базы в пользу плоских файлов и двойного поиска, а аргументация его авторов почти совпала с нашей. Отклонили мы его по другой причине — memoir это отдельный инфраструктурный слой, сервис плюс модель на подборе, тогда как у нас zero-infra и обычный markdown, который харнесс читает нативно. Идею иерархических путей, впрочем, забрали в бэклог, она пригодится на следующем этапе.
Когда это сломается
Порог мы назвали заранее, чтобы потом не спорить с собой задним числом: больше 150–200 записей И заметное падение качества подбора, когда модель начинает промахиваться мимо нужного файла. Два условия, не одно. Сейчас записей 135, промахов не видно — схема работает.
Разница в том, что второе число мы теперь держим не как разовый снимок, а с производной. При темпе плюс одна запись в день нижнюю границу, 150, мы проходим примерно к концу августа 2026-го, а верхнюю, 200, — к концу октября. Это не «схема на исходе»; это ровно то, ради чего порог и назывался цифрой заранее — чтобы момент наступил по календарю, а не по ощущению «что-то агент стал туповат».
И первый шаг на этом пороге — не вектор, а тематические под-индексы. Причём резать надо не по проектам, как хочется на первый взгляд. Гардрейлы обязаны грузиться всегда, иначе не сработают проактивно, а значит линия реза идёт между процессным ядром, которое всегда в контексте, и проектными листьями, которые подтягиваются по требованию.
Тут есть тонкость, которую мы поняли не сразу: подбор тел идёт не через оглавление. Харнесс сопоставляет задачу с описаниями файлов напрямую. А это значит, что под-индексы срежут налог на старте, но на качество подбора не повлияют вообще. Полезно понять это до того, как разрежете файл и будете гадать, почему не помогло.
Вектор при всём этом не приближается ни на шаг. Он окупается на десяти тысячах документов, а мы прибавляем по записи в день: до его точки безубыточности при таком темпе идти не годы, а десятилетия. Растущий индекс — это проблема бюджета контекста на старте сессии, и лечится она нарезкой, а не сменой способа поиска. Две разные боли, которые легко перепутать и в итоге купить эмбеддинги от головной боли, которая болит в другом месте.
Так что если у вас меньше десяти тысяч документов и один человек на всё — посчитайте сначала, что даст вам косинус сверх того, что модель уже делает с оглавлением на двадцать килобайт.
Статья опубликована в блоге keepware как canonical. Кросспост — vc.ru / Habr — с указанием канонической ссылки.
По той же логике честной инженерии — без хайпа, с конкретными числами — устроено шифрование данных в todolist: AES-256-GCM на сервере, SQLCipher на устройстве, и почему это не end-to-end.
