
Введение
На днях читал статью про память для AI-агентов — одну из тех, где рядом мирно уживаются SQLite, экономия токенов, поиск по накопленным знаниям и надежда наконец перестать кормить модель одними и теми же простынями контекста при каждом новом запуске.
Сама статья была вполне добротной, однако вскоре отошла на второй план, потому что куда живее оказалось обсуждение под ней.
Один читатель спросил, чем база данных в сущности лучше обычных AGENTS.md и MEMORY.md: модель всё равно получает текст, только в одном случае читает его из файла, а в другом — из таблицы. Другой заметил, что markdown-файлов можно сделать сколько угодно, связать ссылками, положить в Git и не изобретать очередной велосипед. Кто-то работал с Hermes, кто-то хранил общую память нескольких агентов в Obsidian, а некоторым вполне хватало трёх файлов — с описанием проекта, его текущим состоянием и правилами внесения изменений.
Спор поначалу казался знакомым до скуки: файлы против базы, Obsidian против специализированной системы, полнотекстовый поиск против векторного. Такие разговоры в технической среде обычно быстро обрастают любимыми инструментами, личными привычками и лёгким подозрением, что собеседник просто не дорос до очевидного решения.
Но чем дольше я читал комментарии, тем яснее становилось: спорящие не столько защищают разные технологии, сколько по-разному понимают сам предмет разговора.
Один человек думает о памяти как о наборе сведений, которые агент должен прочитать перед началом работы. Другому нужен механизм, способный вытащить из сотен записей несколько действительно уместных. Третий хочет вернуться к незавершённой задаче без повторного обхода всего проекта, а четвёртый пытается сохранить причины решений, старые тупики и опыт, который обычно исчезает быстрее любой документации.
Все они говорят о памяти, поэтому разговор внешне остаётся единым. На деле каждый держит в руках свою часть слона и довольно уверенно объясняет остальным, как именно тот устроен.
Именно поэтому аргументы так часто проходят мимо цели. Один доказывает, что текст удобно хранить в файлах, другой объясняет ценность автоматического отбора, третий рассуждает о состоянии процесса, а четвёртый — о том, как не повторить старую ошибку спустя полгода. Никто из них не обязательно заблуждается; просто каждый решает свою задачу и по привычке называет её одним словом.
Памятью.
Чтобы понять, отчего этот спор возвращается снова и снова, придётся сначала развести понятия, которые в повседневной речи давно слиплись в одно.
Память как документация
Самое простое представление о памяти агента возникает почти само собой: если модель забывает проект между сессиями, значит, нужно записать всё важное туда, где оно не исчезнет вместе с закрытым окном чата.
Так появляются AGENTS.md, MEMORY.md, ARCHITECTURE.md, CURRENT_STATE.md и десятки других файлов, названия которых могут различаться, но замысел остаётся прежним: оставить для следующего сеанса несколько аккуратных записок о том, что это за проект, как он устроен, какие правила в нём действуют и до какого места добралась работа.
Подход настолько естественный, что вначале даже не воспринимается как отдельная система памяти. Скорее это привычная документация, только адресованная не новому сотруднику, который выйдет на работу в понедельник, а агенту, который через час придет в проект с выражением полного непонимания на виртуальном лице.
И надо признать, во многих случаях этого вполне хватает.
Файлы лежат рядом с кодом, меняются вместе с ним, проходят через Git и остаются понятными без дополнительных инструментов. Человек может открыть их в любом редакторе, просмотреть историю, восстановить причину изменения, а при необходимости поправить одну фразу, не запуская отдельный сервис и не вспоминая, на каком порту он давеча слушал.
Есть в такой памяти и ещё одно достоинство, которое легко недооценить: она обозрима. Несколько небольших документов можно прочитать целиком, составить о них собственное мнение и заметить противоречие, не полагаясь на поисковый алгоритм, который что-то нашёл, а что-то счёл недостаточно близким по смыслу.
Когда проект невелик, эта простота не признак примитивности, а преимущество. Если правила занимают пару страниц, текущая работа описывается несколькими абзацами, а архитектура ещё помещается в голове одного человека, отдельная база памяти способна принести больше забот, чем пользы. Получается примерно как с домашней библиотекой: для трёх полок не нужен электронный каталог, достаточно знать, что книги по программированию стоят слева, а та самая синяя, которую все ищут, опять лежит возле кровати.
Поэтому вопрос «зачем здесь база данных?» нельзя заранее считать признаком технической отсталости. Иногда честный ответ звучит так: незачем.
Проблема появляется не в формате и даже не в количестве файлов, а значительно позже, когда документы начинают понемногу выполнять работу, для которой их исконно не создавали.
Сначала в MEMORY.md лежат пять важных решений. Затем двадцать. Потом туда добавляются правила, исключения из правил, временные предупреждения, заметки о старых сбоях, причины нескольких странных компромиссов и короткое напоминание о задаче, которую обязательно нужно продолжить после выходных.
Со временем появляются новые файлы, чтобы разгрузить старые. Их связывают ссылками, делят по темам, снабжают оглавлениями и краткими инструкциями о том, в каком порядке всё это читать. Система продолжает работать, однако незаметно меняется сама задача: раньше нужно было сохранить несколько сведений, теперь — выбрать из множества документов те, которые имеют отношение к очередной работе.
Файлы всё ещё прекрасно хранят текст. Они не стали хуже, не устарели и не проиграли какой-то более современной технологии. Просто накопленное знание разрослось, а внимание агента осталось конечным.
В этот момент спор о носителе начинает уводить в сторону. Можно держать сто заметок в markdown, одну большую базу SQLite или аккуратный сад в Obsidian — трудность будет прежней: кто и по какому признаку решит, что сегодня агенту нужны именно эти три фрагмента, а остальные девяносто семь лучше пока не трогать?
До этой границы документация была памятью сама по себе. После неё она всё чаще становится материалом, из которого память ещё предстоит собрать.
Память как поиск
Когда знаний становится больше, чем можно безболезненно перечитать перед каждой задачей, память перестаёт быть просто местом хранения и постепенно обзаводится новой обязанностью — находить нужное прежде, чем агент успеет утонуть в полезной информации.
В проекте к этому моменту уже успевают осесть архитектурные решения, их последствия, правила, их исключения, результаты расследований, старые предупреждения, странные обходные пути и несколько заметок, смысл которых понятен только человеку, написавшему их в половине третьего ночи после особенно удачного знакомства с причиной бага.
Ничего из этого не хочется терять. Всё когда-нибудь может пригодиться.
Именно слово «когда-нибудь» обычно и создаёт проблему.
Если каждый раз загружать весь накопленный архив, память начинает напоминать сборы в поездку: одному хватает рюкзака с планшетом и носками, другая берёт чемодан, где на всякий случай лежит одежда на жару, дождь, внезапный ужин, случайный поход в театр и, вероятно, небольшую смену эпохи.
Формально лишних вещей там нет.
Всё полезное.
Только нужную вещь приходится искать среди десятка столь же предусмотрительно взятых.
Здесь на сцену выходят SQLite, полнотекстовый поиск, BM25, embeddings, векторные индексы, семантическое ранжирование и прочие инструменты, вокруг которых спор о памяти часто и начинает вращаться, хотя сами по себе они памятью не являются. Это скорее каталог, который помогает не бродить между стеллажами, пока библиотекарь уже выключает свет.
Ни база данных, ни векторное хранилище не экономят токены одним фактом своего существования. Если система складывает текст в таблицы, а затем по каждому запросу выгружает модели всё содержимое, она отличается от гигантского MEMORY.md главным образом тем, что теперь у проблемы есть схема данных.
Выигрыш появляется в момент отбора.
Агент получает не весь проектный архив, а несколько фрагментов, которые относятся к текущей работе: одно правило, пару решений, предупреждение о старом инциденте и, возможно, заметку о файле, где подобная проблема уже пряталась в прошлый раз.
Размер проекта при этом может продолжать расти, а объём передаваемого контекста остаётся связанным не с общей массой накопленного знания, а со сложностью конкретной задачи.
Именно на этом месте участники спора обычно начинают разговаривать мимо друг друга.
Один говорит, что markdown-файлов можно сделать сколько угодно, связать ссылками и разложить по папкам. Он прав: знания действительно можно организовать таким образом, причём прозрачно и без лишней инфраструктуры.
Другой отвечает, что база позволяет доставать только релевантные части. И он тоже прав, но уже в другом вопросе — не о хранении, а об автоматическом выборе.
Связанные файлы предлагают заранее проложенный маршрут. Поиск пытается построить его на ходу, исходя из запроса, текущего контекста и уже известных связей.
Пока проект мал, эта разница почти не ощущается. Человек и сам знает, какой файл открыть, а агенту можно прямо написать: «прочитай вот эти три документа и не ходи гулять по репозиторию без надобности».
Но по мере роста ручной маршрут начинает ветвиться. Сводки устаревают, количество файлов увеличивается, ссылки ведут по цепочкам, а важное решение, которое точно где-то было, обнаруживается обычно после того, как его уже приняли заново.
Retrieval здесь нужен не ради модного слова и не ради особой любви к векторным базам. Он нужен, чтобы разорвать прямую зависимость между объёмом накопленной памяти и объёмом очередного контекста.
При этом противопоставление «markdown или база» часто оказывается ложным. Канонические правила могут по-прежнему лежать в файлах, заметки — в Obsidian, решения — рядом с кодом, а поисковый слой лишь индексирует всё это и подбирает несколько уместных фрагментов.
Файлы сохраняют.
Индекс помогает находить.
Семантический поиск угадывает, что два текста говорят об одном и том же, даже если их авторы использовали разные слова.
Каждый инструмент делает свою работу, и неприятности начинаются как раз тогда, когда один из них объявляют универсальным ответом на всё.
Есть, правда, и обратная опасность: поисковая система может уверенно выбрать не то. Особенно если память накоплена без структуры, записи противоречат друг другу, устаревшие решения не помечены, а семантически близкий текст оказался просто удачно сформулированной ересью двухлетней давности.
Поэтому хорошая память должна уметь не только находить, но и различать актуальное, устаревшее, отменённое и сомнительное. Иначе retrieval превращается в очень быстрого библиотекаря, который безошибочно приносит книгу, но не замечает, что это старое издание с вырванной последней главой.
Даже при хорошем поиске остаётся ещё один предел.
Система может найти все релевантные правила, поднять похожие решения и выдать агенту нужные документы, однако ничего не сказать о том, где именно остановилась работа, какая гипотеза сейчас проверяется и почему один из вариантов решили отложить.
Поиск возвращает сохранённое знание.
Но живая работа не всегда успевает превратиться в знание.
Память как состояние работы
Даже хороший поиск однажды упирается в довольно простую вещь: можно безошибочно найти все правила, архитектурные решения и похожие задачи, но всё равно не понять, что именно происходит в проекте сейчас.
Допустим, в документации записано, что все внешние интеграции проходят через слой адаптеров. Это знание устойчивое, почти геологическое: оно лежит в основании системы, переживает недели и месяцы, иногда даже нескольких архитекторов.
А теперь другой тип записи:
Проверяем разделение адаптеров по доменам; два варианта уже отвергнуты, третий частично реализован, работа остановлена на проверке обратной совместимости.
Это уже не знание о проекте, а след его движения.
Через неделю запись может устареть, через месяц — потерять смысл, а через квартал — превратиться в короткое примечание к давно принятому решению. Но сегодня без неё агент не поймёт, куда вернуться, если пользователь скажет: «Продолжи с того места, где остановились».
Именно здесь документация и "retrieval" перестают быть достаточными. Они могут рассказать, как устроен проект и какие решения были приняты раньше, но не обязаны хранить рабочее напряжение текущего момента: какую гипотезу проверяют, что уже сделано, какой вариант отложили, какие файлы оказались важными и почему задача внезапно замерла на полпути.
Человек восстанавливает такое состояние почти незаметно. Он помнит вчерашний разговор, пару открытых окон, неудачный тест, раздражающий шаблоный код и мысль, которую не успел договорить самому себе перед уходом. Агенту всё это приходится собирать заново, если система не сохранила след.
Поэтому в памяти начинают появляться сущности, которые на первый взгляд скорее принадлежат трекеру задач, чем «второму мозгу»: активная задача, гипотеза, статус проверки, список затронутых файлов, временные ограничения, чекпоинт, причина остановки.
Стороннику классического MEMORY.md такая конструкция может показаться чрезмерной, и его нетрудно понять. Когда память внезапно обрастает задачами, решениями, инцидентами и переходами между стадиями, возникает подозрение, что кто-то хотел сохранить пару заметок, а проснулся владельцем маленькой ERP.
Но причина здесь не в страсти к усложнению.
Рабочее состояние по природе своей не похоже на документацию. Оно меняется быстрее, содержит черновые гипотезы, допускает противоречия и живёт ровно столько, сколько длится сама задача. Если пытаться вносить всё это в архитектурные файлы, они быстро превращаются в смесь вечных правил, вчерашних предположений и позавчерашних тревог.
Документация должна быть относительно спокойной.
Рабочая память, напротив, обязана быть подвижной.
Она хранит не только то, что уже известно, но и то, что пока проверяется; не только решения, но и незаконченные развилки; не только результат, но и точку, в которой пришлось остановиться.
Особенно ценным оказывается список файлов, с которыми агент уже работал. На бумаге это выглядит мелочью, излишней бюрократией, но при возвращении к задаче такой след экономит значительную часть повторного поиска. Агент не блуждает по репозиторию с фонариком, а начинает с тех мест, где работа действительно шла.
Чекпоинты здесь играют ту же роль, что зарубки на тропе: они не объясняют весь лес, зато позволяют понять, откуда пришли, куда собирались и на каком повороте свернули не туда.
Память состояния полезна не потому, что знает больше документации, а потому, что знает иное. Она удерживает незавершённость — вещь, которую статические документы переносят плохо, а реальная разработка производит ежедневно и в немалом количестве.
Однако даже хорошо сохранённое состояние не гарантирует, что проект не повторит старую ошибку. Можно точно знать, где остановилась работа, и всё равно снова выбрать путь, который уже однажды оказался тупиковым.
Для этого нужна память другого рода — та, что сохраняет не только ход работы, но и цену прошлых решений.
Память как опыт
У каждого достаточно старого проекта есть участки кода, которые вызывают у нового разработчика почти религиозное недоумение.
Он смотрит на условие, обёртку, странный обходной путь или метод, разбитый на три части без видимой причины, и первая мысль возникает сама собой:
Кто вообще так сделал?
Иногда ответ находится быстро и не приносит облегчения.
Сделал человек, который до этого проверил два более красивых варианта, сломал обратную совместимость, получил редкий сбой на старом клиенте, откатил изменения и оставил после себя решение, внешне похожее на инженерную оплошность, а по сути — на шрам.
Код сохранил результат.
Задача, возможно, сохранила дату.
Документация могла зафиксировать текущее устройство.
Но весь путь к решению, включая неудачные попытки, ограничения и цену компромисса, чаще всего растворился где-то между закрытым чатом, забытым комментарием и фразой «потом надо будет нормально описать».
Разумеется, нормально никто уже не описал.
Так и исчезает опыт проекта.
Он редко живёт в одном месте, потому что сам по себе состоит не из фактов, а из связей между ними. Решение оказалось плохим не вообще, а при определённой нагрузке; архитектурное ограничение появилось не из любви к строгости, а после конкретного инцидента; неуклюжий интерфейс сохранили не потому, что всем нравилось страдать, а потому что на нём сидел старый клиент, который никто не решался трогать.
Без этого контекста следующий разработчик видит только странность и естественным образом пытается её исправить.
Агент поступает так же, только быстрее.
Он находит локально красивое решение, объясняет его преимущества, аккуратно переписывает код и с чистой совестью приводит систему к ошибке, которую команда уже однажды оплатила неделей работы.
Именно поэтому память опыта нужна не для того, чтобы хранить ещё больше текста, а чтобы не терять выводы, добытые предыдущими попытками.
Такая память может содержать короткие записи:
этот путь уже проверяли, он ломает совместимость;
ограничение появилось после редкого сбоя;
решение выглядит избыточным, но защищает старый контракт;
компромисс принят сознательно;
идея привлекательна, однако в прошлый раз привела к каскаду побочных изменений
На первый взгляд это напоминает обычные заметки, но разница всё же есть. Заметка сообщает, что произошло. Опыт объясняет, почему это важно помнить.
В этом смысле память проекта постепенно становится похожа не на архив, а на инженерное чутьё, только вынесенное наружу и доступное не одному человеку, а всей команде и любому агенту, который придёт после.
Такой слой особенно ценен там, где работа длится месяцами, а решения проходят через несколько моделей, разработчиков и сменившихся приоритетов. Чем дольше живёт проект, тем больше в нём вещей, которые невозможно понять, просто прочитав код и актуальную документацию.
Именно здесь становятся интересны Obsidian, second brain, knowledge graph и другие системы, стремящиеся сохранять не только итог, но и связи между идеями, причины решений, ход рассуждений и историю понимания проблемы.
Правда, тут легко впасть в противоположную крайность и начать хранить вообще всё.
Каждый чат.
Каждую гипотезу.
Каждое сомнение.
Каждую фразу агента, включая ту, где он три абзаца объясняет, что собирается ещё немного подумать.
Получается не память, а цифровой чердак, где теоретически лежит всё нужное, но практически первым находится коробка с проводами от неизвестных устройств.
Опыт требует отбора.
Нужно сохранять не весь путь, а то, что способно повлиять на будущие решения: ошибку, которую легко повторить; причину странного ограничения; отвергнутый вариант, к которому кто-то непременно вернётся; компромисс, который без пояснения выглядит глупостью.
Такая запись может быть короткой, иногда всего в одну фразу, однако её ценность определяется не объёмом, а тем, сколько повторной работы она предотвращает.
В этом и состоит отличие накопленного знания от накопленного понимания.
Знание говорит, как система устроена.
Опыт напоминает, почему она стала именно такой.
Где на самом деле проходит граница
После всех этих различий исходный спор про markdown и SQLite начинает выглядеть не столько техническим, сколько оптическим.
Каждый участник смотрит на память с той высоты, до которой успел дорасти его проект, его рабочий процесс и, чего уж там, количество однажды повторённых ошибок.
Тому, кто ведёт небольшой репозиторий, где архитектура ещё помещается в голове, правила занимают две страницы, а незавершённую задачу можно восстановить по последнему коммиту, вполне может хватать AGENTS.md, MEMORY.md и пары аккуратных заметок. В такой ситуации специализированная система памяти действительно рискует оказаться лишней, потому что сложность добавляется сразу, а выгода обещает прийти когда-нибудь потом, возможно, вместе с единорогом и полной документацией.
У другого человека проект существует несколько лет, пережил несколько архитектурных поворотов, накопил сотни задач, десятки спорных решений и такое количество исключений, что сами правила уже начинают интересоваться, нет ли у них собственных исключений. Ему ручной подбор контекста обходится дороже, чем индекс, поиск и автоматическая сборка нужной порции знаний.
Третий работает сразу с несколькими агентами, которые сменяют друг друга, продолжают чужую работу и возвращаются к задачам спустя недели. Для него особенно важна не библиотека проектных знаний, а возможность восстановить рабочее состояние без очередной археологической экспедиции по репозиторию.
Четвёртый больше всего боится не забыть правило, а снова повторить старую ошибку, потому что красивых решений в проекте уже было достаточно, и часть из них до сих пор вспоминают без особой теплоты.
Все они выбирают память, но выбирают её для разных условий.
Поэтому вопрос «что лучше — markdown или база данных?» напоминает спор о том, какой транспорт совершеннее. Один живёт в двух кварталах от работы и искренне не понимает, зачем кому-то автомобиль; другой каждое утро везёт детей через полгорода; третий перевозит мебель, а четвёртый вообще работает на корабле. Название задачи одно — добраться, — но обувь почему-то не всегда побеждает.
Так же и с памятью.
Markdown может быть каноническим источником правил и решений. Obsidian — удобной человеческой средой, где заметки связываются в понятную автору карту. SQLite — персональным структурированным хранилищем. Полнотекстовый поиск — быстрым способом найти точную формулировку. Семантический слой — шансом поднять близкое по смыслу, даже если в запросе использованы другие слова. Рабочая память — механизмом возврата к незавершённой задаче. Ничто из перечисленного не обязано объявлять остальных устаревшими.
Системы начинают конфликтовать лишь тогда, когда одну из них пытаются оценивать по чужой мерке.
От markdown требуют автоматического отбора среди тысяч записей и затем удивляются, что он сам не догадался, какие три абзаца нужны агенту сегодня. От семантического поиска ждут безусловной истины, хотя он умеет находить близкое по смыслу, но не знает, что найденное решение отменили полгода назад. От task memory хотят вечной архитектурной мудрости, хотя она хранит прежде всего ход текущей работы. А базу данных порой хвалят уже за сам факт её наличия, словно таблицы автоматически превращают текст в знание.
Никакой носитель не исправляет плохо устроенную память.
Если записи противоречат друг другу, устаревшие правила не помечены, задачи не закрываются, а опыт сохраняется в виде бесконечных сырых логов, то база лишь поможет быстрее найти нужный хаос.
Поэтому сравнивать стоит не то, где лежит текст, а то, как устроен весь путь знания: кто его создаёт, как оно обновляется, по какому признаку считается актуальным, каким образом попадает в контекст и что происходит, когда оно перестаёт быть полезным.
Есть и более прозаический критерий — человеческий труд.
Память, которую нужно постоянно кормить вручную, со временем начинает недоедать. Сначала все дисциплинированно обновляют сводки, фиксируют решения и связывают заметки, затем появляется срочная задача, потом ещё одна, после чего CURRENT_STATE.md тихо остаётся жить в прошлой неделе и продолжает уверенно рассказывать агенту о мире, которого уже нет.
Автоматизация не отменяет эту проблему, но меняет цену поддержания порядка. Записать правило командой, зафиксировать чекпоинт вместе с переходом задачи, сохранить изменённые файлы по ходу работы проще, чем каждый раз вспоминать, в какой документ и в какой раздел следует внести очередную крупицу опыта.
И здесь проходит, пожалуй, одна из самых важных границ.
Статическая память ждёт, что человек или агент заранее подготовит нужный контекст.
Динамическая пытается собрать его сама — из текущей задачи, стадии работы, активных правил, связанных решений и прошлого опыта.
Ни одна из них не является безусловно лучшей. Первая выигрывает прозрачностью и простотой, вторая — масштабируемостью и способностью приспосабливаться к ситуации.
Проблема начинается не тогда, когда кто-то выбирает markdown вместо базы данных или наоборот.
Она начинается, когда решение, отлично работавшее на прежнем масштабе, продолжают считать достаточным после того, как проект давно стал другим.
P.S.
Для тех, кто пролистал: спор о памяти для AI-агентов начинается не с выбора между markdown и Obsidian, а с того, какую именно задачу каждый из этих инструментов должен решать.
А если дочитали до конца — поделитесь в комментариях своим опытом и тем, что вы сами называете памятью агента.
