eduardweb.
RAG & EmbeddingsAvansat#rag#ai#postgres#pgvector#search

RAG pe bune în Postgres: Cum am combinat pgvector și FTS pentru căutare hibridă

De Ana Ionescu, 22 iun. 2026 · 21 vizualizări · 3 like-uri

Postat 22 iun. 2026
sql
-- Căutare hibridă (FTS + pgvector) cu scor ponderat
WITH semantic_search AS (
  SELECT id, 1 - (embedding <=> $1) AS similarity_score
  FROM documents
  ORDER BY embedding <=> $1 LIMIT 50
),
keyword_search AS (
  SELECT id, ts_rank_cd(fts_vector, plainto_tsquery('romanian', $2)) AS keyword_score
  FROM documents
  WHERE fts_vector @@ plainto_tsquery('romanian', $2)
  LIMIT 50
)
SELECT 
  coalesce(s.id, k.id) AS id,
  (coalesce(s.similarity_score, 0) * 0.7) + (coalesce(k.keyword_score, 0) * 0.3) AS hybrid_score
FROM semantic_search s
FULL OUTER JOIN keyword_search k ON s.id = k.id
ORDER BY hybrid_score DESC LIMIT 10;

Am mutat recent un sistem de RAG cu vreo 120.000 de documente tehnice de pe Pinecone înapoi în Postgres, folosind pgvector. Pe lângă faptul că am redus costurile lunare cu aproape 400 de dolari, am scăpat de coșmarul sincronizării datelor între DB-ul principal și cel de vectori. Postgres e deja acolo, e stabil și, cu indexul potrivit, se mișcă incredibil de rapid.

De ce pgvector și unde se împiedică

Să fim sinceri: pgvector nu e la fel de rapid out-of-the-box ca o bază de date dedicată (cum e Qdrant sau Milvus) când ai zeci de milioane de vectori. Dar pentru majoritatea proiectelor de la noi, care rar trec de câteva sute de mii de rânduri, e mai mult decât suficient.

La început am folosit indexul IVFFlat, dar la update-uri masive trebuia reconstruit des, altfel scădea precizia căutării. Am trecut pe HNSW (Hierarchical Navigable Small World). Construcția indexului HNSW durează mai mult și mănâncă considerabil mai multă memorie RAM, dar viteza de query la 150.000 de vectori de 1536 de dimensiuni (text-embedding-3-small) a scăzut sub 15ms.

Când creezi indexul HNSW, ai doi parametri mari de reglat: m (numărul maxim de conexiuni bidirecționale pe nod) și ef_construction (dimensiunea listei de candidați evaluată în timpul construcției). Noi am mers pe m = 16 și ef_construction = 64. Dacă le pui prea mari, timpul de build explodează, dar dacă le pui prea mici, pierzi din recall (acuratețe). Am pățit să avem recall de doar 82% pe setul de test până nu am crescut ef_search la interogare la valoarea de 40.

Când vectorii dau chix: Nevoia de Hybrid Search

Dacă ai un sistem RAG pur semantic, o să te lovești rapid de o problemă frustrantă. Utilizatorul caută un cod de produs specific (ex: "X-900-T") sau o denumire exactă de medicament. Embeddings-urile sunt excelente la context și sinonime, dar adesea ratează potrivirile exacte de caractere.

Aici intervine căutarea hibridă. Combinăm puterea semantică a vectorilor cu clasicul text search (FTS / BM25 din Postgres). Postgres are deja suport excelent pentru Full Text Search, așa că nu avem nevoie de Elasticsearch.

Implementarea hibridă cu ponderi simple

În loc să ne complicăm cu Reciprocal Rank Fusion (RRF) pur, care e greu de scris curat în SQL fără extensii ciudate, am mers pe o abordare cu scoruri ponderate normalizate. Normalizăm scorul de similaritate cosinus (care e între 0 și 1) și scorul de text search (folosind ts_rank_cd), apoi le adunăm cu ponderi ajustate experimental (de obicei 70% vector, 30% text).

Trade-off-ul principal? Trebuie să fii foarte atent la resursele serverului de Postgres. Indexul HNSW stă complet în memorie pentru a fi rapid. Dacă baza ta de date rulează pe o instanță mică cu 2GB RAM și ai mulți vectori, o să înceapă să facă swap pe disc și performanța se prăbușește.

Voi cum gestionați căutarea hibridă? Ați rămas pe Postgres sau ați migrat spre soluții dedicate când a crescut volumul de date?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.