Гибридный поиск в Pinecone и pgvector
Настройте гибридный поиск в Pinecone с режимом индексации sparse-dense и в pgvector с помощью параллельных запросов и объединения через RRF, сравнив качество поиска на своём наборе данных.
«Гибридный поиск в Pinecone и pgvector» — бесплатный урок AI Engineering Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Engineering Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Engineering Academy содержит 4 уроков всего.
Нативный гибридный поиск в векторных базах данных
Хотя гибридный поиск можно реализовать самостоятельно с помощью двух отдельных индексов и объединения RRF, современные векторные базы данных всё чаще предлагают встроенный гибридный поиск, который обрабатывает разреженный и плотный поиск в рамках одного запроса. Pinecone и pgvector поддерживают гибридные режимы, но используют разные архитектурные решения и имеют разные компромиссы. Понимание обоих вариантов поможет Вам выбрать подходящий инструмент для Вашей инфраструктуры.
Режим разреженно-плотного индекса Pinecone
Pinecone поддерживает разреженно-плотный индекс, в котором каждая векторная запись хранит как плотное встраивание (в виде массива чисел с плавающей точкой), так и разреженный вектор (в виде словаря, сопоставляющего идентификатор токена и вес). Запросы могут задавать одновременно плотный и разреженный векторы запроса, а Pinecone объединяет результаты с помощью взвешенной линейной комбинации. Это отличается от RRF: Pinecone объединяет исходные оценки с настраиваемыми весами, называемыми alpha.
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone(api_key='YOUR_API_KEY')
# Create a sparse-dense index
pc.create_index(
name='hybrid-index',
dimension=1536, # dense vector dimension
metric='dotproduct', # must use dotproduct for hybrid
spec=ServerlessSpec(cloud='aws', region='us-east-1'),
)
index = pc.Index('hybrid-index')Генерация разреженных векторов с помощью SPLADE
Чтобы использовать разреженно-плотный режим Pinecone, необходимо создать разреженные векторы для каждого документа. Наиболее эффективный подход — SPLADE (Sparse Lexical and Expansion), нейросетевая модель, создающая обученные разреженные представления с расширением: она добавляет в разреженный вектор семантически связанные термины, которых буквально нет в тексте. В качестве альтернативы можно использовать простые веса терминов BM25 как разреженные векторы.
from transformers import AutoTokenizer, AutoModelForMaskedLM
import torch
# Load SPLADE model
tokenizer = AutoTokenizer.from_pretrained('naver/splade-cocondenser-ensembledistil')
model = AutoModelForMaskedLM.from_pretrained('naver/splade-cocondenser-ensembledistil')
def generate_sparse_vector(text: str) -> dict:
tokens = tokenizer(text, return_tensors='pt', truncation=True, max_length=512)
with torch.no_grad():
output = model(**tokens)
logits = output.logits
# Max-pool over sequence length and apply log1p activation
sparse = torch.max(torch.log1p(torch.relu(logits)), dim=1).values.squeeze()
# Return non-zero indices and values as a sparse dict
nz_indices = sparse.nonzero().squeeze().tolist()
nz_values = sparse[nz_indices].tolist()
return {'indices': nz_indices, 'values': nz_values}Загрузка гибридных векторов в Pinecone
Каждая запись Pinecone в гибридном индексе хранит id, плотный массив values, словарь sparse_values с полями indices и values, а также необязательные metadata. Загружайте записи пакетами по 100 элементов, чтобы добиться максимальной пропускной способности и не превысить ограничения Pinecone на размер запроса.
def upsert_hybrid_docs(index, documents: list[dict], batch_size: int = 100):
records = []
for doc in documents:
dense_vec = embed(doc['text']) # OpenAI embedding
sparse_vec = generate_sparse_vector(doc['text']) # SPLADE
records.append({
'id': doc['id'],
'values': dense_vec,
'sparse_values': sparse_vec,
'metadata': {'text': doc['text'], 'source': doc['source']},
})
for i in range(0, len(records), batch_size):
batch = records[i:i + batch_size]
index.upsert(vectors=batch)
print(f'Upserted batch {i//batch_size + 1}')Запрос к гибридному индексу Pinecone
Гибридный запрос передаёт в API запросов Pinecone одновременно плотный vector и sparse_vector. Параметр alpha (от 0 до 1) управляет соотношением: alpha=1.0 означает полностью плотный поиск, alpha=0.0 — полностью разреженный, а alpha=0.5 задаёт одинаковый вес обоим вариантам. Настраивайте alpha на проверочном наборе: типичные оптимальные значения находятся между 0.3 и 0.7.
def hybrid_query_pinecone(index, query: str, alpha: float = 0.5, top_k: int = 5):
dense_vec = embed(query)
sparse_vec = generate_sparse_vector(query)
# Scale vectors by alpha for weighted fusion
scaled_dense = [v * alpha for v in dense_vec]
scaled_sparse = {
'indices': sparse_vec['indices'],
'values': [v * (1 - alpha) for v in sparse_vec['values']],
}
results = index.query(
vector=scaled_dense,
sparse_vector=scaled_sparse,
top_k=top_k,
include_metadata=True,
)
return results.matchespgvector: векторный поиск в PostgreSQL
Расширение pgvector добавляет в PostgreSQL тип столбца vector и операторы расстояния. Для гибридного поиска выполните параллельно два запроса — запрос приблизительного поиска ближайших соседей по векторному столбцу и полнотекстовый поисковый запрос с использованием встроенных в PostgreSQL tsvector и tsquery, — а затем объедините результаты с помощью RRF в коде приложения.
-- Setup: enable extensions
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- Create hybrid-capable table
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
embedding vector(1536),
content_tsv tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED
);
-- Indexes for both search modes
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
CREATE INDEX ON documents USING GIN (content_tsv);Выполнение плотных и разреженных запросов в pgvector
В pgvector выполняйте плотный запрос с помощью оператора косинусного расстояния <=>, чтобы найти ближайших соседей для встраивания запроса, а разреженный запрос — с помощью ts_rank_cd для оценки совпадений ключевых слов со столбцом tsvector. Получите оба набора результатов независимо, а затем объедините их с помощью RRF в Python.
import psycopg2
import json
def pgvector_hybrid_search(conn, query: str, top_k: int = 5):
dense_vec = embed(query)
tsquery = ' & '.join(query.split()) # simple AND query
# Dense query
dense_sql = '''
SELECT id, content, 1 - (embedding <=> %s::vector) AS score
FROM documents
ORDER BY embedding <=> %s::vector
LIMIT 20
'''
# Sparse query
sparse_sql = '''
SELECT id, content, ts_rank_cd(content_tsv, to_tsquery('english', %s)) AS score
FROM documents
WHERE content_tsv @@ to_tsquery('english', %s)
ORDER BY score DESC
LIMIT 20
'''
with conn.cursor() as cur:
cur.execute(dense_sql, [json.dumps(dense_vec), json.dumps(dense_vec)])
dense_rows = cur.fetchall()
cur.execute(sparse_sql, [tsquery, tsquery])
sparse_rows = cur.fetchall()
return fuse_with_rrf(dense_rows, sparse_rows, top_k)Применение RRF к результатам pgvector
Собрав ранжированные строки из обоих запросов PostgreSQL, примените RRF: извлеките идентификаторы документов в порядке их ранжирования из каждого набора результатов и запустите алгоритм объединения. Итоговый объединённый список содержит документы с верхними K позициями, наиболее релевантные одновременно по смыслу и по совпадению ключевых слов.
def fuse_with_rrf(dense_rows, sparse_rows, top_k: int = 5, k: int = 60):
from collections import defaultdict
doc_texts = {}
scores = defaultdict(float)
for rank, row in enumerate(dense_rows, start=1):
doc_id, text, _ = row
scores[doc_id] += 1.0 / (k + rank)
doc_texts[doc_id] = text
for rank, row in enumerate(sparse_rows, start=1):
doc_id, text, _ = row
scores[doc_id] += 1.0 / (k + rank)
doc_texts[doc_id] = text
ranked = sorted(scores.items(), key=lambda x: x[1], reverse=True)
return [{'id': doc_id, 'text': doc_texts[doc_id], 'rrf_score': s}
for doc_id, s in ranked[:top_k]]Оценка качества поиска
После реализации гибридного поиска строго оцените его качество в сравнении с базовым поиском только по плотным векторам. Используйте эталонный набор тестов как минимум из 100 запросов с известными релевантными документами. Измерьте NDCG@5, MRR и долю попаданий@3. Типичное улучшение от гибридного поиска составляет от 5 до 20 процентов по NDCG@5; наибольший прирост наблюдается для запросов с точными техническими терминами, которые плотные модели пропускают.
def evaluate_retrieval(search_fn, test_queries, golden_relevant, k=5):
ndcg_scores = []
hit_count = 0
for query, relevant_ids in zip(test_queries, golden_relevant):
results = search_fn(query, top_k=k)
retrieved_ids = [r['id'] for r in results]
# Hit rate
if any(rid in relevant_ids for rid in retrieved_ids):
hit_count += 1
# Simplified NDCG (binary relevance)
dcg = sum(
1.0 / (i + 1)
for i, rid in enumerate(retrieved_ids)
if rid in relevant_ids
)
idcg = sum(1.0 / (i + 1) for i in range(min(len(relevant_ids), k)))
ndcg_scores.append(dcg / idcg if idcg > 0 else 0)
return {
'hit_rate': hit_count / len(test_queries),
'ndcg': sum(ndcg_scores) / len(ndcg_scores),
}Настройка Alpha в гибридном режиме Pinecone
Параметр alpha в гибридном запросе Pinecone требует систематической настройки. Полезный подход — стратификация по типу запроса: разделите тестовые запросы на преимущественно семантические (перефразирования, концептуальные запросы) и преимущественно лексические (точные термины, коды), а затем отдельно измерьте оптимальное значение alpha для каждой группы. В некоторых промышленных системах реализован динамический выбор alpha: система классифицирует запрос во время выполнения и автоматически выбирает подходящее значение alpha.
def find_optimal_alpha(index, test_queries, golden_relevant, alphas=None):
if alphas is None:
alphas = [0.0, 0.2, 0.4, 0.5, 0.6, 0.8, 1.0]
best_alpha, best_score = 0.5, 0
for alpha in alphas:
hits = 0
for query, relevant in zip(test_queries, golden_relevant):
results = hybrid_query_pinecone(index, query, alpha=alpha, top_k=5)
if any(r['id'] in relevant for r in results):
hits += 1
hit_rate = hits / len(test_queries)
print(f'alpha={alpha}: hit_rate={hit_rate:.3f}')
if hit_rate > best_score:
best_score, best_alpha = hit_rate, alpha
return best_alphaВыбор между гибридными Pinecone и pgvector
Выбирайте гибридный Pinecone, если Вам нужна управляемая инфраструктура и автоматическое масштабирование, а также если Вы хотите передать создание разреженных векторов одной службе через единый API. Выбирайте гибридный pgvector, если у Вас уже есть инфраструктура PostgreSQL, необходима транзакционная согласованность документов и их метаданных, нужна полная гибкость SQL для фильтрации или требуется избежать привязки к поставщику. Оба подхода обеспечивают отличное качество гибридного поиска при правильной настройке.
Быстрая проверка
Проверьте, насколько хорошо Вы поняли реализацию гибридного поиска из этого урока.
Итоги урока
В этом уроке Вы узнали, что разреженно-плотный режим Pinecone хранит плотный и разреженный векторы для каждой записи и объединяет их с помощью настраиваемого параметра alpha, гибридный поиск pgvector сочетает полнотекстовый поиск SQL с векторными запросами, объединяемыми с помощью RRF в коде приложения, а настройка alpha на проверочном наборе необходима для поиска оптимального баланса с учётом распределения Ваших запросов. Далее мы рассмотрим двухэтапный поиск с повторным ранжированием.
Изучай Python с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 30
- Уроки
- 120
Часто задаваемые вопросы
Урок «Гибридный поиск в Pinecone и pgvector» бесплатный?
Да — полный текст урока «Гибридный поиск в Pinecone и pgvector» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Engineering Academy, подпишись на CoddyKit PRO. Курс AI Engineering Academy содержит 4 уроков всего.
Чему я научусь в уроке «Гибридный поиск в Pinecone и pgvector»?
Настройте гибридный поиск в Pinecone с режимом индексации sparse-dense и в pgvector с помощью параллельных запросов и объединения через RRF, сравнив качество поиска на своём наборе данных. Ты практикуешь AI Engineering Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Engineering Academy?
Предыдущий опыт не требуется. AI Engineering Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Гибридный поиск в Pinecone и pgvector»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Engineering Academy?
Да. Каждый урок AI Engineering Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Плотный и разреженный поиск: компромиссы
- Реализация поиска по ключевым словам BM25
- Объединение оценок с помощью слияния обратных рангов
- Гибридный поиск в Pinecone и pgvector