Поиск ближайших векторов KNN

Manticore Search поддерживает добавление эмбеддингов, сгенерированных моделями машинного обучения, к каждому документу, а затем поиск ближайших соседей по ним. Это позволяет строить такие функции, как поиск по сходству, рекомендации, семантический поиск и ранжирование релевантности на основе алгоритмов NLP, а также поиск по изображениям, видео и звуку.

Чтобы объединить векторный поиск KNN с полнотекстовым поиском для лучшей релевантности, см. Гибридный поиск.

Что такое эмбеддинг?

Эмбеддинг - это способ представления данных, таких как текст, изображения или звук, в виде векторов в многомерном пространстве. Эти векторы строятся так, чтобы расстояние между ними отражало сходство представляемых ими данных. Обычно для этого используют такие алгоритмы, как word embeddings (например, Word2Vec, BERT) для текста или нейросети для изображений. Многомерная природа векторного пространства, где у каждого вектора много компонент, позволяет описывать сложные и тонкие связи между объектами. Их сходство оценивают по расстоянию между этими векторами, которое часто измеряют с помощью евклидова расстояния или косинусного сходства.

Manticore Search поддерживает поиск k ближайших соседей (KNN) с использованием библиотеки HNSW. Эта функциональность входит в состав Manticore Columnar Library.

Настройка таблицы для поиска KNN

Чтобы выполнять поиск KNN, сначала нужно настроить таблицу. Вещественные векторы и поиск KNN поддерживаются только в таблицах real-time (не в plain-таблицах). В таблице должен быть как минимум один атрибут float_vector, который служит вектором данных. Нужно указать следующие свойства:

  • knn_type: Обязательный параметр; сейчас поддерживается только hnsw.

  • knn_dims: Обязательный параметр, задающий размерность индексируемых векторов.

  • hnsw_similarity: Обязательный параметр, задающий функцию расстояния, используемую индексом HNSW. Допустимые значения:

    • L2 - Квадрат евклидова расстояния L2
    • IP - Скалярное произведение
    • COSINE - Косинусное сходство

    Примечание: При использовании сходства COSINE векторы автоматически нормализуются при вставке. Это означает, что сохраненные значения векторов могут отличаться от исходных входных значений, поскольку они будут преобразованы в единичные векторы (векторы с математической длиной/модулем 1.0), чтобы можно было эффективно вычислять косинусное сходство. Такая нормализация сохраняет направление вектора, одновременно стандартизируя его длину.

  • hnsw_m: Необязательный параметр, задающий максимальное число исходящих связей в графе. Значение по умолчанию - 16.

  • hnsw_ef_construction: Необязательный параметр, задающий компромисс между временем построения и точностью. Значение по умолчанию - 200.

ПРИМЕЧАНИЕ: Построение графа HNSW при сохранении чанков RT, при слиянии чанков OPTIMIZE TABLE / auto-optimize и при перестройке KNN через ALTER TABLE ... ADD/DROP/REBUILD выполняется параллельно по умолчанию на многопроцессорных хостах; число worker-потоков управляется настройкой searchd knn_parallel_build (установите 1, чтобы принудительно использовать последовательный путь). Это влияет только на скорость построения. Поскольку параллельное построение HNSW может вставлять векторы в другом порядке, итоговый граф может не быть побитово идентичен последовательной сборке.

‹›
  • SQL
  • JSON
  • Config
📋
create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' );
‹›
Response
Query OK, 0 rows affected (0.01 sec)

Вставка векторных данных

Автоэмбеддинги (рекомендуется)

Самый простой способ работать с векторными данными - использовать автоэмбеддинги. С этой функцией вы создаете таблицу с параметрами MODEL_NAME и FROM, а затем просто вставляете текстовые данные - Manticore автоматически генерирует эмбеддинги за вас.

Создание таблицы с автоэмбеддингами

