effidevFlutter · Edge de Cloudflare · Optimización de costes en la nube
Español

pgvector vs Cloudflare Vectorize: Búsqueda Híbrida RAG e Implementación de Optimización de Latencia

pgvector vs Cloudflare Vectorize: Búsqueda Híbrida RAG e Implementación de Optimización de Latencia

Uno de los errores más comunes al construir un sistema de Generación Aumentada por Recuperación (RAG) es confiar únicamente en la búsqueda vectorial para recuperar todos los documentos. Cuando los usuarios buscan códigos de error, IDs de pedido o nombres de productos específicos, la similitud de coseno suele devolver documentos semánticamente parecidos pero incorrectos. Para solucionar esto, es fundamental la Búsqueda Híbrida (combinando búsqueda léxica BM25 con búsqueda vectorial).

¿Dónde conviene alojar el motor de búsqueda híbrida? Usar pgvector en una base de datos PostgreSQL centralizada versus Cloudflare Vectorize en el Edge ofrece resultados totalmente opuestos en términos de latencia, precisión y complejidad operativa.

Este artículo compara la arquitectura de pgvector y Cloudflare Vectorize, mide sus latencias (Edge vs DB Central), ofrece una implementación completa del algoritmo Reciprocal Rank Fusion (RRF) en TypeScript y presenta técnicas de optimización de latencia.

Resumen Clave

  • pgvector ofrece consistencia transaccional ACID y JOINs de SQL nativos entre datos relacionales y embeddings, pero los índices HNSW consumen mucha RAM y el viaje de ida y vuelta de red (RTT) añade de 30ms a 100ms de latencia.
  • Cloudflare Vectorize opera directamente en los PoPs del Edge junto a Workers y Workers AI, ofreciendo consultas vectoriales ultra rápidas (0-5ms), aunque el filtrado de metadatos es más limitado.
  • Combinar la búsqueda vectorial con búsqueda por palabras clave (BM25) mediante Reciprocal Rank Fusion (RRF) aumenta la precisión/cobertura de recuperación RAG en más del 25%.
  • Generar embeddings directamente en el Edge con Workers AI (@cf/baai/bge-small-en-v1.5) y aplicar una capa de caché KV es el patrón óptimo en producción.

Comparativa de Arquitectura: pgvector vs Cloudflare Vectorize

Característica pgvector (PostgreSQL Extension) Cloudflare Vectorize
Arquitectura Extensión RDBMS centralizada Índice vectorial distribuido en el Edge
Algoritmo de Índice IVFFlat, HNSW Motor HNSW distribuido personalizado
Latencia (RTT) 30ms – 150ms según la distancia Co-localizado con Worker: 2ms – 10ms
Filtrado de Metadatos Soporte completo SQL WHERE y JOIN Filtros Key-Value (eq, in, ne)
Dimensiones Máximas Hasta 16,000 dimensiones Hasta 1,536 dimensiones
Búsqueda de Palabras Clave DB única mediante tsvector o pg_trgm Requiere D1 (SQLite FTS5) o motor externo

Benchmarks de Latencia (p95)

En una prueba con 1.000.000 de vectores (1536 dimensiones), la latencia p95 de recuperación de contexto total fue:

Implementación de Reciprocal Rank Fusion (RRF) en TypeScript

export interface Env {
  VECTOR_INDEX: VectorizeIndex;
  DB: D1Database;
  AI: Ai;
}

function reciprocalRankFusion(
  vectorResults: Array<{ id: string }>,
  keywordResults: Array<{ id: string }>,
  k = 60
): Map<string, number> {
  const rrfScores = new Map<string, number>();

  vectorResults.forEach((doc, index) => {
    const rank = index + 1;
    const currentScore = rrfScores.get(doc.id) || 0;
    rrfScores.set(doc.id, currentScore + 1 / (k + rank));
  });

  keywordResults.forEach((doc, index) => {
    const rank = index + 1;
    const currentScore = rrfScores.get(doc.id) || 0;
    rrfScores.set(doc.id, currentScore + 1 / (k + rank));
  });

  return rrfScores;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const query = url.searchParams.get("q");

    if (!query) return new Response("Missing query 'q'", { status: 400 });

    const embeddingResponse = await env.AI.run("@cf/baai/bge-base-en-v1.5", {
      text: [query],
    });
    const queryVector = embeddingResponse.data[0];

    const vectorPromise = env.VECTOR_INDEX.query(queryVector, { topK: 20 });
    const keywordPromise = env.DB.prepare(
      `SELECT id FROM documents_fts WHERE content MATCH ? ORDER BY rank LIMIT 20`
    ).bind(query).all<{ id: string }>();

    const [vectorRes, keywordRes] = await Promise.all([vectorPromise, keywordPromise]);

    const vectorList = vectorRes.matches.map((m) => ({ id: m.id }));
    const keywordList = (keywordRes.results || []).map((r) => ({ id: r.id }));

    const rrfScores = reciprocalRankFusion(vectorList, keywordList, 60);
    const sortedDocs = Array.from(rrfScores.entries())
      .sort((a, b) => b[1] - a[1])
      .slice(0, 5);

    const docIds = sortedDocs.map(([id]) => id);
    if (docIds.length === 0) return Response.json({ results: [] });

    const placeholders = docIds.map(() => "?").join(",");
    const finalDocs = await env.DB.prepare(
      `SELECT id, title, content FROM documents WHERE id IN (${placeholders})`
    ).bind(...docIds).all();

    return Response.json({
      query,
      results: finalDocs.results,
      rrfScores: Object.fromEntries(sortedDocs)
    });
  },
};

Conclusión

Utiliza pgvector si ya cuentas con una infraestructura PostgreSQL y necesitas filtros relacionales complejos e interactivos con consistencia ACID. Elige Cloudflare Vectorize para construir chatbots RAG globales y serverless con latencias inferiores a los 50ms.