Автовекторизация (autoembedding)
Эмбеддер — модель, преобразующая данные в векторные представления.
Автовекторизация — это специальная функция Reindexer для преобразования данных из заданных полей в векторные представления. Представляет собой конвейер, автоматизирующий весь процесс от источника данных до индексирования и выборки. Автовекторизация позволяет с помощью подключенной пользовательской модели векторизации добавлять или обновлять данные из сырого представления в используемые векторные индексы, а также использовать входные данные пользователя для фильтрации данных в векторных индексах.
Сырыми данными в общем случае могут служить любые данные: текст, числа, аудиоданные, видеоданные, изображения. В Reindexer в качестве сырых данных для векторизации могут служить любые индексируемые данные. Автовекторизация допустима для объединения любых скалярных индексов и индексов-массивов, и не работает для композитных и sparse-индексов. А результат (эмбеддинги) может быть сохранён в любом подходящем типе векторных индексов.
Для поиска в индексе или фильтрации поддерживается только текстовое представление, а преобразование зависит от предоставленной модели векторизации. Пользовательская модель векторизации, в общем случае, может быть любой и выбирается исходя из текущих целей: модель векторизации текста для семантического поиска, RAG, рекомендаций и т.д. Подключение модели в Reindexer происходит как конфигурация подключения к независимому специализированному сервису векторизации.
Сервис векторизации (эмбеддер) должен выполнять роль прокси между Reindexer’ом и моделью, преобразующей исходные данные в эмбеддинги. Сервис может меняться независимо от Reindexer простой перенастройкой подключения. Сервисов может быть несколько, но не более двух на каждый векторный индекс в базе данных.
Требования к сервису векторизации
Сервис векторизации - это отдельный сервис с HTTP-интерфейсом, который должен быть реализован пользователем для возможности работы автовекторизации.
HTTPS в данный момент не поддерживается
Сервис должен предоставлять интерфейс, соответствующий описанной ниже спецификации OpenAPI. На текущий момент необходимым является только один обработчик для POST /api/v1/embedder/{name}/produce. Reindexer будет отправлять данные для векторизации в этот эндпоинт и ожидать эмбеддинг в ответе.
Текущий API сервиса векторизации находится в бета-версии и может измениться в будущих релизах
Для различных индексов возможно подключать собственные модели (сервисы векторизации) для получения различных векторных представлений (для семантического поиска, RAG, рекомендаций и т.д). Возможно использование одного сервиса для всех индексов, но следует учитывать нагрузку на базу и сервис.
Конфигурация автовекторизации
Настройка автовекторизации производится для каждого векторного индекса отдельно. Для этого в определение целевого индекса в поле config требуется добавить поле embedding. Поле embedding, в свою очередь, состоит из двух независимых полей: upsert_embedder и query_embedder.
Где:
upsert_embedder— это описание векторизатора (сервиса), который будет использоваться для векторизации документов при их вставке и модификации. Также может быть использован для задании значений векторов в уже имеющихся документах;query_embedder— это описание векторизатора (сервиса), который будет использоваться для векторизации пользовательских текстовых запросов для KNN-поиска в векторном индексе.
В общем случае оба векторизатора абсолютно независимы, настраиваются отдельно, могут существовать вместе или поодиночке, исходя из решаемой задачи. В то же время они могут описывать один и тот же сервис и использовать одну и ту же модель. Если ни один векторизатор на задан, автовекторизация для целевого индекса использоваться не будет.
Нужно понимать, что операции, выполняемые upsert_embedder, носят обязательный характер. Если сервис векторизации не ответил или вернул ошибку - операция отменяется. В случае сбоя сервиса с query_embedder для гибридных выборок значение меняется на пустое, когда от пользователя приходит несколько полнотекстовых и векторных запросов. Запрос в этом случае будет эквивалентен запросу без фильтрации по KNN-запросу со строкой.
Ниже представлена часть конфигурации для одного векторного индекса. Для примера, показан вариант конфигурации двух векторизаторов (значения в <> текстовые и должны быть заданы пользователем):
{
"indexes": [
{
"name": "vec",
"config": {
"dimension": 1024,
"metric": "inner_product",
"embedding": {
"upsert_embedder": {
"name": "my-embedder",
"URL": "http://127.0.0.1:8000",
"cache_tag": "hnsw",
"fields": ["name", "value"],
"embedding_strategy": "always",
"pool": {
"connections": 3,
"connect_timeout_ms": 500,
"read_timeout_ms": 500,
"write_timeout_ms": 500
}
}
}
}
}
]
}
Параметры конфигурации подключения автовекторизатора
| Имя параметра | Описание | Обязательный |
|---|---|---|
name |
Имя сервиса векторизации. Если не указано, используется логика генерации по умолчанию, в нижнем регистре: <NS_NAME>_<INDEX_NAME>, для идентификации в логе | Нет |
URL |
URL сервиса векторизации. Адрес сервиса, на который будут отправляться запросы на создание эмбеддингов | Да |
cache_tag |
Имя (или идентификатор), используемое для доступа к кешу. Если этот параметр не указан или там пустая строка, то кеширование не используется. Имя может быть неуникальным, в этом случае разные векторизаторы могут помещать результат в один и тот же кеш. Однако необходимо учесть, что это работает корректно, если исходные данные для векторизаторов не пересекаются, или если векторизаторы возвращают абсолютно одинаковые значения для одного и того же набора входных данных. | Нет |
fields |
Список индексных полей в текущем пространстве имен базы данных, для которых необходимо вычислять эмбеддинг (не имеет смысла для query_embedder) |
Да |
embedding_strategy |
Стратегия внедрения эмбеддингов в индекс (не имеет смысла для query_embedder) |
Нет |
pool |
Конфигурация пула подключений к сервису векторизации | Нет |
Список возможных значений параметра embedding_strategy:
| Имя параметра | Описание |
|---|---|
always |
Векторизация выполняется всегда. Если пользователь указал значение для поля (непустой вектор), оно будет перезаписано. Значение по умолчанию |
empty_only |
Если пользователь указал значение для поля (непустой вектор), значение поля перезаписано не будет. Если поле пустое, то эмбеддинг выполняется автоматически |
strict |
Если пользователь указал значение для поля (непустой вектор), операция отклоняется, возвращается ошибка. Если поле пустое, то эмбеддинг выполняется автоматически |
При необходимости можно настроить пул подключений (pool). Иначе будут использоваться настройки по умолчанию:
| Имя параметра | Описание | Обязательный | Допустимые значения | Значение по умолчанию |
|---|---|---|---|---|
connections |
Количество подключений к сервису (единиц) | Нет | [1..1024] | 10 |
connect_timeout_ms |
Тайм-аут подключения/переподключения к сервису векторизации (в миллисекундах) | Нет | Не менее 100 | 300 |
read_timeout_ms |
Тайм-аут получения данных от сервиса векторизации (в миллисекундах) | Нет | Не менее 500 | 5000 |
write_timeout_ms |
Тайм-аут отправки данных в сервис векторизации (в миллисекундах) | Нет | Не менее 500 | 5000 |
upsert_embedder и query_embedder
upsert_embedder — преобразует документы/фрагменты текста, которые необходимо сохранить в векторной базе данных (индексировать), в эмбеддинги. Используется в операциях: insert, update, upsert. А так же в команде create_embeddings. Извлечённые из документа данные отправляются в сервис векторизации в формате JSON (значение параметра format также будет json).
query_embedder — преобразует поисковый запрос пользователя в эмбеддинг, чтобы затем использовать этот эмбеддинг при KNN-поиске. Используется для запросов, содержащих KNN-условия со строкой (например, WhereKnnString в Go). При этом в сервис векторизации отправляется текст пользовательского запроса, а в параметре format — значение text.
Пример представления данных в запросе к сервису векторизации:
-
В момент вставки/обновления (для
upsert_embedder). Передаются все указанные в конфигурации поля из векторизуемого документа (или нескольких):{"data":[{"field0":val0, "field1":[val10, val11], ...}, ..., {"field0":val0, "field1":[], ...}]} -
В момент запроса на выборку, например, при использовании
WhereKNNString(дляquery_embedder). Пользовательские запросы передаются в виде строк (несколько строк может быть передано только в случае, если в ядре будет собран батч из нескольких запросов):{"data":["WhereKNN input search text", ...]}
В ответ, в соответствии со спецификацией, ожидается:
[
"_comment": "One array corresponds to one object/string that came to produce",
[
"_comment": "One such object corresponds to one chunk. At this stage, there should always be one chunk, and the data from the 'chunk' itself will be ignored - only the vector from the embedding field will be used",
{
"chunk": "some data",
"embedding": [ 1.1, 0.7, ...]
},
{
"chunk": "more data",
"embedding": [ 0.1, -1.0, ...]
},
...
],
...
]
Пример конфигурирования автоэмбедирования
connectConfig := &bindings.EmbedderConnectionPoolConfig{
Connections: 3,
ConnectTimeout: 500,
ReadTimeout: 500,
WriteTimeout: 500,
}
embedderConfig := &bindings.EmbedderConfig{
Name: "test-embedder",
URL: "http://127.0.0.1:8000",
Fields: []string{"name", "value"},
CacheTag: "HNSW",
EmbeddingStrategy: "always",
ConnectionPoolConfig: connectConfig,
}
embeddingConfig := &bindings.EmbeddingConfig{
UpsertEmbedder: embedderConfig,
}
hnswSTOpts := reindexer.FloatVectorIndexOpts{
Metric: "inner_product",
Dimension: 1024,
M: 8,
EfConstruction: 100,
StartSize: 256,
MultithreadingMode: 1,
EmbeddingConfig: embeddingConfig,
}
indexDef := reindexer.IndexDef{
Name: "vec",
JSONPaths: []string{"vec"},
IndexType: "hnsw",
FieldType: "float_vector",
Config: hnswSTOpts,
}
err := DB.AddIndex("embedding_hnws", indexDef)
if err != nil {
panic(err)
}
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/vectors_db/namespaces/test_ns/indexes' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "vec",
"json_paths": ["vec"],
"field_type": "float_vector",
"index_type": "hnsw",
"config": {
"dimension": 1024,
"metric": "inner_product",
"m": 8,
"ef_construction": 100,
"strat_size": 256,
"multithreading": 1,
"embedding": {
"upsert_embedder": {
"name": "test-embedder",
"URL": "http://127.0.0.1:8000",
"cache_tag": "HNSW",
"fields": ["name", "value"],
"embedding_strategy": "always",
"pool": {
"connections": 3,
"connect_timeout_ms": 500,
"read_timeout_ms": 500,
"write_timeout_ms": 500
}
}
}
}
}'
Конфигурация кеша автовекторизатора
При автовекторизации кеширование результатов используется для повышения производительности. Обращение к удаленному сервису и процесс векторизации могут быть небыстрыми операциями. В то же время, для реальных пользовательских запросов очень часто можно подобрать достаточный размер кеша, обеспечивающий высокий hitrate.
Кеш эмбеддиногов в Reindexer работает по принципу LRU и имеет гибридную структуру: ключи (векторизуемые значения) всегда хранятся in-memory, а значения соответствующих им векторов — на диске. Таких кешей может быть несколько. Все они существуют на уровне конкретной БД, и один кеш может использоваться одновременно в нескольких индексах и/или нескольких неймспейсах. Доступ к кешу осуществляется по идентификатору (cache_tag). По умолчанию кеширование отключено. Для того чтобы кеширование заработало, необходимо произвести соответствующую настройку.
Настройка кеша для автовекторизации производится в два шага:
- Необходимо задать параметр
cache_tag, который был описан выше и является частью поля*_embedderизconfigв описании целевого векторного индекса; - Включить кеширование для векторизаторов на уровне базы данных. Для этого существует специальная запись в системном неймспейсе
#configс типомembedders. Эта запись также необязательна: если её нет, то кеширование не используется для всех автовекторизаторов, несмотря на установленныйcache_tag.
Параметры для настройки двух кешей:
{
"type":"embedders",
"caches":[
{
"cache_tag":"*",
"max_cache_items":1000000,
"hit_to_cache":1
},
{
"cache_tag":"the jungle book",
"max_cache_items":2025,
"hit_to_cache":3
}
]
}
| Имя параметра | Описание | Обязательный | Допустимые значения | Значение по умолчанию |
|---|---|---|---|---|
cache_tag |
Имя идентификатора, используемое для доступа к кешу. Можно использовать специальный символ *, в этом случае настройки применяются ко всем настроенным векторизаторам, у которых задан непустой cache_tag при условии, что нет более конкретной специализации. Специализация определяется соответствием значений cache_tag в этой конфигурации и конфигурации векторизатора (например, the jungle book) |
Да | Любой текст | - |
max_cache_items |
Максимальное количество элементов (уникальных ключей) в кеше эмбеддингов. Этот кеш будет активирован, только если свойство max_cache_items не установлено в значение 0. Он хранит результаты вычислений эмбеддинга |
Нет | [0..INT_MAX] | 1000000 |
hit_to_cache |
Это значение определяет, сколько запросов требуется для помещения результатов в кеш. Например, при значении 2 первый запрос будет выполнен без кеширования, второй запрос сгенерирует запись кеша и поместит результаты в кеш, а третий запрос уже получит результат из кеша. Значение 0 и 1 означают, что добавленное значение сразу помещается в кеш |
Нет | [0..INT_MAX] | 1 |
nsConfig := make([]reindexer.DBEmbeddersConfig, 2)
nsConfig[0].CacheTag = "*"
nsConfig[0].MaxCacheItems = 1000000
nsConfig[0].HitToCache = 1
nsConfig[1].CacheTag = "the jungle book"
nsConfig[1].MaxCacheItems = 2025
nsConfig[1].HitToCache = 3
item := reindexer.DBConfigItem{
Type: "caches",
Embedders: &nsConfig,
}
err := DB.Upsert(reindexer.ConfigNamespaceName, item)
if err != nil {
panic(err)
}
UPDATE #config
SET caches = [{"cache_tag":"*","max_cache_items":1000000,"hit_to_cache":1}, {"cache_tag":"the jungle book","max_cache_items":2025,"hit_to_cache":3}]
WHERE type = 'embedders'
curl -X 'PATCH' \
'http://127.0.0.1:9088/api/v1/db/vectors_db/namespaces/%23config/items' \
-H 'accept: application/json' \
-H 'Content-Type: */*' \
-d '
{
"type": "embedders",
"caches": [
{
"cache_tag": "*",
"max_cache_items": 1000000,
"hit_to_cache": 1
},
{
"cache_tag": "the jungle book",
"max_cache_items": 2025,
"hit_to_cache": 3
}
]
}'
Создание и обновление эмбеддингов для существующих документов
Для создания и обновления эмбеддингов существующих в неймспейсе документов предусмотрена отдельная action-команда: create_embeddings. Она применяется в случаях, когда необходимо добавить встроенные значения в существующие документы после настройки автоматического векторизатора, или для обновления существующих значений при смене векторизатора. Обновление происходит для всех векторизаторов в указанном пространстве имен.
Эта команда не выполняет блокировку целевого неймпейса. Вместо этого, она делает полную выборку и последовательный обход/векторизацию всех документов в соответствии с заданной стратегией векторизации. Это означает, что если в процессе выполнения команды какой-то из существующих документов будет обновлён, она может вновь восстановить его прежнее значение.
На текущий момент это действие не проксируется при использовании RAFT-кластера или шардированного кластера и выполняется только на том узле, на котором было инициировано. В случае с RAFT-кластером его требуется выполнять на узле-лидере.
Формат команды в JSON:
{
"type": "action",
"action": {
"command": "create_embeddings",
"namespace": "*",
"batch_size": 100
}
}
DB.Upsert(reindexer.ConfigNamespaceName, []byte("{\"type\":\"action\",\"action\":{\"command\":\"create_embeddings\", \"namespace\":\"*\", \"batch_size\":100}}"))
UPDATE #config
SET action = {"command": "create_embeddings", "namespace": "*", "batch_size": 100}
WHERE type = 'action'
reindexer_tool \
--dsn cproto://127.0.0.1:6534/vectors_db \
-c '\upsert #config {"type":"action","action":{"command":"create_embeddings", "namespace":"*", "batch_size":100}}'
Особое внимание необходимо уделить настройкам стратегии обновления эмбеддингов. Стратегия strict не рекомендуется на данном этапе.
- Если документы содержат значения эмбеддингов, но есть также документы без заполненных векторов, то рекомендуется использовать стратегию
empty_only. - Если необходимо заменить все значения в поле, для которого настроено автоматическая векторизация, то следует использовать стратегию
always.
Стратегия задается в настройках векторизатора для индекса и может быть задана индивидуально в конфигурации: ("config": {"embedding": { "upsert_embedder": { "embedding_strategy": ...).
Параметры
| Имя параметра | Описание | Обязательнный |
|---|---|---|
namespace |
Целевой неймспейс (* – применяет команду ко всем неймспейсам в БД) |
Да |
batch_size |
Число, определяющее, сколько документов будут обновляться в неймспейсе за один раз (размер пачки в транзакции) | Нет |
Параметром batch_size можно регулировать нагрузку на Reindexer и векторизирующий сервис
Команда удаления кеша для эмбеддингов
Эта команда применяется, когда необходимо сбросить содержимое кешей для всех или конкретных векторизаторов.
Формат команды в JSON:
{"type":"action","action":{"command":"clear_embedders_cache", "cache_tag":"*"}}
DB.Upsert("#config", []byte("{\"type\":\"action\",\"action\":{\"command\":\"clear_embedders_cache\", \"cache_tag\":\"*\"}}"))
UPDATE #config
SET action = {"command": "clear_embedders_cache", "cache_tag": "*"}
WHERE type = 'action'
reindexer_tool \
--dsn cproto://127.0.0.1:6534/vectors_db \
-c '\upsert #config {"type":"action","action":{"command":"clear_embedders_cache", "cache_tag":"*"}}'
| Имя параметра | Описание | Обязательный |
|---|---|---|
cache_tag |
Идентификатор целевого кеша (* — применить команду ко всем кешам) |
Да |
Примеры KNN-запросов с автовекторизацией
Поиск KNN с автоматическим созданием эмбеддингов работает аналогично обычному поиску KNN, но ожидает строку вместо вектора. Эта строка передается в сервис векторизации, который возвращает соответствующий вектор значений (эмбеддинг). Полученный вектор используется при выполнении поиска в базе данных как фактическое значение для фильтрации.
Перед использованием необходимо настроить поле query_embedder в конфигурации целевого индекса. Если по каким-то причинам векторизирующий сервис не отвечает или недоступен, для гибридных запросов (полнотекстовый + KNN) фильтр по данному условию будет проигнорирован, а запрос будет выполнен как полнотекстовый. В логе при этом появится соответствующая запись: Failed to get embedding for '<INDEX_NAME>'. Problem with client: ....
Все запросы обрабатываются по принципу KNN-поиска. Допускает комбинировать данный тип запросов как и любые другие запросы KNN.
Поиск KNN-векторов:
knnBaseSearchParams := reindexer.BaseKnnSearchParam{}.SetK(4291)
// brute force
it := db.Query("test_ns").WhereKnnString("vec_bf", "<text to embed calculate>", knnBaseSearchParams).Exec()
defer it.Close()
// hnsw
hnswSearchParams, err := reindexer.NewIndexHnswSearchParam(100000, knnBaseSearchParams)
if err != nil {
panic(err)
}
it := db.Query("test_ns").WhereKnnString("vec_hnsw", "<text to embed calculate>", hnswSearchParams).Exec()
defer it.Close()
// ivf
ivfSearchParams, err := reindexer.NewIndexIvfSearchParam(10, knnBaseSearchParams)
if err != nil {
panic(err)
}
it := db.Query("test_ns").WhereKnnString("vec_ivf", "<text to embed calculate>", ivfSearchParams).Exec()
defer it.Close()
SELECT * FROM test_ns WHERE KNN(vec_bf, '<text to embed calculate>', k=100)
SELECT * FROM test_ns WHERE KNN(vec_hnsw, '<text to embed calculate>', k=100, ef=200)
SELECT * FROM test_ns WHERE KNN(vec_ivf, '<text to embed calculate>', k=100, nprobe=10)
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/vectors_db/query' \
--header 'Content-Type: application/json' \
--data-raw '{
"namespace": "test_ns",
"type": "select",
"filters": [
{
"op": "and",
"cond": "knn",
"field": "vec_bf",
"value": "<text to embed calculate>",
"params": {"k": 100}
}
],
}'