При создании таблицы для автоэмбеддингов укажите:

  • MODEL_NAME: Модель эмбеддингов, которую нужно использовать
  • FROM: Какие поля использовать для генерации эмбеддингов (пустое значение означает все текстовые/строковые поля)
  • API_KEY: Обязателен для удаленных моделей (OpenAI, Voyage, Jina). API-ключ проверяется при создании таблицы путем выполнения реального запроса к API.
  • API_URL: Необязателен. Пользовательский URL конечной точки API. Если не указан, используется конечная точка провайдера по умолчанию (например, https://api.openai.com/v1/embeddings для OpenAI).
  • API_TIMEOUT: Необязателен. Таймаут HTTP-запросов к API в секундах. Значение по умолчанию - 10 секунд. Установите '0', чтобы использовать таймаут по умолчанию. Применяется и к проверочным запросам при создании таблицы, и к генерации эмбеддингов во время операций INSERT.
  • MAX_INPUT_TOKENS: необязательно. Ограничивает число токенов, берущихся из каждого входного текста перед построением эмбеддинга; более длинные тексты обрезаются. '0' (по умолчанию) означает собственный лимит контекста модели. Модели с длинным контекстом, такие как Qwen/Qwen3-Embedding-0.6B, принимают до 32 768 токенов, а время построения эмбеддинга на CPU растет сверхлинейно с длиной входа (документ на 5 КБ может обрабатываться несколько минут), поэтому задайте ограничение (например, '512'), если текстовые поля могут содержать длинный или неограниченный по размеру контент. Применяется к локальным моделям; позже настройку можно изменить с помощью ALTER TABLE ... MODIFY COLUMN ... MAX_INPUT_TOKENS='...' без повторного построения эмбеддингов для существующих строк.

Для удаленных моделей MODEL_NAME можно записывать в двух формах:

  • Устаревшая форма с префиксом провайдера: openai/text-embedding-ada-002, voyage/voyage-3.5-lite, jina/jina-embeddings-v4
  • Явная форма provider-signal для пользовательских endpoint'ов: openai:text-embedding-ada-002, openai:openai/text-embedding-ada-002, voyage:custom-model, jina:custom-model

Когда вы используете форму provider:model вместе с API_URL, часть перед : только выбирает формат запроса. Часть после : отправляется на удаленную конечную точку без изменений. Это полезно для шлюзов, совместимых с OpenAI, таких как OpenRouter или LiteLLM.

Поддерживаемые модели эмбеддингов:

Тип модели Пример Требуется API-ключ Примечания
ONNX (рекомендуется) Xenova/all-MiniLM-L6-v2 Нет Локальные модели из любого репозитория Hugging Face, который содержит файл .onnx. Работают на быстром бэкенде Manticore ONNX Runtime. Список: модели feature-extraction ONNX.
Sentence Transformers sentence-transformers/all-MiniLM-L6-v2 Нет Локальные модели на базе BERT, загружаются автоматически. Те же веса, что и в строке ONNX выше, но на CPU эмбеддинги строятся в 10–20 раз медленнее — используйте ONNX, если он есть в репозитории. См. Выбор локальной модели эмбеддингов.
Qwen Qwen/Qwen3-Embedding-0.6B Нет Локальные модели семейства Qwen
Llama TinyLlama/TinyLlama-1.1B-Chat-v1.0 Нет Локальные модели семейства Llama
Mistral Locutusque/TinyMistral-248M-v2 Нет Локальные модели семейства Mistral
Gemma h2oai/embeddinggemma-300m Нет Локальные модели семейства Gemma
OpenAI openai/text-embedding-ada-002 or openai:text-embedding-ada-002 Да API_KEY='***'
Voyage voyage/voyage-3.5-lite or voyage:voyage-3.5-lite Да API_KEY='***'
Jina jina/jina-embeddings-v4 or jina:jina-embeddings-v4 Да API_KEY='***'

Требования к формату локальной модели:

  • Поддерживаемые форматы весов: safetensors (один файл или шардированный через model.safetensors.index.json), квантованный GGUF и ONNX
  • Поддерживаемые семейства: BERT/Sentence Transformers, Qwen, Llama, Mistral, Gemma, T5
  • Проверенные модели: TinyLlama/TinyLlama-1.1B-Chat-v1.0, Locutusque/TinyMistral-248M-v2, Qwen/Qwen3-Embedding-0.6B, h2oai/embeddinggemma-300m
  • Другие модели из этих семейств тоже могут работать, но это не гарантируется
  • Для закрытых репозиториев Hugging Face передайте свой токен доступа Hugging Face как API_KEY
Выбор локальной модели эмбеддингов

MODEL_NAME определяет не только качество, но и скорость построения эмбеддингов для каждого документа на CPU — разница может быть на порядок (см. эмбеддинги в 14 раз быстрее с ONNX). Репозиторий с файлом .onnx работает через среду выполнения ONNX Runtime; репозиторий safetensors с той же самой моделью работает через гораздо более медленный путь Candle. Приблизительные показатели, замеренные на небольшом инстансе с 4 vCPU и короткими документами (~300 символов):

Модель Формат Размерность Загрузка Скорость на CPU
Xenova/all-MiniLM-L6-v2 ONNX 384 ~90 MB ~30–40 док./с
sentence-transformers/all-MiniLM-L6-v2 safetensors 384 ~90 MB ~2–3 док./с
BAAI/bge-base-en-v1.5 safetensors 768 ~420 MB ~2–3 док./с
  • Начните с Xenova/all-MiniLM-L6-v2 — модели, использованной в примерах выше. Она одинаково хорошо подходит для интерактивных запросов и массовой загрузки, а ее качества достаточно для большинства поисковых приложений.
  • Более высокая позиция в рейтинге редко оправдывает стоимость индексации на CPU. Переход с небольшой ONNX-модели на более крупную модель safetensors обычно делает построение эмбеддинга для каждого документа в 10–20 раз дороже, а итоговая релевантность меняется лишь незначительно. Прежде чем переходить на «лучшую» модель, сравните ее с моделью по умолчанию на нескольких десятках собственных запросов и текстов.
  • Оцените время перед большой загрузкой. Стоимость построения эмбеддингов растет линейно: миллисекунды на документ умножаются на число строк. При ~300 мс/док. 15 000 документов займут примерно 75 минут; при ~30 мс/док. — меньше 10 минут. Вставьте несколько сотен реальных строк во временную таблицу и экстраполируйте результат, прежде чем привязывать рабочую таблицу к модели.
  • Медленная загрузка обычно указывает на модель, а не на настройки. Если вставки еле движутся или массовая загрузка показывает тайм-ауты и повторные попытки при построении эмбеддингов, обычная причина — модель не в формате ONNX или слишком большая модель. Переход на ONNX-репозиторий решает проблему; увеличение embeddings_threads не делает медленную модель быстрой и может лишить параллельные задачи рабочих потоков.

Дополнительную информацию о настройке атрибута float_vector можно найти здесь.

‹›
  • SQL
  • Config
📋

Использование локальной ONNX-модели — рекомендуется (ключ API не нужен)

CREATE TABLE products (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='Xenova/all-MiniLM-L6-v2' FROM='title'
);

Использование sentence-transformers (ключ API не нужен; работает через путь Candle — если доступно, используйте ONNX выше)

CREATE TABLE products_st (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='sentence-transformers/all-MiniLM-L6-v2' FROM='title'
);

Использование локальных эмбеддингов Qwen (API-ключ не нужен)

CREATE TABLE products_qwen (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='Qwen/Qwen3-Embedding-0.6B' FROM='title' CACHE_PATH='/opt/homebrew/var/manticore/.cache/manticore'
);

Использование OpenAI (требуется параметр API_KEY)

CREATE TABLE products_openai (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='openai/text-embedding-ada-002' FROM='title,description' API_KEY='...'
);

Использование OpenAI с пользовательским API URL и таймаутом (необязательно)

CREATE TABLE products_openai_custom (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='openai:text-embedding-ada-002' FROM='title,description'
    API_KEY='***' API_URL='https://custom-api.example.com/v1/embeddings' API_TIMEOUT='30'
);

Использование шлюза, совместимого с OpenAI, который ожидает модельный идентификатор с указанием провайдера

CREATE TABLE products_openrouter (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='openai:openai/text-embedding-ada-002' FROM='title,description'
    API_KEY='***' API_URL='https://openrouter.ai/api/v1/embeddings' API_TIMEOUT='30'
);

Использование всех текстовых полей для эмбеддингов (FROM пуст)

CREATE TABLE products_all (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='Xenova/all-MiniLM-L6-v2' FROM=''
);
Вставка данных с автоэмбеддингами

При использовании автоэмбеддингов вы можете:

  • Не указывать поле вектора и позволить Manticore сгенерировать эмбеддинги из полей, перечисленных в FROM
  • Явно передать свой вектор для строки
  • Передать (), чтобы пропустить генерацию и сохранить нулевой вектор

Если позже вы выполните ALTER TABLE ... REBUILD EMBEDDINGS, строки, которые сейчас содержат нулевые векторы из (), тоже будут пересозданы.

‹›
  • SQL
  • JSON
📋

Вставка только текстовых данных - эмбеддинги генерируются автоматически

INSERT INTO products (title) VALUES
('machine learning artificial intelligence'),
('banana fruit sweet yellow');

Вставка пользовательского вектора

INSERT INTO products (title, embedding_vector) VALUES
('machine learning artificial intelligence', (0.653448,0.192478,0.017971,0.339821));

Вставка нескольких полей - оба используются для эмбеддинга, если FROM='title,description'

INSERT INTO products_openai (title, description) VALUES
('smartphone', 'latest mobile device with advanced features'),
('laptop', 'portable computer for work and gaming');

Вставка пустого вектора (без автоматической генерации; сохраняется нулевой вектор)

INSERT INTO products (title, embedding_vector) VALUES
('no embedding item', ());
Поиск с автоэмбеддингами

Поиск работает так же - передайте текст запроса, и Manticore сгенерирует эмбеддинги и найдет похожие документы:

‹›
  • SQL
  • JSON
📋
SELECT id, knn_dist() FROM products WHERE knn(embedding_vector, 'machine learning');
‹›
Response
+------+------------+
| id   | knn_dist() |
+------+------------+
|    1 | 0.12345678 |
|    2 | 0.87654321 |
+------+------------+
2 rows in set (0.00 sec)

Ручная вставка векторов

В качестве альтернативы вы можете вручную вставлять предварительно вычисленные векторные данные, убедившись, что они соответствуют размерности, указанной при создании таблицы. Вы также можете вставить пустой вектор; это означает, что документ будет исключен из результатов векторного поиска.

Важно: При использовании hnsw_similarity='cosine' векторы автоматически нормализуются при вставке до единичных векторов (векторов с математической длиной/модулем 1.0). Такая нормализация сохраняет направление вектора, одновременно стандартизируя его длину, что требуется для эффективных вычислений косинусного сходства. Это означает, что сохраненные значения будут отличаться от исходных входных значений.

‹›
  • SQL
  • JSON
📋
insert into test values ( 1, 'yellow bag', (0.653448,0.192478,0.017971,0.339821) ), ( 2, 'white bag', (-0.148894,0.748278,0.091892,-0.095406) );
‹›
Response
Query OK, 2 rows affected (0.00 sec)

Векторный поиск KNN

Теперь вы можете выполнять поиск KNN с помощью предложения knn как в формате SQL, так и в формате JSON. Оба интерфейса поддерживают одни и те же основные параметры, обеспечивая единообразный опыт независимо от выбранного формата:

  • SQL: select ... from <table name> where knn ( <field>, <query vector> [,<options>] )
  • JSON:
    POST /search
    {
        "table": "<table name>",
        "knn":
        {
            "field": "<field>",
            "query": "<text or vector>",
            "ef": <ef>,
            "rescore": <rescore>,
            "oversampling": <oversampling>
        }
    }

Параметры:

  • field: Имя атрибута float_vector, содержащего векторные данные.
  • k: Устаревший параметр. Вместо него используйте limit в запросе. Он использовался для задания количества документов, которое должен вернуть один индекс HNSW. Однако фактическое число документов в итоговых результатах может отличаться. Например, если система работает с таблицами real-time, разделенными на disk chunks, каждый chunk может вернуть k документов, что приведет к итоговому числу больше указанного k (так как суммарное количество будет num_chunks * k). С другой стороны, итоговое число документов может оказаться меньше k, если после запроса k документов часть из них будет отфильтрована по определенным атрибутам. Важно отметить, что параметр k не применяется к ramchunks. В контексте ramchunks процесс получения работает иначе, и поэтому влияние параметра k на число возвращаемых документов неприменимо.
  • query: (Рекомендуемый параметр) Поисковый запрос, который может быть:
    • Текстовой строкой: автоматически преобразуется в эмбеддинги, если для поля настроены автоэмбеддинги. Вернет ошибку, если у поля нет автоэмбеддингов.
    • Массивом векторов: работает так же, как query_vector.
  • query_vector: (Устаревший параметр) Поисковый вектор в виде массива чисел. По-прежнему поддерживается для обратной совместимости. Примечание: Используйте либо query, либо query_vector, но не оба параметра в одном запросе.
  • ef: необязательный размер динамического списка, используемого во время поиска. Более высокое значение ef дает более точный, но более медленный поиск. Значение по умолчанию - 10.
  • rescore: Включает повторное оценивание KNN (по умолчанию включено). Установите 0 в SQL или false в JSON, чтобы отключить повторное оценивание. После завершения поиска KNN с использованием квантизованных векторов (с возможным oversampling) расстояния пересчитываются по исходным (full-precision) векторам, и результаты пересортировываются для повышения точности ранжирования.
  • oversampling: Задает коэффициент (значение float), на который умножается k при выполнении поиска KNN, из-за чего с использованием квантизованных векторов извлекается больше кандидатов, чем требуется. По умолчанию применяется oversampling=3.0. Эти кандидаты можно затем переоценить, если повторное оценивание включено. Oversampling также работает и с неквантизованными векторами. Поскольку он увеличивает k, а это влияет на работу индекса HNSW, он может вызвать небольшое изменение точности результатов.
  • early_termination: Включает или отключает адаптивное раннее завершение при обходе графа HNSW. По умолчанию включено. Установите 0 в SQL или false в JSON, чтобы отключить. Подробности см. в разделе Раннее завершение.

Когда задан текстовый запрос (то есть Manticore сначала встраивает строку перед поиском), число потоков, используемых библиотекой embeddings, можно переопределить для каждого запроса в SQL с помощью OPTION embeddings_threads = N. Это значение ограничивает вызов embeddings только для этого запроса, переопределяя глобальную настройку embeddings_threads; 0 означает отсутствие ограничения. Эта опция не влияет на запрос, если он передан в виде массива векторов.

Документы всегда сортируются по расстоянию до вектора поиска. Любые дополнительные критерии сортировки, которые вы укажете, будут применяться после этого основного условия сортировки. Чтобы получить расстояние, есть встроенная функция knn_dist().

‹›
  • SQL
  • JSON
📋
select id, knn_dist() from test where knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { ef=2000, oversampling=3.0, rescore=1 } );
‹›
Response
+------+------------+
| id   | knn_dist() |
+------+------------+
|    1 | 0.28146550 |
|    2 | 0.81527930 |
+------+------------+
2 rows in set (0.00 sec)

