Почему Weaviate
У Qdrant и Chroma вы сами реализуете hybrid search: берёте BM25 из
Elasticsearch или BM25s-библиотеки, считаете sparse-вектора, запускаете
два запроса, сливаете результаты вручную. В Weaviate это встроено из
коробки: один вызов query.hybrid() — и база сама делает
BM25 по инвертированному индексу, векторный поиск по HNSW, нормирует
оба Score и сливает через RelativeScoreFusion.
Второй ключевой кейс — multi-tenancy: если строите SaaS-продукт, где у каждого клиента свои данные, Weaviate нативно изолирует тенантов внутри одной коллекции без дополнительной инфраструктуры. Qdrant тоже поддерживает, но в Weaviate это глубже интегрировано на уровне схемы.
Архитектура: три индекса вместо одного
Большинство векторных баз хранят только один вид индекса — HNSW для приближённого поиска ближайших соседей. Weaviate хранит три независимых структуры для каждой коллекции:
- HNSW vector index — иерархический граф для семантического поиска; держится в RAM, восстанавливается из WAL после рестарта.
- BM25 inverted index — инвертированный индекс слов для ключевого поиска; хранится на диске, LSM-дерево сегментов.
- LSM-tree object storage — сами объекты + их свойства; memtable в RAM, сегменты на диске, фоновое слияние.
WAL (Write-Ahead Log) — ключ к надёжности. При записи Weaviate сначала пишет в WAL, потом в memtable. При падении — восстанавливает HNSW граф и объекты по логу. Для HNSW WAL содержит результаты вычислений (куда поместить вектор, каких соседей связать), а не сырые данные.
Schema: коллекции, свойства, векторайзеры
В Weaviate данные хранятся в коллекциях (раньше назывались «классами»). Коллекция — это как таблица в PostgreSQL, только с векторным индексом. Схема определяет: какие свойства есть у объектов, как их токенизировать для BM25, какой векторайзер использовать.
import weaviate
from weaviate.classes.config import Configure, Property, DataType, Tokenization
with weaviate.connect_to_local() as client:
client.collections.create(
name="Articles",
# Свойства объектов
properties=[
Property(
name="title",
data_type=DataType.TEXT,
tokenization=Tokenization.WORD, # BM25: разбивать по словам
index_searchable=True, # Включить в BM25 поиск
index_filterable=True, # Включить в фильтры
),
Property(
name="content",
data_type=DataType.TEXT,
tokenization=Tokenization.WORD,
vectorize_property_name=False, # Не добавлять "content:" к векторизации
),
Property(
name="category",
data_type=DataType.TEXT,
tokenization=Tokenization.FIELD, # Целиком как один токен (для точного match)
skip_vectorization=True, # Не влияет на embedding
),
Property(
name="published_at",
data_type=DataType.DATE,
index_range_filters=True, # Оптимизация для >, <, >=, <=
),
Property(
name="rating",
data_type=DataType.NUMBER,
index_range_filters=True,
),
Property(
name="tags",
data_type=DataType.TEXT_ARRAY, # Массив строк
tokenization=Tokenization.WORD,
),
Property(
name="source_id",
data_type=DataType.TEXT,
skip_vectorization=True,
index_searchable=False, # Только фильтры, не BM25
),
],
# Векторайзер: автоматически векторизует объекты при вставке
vector_config=Configure.Vectors.text2vec_openai(
model="text-embedding-3-small",
),
# Generative модель для RAG-запросов
generative_config=Configure.Generative.openai(
model="gpt-4o-mini",
),
)
Типы данных
Подключение и базовые операции
Python v4 клиент вышел как GA в 2024. Он использует gRPC вместо REST
для большинства операций — это ускоряет bulk import и запросы в ~3–5 раз.
Порт gRPC по умолчанию: 50050.
import weaviate
from weaviate.auth import AuthApiKey
# Локальный Docker (gRPC: 50050, REST: 8080)
client = weaviate.connect_to_local()
# Weaviate Cloud (managed)
client = weaviate.connect_to_weaviate_cloud(
cluster_url="https://your-cluster.weaviate.cloud",
auth_credentials=AuthApiKey("your-wcd-api-key"),
)
# Embedded (в процессе Python, для тестов)
client = weaviate.connect_to_embedded(
version="1.26.0",
persistence_data_path="./weaviate_data",
)
# ВСЕГДА используй контекстный менеджер — он закроет gRPC-соединение
with weaviate.connect_to_local() as client:
articles = client.collections.use("Articles")
# ... ваши операции ...
CRUD операции
from weaviate.util import generate_uuid4
import weaviate.classes as wvc
with weaviate.connect_to_local() as client:
articles = client.collections.use("Articles")
# ── Insert single ──────────────────────────────────────────────────
uuid = articles.data.insert(
properties={
"title": "Введение в RAG",
"content": "RAG (Retrieval-Augmented Generation) — подход...",
"category": "AI",
"rating": 4.8,
"tags": ["rag", "llm", "python"],
},
uuid=generate_uuid4(), # опционально; генерируется автоматически
)
print(uuid) # UUID вставленного объекта
# ── Bulk insert (в 10-100× быстрее одиночных) ─────────────────────
objects = [
wvc.data.DataObject(
properties={
"title": f"Статья {i}",
"content": "...",
"category": "Tech",
"rating": 4.0,
},
uuid=generate_uuid4(),
)
for i in range(1000)
]
result = articles.data.insert_many(objects)
if result.has_errors:
for err in result.errors.values():
print(err)
# ── Update (partial — только указанные поля) ──────────────────────
articles.data.update(
uuid=uuid,
properties={"rating": 4.9}, # остальные поля не трогаются
)
# ── Replace (full — остаются только указанные поля) ───────────────
articles.data.replace(
uuid=uuid,
properties={"title": "Новый заголовок", "content": "Новый контент"},
)
# ── Delete by ID ──────────────────────────────────────────────────
articles.data.delete_by_id(uuid)
# ── Delete by filter ──────────────────────────────────────────────
from weaviate.classes.query import Filter
articles.data.delete_many(
where=Filter.by_property("category").equal("Draft")
)
# ── Get single object ─────────────────────────────────────────────
obj = articles.query.fetch_object_by_id(
uuid=uuid,
return_properties=["title", "rating"],
)
print(obj.properties)
Hybrid search: alpha и RelativeScoreFusion
Hybrid search в Weaviate — это не просто «запустить два запроса».
Это единый механизм с общим пайплайном нормализации и слияния.
Ключевой параметр — alpha, который балансирует между
семантическим и ключевым поиском.
Типы Fusion
Прежде чем складывать оценки через alpha, нужно нормализовать их — у BM25 и векторного поиска разные диапазоны. Weaviate предлагает два алгоритма:
auto_limit. Рекомендуется.auto_limit. Менее точен, но стабилен.from weaviate.classes.query import HybridFusion
with weaviate.connect_to_local() as client:
articles = client.collections.use("Articles")
results = articles.query.hybrid(
query="управление выдачей в RAG",
alpha=0.75, # 75% vector, 25% BM25
fusion_type=HybridFusion.RELATIVE_SCORE, # нормализация по score
limit=10,
return_metadata=["score", "explain_score"], # показать итоговый score
)
for obj in results.objects:
print(obj.properties["title"])
print(f" score: {obj.metadata.score:.4f}")
BM25 токенизация
Токенизация определяет, как Weaviate разбивает текст при индексировании и при поиске. Выбор напрямую влияет на качество keyword search.
Управление выдачей: все рычаги
Это самый важный раздел. Weaviate даёт несколько независимых механизмов влиять на то, что вернётся и в каком порядке: режим запроса, пороги distance/certainty, фильтры, autocut, named vectors, reranking и generative search.
Режимы запроса
Distance и Certainty: пороги релевантности
Оба параметра ограничивают выдачу по близости к запросу. Это главный способ отсечь нерелевантные результаты в векторном поиске.
from weaviate.classes.query import MetadataQuery
with weaviate.connect_to_local() as client:
articles = client.collections.use("Articles")
# ── distance: максимальное расстояние (0 = идентично, 2 = противоположно) ──
# Для cosine distance: 0 = идентично, 1 = ортогонально, 2 = противоположно
results = articles.query.near_text(
query="machine learning basics",
distance=0.4, # отсекаем всё с distance > 0.4
limit=20,
return_metadata=MetadataQuery(distance=True),
)
# ── certainty: уверенность 0.0–1.0 (только для cosine) ─────────────
# certainty = 1 - distance/2 (преобразование из distance)
results = articles.query.near_text(
query="machine learning basics",
certainty=0.8, # хотим уверенность >= 80%
limit=20,
return_metadata=MetadataQuery(certainty=True, distance=True),
)
for obj in results.objects:
print(f"{obj.properties['title']}")
print(f" distance={obj.metadata.distance:.3f}")
print(f" certainty={obj.metadata.certainty:.3f}")
# ── В hybrid: score_threshold вместо distance ────────────────────
# score в hybrid — это нормализованный 0–1, выше = лучше
results = articles.query.hybrid(
query="...",
alpha=0.75,
limit=20,
return_metadata=MetadataQuery(score=True),
)
# Фильтруем вручную (нет прямого score_threshold в hybrid v4)
relevant = [o for o in results.objects if o.metadata.score > 0.6]
Фильтры: WHERE для векторной БД
Фильтры в Weaviate работают через indexFilterable=True
(отдельный оптимизированный индекс). Они применяются до или после
векторного поиска, не влияя на score объектов.
* — любые символы, ? — один символ.from weaviate.classes.query import Filter
with weaviate.connect_to_local() as client:
articles = client.collections.use("Articles")
# ── Простые условия ───────────────────────────────────────────────
f_published = Filter.by_property("category").equal("AI")
f_rating = Filter.by_property("rating").greater_or_equal(4.0)
f_date = Filter.by_property("published_at").greater_than("2024-01-01T00:00:00Z")
# ── Логические операторы ──────────────────────────────────────────
# AND: оба условия должны выполняться
f_and = f_published & f_rating
# OR: хотя бы одно
f_or = (
Filter.by_property("category").equal("AI") |
Filter.by_property("category").equal("ML")
)
# NOT: инверсия
f_not = ~Filter.by_property("category").equal("Spam")
# all_of / any_of для нескольких условий
f_complex = Filter.all_of([
Filter.by_property("rating").greater_than(4.0),
Filter.by_property("category").equal("AI"),
Filter.by_property("tags").contains_any(["rag", "llm"]),
])
# ── Wildcard поиск ────────────────────────────────────────────────
f_like = Filter.by_property("title").like("*Retrieval*")
# ── Фильтрация через reference (ссылка на другую коллекцию) ──────
f_ref = (
Filter.by_ref("hasAuthor")
.by_property("country")
.equal("Russia")
)
# ── Применение в запросах ─────────────────────────────────────────
results = articles.query.hybrid(
query="векторный поиск",
alpha=0.75,
where=f_complex, # ← фильтр прямо в запрос
limit=10,
)
# Fetch с сортировкой (без семантики)
from weaviate.classes.query import Sort
results = articles.query.fetch_objects(
where=f_date,
sort=Sort.by_property("rating", ascending=False),
limit=50,
)
Autocut: автоматическое отсечение по кластерам
Классическое limit=10 возвращает ровно 10 результатов,
даже если 7-й по счёту уже нерелевантен. Autocut решает это иначе:
он анализирует разрывы в score и автоматически обрезает выдачу на
границе кластера.
with weaviate.connect_to_local() as client:
articles = client.collections.use("Articles")
# auto_limit=1 → вернуть только первый кластер
# auto_limit=2 → первые два кластера
# Требует RELATIVE_SCORE_FUSION (по умолчанию)
results = articles.query.hybrid(
query="retrieval augmented generation",
alpha=0.75,
limit=50, # запрашиваем с запасом
auto_limit=1, # но вернём только первый «скачок» релевантности
)
print(f"Вернулось {len(results.objects)} объектов из 50 запрошенных")
HybridFusion.RELATIVE_SCORE (по умолчанию с v1.24).
Named vectors: несколько пространств на объект
Разные части документа несут разную семантику. Заголовок и тело статьи лучше хранить в разных векторных пространствах: при поиске по заголовку берём title_vec, при поиске по смыслу — content_vec.
from weaviate.classes.config import Configure, Property, DataType
with weaviate.connect_to_local() as client:
# ── Определяем коллекцию с несколькими векторами ──────────────────
client.collections.create(
name="Documents",
properties=[
Property(name="title", data_type=DataType.TEXT),
Property(name="summary", data_type=DataType.TEXT),
Property(name="content", data_type=DataType.TEXT),
Property(name="language", data_type=DataType.TEXT, skip_vectorization=True),
],
vector_config={
# Вектор для заголовка (маленькая модель, быстро)
"title_vec": Configure.NamedVectors.text2vec_openai(
source_properties=["title"], # ← только title
model="text-embedding-3-small",
),
# Вектор для контента (большая модель, точнее)
"content_vec": Configure.NamedVectors.text2vec_openai(
source_properties=["summary", "content"],
model="text-embedding-3-large",
),
},
)
docs = client.collections.use("Documents")
# ── Запрос по конкретному named vector ────────────────────────────
# Поиск по заголовку
results = docs.query.near_text(
query="введение в machine learning",
target_vector="title_vec", # ← используем только этот вектор
limit=5,
)
# Поиск по контенту
results = docs.query.hybrid(
query="обучение с подкреплением в robotics",
target_vector="content_vec", # ← только content
alpha=0.75,
limit=10,
)
# ── Multi-target: поиск по нескольким векторам сразу ─────────────
from weaviate.classes.query import TargetVectors
results = docs.query.near_text(
query="machine learning",
target_vector=TargetVectors.manual_weights({
"title_vec": 0.3, # 30% вес на заголовок
"content_vec": 0.7, # 70% вес на контент
}),
limit=10,
)
Reranking: переупорядочивание результатов
Hybrid search возвращает хорошие результаты, но не идеальные. Reranker — это вторая модель, которая берёт топ-N результатов и пересортирует их с более глубоким анализом релевантности. Требует настройки reranker-модуля в коллекции.
from weaviate.classes.config import Configure
from weaviate.classes.query import Rerank, MetadataQuery
with weaviate.connect_to_local() as client:
# ── 1. Создать коллекцию с reranker ───────────────────────────────
client.collections.create(
name="ArticlesWithRerank",
properties=[...],
vector_config=Configure.Vectors.text2vec_openai(model="text-embedding-3-small"),
# Cohere reranker (нужен API ключ)
reranker_config=Configure.Reranker.cohere(
model="rerank-english-v3.0",
),
# Или локальный cross-encoder:
# reranker_config=Configure.Reranker.transformers(),
)
articles = client.collections.use("ArticlesWithRerank")
# ── 2. Запрос с reranking ─────────────────────────────────────────
results = articles.query.hybrid(
query="best practices for RAG pipelines",
alpha=0.75,
limit=50, # сначала берём 50 кандидатов...
rerank=Rerank(
property="content", # reranker смотрит это поле
query="RAG optimization techniques", # можно уточнить запрос
),
return_metadata=MetadataQuery(
score=True, # score после fusion
rerank_score=True, # score после reranker
),
)
# Вернётся 50 объектов, пересортированных reranker'ом
for obj in results.objects[:5]:
print(obj.properties["title"])
print(f" fusion score: {obj.metadata.score:.3f}")
print(f" rerank score: {obj.metadata.rerank_score:.3f}")
Generative search: RAG прямо в запросе
Generative search — это встроенный RAG в Weaviate. Не нужно отдельно извлекать документы, формировать промпт и вызывать LLM — всё происходит в одном запросе к базе.
from weaviate.classes.generate import GenerativeSearch
with weaviate.connect_to_local() as client:
articles = client.collections.use("Articles") # нужен generative_config
# ── Single prompt: свой промпт для каждого результата ─────────────
results = articles.query.hybrid(
query="что такое RAG",
alpha=0.75,
limit=5,
return_properties=["title", "content"],
generate=GenerativeSearch.single(
# {поле} — подстановка из properties объекта
prompt="Объясни одним предложением по-русски: {content}"
),
)
for obj in results.objects:
print(f"Оригинал: {obj.properties['title']}")
print(f"Генерация: {obj.generated}")
# ── Grouped prompt: агрегация всех результатов ────────────────────
results = articles.query.hybrid(
query="лучшие практики для RAG",
alpha=0.75,
limit=10,
return_properties=["title", "content"],
generate=GenerativeSearch.grouped(
# Все результаты передаются как контекст
prompt="""На основе этих статей составь краткий список
ТОП-5 практик для построения RAG-системы. Отвечай по-русски.""",
),
)
# Групповой ответ — в grouped_task
print(results.generated) # итоговый ответ LLM
Контроль возвращаемых данных
from weaviate.classes.query import MetadataQuery
with weaviate.connect_to_local() as client:
articles = client.collections.use("Articles")
results = articles.query.hybrid(
query="...",
alpha=0.75,
limit=10,
offset=20, # пагинация
# Какие свойства вернуть (по умолчанию — все)
return_properties=["title", "category", "rating"],
# Какие метаданные включить в ответ
return_metadata=MetadataQuery(
uuid=True, # ID объекта
score=True, # итоговый hybrid score
distance=True, # дистанция в векторном пространстве
certainty=True, # certainty (только cosine)
explain_score=True, # подробно: вклад BM25 и vector отдельно
creation_time=True, # когда создан
last_update_time=True,
),
# Включить вектор в ответ (по умолчанию: нет — экономим трафик)
include_vector=False,
)
for obj in results.objects:
print(obj.uuid)
print(obj.properties["title"])
print(f"score={obj.metadata.score:.3f}")
if obj.metadata.explain_score:
print(obj.metadata.explain_score) # «BM25: 0.8, Vector: 0.9»
Multi-tenancy: изоляция данных
Если вы строите SaaS-приложение, где каждый клиент должен видеть только свои документы — multi-tenancy встроен в Weaviate на уровне хранилища. Каждый тенант получает физически изолированные сегменты, а не просто фильтр WHERE.
from weaviate.classes.config import Configure
from weaviate.classes.tenants import Tenant, TenantActivityStatus
with weaviate.connect_to_local() as client:
# ── Создать коллекцию с multi-tenancy ─────────────────────────────
client.collections.create(
name="KnowledgeBase",
properties=[
Property(name="title", data_type=DataType.TEXT),
Property(name="content", data_type=DataType.TEXT),
],
vector_config=Configure.Vectors.text2vec_openai(model="text-embedding-3-small"),
multi_tenancy_config=Configure.multi_tenancy(
enabled=True,
auto_tenant_creation=True, # создавать тенанта автоматически при вставке
),
)
kb = client.collections.use("KnowledgeBase")
# ── Управление тенантами ──────────────────────────────────────────
# Создать тенантов
kb.tenants.create(tenants=[
Tenant(name="acme_corp"),
Tenant(name="globex_inc"),
Tenant(name="initech"),
])
# Получить список тенантов
tenants = kb.tenants.get()
for t in tenants.values():
print(f"{t.name}: {t.activity_status}")
# Деактивировать тенанта (данные остаются, но не доступны)
kb.tenants.update(tenants=[
Tenant(name="initech", activity_status=TenantActivityStatus.INACTIVE)
])
# ── Вставка данных в тенанта ──────────────────────────────────────
acme_kb = kb.with_tenant("acme_corp") # ← переключаемся на тенанта
acme_kb.data.insert(properties={
"title": "Внутренняя документация ACME",
"content": "...",
})
# ── Запрос только в данных тенанта ────────────────────────────────
results = acme_kb.query.hybrid(
query="документация по API",
alpha=0.75,
limit=10,
)
# globex_inc никогда не увидит эти данные
Векторайзеры: автоматическая векторизация
Уникальная особенность Weaviate — не нужно самостоятельно вызывать API эмбеддингов. Достаточно настроить vectorizer при создании коллекции, и Weaviate автоматически векторизует объекты при вставке и запросы при поиске.
import weaviate
from weaviate.classes.config import Configure, Property, DataType
# Docker-compose для text2vec-openai:
# weaviate:
# image: semitechnologies/weaviate:1.26.0
# environment:
# ENABLE_MODULES: text2vec-openai
# OPENAI_APIKEY: sk-...
with weaviate.connect_to_local(
headers={"X-OpenAI-Api-Key": "sk-..."}, # ключ можно передать в заголовке
) as client:
client.collections.create(
name="Articles",
properties=[
Property(
name="title",
data_type=DataType.TEXT,
skip_vectorization=False, # включить в вектор
vectorize_property_name=False, # не добавлять "title:" к тексту
),
Property(
name="internal_id",
data_type=DataType.TEXT,
skip_vectorization=True, # техническое поле — не векторизовать
),
],
vector_config=Configure.Vectors.text2vec_openai(
model="text-embedding-3-small",
vectorize_collection_name=False, # не добавлять "Articles " в начало
),
)
# При вставке — Weaviate сам вызовет OpenAI API
articles = client.collections.use("Articles")
articles.data.insert(properties={"title": "Введение в RAG", "internal_id": "ART-001"})
# При запросе — тоже автоматически
results = articles.query.near_text(query="документы и контекст", limit=5)
Производительность: HNSW параметры
Weaviate использует HNSW (Hierarchical Navigable Small World) для векторного поиска. Параметры графа влияют на компромисс между скоростью, памятью и точностью поиска.
from weaviate.classes.config import Configure, VectorDistances
with weaviate.connect_to_local() as client:
client.collections.create(
name="ArticlesTuned",
properties=[...],
vector_config=Configure.Vectors.text2vec_openai(
model="text-embedding-3-small",
# HNSW параметры — задаются при создании коллекции
vector_index_config=Configure.VectorIndex.hnsw(
ef_construction=128, # качество построения
max_connections=64, # рёбра в графе (M)
ef=-1, # -1 = динамический ef
dynamic_ef_min=100,
dynamic_ef_max=500,
dynamic_ef_factor=8, # ef = limit * 8
flat_search_cutoff=40_000,
distance_metric=VectorDistances.COSINE,
quantizer=Configure.VectorIndex.Quantizer.pq(
# Product Quantization для сжатия (опционально)
segments=96,
training_limit=100_000,
),
),
),
)
# Изменить ef в рантайме (без пересоздания коллекции)
collection = client.collections.use("ArticlesTuned")
collection.config.update(
vector_config_updates={"": Configure.VectorIndex.hnsw(
ef=200, # фиксированный ef для высокой точности
)}
)
Scroll API: полный обход коллекции
fetch_objects с offset плохо масштабируется: при offset=100000
база всё равно пробегает 100000 объектов. Для полного обхода используйте
cursor-пагинацию через after.
import json
from pathlib import Path
def export_collection(client, collection_name: str, output_file: str, batch: int = 200):
"""Экспортирует все объекты коллекции через cursor-пагинацию."""
collection = client.collections.use(collection_name)
path = Path(output_file)
with path.open("w", encoding="utf-8") as f:
cursor = None
total = 0
while True:
results = collection.query.fetch_objects(
limit=batch,
after=cursor, # ← cursor вместо offset
return_properties=["title", "content", "category"],
return_metadata=["uuid"],
)
if not results.objects:
break
for obj in results.objects:
record = {"uuid": str(obj.uuid), **obj.properties}
f.write(json.dumps(record, ensure_ascii=False) + "\n")
cursor = results.objects[-1].uuid # следующая страница от последнего uuid
total += len(results.objects)
print(f" Экспортировано: {total}")
print(f"Готово: {total} объектов → {output_file}")
with weaviate.connect_to_local() as client:
export_collection(client, "Articles", "articles_export.jsonl")
LangChain интеграция
from langchain_weaviate.vectorstores import WeaviateVectorStore
from langchain_openai import OpenAIEmbeddings
import weaviate
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
with weaviate.connect_to_local() as weaviate_client:
# ── Создать или подключиться к коллекции через LangChain ──────────
vectorstore = WeaviateVectorStore(
client=weaviate_client,
index_name="LangchainArticles",
text_key="content",
embedding=embeddings,
attributes=["title", "category"], # доп. поля для метаданных
)
# ── Добавить документы ────────────────────────────────────────────
from langchain_core.documents import Document
docs = [
Document(page_content="...", metadata={"title": "RAG Guide", "category": "AI"})
]
vectorstore.add_documents(docs)
# ── Retriever с hybrid search ─────────────────────────────────────
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={
"k": 5,
"fetch_k": 20, # кандидатов для MMR
"lambda_mult": 0.6, # баланс relevance/diversity
},
)
# ── Similarity search с фильтром ──────────────────────────────────
results = vectorstore.similarity_search(
query="vector databases comparison",
k=5,
where_filter={ # Weaviate-style where filter
"path": ["category"],
"operator": "Equal",
"valueText": "AI",
},
)
Типичные ошибки
client.close()
или не использовать with-блок — соединение утечёт.
with weaviate.connect_to_local() as client: ... — всегда.
data.insert() — отдельный gRPC-запрос. 1000 объектов
= 1000 запросов. Используйте data.insert_many(objects)
— быстрее в 10–100 раз.
internal_id, status, source_url
не несут семантики, но по умолчанию попадают в embedding. Это «загрязняет»
вектор и снижает качество поиска. Всегда ставьте
skip_vectorization=True для технических полей.
auto_limit работает только с HybridFusion.RELATIVE_SCORE.
С RANKED_FUSION — autocut игнорируется, возвращаются все результаты.
Tokenization.FIELD (точное совпадение)
или Tokenization.LOWERCASE.
fetch_objects(offset=100000) — база пробегает 100000 объектов,
чтобы их пропустить. При >10000 записей используйте cursor-пагинацию
через параметр after=last_uuid.
ef_construction и max_connections задаются
только при создании коллекции — это параметры построения HNSW графа.
Для изменения нужно пересоздать коллекцию и переиндексировать данные.
Единственный параметр, изменяемый в рантайме — ef.
Шпаргалка
# ── Подключение ───────────────────────────────────────────────────────
with weaviate.connect_to_local() as client: # Docker
with weaviate.connect_to_weaviate_cloud(url, auth) as: # WCD
with weaviate.connect_to_embedded() as: # In-process
# ── Коллекции ─────────────────────────────────────────────────────────
client.collections.create(name, properties, vector_config, generative_config)
client.collections.use("Name") # get reference
client.collections.delete("Name") # удалить коллекцию
client.collections.list_all() # все коллекции
# ── CRUD ──────────────────────────────────────────────────────────────
col.data.insert(properties, uuid)
col.data.insert_many([DataObject(...)]) # bulk — всегда!
col.data.update(uuid, properties) # частичное обновление
col.data.replace(uuid, properties) # полная замена
col.data.delete_by_id(uuid)
col.data.delete_many(where=Filter...)
# ── Запросы ───────────────────────────────────────────────────────────
col.query.hybrid(query, alpha=0.75, fusion_type, where, limit, auto_limit)
col.query.near_text(query, distance, certainty, target_vector, limit)
col.query.near_vector(vector, distance, limit)
col.query.bm25(query, limit, where)
col.query.fetch_objects(where, sort, limit, after) # cursor-пагинация
# ── Alpha ────────────────────────────────────────────────────────────
# 0.0 = чисто BM25 | 0.5 = баланс | 0.75 = рек. для RAG | 1.0 = чисто vector
# ── Fusion types ──────────────────────────────────────────────────────
HybridFusion.RELATIVE_SCORE # default v1.24+, нужен для autocut
HybridFusion.RANKED_FUSION # RRF, без autocut
# ── Фильтры ───────────────────────────────────────────────────────────
Filter.by_property("f").equal(v)
Filter.by_property("f").greater_than(v)
Filter.by_property("f").like("*pattern*")
Filter.by_property("tags").contains_any(["a","b"])
Filter.all_of([f1, f2]) # AND
Filter.any_of([f1, f2]) # OR
f1 & f2 # AND | f1 | f2 # OR | ~f1 # NOT
Filter.by_ref("ref").by_property("field").equal(v)
# ── Named vectors ─────────────────────────────────────────────────────
col.query.near_text(query, target_vector="vec_name")
TargetVectors.manual_weights({"title_vec": 0.3, "content_vec": 0.7})
# ── Autocut ───────────────────────────────────────────────────────────
col.query.hybrid(query, limit=50, auto_limit=1) # первый кластер
# ── Reranking ─────────────────────────────────────────────────────────
Rerank(property="content", query="уточнённый запрос")
# ── Generative search ─────────────────────────────────────────────────
GenerativeSearch.single(prompt="Резюмируй: {content}")
GenerativeSearch.grouped(prompt="Объедини эти статьи...")
# ── Metadata ──────────────────────────────────────────────────────────
MetadataQuery(uuid, score, distance, certainty, explain_score, creation_time)
# ── Multi-tenancy ─────────────────────────────────────────────────────
kb.with_tenant("tenant_id").query.hybrid(...)
kb.tenants.create([Tenant("name")])
kb.tenants.update([Tenant("name", activity_status=TenantActivityStatus.INACTIVE)])
# ── HNSW (только при создании, не меняются): ─────────────────────────
# ef_construction=128, max_connections=64
# ef меняется: col.config.update(vector_config_updates={...})
# ── Токенизация (для BM25): ───────────────────────────────────────────
# WORD: статьи | FIELD: UUID/коды | LOWERCASE: email/url | WHITESPACE: акронимы
Практические задания
-
Hybrid RAG с фильтрами. Создайте коллекцию «TechDocs»
со свойствами
title,content,language,version(number),tags(text[]). Вставьте 50 документов. Напишите функциюsearch_docs(query, lang, min_version, tags), которая делает hybrid search (alpha=0.75) с фильтрами по языку, версии и тегам. Добавьте autocut=1 и верните только explain_score. -
Named vectors + reranking. Создайте коллекцию с двумя
named vectors:
title_vec(только title) иbody_vec(title + content). Напишите функцию поиска, которая принимает параметрsearch_by: Literal["title", "body", "both"]и в зависимости от него использует нужный named vector или TargetVectors.manual_weights. Добавьте reranking черезRerank. -
Multi-tenant SaaS. Смоделируйте базу знаний для трёх
клиентов. Создайте коллекцию с multi-tenancy, добавьте по 20 документов
каждому тенанту, реализуйте функцию
ask_kb(tenant_id, question), которая делает hybrid search + generative search и возвращает ответ LLM. Проверьте, что тенанты не видят данные друг друга.