Исследование размера и жизненного цикла vector.db
Дата фиксации: 10 июля 2026 года.
Эта заметка сохраняет результаты исследования векторной памяти Велеса для последующего продолжения работы. Она относится к текущей реализации в veles/agent/vector_memory.py и к реальной базе из рабочей среды клиента.
Решение от 25 августа 2026 года
Повторное наблюдение под нагрузкой показало около 3,36 ГБ вvector.db, около 774 МБ в журнале WAL и 77 579 фрагментов. Главной причиной высокой резидентной памяти оказался не сервер Nerve, а подготовка всех эмбеддингов крупного файла в одном списке Python. Одновременно в памяти оставались исходный текст, все фрагменты, строки для запроса, списки чисел из ответов и буферы SQLite. Сырые аварийные архивы давали значительную долю нагрузки.
Реализовано следующее решение:
- один файл допускается к индексации только до
tools.vectorMemory.maxFileBytes; значение по умолчанию — 8 МиБ; memory/*-raw-archive.mdсохраняются на диске, но исключены из обнаружения и обоих видов поиска;- файл разбивается и отправляется поставщику порциями не более 20 фрагментов;
- до первого запроса эмбеддингов все фрагменты подсчитываются во временной базе, проверяются предел в 8192 фрагмента и нижняя оценка текстовой записи; после первого ответа полная оценка с известной размерностью вектора проверяется до упаковки и публикации, а поколение более 64 МиБ отклоняется; за один проход полное чтение, первичное хеширование, переиндексация и постановка исчезнувших путей в очередь выполняются ограниченно, не более чем для 20 файлов каждого вида работы;
- обнаружение путей не создаёт общий список в памяти Python: дерево обходится потоком, полный снимок имён складывается порциями во временную дисковую базу, а между каждыми 100 просмотренными записями управление возвращается шлюзу; сортировка читается из индекса временного снимка, а позиция справедливого продолжения сохраняется в служебных метаданных основного индекса;
- список уже индексированных путей сверяется с тем же временным снимком дисковыми порциями по 100 строк, без отдельного запроса Python для каждого файла; если обход каталога завершился не полностью из-за ошибки чтения, удаление якобы исчезнувших путей на этом проходе не выполняется;
- суммарная расчётная запись поколений за проход не превышает 64 МиБ; обращения к поставщику заранее резервируются для целого файла, а допустимый файл, которому мало обычного бюджета, может превысить его только первым, не делит обращения с другим файлом и всё равно ограничен числом фрагментов;
- каждая порция сразу переводится в двоичный вид и записывается во временную дисковую базу, поэтому список эмбеддингов всего файла больше не удерживается в Python;
- одинаковый текст внутри обработки использует один запрос эмбеддинга, но отдельные пути и диапазоны строк сохраняются как самостоятельные источники;
- полностью подготовленное поколение файла заменяет прежнее одной ограниченной транзакцией; перед публикацией и перед постановкой отклонённого поколения в очередь повторно и с тем же пределом размера проверяются хеш исходника и редакция настроек;
- повторный сбой эмбеддингов для неизменившегося файла не переписывает текст, полнотекстовый индекс и временные метки;
- первый сбой поставщика открывает общий интервал ожидания, поэтому остальные файлы не создают сотни одинаковых неудачных запросов;
- новые и обновлённые строки хранят вектор только в
chunks_vec; полеchunks.embeddingостаётся для чтения старых баз, но новая JSON-копия туда не записывается; - размерность сначала восстанавливается из объявления
chunks_vec; несовпадение размерности прекращает публикацию и никогда не удаляет действующую таблицу; - параметры разбиения новых и обновлённых файлов записываются в
files.index_signature, поэтому их последующее изменение приводит к пофайловому обновлению; пустая подпись старой схемы остаётся явным признаком наследия и сама по себе не запускает массовую перезапись; - размер, время изменения и другие признаки исходника сохраняются в
files, поэтому неизменившиеся файлы не хешируются заново при каждом минутном проходе; - исключённый, исчезнувший или явно удаляемый крупный путь сначала удаляется из
filesи сразу пропадает из поиска, затемcleanup_queueудаляет до 100 фрагментов за транзакцию; новые полнотекстовые строки имеют индексированное соответствие с фрагментом, а старые очищаются справедливо чередуемыми окнами до 100 строк поrowid; - при постановке старого полнотекстового поколения в очередь фиксируется верхняя граница
rowid, поэтому очистка не гонится за новыми строками других файлов; соответствиеfiles.fts_map_completeпозволяет современным поколениям вообще не выполнять общий проход; - режим двоичного, смешанного или старого JSON-хранилища и неизвестность профиля прежней схемы записываются в
vector_index_metaдо добавления новых столбцов; запуск не соединяет целиком многогигабайтные таблицы ради доказательства полноты двоичных копий, а неизменившиеся поколения с неизвестным профилем сохраняются без повторного полного хеширования; - компактный счётчик оставшихся JSON-поколений уменьшается в одной транзакции с удалением или заменой их строк в
files, в том числе пока доступность двоичного расширения ещё не выяснена; нулевое значение снимает ограничение смысловой индексации без обходаchunks; - точное удаление и фоновая очистка одинаково обрабатывают исторические пути с
/и\; - соединение ограничивает внутренний кэш, задаёт автоматические контрольные точки и предпочтительный предел журнала 64 МиБ; обычный проход использует неблокирующую контрольную точку, а усечение выполняется при спокойном завершении; занятый читатель откладывает его без изменения данных.
VACUUM, удаление vector.db и очистка старых JSON-векторов в действующей базе по-прежнему запрещены.
Ручное уплотнение от 25 августа 2026 года
Для уменьшения активногоvector.db добавлена одна узкая команда veles vector-memory compact. Без --yes она только показывает план; изменения начинаются только при явном --yes.
Контракт операции:
- шлюз и все процессы агента для рабочей области должны быть остановлены; межпроцессная блокировка владельца не позволяет уплотнению и индексатору одновременно открыть базу;
- после получения блокировки команда выполняет контрольную точку WAL, чтобы ожидающие записи вошли в стабильный снимок; замена не продолжается с непустым журналом;
- на той же файловой системе создаётся свежая теневая база, в которую попадают только живые файлы и фрагменты;
- достоверные старые JSON-векторы переносятся в двоичное хранилище; исторические нулевые заглушки считаются отсутствующими смысловыми данными, но их фрагменты остаются в полнотекстовом поиске, а недостающие векторы не строятся через поставщика модели;
- полнотекстовый индекс строится заново; целостность, количества строк, размерность и связи проверяются до публикации;
- после проверок теневая база атомарно заменяет активную, а байт-в-байт резервная копия прежней базы с отметкой времени всегда остаётся в
memory; - исходные файлы, записи памяти и настройки не изменяются, внешние вызовы к поставщику модели не выполняются;
- любая ошибка до атомарной замены оставляет прежний
vector.dbактивным.
vector.db, но не возвращает место тому в целом, пока оператор не проверит новую базу и вручную не переместит или не удалит резервную копию. Это намеренная цена безопасного отката.
Разделы ниже фиксируют первоначальное исследование от 10 июля и принятый тогда консервативный промежуточный план. Если они расходятся с решениями от 25 августа, актуальными считаются разделы выше; исторические числа и варианты сохранены как контекст диагностики.
Важно различать источники сведений:
- код и тесты исследовались в рабочей копии
C:\projects\veles; - числовая диагностика ниже получена другим агентом непосредственно из реальной базы
/data/veles/workspace/memory/vector.db; - результаты осмотра тестовой базы намеренно не используются как доказательство состояния клиентских данных;
- никакие изменения реальной базы в рамках этого исследования не выполнялись.
Краткий вывод
Первоначальная гипотеза о массовом сохранении удалённых файлов в индексе не подтвердилась для исследованной реальной базы. На момент проверки:- все записи
files.pathсуществовали на диске; - хеши проиндексированных файлов совпадали с текущим содержимым;
- проиндексированных, но больше не обнаруживаемых файлов не было;
- основных фрагментов без родительской записи
filesв проверенных агрегатах не найдено; - число свободных страниц SQLite было практически нулевым.
vector.db архитектурная: каждый эмбеддинг хранится как большой JSON в chunks.embedding и повторно как двоичный вектор float32 в chunks_vec. Текст также хранится в chunks.text и во внутренних таблицах FTS5. Отдельно большой объём занимает WAL.
На момент первого исследования было принято консервативное промежуточное решение не менять запись новых строк. Позднее его заменило решение от 25 августа: старые строки не переписываются, но новые JSON-копии эмбеддингов больше не создаются.
Фактическое состояние реальной базы на 10 июля
Размеры файлов
SQLite работал в режиме WAL.
Страницы SQLite
Проверка арифметики:
VACUUM без предварительного удаления живых данных смог бы вернуть лишь около 360 КБ, переписав при этом весь файл объёмом 1,24 ГБ.
Сопоставление с файловой системой
Результаты проверки:files:
docs/Astras/astras_issues_2025.json;memory/HISTORY.md.
Ограничения диагностики
Полные запросы поdbstat, SUM(LENGTH(embedding)), виртуальным таблицам и некоторым соединениям выполнялись слишком долго или завершались по тайм-ауту. Поэтому точного распределения 1,24 ГБ по внутренним таблицам нет.
При последующей диагностике нельзя без необходимости полностью читать все JSON-эмбеддинги и таблицы sqlite-vec/FTS5 на работающем экземпляре.
Как данные хранились на 10 июля
Основные уровни хранения:files— путь, проект, хеш файла, модель и состояние эмбеддингов.chunks— текст каждого фрагмента и JSON-представление эмбеддинга.chunks_fts— полнотекстовое представление текста и внутренние таблицы FTS5.chunks_vec— двоичное представление того же эмбеддинга через sqlite-vec.
chunks_vec равна 1536.
Для одного фрагмента:
- двоичный вектор
float32:1536 × 4 = 6144байта; - JSON того же вектора часто занимает около 30 КБ или больше;
- текст хранится как минимум в
chunksи FTS5; - дополнительно присутствуют индексы и служебные страницы SQLite.
chunks_vec.
Что прежний жизненный цикл уже делал правильно
Изменение файла
Полный проход вычислял SHA-256. Если хеш изменялся,_index_file() получал новое содержимое, а перед вставкой вызывал _clear_file_data() для старого пути.
При нормальном успешном проходе старые основные фрагменты файла заменялись.
Удаление вручную
_sync_once() сравнивал files.path с _discover_files() и вызывал _remove_file() для отсутствующих путей.
Ручное изменение файловой системы поэтому согласовывалось не мгновенно, а при следующем успешном проходе. По умолчанию интервал был равен 60 секундам.
Удаление через RPC рабочей области
ПрежнийVelesControlRuntime.delete_workspace_path_file() сначала переименовывал файл в карантин, вызывал физическую очистку delete_file_index(), затем удалял карантин. При ошибке очистки исходный файл восстанавливался. Текущий порционный контракт описан в актуальном разделе выше и в docs/developers/backend_ru.mdx.
Скрытие разговора
Стандартноеtools.sessionMemory.deleteBehavior = "hide" не удаляет файл памяти. Такой разговор по замыслу остаётся на диске и в векторном индексе. Это живые данные, а не мусор.
Старые файлы памяти
memory/**/*.md индексируются без ограничения по возрасту. Временной спад влияет только на оценку результатов и не удаляет файлы или фрагменты.
Найденные риски в прежнем коде
Ниже перечислены реальные дефекты реализации. Большинство из них не проявились как массовый мусор в исследованной базе, но могут приводить к будущему росту или рассинхронизации.1. Перезапись при повторной ошибке эмбеддингов
Файл сembed_ok=0 повторно обрабатывался каждый проход. При новой ошибке прежний код снова удалял и вставлял те же chunks и FTS-строки.
Это не меняет полезные данные, но создаёт новые записи WAL и сегменты/метки удаления FTS5 каждые 60 секунд. Для большого файла такой цикл может быстро раздувать WAL.
2. Гонка удаления с незавершённым индексированием
Последовательность возможной ошибки:- синхронизация прочитала файл;
- начался сетевой запрос эмбеддингов;
- в это время RPC удалил файл и очистил индекс;
- запрос эмбеддингов завершился;
- старая задача снова вставила уже удалённый файл.
3. Удаление векторов зависит от _dims
_clear_file_data() и delete_file_index() удаляют строки chunks_vec только когда _dims уже восстановлен. До первого успешного восстановления размерности удаление может пропустить двоичные векторы.
Для удаления по идентификатору размерность не нужна. Достаточно существования и доступности виртуальной таблицы.
4. Исторические разделители путей
Старая реализация на Windows сохранялаstr(relative_to(...)), то есть пути с \. Сменившая её реализация стала создавать пути через .as_posix().
Очистка должна учитывать оба варианта независимо от ОС, на которой открыта база. Не требуется массово переписывать пути или идентификаторы.
5. Скрытые ошибки FTS/vector
Часть исключений при удалении и вставке вспомогательных строк подавляется. После этого запись файла может выглядеть успешной, хотя одна из вспомогательных таблиц не обновилась. Прежний FTS-поиск читалchunks_fts напрямую, поэтому потерянная FTS-строка теоретически могла оставаться видимой. Векторный поиск соединял chunks_vec с chunks, поэтому вектор без основного фрагмента занимал место, но не возвращался поиском.
6. Дубликаты FTS внутри одного прохода
chunks.id уникален, но в FTS используется обычный INSERT. Если алгоритм разбиения создаст одинаковый chunk_id несколько раз, основная таблица заменит строку, а FTS сохранит несколько записей.
7. Опасная смена размерности
Если новая размерность отличалась от объявленияchunks_vec, прежний _ensure_vector_table() выполнял DROP TABLE chunks_vec.
Это противоречит требованию сохранения клиентского индекса. До появления отдельного безопасного перестроения несовпадение размерности должно оставлять текущую таблицу нетронутой и прекращать новую запись.
8. Нет отпечатка алгоритма разбиения
Прежнее условие пропуска учитывало хеш файла, модель иembed_ok, но не chunkTokens, chunkOverlap и версию алгоритма. Изменение этих параметров не перестраивало неизменившиеся файлы.
Это реальный недостаток, но его исправление потребует метаданных/миграции и не относится к причине размера исследованной базы. В первом минимальном исправлении его решено не включать.
9. Физический размер не уменьшается после логического удаления
SQLite повторно использует освобождённые страницы, но обычно не возвращает их файловой системе без уплотнения. WAL также может сохранять достигнутый размер для повторного использования. В исследованной базе это не главный источник размера основной БД, потому что список свободных страниц почти пуст.Исторический минимальный план (заменён решением от 25 августа)
Цель первого изменения — устранить будущую лишнюю запись и опасные гонки без изменения форматаvector.db.
Восстановление размерности
- Сначала читать
float[N]из SQL-объявленияchunks_vecвsqlite_master. - Оставить чтение JSON запасным путём.
- Не прекращать запись JSON-эмбеддингов.
- При несовпадении размерности не удалять
chunks_vec; отменять индексирование файла до любых изменений БД и сообщать о необходимости отдельного перестроения.
Повторные ошибки эмбеддингов
- Первая ошибка для нового или изменённого файла по-прежнему создаёт актуальный текстовый индекс с
embed_ok=0. - Если хеш и модель уже совпадают и
embed_ok=0, следующая неудачная попытка не должна выполнятьDELETE,INSERTили менять временные метки. - Успешная попытка позднее заполняет векторы и устанавливает
embed_ok=1.
Защита от гонки
- Читать точный снимок содержимого и вычислять его хеш.
- Получать эмбеддинги вне транзакции.
- Непосредственно перед первой операцией SQLite повторно проверить существование и хеш файла.
- Если файл исчез или изменился, отбросить подготовленный результат.
- После проверки выполнять все синхронные изменения без промежуточных
await.
Транзакции и удаление
- Замену одного файла выполнять в явной транзакции под существующей блокировкой.
- При любой ошибке выполнять откат.
- Проверять существование
chunks_vecнезависимо от_dims. - Всегда рассматривать варианты пути с
/и\. - При точном удалении очищать все FTS-строки этого пути, включая исторические дубликаты.
- Не скрывать ошибку обязательной очистки, чтобы карантинный механизм смог вернуть файл.
- В пределах одной индексации пропускать уже встреченный
chunk_id.
Ограничение WAL
При открытии соединения:- проверить размер
vector.db-wal; - если он больше 64 МБ, выполнить лучший возможный
PRAGMA wal_checkpoint(TRUNCATE); - результат
BUSYсчитать безопасной отсрочкой; - не считать невозможность контрольной точки ошибкой индексации;
- повторить попытку при чистом завершении.
Что не меняется
- схема и названия таблиц;
- формат существующих строк;
- JSON в
chunks.embedding; - периодический полный проход;
- поведение ручных изменений файловой системы;
- RPC шлюза;
- область индексируемых файлов;
- правила хранения старых файлов памяти.
Ожидаемый результат первого исправления
Что уменьшится
При отсутствии долгоживущих читателей WAL сможет сократиться с примерно 310 МБ до диапазона 0–64 МБ. Ожидаемая немедленная экономия общего места: примерно 246–310 МБ.Что не уменьшится
Основнойvector.db размером около 1,24 ГБ существенно не уменьшится, потому что:
- почти все его страницы заняты живыми данными;
- JSON-эмбеддинги сохраняются;
- автоматический
VACUUMне выполняется.
Как изменится будущий рост
- повторные ошибки эмбеддингов перестанут переписывать неизменившийся текстовый индекс;
- FTS перестанет получать одинаковый идентификатор несколько раз за один проход;
- WAL будет иметь ограничение и возможность усечения;
- обычная индексация новых данных продолжит использовать прежний объём на фрагмент.
Осознанно отклонённые изменения
Периодическая сборка мусора
Не добавляется, потому что реальная база не показала отсутствующие исходники или основные фрагменты безfiles. Полный обход больших виртуальных таблиц каждый час добавил бы нагрузку и код без доказанной пользы.
PRAGMA user_version и общая система миграций
Не добавляются в первом исправлении, потому что формат базы не меняется.
Автоматический VACUUM и auto_vacuum
Не добавляются. Для существующей базы включение инкрементального уплотнения всё равно потребовало бы полного VACUUM. При текущем freelist_count результат не оправдывает переписывание 1,24 ГБ и необходимость большого временного свободного места.
Ограничение файла в 2 МБ
Это исторически отклонённый вариант до решения от 25 августа. Значение 2 МиБ не добавлялось: оно молча удалило бы из поиска уже поддерживаемые большие документы и не решило бы дублирование векторов. Актуальная реализация использует настраиваемый предел 8 МиБ по умолчанию и отдельные пределы производной работы.Немедленная интеграция файлового наблюдателя
Не добавляется. Полная синхронизация уже согласовала реальную базу, а событийная очередь потребовала бы фильтрации всплесков, WAL/SHM и файлов памяти. Точное RPC-удаление остаётся немедленным, ручные изменения — согласованными в пределах интервала.Широкий административный интерфейс
Набор командstats, repair, rebuild, vacuum, checkpoint и новых RPC не добавляется. Он увеличил бы поверхность поддержки без необходимости для согласованной базы. Позднее добавленная узкая команда veles vector-memory compact для остановленного Велеса не вводит общего административного интерфейса или сетевого метода.
Автоматическое удаление JSON-эмбеддингов
Не выполняется при запуске или фоновой синхронизации. Только явная команда с--yes при остановленном Велесе переносит достоверные старые JSON-векторы в двоичный вид в отдельной теневой базе, а байт-в-байт копия прежней базы остаётся для отката.
Исторический проект уменьшения основного файла
Ниже сохранён первоначальный выбор между постепенным переходом и полной теневой миграцией. Текущее решение — узкая явная команда с теневой базой и обязательной резервной копией, описанная в актуальном разделе выше.Возможные стратегии
Постепенное уменьшение новых данных
Это исторический вариант, который не был принят в решении от 25 августа. Предлагалось хранить полный JSON только у одного представительного фрагмента каждого файла, а остальные векторы оставлять только вchunks_vec.
Плюсы:
- небольшое изменение кода;
- старый
_restore_dims()продолжает находить образец; - существующая база не переписывается автоматически.
- основной файл сразу не уменьшается;
- контракт «один JSON на файл» надо поддерживать при удалении и переиндексации;
- старые версии снова записывают полный JSON.
Полная теневая миграция
Последовательность:- Остановить синхронизацию и новые записи.
- Создать согласованную резервную копию средствами SQLite, а не обычным копированием активного WAL-файла.
- Работать только с теневой копией.
- Для каждого JSON-эмбеддинга проверить существование соответствующего двоичного вектора.
- Восстановить отсутствующий
chunks_vecиз валидного JSON, если размерность совпадает. - Обнулить JSON только после подтверждения двоичной копии.
- Сохранить повреждённые или единственные копии без изменений.
- Выполнить уплотнение в новый файл через
VACUUM INTO. - Загрузить sqlite-vec и проверить целостность, количества идентификаторов, размерность, FTS и результаты известных запросов.
- Закрыть соединения, выполнить контрольную точку и атомарно переключить файлы.
- Хранить исходную базу без автоматического удаления.
vector.db активным.
Контракт отката после полной миграции
Нужно заранее выбрать один из вариантов:- старый выпуск обязан открывать новую активную базу без дополнительных действий — тогда JSON лучше сохранять;
- откат выполняется восстановлением исходной резервной базы — тогда проверенные JSON-копии можно удалить.
Первоначальный список проверок для минимального исправления
Дополнить существующие тесты следующими сценариями:- Первый сбой эмбеддингов создаёт текстовый индекс с
embed_ok=0. - Второй одинаковый сбой вызывает API, но не меняет строки, временные метки и
Connection.total_changes. - Следующая успешная попытка добавляет векторы и устанавливает
embed_ok=1. - Файл удаляется во время приостановленного запроса эмбеддингов и не возвращается в индекс.
- Файл изменяется во время запроса, и устаревший снимок не фиксируется.
- Ошибка на каждом этапе замены файла откатывает всю транзакцию.
- При
_dims=Noneсуществующие строкиchunks_vecвсё равно удаляются. - Пути с
/и\очищаются одинаково. - Повторный
chunk_idсоздаёт только одну FTS-строку. - Размерность восстанавливается из
sqlite_master, даже когда JSON отсутствует. - Несовпадение размерности не удаляет
chunks_vec. - Контрольная точка WAL не меняет количество
files,chunks, FTS и векторов. - Активный читатель приводит к безопасному
BUSY, а не к ошибке синхронизации.
Проверка на копии реальной базы после реализации
Не работать непосредственно с единственным клиентским экземпляром. На согласованной копии зафиксировать до и после:- размеры
vector.db, WAL и SHM; page_count,page_size,freelist_count;- количества
filesиchunks; - количества по проектам;
- список обнаруживаемых, но не проиндексированных файлов;
PRAGMA integrity_check;- результаты нескольких известных FTS- и векторных запросов.
- логические количества и результаты поиска не меняются;
- массовой переиндексации 735 существующих файлов нет;
- основной файл остаётся примерно прежнего размера;
- WAL сокращается при отсутствии блокирующих читателей.
Полезные указатели в коде
veles/agent/vector_memory.py_restore_dims()— восстановление размерности и чтение сохранённого режима хранилища без полного обхода фрагментов;_ensure_vector_table()— безопасная проверка размерности без удаления действующей таблицы;_discover_files()— область индексирования;_sync_once_locked()— ограниченный проход, скрытие отсутствующих путей и проверка снимков;_spool_chunks()/_embed_staged_chunks()— предварительный подсчёт и порционная подготовка эмбеддингов;_replace_file_from_stage()— атомарная публикация готового поколения;_clear_file_data()/_drain_cleanup_queue()/delete_file_index()— точная и порционная очистка;sync_loop()— периодический проход.
veles/api/runtime.py_cleanup_vector_index_for_deleted_file();delete_workspace_path_file()— карантин и восстановление при ошибке.
veles/agent/loop.py- создание, запуск, горячая смена конфигурации и закрытие менеджера.
veles/config/schema.pyVectorMemoryConfig, включая интервал и параметры разбиения.
tests/test_vector_memory_indexing.py,tests/test_vector_search_tool.pyиtests/test_workspace_file_delete.py- проверки ограничений, сбоев, старых схем, пустых файлов, разделителей путей и точного удаления.
Решения, которые нельзя забыть
- Реальная база сейчас согласована с файловой системой; не называть размер доказательством оставшихся удалённых файлов.
VACUUMбез удаления JSON почти ничего не даст приfreelist_count = 88.- WAL и основной файл — разные проблемы.
- Ручное уплотнение выполняется только через
veles vector-memory compact --yesпри остановленном Велесе; блокировку владельца нельзя обходить. - Резервная копия прежней базы всегда сохраняется; не обещать уменьшение общего объёма тома до её ручного перемещения или удаления после проверки.
- Не удалять
vector.dbради смены модели или исправления индекса. - Не удалять
chunks_vecпри несовпадении размерности. - Не очищать JSON-эмбеддинги автоматически без отдельного согласия на миграцию и контракт отката.
- Безопаснее временно хранить лишние данные, чем повредить или молча перестроить клиентскую базу.