Несколько векторов на документ

Атрибут float_vector_array хранит по несколько векторов на документ вместо одного: фрагменты статьи, фотографии товара, ключевые кадры видео. Все векторы из всех документов индексируются вместе, а поиск рассматривает векторы документа как альтернативные представления одного и того же документа:

  • Документ считается совпадением, если любой из его векторов близок к вектору запроса.
  • Каждый совпавший документ возвращается ровно один раз, а knn_dist() сообщает расстояние до его ближайшего вектора. Остальные векторы документа не создают дополнительных строк.
  • k считает документы, а не векторы. knn(v, 10, ...) запрашивает 10 ближайших документов, сколько бы векторов они ни содержали вместе.
  • Документ без векторов ([] или если атрибут опущен) никогда не возвращается, поскольку он не близок ни к чему.

Вектор запроса по-прежнему представляет собой один вектор из KNN_DIMS элементов, точно как для float_vector. Каждый вектор, хранящийся в массиве, индексируемом по KNN, тоже должен иметь KNN_DIMS элементов.

При HNSW_SIMILARITY='cosine' каждый сохраненный вектор нормализуется отдельно, поэтому векторы документа сравниваются с запросом по одному, а не как один длинный склеенный вектор.

Все остальное на этой странице применяется без изменений: фильтрация, предфильтр/постфильтр, квантование, раннее завершение и повторное ранжирование работают так же. Автоэмбеддинги могут заполнить массив за вас, по одному вектору на фрагмент - см. ниже Стратегии разбиения на фрагменты.

