Пятислойная иммутабельная память для ИИ-агента: почему одной векторной базы никогда не хватит
Попытка хранить всю долговременную память ИИ-агента в одном векторном индексе проваливается, потому что точный поиск по словам, семантическая близость, многошаговый обход связей и история смены решений требуют физически разных структур данных. Инкрементальный индекс SQLite FTS5 с ранжированием BM25 поверх 66 995 сессий и 538 956 сообщений (1,35 ГБ) избавил от падений среды разработки и ускорил старт сессии с 15 секунд до десятых долей секунды. Для защиты памяти от деградации внедрены детерминированная санитизация ключей через UUID5, битемпоральная запись без удаления старых фактов и ночной цикл консолидации с затуханием по распределению Вейбулла.

Точкой отсчёта для моей архитектуры памяти стал глухой краш редактора кода. При попытке открыть историю диалогов с ИИ-агентом процесс Extension Host превысил лимит памяти и рухнул вместе с терминалом. За полгода работы накопилось 2,45 ГБ сырых JSONL-логов сессий, и расширение на старте пыталось прочитать их заголовки с диска. Вдобавок заводская настройка cleanupPeriodDays=30 молча удаляла сессии старше месяца — пока я не заменил её на 99999, часть зимнего архива исчезла.
Ревизия показала, что две из четырёх моих баз памяти мертвы, так как требовали ручного запуска демонов. Главное же открытие состояло в том, что «память агента» — это пять разных классов вопросов, и под каждый нужен свой движок.
Почему SQLite FTS5 выиграл у Qdrant и grep на полумиллионе сообщений?
Для эпизодической памяти («где мы обсуждали настройку вебхуков три месяца назад») grep по гигабайтам текста был слишком медленным, а подъём локального Qdrant с fastembed на старте сессии тормозил запуск на 5–15 секунд и сжирал сотни мегабайт RAM.
Победил встроенный в Python движок SQLite FTS5 с ранжированием BM25 и режимом WAL. Индексатор search_chats.py сверяет file_mtime с таблицей sessions: если расхождение меньше секунды, файл пропускается. Сырые логи старше 14 дней и дампы свыше 20 МБ уезжают в холодный архив — рабочий каталог похудел с 2,45 ГБ до 527 МБ, краши прекратились, а индекс вырос до 66 995 сессий и 538 956 сообщений (290 544 от человека и 248 412 от ассистента) при весе 1 353 МБ. Для критичных справочников (вроде 20-главной инструкции, собранной из 2 240 сообщений и 177 расшифрованных через Deepgram голосовых заметок) действует 4-уровневое резервирование: конспект в рабочей памяти, указатель в MEMORY.md (строго до 200 строк и ~22 КБ), правило маршрутизации и векторный слепок.
Зачем нужен детерминированный UUID5 в векторной базе и как работает битемпоральность?
Когда точных слов в запросе нет (человек спрашивает «чем мыть фрукты без химии», а в базе заметка про щелочной режим ионизатора pH 11.5), вступает семантический слой на эмбеддингах (размерность 384 локально и 1536 для книг). В жизни идентификаторы документов выглядят как строки knowledge:42 или session/2026-04-10/msg-17 с кириллицей, но векторный движок Qdrant требует в качестве point_id либо целое число, либо строгий UUID. Случайный uuid4() плодит дубликаты при переиндексации, поэтому мы ввели детерминированный uuid5 поверх пространства имён DNS:
```python import uuid
def to_deterministic_point_id(raw_doc_id: str) -> str:
# Превращает любую строку с кириллицей и спецсимволами в стабильный ASCII UUID5 return str(uuid.uuid5(uuid.NAMESPACE_DNS, raw_doc_id))
point_id = to_deterministic_point_id(doc_id) payload = {**metadata, "doc_id": doc_id, "content": text_chunk} ```
Одна и та же строка всегда даёт идентичный UUID, превращая повторную запись в идемпотентный upsert, а исходный ключ сохраняется в payload.
Чтобы агент помнил историю смены решений («почему весной мы выбрали другой движок?»), мы позаимствовали идею из Datomic и запретили UPDATE с затиранием прошлого. Каждая связь имеет поля valid_from, valid_to и ingested_at, а в базе второго мозга также счётчики востребованности use_count и last_used. Когда решение устаревает, у старого ребра проставляется valid_to, и рядом вставляется новая запись со ссылкой supersedes.
Как алгоритмический сон с кривой Вейбулла очищает память от противоречий?
Чтобы база не превращалась в свалку, фоновый пайплайн консолидации (dream и модули sleeptime) периодически проводит уборку в три этапа:
- Кластеризация дублей (SHMR): матрица косинусной близости на эмбеддингах размерности 1024 (
Qwen3-Embedding-0.6Bв LanceDB) схлопывает записи со сходством>= 0.70. - Селективное затухание по распределению Вейбулла: мимолётные наблюдения (
observation) затухают за 336 часов (~2 недели), рабочие факты — за 2 160 часов, а личные предпочтения (preference), процедуры и паттерны ошибок (error_pattern) защищены спискомEXEMPT_TYPES. - Поиск противоречий без LLM: детерминированный сканер ищет конфликты за 7 дней по взвешенным сигналам (
negation: 0.35,antonym: 0.25,numeric: 0.25,temporal: 0.15) при пороге0.40.
Эта пятислойная схема памяти (SQLite FTS5, LanceDB и темпоральный граф) встроена в ядро Nomad AI и облачной платформы agent.nomadnet.ai. Курс по инженерии памяти доступен в Hamidun Academy (72-часовой триал), а скрипты индексации лежат в библиотеке Vibe Club.
- 12 нояб. 2026 г.Claude in Chrome требует разрешать каждый клик на сайтах с категорией 3
- 12 нояб. 2026 г.Norwest собрал рекордный фонд на $4 млрд и ставит на агентов, голос и физический ИИ
- 12 нояб. 2026 г.AWS показала, как 100% точности поиска скрывают провал ИИ-агента
- 12 нояб. 2026 г.Garage Eight убрала чужие апрувы в 69 MR внутреннего сервиса с AI-ревью
Вопросы и ответы по материалу
Почему SQLite FTS5 выиграл у Qdrant и grep на полумиллионе сообщений?
Для эпизодической памяти («где мы обсуждали настройку вебхуков три месяца назад») grep по гигабайтам текста был слишком медленным, а подъём локального Qdrant с fastembed на старте сессии тормозил запуск на 5–15 секунд и сжирал сотни мегабайт RAM. Победил встроенный в Python движок SQLite FTS5 с ранжированием BM25 и режимом WAL. Индексатор search_chats.py сверяет file_mtime с таблицей sessions: если расхождение меньше секунды, файл пропускается.
Зачем нужен детерминированный UUID5 в векторной базе и как работает битемпоральность?
Когда точных слов в запросе нет (человек спрашивает «чем мыть фрукты без химии», а в базе заметка про щелочной режим ионизатора pH 11.5), вступает семантический слой на эмбеддингах (размерность 384 локально и 1536 для книг). В жизни идентификаторы документов выглядят как строки knowledge:42 или session/2026-04-10/msg-17 с кириллицей, но векторный движок Qdrant требует в качестве point_id либо целое число, либо строгий UUID.
Как алгоритмический сон с кривой Вейбулла очищает память от противоречий?
Чтобы база не превращалась в свалку, фоновый пайплайн консолидации (dream и модули sleeptime) периодически проводит уборку в три этапа: Кластеризация дублей (SHMR): матрица косинусной близости на эмбеддингах размерности 1024 (Qwen3-Embedding-0.6B в LanceDB) схлопывает записи со сходством >= 0.70.
Внедряйте ИИ быстрее рынка вместе с инженерами и фаундерами
Внутри Vibe Club (hamidun.com/club): еженедельные живые мастермайнды и разборы архитектуры с Жемалом Хамидуном, закрытый Tech Radar, LMS и обновляемая база из 370+ боевых скиллов.
Главное из мира ИИ — раз в неделю
7 ключевых событий недели, отобранных вручную. Без шума, репостов и пресс-релизов.
Готово! Проверьте почту — мы отправили подтверждение.