AI Engineering Academy · Lektion

Hybrid sökning i Pinecone och pgvector

Konfigurera hybrid sökning i Pinecone med sparse-dense index mode och i pgvector med parallella frågor och RRF-sammanslagning, och jämför kvaliteten på informationshämtningen i ert dataset.

Lektion 4 av 413 steg

Hybrid sökning i Pinecone och pgvector är en gratis lektion i AI Engineering Academy på CoddyKit. Detta är lektion 4 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för AI Engineering Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i AI Engineering Academy innehåller totalt 4 lektioner.

Inbyggd hybridsökning i vektordatabaser

Även om Ni kan implementera hybridsökning själv med två separata index och RRF-fusion erbjuder produktionsinriktade vektordatabaser i allt högre grad inbyggd hybridsökning som hanterar gles och tät hämtning i en enda fråga. Både Pinecone och pgvector stöder hybridlägen, men med olika arkitekturval och avvägningar. Genom att förstå båda alternativen kan Ni välja rätt verktyg för Er infrastruktur.

Pinecones glesa och täta indexläge

Pinecone stöder ett gles-tätt index där varje vektorpost lagrar både en tät embedding (som en flyttalsarray) och en gles vektor (som en ordlista från token-ID till vikt). Frågor kan ange både en tät frågevektor och en gles frågevektor, och Pinecone slår ihop resultaten med en viktad linjär kombination. Detta skiljer sig från RRF – Pinecone använder fusion av råpoäng med konfigurerbara vikter som kallas 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')

Generera glesa vektorer med SPLADE

För att använda Pinecones gles-täta läge behöver Ni generera glesa vektorer för varje dokument. Den mest effektiva metoden är SPLADE (Sparse Lexical and Expansion), en neural modell som producerar inlärda glesa representationer med expansion – den lägger till semantiskt relaterade termer i den glesa vektorn utöver det som bokstavligen förekommer i texten. Alternativt kan Ni använda enkla BM25-termvikter som glesa vektorer.

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}

Upserta hybridvektorer till Pinecone

Varje Pinecone-post i ett hybridindex lagrar en id, en tät values-array, en sparse_values-ordlista med indices och values samt valfria metadata. Upserta poster i batchar om 100 för att maximera genomströmningen och samtidigt hålla Er inom Pinecones gränser för begäransstorlek.

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}')

Fråga Pinecones hybridindex

En hybridfråga skickar både en tät vector och en sparse_vector till Pinecones fråge-API. Parametern alpha (0 till 1) styr balansen: alpha=1.0 är helt tät, alpha=0.0 är helt gles och alpha=0.5 ger båda samma vikt. Ställ in alpha på en valideringsmängd – typiska optimala värden ligger mellan 0.3 och 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: vektorsökning i PostgreSQL

Tillägget pgvector lägger till kolumntypen vector och avståndsoperatorer i PostgreSQL. För hybridsökning kör Ni två frågor parallellt – en fråga om approximerade närmaste grannar på vektorkolumnen och en fulltextsökningsfråga med PostgreSQLs inbyggda tsvector och tsquery – och slår sedan ihop resultaten med RRF i Er applikationskod.

-- 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);

Kör täta och glesa frågor i pgvector

Med pgvector kör Ni den täta frågan med cosinusavståndsoperatorn <=> för att hitta de närmaste grannarna till frågeembeddingens vektor, och den glesa frågan med ts_rank_cd mot kolumnen tsvector för att poängsätta nyckelordsträffar. Hämta båda resultatmängderna separat och fusionera dem sedan med RRF i 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)

Tillämpa RRF på pgvector-resultat

När Ni har samlat in rangordnade rader från båda PostgreSQL-frågorna tillämpar Ni RRF genom att extrahera dokument-ID:n i rangordning från varje resultatmängd och köra fusionsalgoritmen. Den slutliga sammanslagna listan ger Er de top-K-dokument som är mest relevanta både utifrån semantisk betydelse och överlappning av nyckelord.

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]]

Benchmarka hämtningens kvalitet

Efter att Ni har implementerat hybridsökning ska Ni benchmarka den noggrant mot Er baslinje med enbart tät hämtning. Använd en gyllene testmängd med minst 100 frågor och kända relevanta dokument. Mät NDCG@5, MRR och träfffrekvens@3. Typiska förbättringar med hybridsökning ligger mellan 5 och 20 procent för NDCG@5, med störst vinster för frågor som innehåller exakta tekniska termer som täta modeller missar.

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),
    }

Ställ in Alpha i Pinecones hybridläge

Alpha-parametern i Pinecones hybridfråga kräver systematisk inställning. Stratifiering efter frågetyp är en användbar metod: klassificera testfrågorna som huvudsakligen semantiska (omformuleringar, konceptuella frågor) eller huvudsakligen lexikala (exakta termer, koder), och mät optimal alpha separat för varje grupp. Vissa produktionssystem implementerar dynamiskt val av alpha, där frågan klassificeras vid körning och lämpligt alphavärde väljs automatiskt.

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

Välj mellan Pinecone och pgvector Hybrid

Använd Pinecone hybrid när Ni behöver hanterad infrastruktur, automatisk skalning och vill överlåta komplexiteten i genereringen av glesa vektorer till ett enda API. Använd pgvector hybrid när Ni redan har PostgreSQL-infrastruktur, behöver transaktionskonsistens mellan dokument och metadata, vill ha full flexibilitet med SQL för filtrering eller behöver undvika leverantörsinlåsning. Båda metoderna ger utmärkt kvalitet på hybrid hämtning när de ställs in korrekt.

Snabb kontroll

Testa Er förståelse av implementering av hybridsökning från den här lektionen.

Sammanfattning av lektionen

I den här lektionen har Ni lärt Er att: Pinecones gles-täta läge lagrar både täta och glesa vektorer per post och fusionerar dem med en konfigurerbar alphaparameter, pgvector-hybridsökning kombinerar SQL-fulltextsökning med vektorfrågor som fusioneras med RRF i applikationskoden, och att inställning av alpha på en valideringsmängd är avgörande för att hitta den optimala balansen för Er frågefördelning. Härnäst utforskar vi hämtning i två steg med omrangordning.

Gratis att börja

Lär dig Python med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
30
Lektioner
120

Vanliga frågor

Är lektionen ”Hybrid sökning i Pinecone och pgvector” gratis?

Ja – hela texten till ”Hybrid sökning i Pinecone och pgvector” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i AI Engineering Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i AI Engineering Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Hybrid sökning i Pinecone och pgvector”?

Konfigurera hybrid sökning i Pinecone med sparse-dense index mode och i pgvector med parallella frågor och RRF-sammanslagning, och jämför kvaliteten på informationshämtningen i ert dataset. Ni övar på AI Engineering Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig AI Engineering Academy?

Du behöver inga förkunskaper. Utbildningen i AI Engineering Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.

Hur lång tid tar lektionen ”Hybrid sökning i Pinecone och pgvector”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här AI Engineering Academy-lektionen?

Ja. Varje AI Engineering Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Tät kontra gles informationshämtning: avvägningar
  2. Implementera BM25-baserad nyckelordssökning
  3. Reciprocal Rank Fusion för sammanslagning av poäng
  4. Hybrid sökning i Pinecone och pgvector
← Tillbaka till AI Engineering Academy