‹›
  • SQL
  • JSON
📋
CREATE TABLE articles(title text, chunk_vectors float_vector_array knn_type='hnsw' knn_dims='4' hnsw_similarity='l2');
INSERT INTO articles VALUES
  (1, 'first',  [[1,0,0,0],[0,1,0,0]]),
  (2, 'second', [[0,0,1,0]]);
-- doc 1 owns a vector identical to the query and another far from it,
-- so it is returned once, at distance 0
SELECT id, knn_dist() FROM articles WHERE knn(chunk_vectors, 5, (1,0,0,0));
‹›

Стратегии разбиения на фрагменты

По умолчанию модель эмбеддингов читает только столько документа, сколько помещается в ее окно ввода (обычно несколько сотен токенов), а остальное молча отбрасывается. Для заголовка или короткого описания этого достаточно. Для длинной статьи - нет: все, что написано после точки отсечения, уже не удастся извлечь, и об этом не сообщается никакой ошибкой.

Стратегия разбиения определяет, как документ превращается в векторы. Задайте ее с помощью CHUNK_STRATEGY в столбце, привязанном к модели:

Стратегия Векторов на документ Что делает
truncate 1 Кодирует столько, сколько помещается в окно модели, а остальное отбрасывает. Значение по умолчанию и историческое поведение.
mean 1 Делит весь документ на части, кодирует каждую и усредняет их в один вектор. Хвост не теряется, но документ на несколько тем сводится к их среднему.
fixed N Фиксированные окна по MAX_TOKENS токенов.
recursive N Делит по иерархии разделителей: абзац, затем строка, затем предложение, затем пробел; при этом каждая часть укладывается в MAX_TOKENS.
sentence N Границы предложений, упакованные до MAX_TOKENS.

