AI Engineering Academy · Урок

Гибридный поиск в Pinecone и pgvector

Настройте гибридный поиск в Pinecone с режимом индексации sparse-dense и в pgvector с помощью параллельных запросов и объединения через RRF, сравнив качество поиска на своём наборе данных.

Урок 4 из 413 шагов

«Гибридный поиск в 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.matches

pgvector: векторный поиск в 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 — локальная установка не требуется.

Все уроки этого курса

  1. Плотный и разреженный поиск: компромиссы
  2. Реализация поиска по ключевым словам BM25
  3. Объединение оценок с помощью слияния обратных рангов
  4. Гибридный поиск в Pinecone и pgvector
← Назад к AI Engineering Academy