Авторская колонка

Пятислойная иммутабельная память для ИИ-агента: почему одной векторной базы никогда не хватит

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

Пятислойная иммутабельная память для ИИ-агента: почему одной векторной базы никогда не хватит
Hamidun News.
Слушать статью
Паспорт материала и цифры
ПараметрДанные
Главный редактор
Объём эпизодического индекса66 995 сессий и 538 956 сообщений в базе SQLite FTS5 размером 1 353 МБ
База второго мозга и эмбеддингов10 019 структурированных карточек знаний и `brain.db` объёмом 2,37 ГБ
Ключевой факт #2Скорость старта после отказа от тяжёлых хуков: падение латентности инициализации с 5–15 с до < 0,2 с
Пороги ночной консолидациисклейка дублей при косинусном сходстве ≥ 0,70 и детектор противоречий с порогом 0,40 без вызова LLM
  • Объём эпизодического индекса: 66 995 сессий и 538 956 сообщений в базе SQLite FTS5 размером 1 353 МБ
  • База второго мозга и эмбеддингов: 10 019 структурированных карточек знаний и `brain.db` объёмом 2,37 ГБ
  • Скорость старта после отказа от тяжёлых хуков: падение латентности инициализации с 5–15 с до < 0,2 с
  • Пороги ночной консолидации: склейка дублей при косинусном сходстве ≥ 0,70 и детектор противоречий с порогом 0,40 без вызова LLM

Точкой отсчёта для моей архитектуры памяти стал глухой краш редактора кода. При попытке открыть историю диалогов с ИИ-агентом процесс 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`) периодически проводит уборку в три этапа:

1. Кластеризация дублей (SHMR): матрица косинусной близости на эмбеддингах размерности 1024 (`Qwen3-Embedding-0.6B` в LanceDB) схлопывает записи со сходством `>= 0.70`. 2. Селективное затухание по распределению Вейбулла: мимолётные наблюдения (`observation`) затухают за 336 часов (~2 недели), рабочие факты — за 2 160 часов, а личные предпочтения (`preference`), процедуры и паттерны ошибок (`error_pattern`) защищены списком `EXEMPT_TYPES`. 3. Поиск противоречий без LLM: детерминированный сканер ищет конфликты за 7 дней по взвешенным сигналам (`negation: 0.35`, `antonym: 0.25`, `numeric: 0.25`, `temporal: 0.15`) при пороге `0.40`.

Эта пятислойная схема памяти (SQLite FTS5, LanceDB и темпоральный граф) встроена в ядро [Nomad AI](https://nomadnet.ai) и облачной платформы [agent.nomadnet.ai](https://agent.nomadnet.ai). Курс по инженерии памяти доступен в [Hamidun Academy](https://academy.hamidun.com) (72-часовой триал), а скрипты индексации лежат в библиотеке [Vibe Club](https://hamidun.com/club).

Сюжет

Вопросы и ответы по материалу

Почему 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 МБ,

Зачем нужен детерминированный UUID5 в векторной базе и как работает битемпоральность?

Когда точных слов в запросе нет (человек спрашивает «чем мыть фрукты без химии», а в базе заметка про щелочной режим ионизатора `pH 11.5`), вступает семантический слой на эмбеддингах (размерность 384 локально и 1536 для книг). В жизни идентификаторы документов выглядят как строки `knowledge:42` или `session/2026-04-10/msg-17` с кириллицей, но векторный движок Qdrant требует в качестве `point_id` либо целое число, либо строгий UUID. Случайный `uuid4()` плодит дубликаты при переиндексации, поэтому мы ввели детерминированный `uuid5` поверх пространства имён DNS: ```python impo

Как алгоритмический сон с кривой Вейбулла очищает память от противоречий?

Чтобы база не превращалась в свалку, фоновый пайплайн консолидации (`dream` и модули `sleeptime`) периодически проводит уборку в три этапа: 1. Кластеризация дублей (SHMR): матрица косинусной близости на эмбеддингах размерности 1024 (`Qwen3-Embedding-0.6B` в LanceDB) схлопывает записи со сходством `>= 0.70`. 2. Селективное затухание по распределению Вейбулла: мимолётные наблюдения (`observation`) затухают за 336 часов (~2 недели), рабочие факты — за 2 160 часов, а личные предпочтения (`preference`), процедуры и паттерны ошибок (`error_pattern`) защищены списком `EXEMPT_TYPES

ЖХ
Жемал Хамидун — CPO Alpina Digital, продакт-менеджер AlpinaGPT. Делает AI-продукты с 2019 года. Hamidun News — pet project, который вырос в production-агрегатор с 6000+ читателей.
✦ Практика · Hamidun Academy

Соберите рабочего ИИ-агента или сервис своими руками без ручного кода

В Академии Жемала Хамидуна (academy.hamidun.com) открыт бесплатный 72-часовой тест-драйв: интерактивные скетч-уроки по Claude Code и Cursor, живой Tech Radar и готовые шаблоны агентов.

Открыть 72ч бесплатный доступ →Интенсив по Вайб-кодингу
Что вы думаете?
Загружаем комментарии…