truncate и mean создают по одному вектору на документ и работают со столбцом float_vectorfloat_vector_array они хранят массив из одного элемента). fixed, recursive и sentence создают несколько векторов, поэтому требуют столбца float_vector_array; использование их в обычном float_vector отклоняется.

Разница в том, что именно считается совпадением. Когда на документ приходится один вектор, поиск спрашивает: «похож ли этот документ целиком на запрос?», и один релевантный абзац размывается всем окружающим текстом. Когда на фрагмент приходится один вектор, вопрос другой: «содержит ли этот документ что-то похожее?» Каждый фрагмент соревнуется отдельно, а документ возвращается один раз с оценкой по своему ближайшему фрагменту (см. Несколько векторов на документ).

Параметры, все действительны только вместе с MODEL_NAME и KNN_TYPE='hnsw':

  • CHUNK_STRATEGY: один из пяти вариантов выше. По умолчанию truncate.
  • MAX_TOKENS: размер фрагмента в токенах. 0 по умолчанию означает собственный предел модели; большее значение будет снижено до него.
  • OVERLAP_TOKENS: сколько токенов общих у соседних фрагментов, чтобы идея, разрезанная границей, все равно полностью попадала хотя бы в один из них. Требует явного ненулевого MAX_TOKENS. Большое перекрытие уменьшается так, чтобы фрагменты по-прежнему продвигались по документу: fixed и recursive ограничивают его половиной MAX_TOKENS, а sentence подготавливает следующий фрагмент максимум из OVERLAP_TOKENS завершающих полных предложений и всегда сдвигается хотя бы на одно предложение.
  • MAX_CHUNKS: верхняя граница числа векторов на документ. 0 по умолчанию означает без ограничения.

Важные моменты:

  • MAX_CHUNKS отбрасывает текст. При переполнении остаток объединяется с последним сохраненным фрагментом, после чего тот превышает MAX_TOKENS и при кодировании обрезается до окна ввода модели. Видимого разрыва не остается, но хвост теряется.
  • Локальные и удаленные модели режут текст по-разному. Локальные модели разбивают его по реальным токенам модели. У удаленных API-моделей (OpenAI, Voyage, Jina) нет локального токенизатора, поэтому вместо него используется консервативная оценка по байтам, и тот же текст с теми же настройками даст другое число фрагментов, чем локальная модель.

ALTER TABLE ... ADD COLUMN с привязанным к модели float_vector_array, а также ALTER TABLE ... REBUILD EMBEDDINGS для него пока не поддерживаются; существующие строки нельзя заполнить задним числом, поэтому столбец останется пустым. Объявляйте такой столбец при создании таблицы. Для столбца float_vector оба варианта работают нормально, в том числе с mean.

‹›
  • SQL
  • JSON
📋
-- one vector per sentence group, filled automatically from the text
CREATE TABLE articles (
  title text,
  content text,
  chunks float_vector_array knn_type='hnsw' hnsw_similarity='cosine'
     model_name='Xenova/all-MiniLM-L6-v2' from='title,content'
     chunk_strategy='sentence' max_tokens='256' overlap_tokens='32'
);
INSERT INTO articles (id, title, content) VALUES (1, 'Rotating certificates', 'A long guide with many sections ...');
SELECT id, knn_dist() FROM articles WHERE knn(chunks, 5, 'how do I rotate a certificate');

Квантование векторов

Индексы HNSW должны быть полностью загружены в память, чтобы выполнять поиск KNN, что может приводить к значительному потреблению памяти. Чтобы снизить использование памяти, можно применить скалярное квантование - технику, которая сжимает многомерные векторы, представляя каждую компоненту (размерность) ограниченным числом дискретных значений. Manticore поддерживает 8-битное и 1-битное квантование, то есть каждая компонента вектора сжимается с 32-битного float до 8 бит или даже до 1 бита, уменьшая использование памяти в 4 раза или 32 раза соответственно. Эти сжатые представления также позволяют быстрее вычислять расстояния, поскольку больше компонент вектора можно обработать одной SIMD-инструкцией. Хотя скалярное квантование вносит некоторую погрешность аппроксимации, часто это оправданный компромисс между точностью поиска и эффективностью использования ресурсов. Для еще более высокой точности квантование можно сочетать с повторным оцениванием и oversampling: запрашивается больше кандидатов, чем нужно, а расстояния для этих кандидатов пересчитываются с использованием исходных 32-битных float-векторов.

Поддерживаемые типы квантования:

  • 8bit: Каждая компонента вектора квантуется до 8 бит.
  • 1bit: Каждая компонента вектора квантуется до 1 бита. Используется асимметричное квантование: векторы запроса квантуются до 4 бит, а сохраненные векторы - до 1 бита. Такой подход дает более высокую точность, чем более простые методы, но с некоторым компромиссом по производительности.
  • 1bitsimple: Каждая компонента вектора квантуется до 1 бита. Этот метод быстрее, чем 1bit, но обычно менее точен.
create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' quantization='1bit');
Query OK, 0 rows affected (0.01 sec)
POST /sql?mode=raw -d "create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' quantization='1bit')"
[
  {
    "total": 0,
    "error": "",
    "warning": ""
  }
]

Поиск похожих документов по id

ПРИМЕЧАНИЕ: Поиск похожих документов по id требует Manticore Buddy. Если он не работает, убедитесь, что Buddy установлен.

Поиск документов, похожих на конкретный документ по его уникальному ID, - распространенная задача. Например, когда пользователь просматривает определенный товар, Manticore Search может эффективно определить и показать список товаров, которые наиболее похожи на него в векторном пространстве. Вот как это сделать:

  • SQL: select ... from <table name> where knn ( <field>, <k>, <document id> )
  • JSON:
    POST /search
    {
        "table": "<table name>",
        "knn":
        {
            "field": "<field>",
            "doc_id": <document id>,
            "k": <k>
        }
    }

Параметры:

  • field: Имя атрибута float_vector, содержащего векторные данные.
  • k: Это число документов, которые нужно вернуть, и ключевой параметр для индексов Hierarchical Navigable Small World (HNSW). Он задает количество документов, которое должен вернуть один индекс HNSW. Однако фактическое число документов в итоговых результатах может отличаться. Например, если система работает с таблицами real-time, разделенными на disk chunks, каждый chunk может вернуть k документов, что приведет к итоговому числу больше указанного k (так как суммарное количество будет num_chunks * k). С другой стороны, итоговое число документов может оказаться меньше k, если после запроса k документов часть из них будет отфильтрована по определенным атрибутам. Важно отметить, что параметр k не применяется к ramchunks. В контексте ramchunks процесс получения работает иначе, и поэтому влияние параметра k на число возвращаемых документов неприменимо.
  • document id: ID документа для KNN-поиска похожих документов.
‹›
  • SQL
  • JSON
📋
select id, knn_dist() from test where knn ( image_vector, 5, 1 );
‹›
Response
+------+------------+
| id   | knn_dist() |
+------+------------+
|    2 | 0.81527930 |
+------+------------+
1 row in set (0.00 sec)

Фильтрация результатов векторного поиска KNN

Manticore также поддерживает дополнительную фильтрацию документов, возвращаемых поиском KNN, либо по полнотекстовому совпадению, либо по фильтрам атрибутов, либо по обоим сразу.

‹›
  • SQL
  • JSON
📋
select id, knn_dist() from test where knn ( image_vector, 5, (0.286569,-0.031816,0.066684,0.032926) ) and match('white') and id < 10;
‹›
Response
+------+------------+
| id   | knn_dist() |
+------+------------+
|    2 | 0.81527930 |
+------+------------+
1 row in set (0.00 sec)

Стратегии фильтрации: prefilter против postfilter

При объединении векторного поиска KNN с фильтрами атрибутов Manticore поддерживает две стратегии, которые отличаются тем, когда фильтр применяется относительно обхода графа HNSW.

  • Предварительная фильтрация (по умолчанию; prefilter=1 (SQL) или "prefilter": true (JSON, default)) передает фильтр непосредственно в обход HNSW. Каждый кандидат проверяется на соответствие фильтру до добавления в результирующую кучу - в итоговые k результатов попадают только подходящие документы. Это уменьшает число бесполезных вычислений расстояния и гарантирует, что будет возвращено ровно k подходящих документов (если существует k подходящих документов).

  • Постфильтрация (prefilter=0 (SQL) или "prefilter": false (JSON)) сначала выполняет поиск KNN по всему набору данных, а затем применяет фильтр к результатам. Это безопасно и предсказуемо: граф HNSW обходится без вмешательства, а фильтр влияет только на то, какие результаты возвращаются клиенту. Недостаток в том, что на кандидатов, которые в итоге будут отброшены, граф может потратить ресурсы. При жестком фильтре, который совпадает лишь с небольшой долей документов, возвращаемые k результатов могут оказаться значительно меньше запрошенного числа, потому что большинство кандидатов KNN не пройдет фильтр.

Внутри Manticore использует алгоритм на основе ACORN-1 для предварительной фильтрации. Наивная предварительная фильтрация просто пропускала бы несовпадающие узлы, что создает риск потерять "bridge"-узлы, соединяющие разъединенные части графа HNSW, из-за чего полнота резко падает по мере ужесточения фильтра. ACORN-1 избегает этого: когда узел не проходит фильтр, его соседи все равно добавляются в очередь обхода. Это позволяет обходу обходить отфильтрованные узлы и сохранять связность графа. Обход ACORN-1 автоматически включается, когда фильтр проходит менее 60% всех документов.

Автоматический переход на brute-force: Когда включена предварительная фильтрация, Manticore оценивает, что дешевле - выполнить полный перебор расстояний по отфильтрованному подмножеству или обходить граф HNSW. Оценка сравнивает ожидаемое число узлов, посещаемых HNSW, с числом документов, прошедших фильтр. Если отфильтрованное множество достаточно мало, чтобы его прямой перебор был быстрее, Manticore автоматически переключается на brute-force, полностью пропуская HNSW. Это обеспечивает корректность и хорошую производительность даже при экстремальной селективности.

‹›
  • SQL
  • JSON
📋
-- prefilter (default): filter applied during HNSW traversal (ACORN-1 used automatically)
SELECT id, knn_dist() FROM test
WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926) )
AND price < 100;
-- postfilter: KNN runs over full dataset, filter applied to results
SELECT id, knn_dist() FROM test
WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { prefilter=0 } )
AND price < 100;

Раннее завершение

По умолчанию Manticore использует адаптивный алгоритм раннего завершения при обходе графа HNSW. Вместо того чтобы всегда исследовать весь набор кандидатов, заданный ef, он отслеживает скорость, с которой новые кандидаты улучшают результирующий набор, и завершает поиск раньше, когда эта скорость стабильно опускается ниже порога. Это уменьшает число вычислений расстояний без заметного ухудшения качества результатов.

Раннее завершение включено по умолчанию и автоматически отключается, когда k равно 10 или меньше, поскольку для таких маленьких наборов результатов накладные расходы алгоритма не оправданы. Выигрыш в производительности масштабируется с k - чем больше набор результатов, тем больше вычислений расстояния можно сэкономить за счет раннего завершения.

Учтите, что oversampling умножает эффективное k, используемое во время обхода HNSW, поэтому раннее завершение тоже выигрывает от oversampling: более высокое эффективное k означает больше кандидатов, которые потенциально можно пропустить.

Чтобы явно управлять ранним завершением, используйте параметр early_termination:

‹›
  • SQL
  • JSON
📋
-- disable early termination
SELECT id, knn_dist() FROM test WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { ef=200, early_termination=0 } );
-- enable early termination explicitly (default)
SELECT id, knn_dist() FROM test WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { ef=200, early_termination=1 } );

Когда стоит отключить раннее завершение:

  • Когда критически важна точность набора результатов и вы не можете позволить себе никакой аппроксимации сверх той, которую уже обеспечивает HNSW.
  • Когда используются низкие значения k (примерно 30 или меньше), при которых раннее завершение дает мало выигрыша в производительности, но может снизить точность.
Last modified: September 06, 2026