Integrar Retrieval-Augmented Generation (RAG) basado en PostgreSQL y pgvector permite incorporar conocimiento propio a modelos de IA generativa. El flujo típico —indexar textos, recuperar embeddings, consultar con LLM— parece trivial en entorno demo, pero la fricción real llega con miles o millones de consultas, latencias constantes de sub-600ms, y datasets con cientos de millones de tokens.
En este artículo veremos cómo llevamos RAG en producción usando chunking semántico y rerank avanzado con OpenAI y Anthropic sobre pgvector, optimizando costes, throughput y calidad para cargas reales de negocio.
Arquitectura RAG sobre pgvector: mínimo necesario para escalar
Stack y patrones de integración
- Backend en Node.js (TypeScript 5.4) o Python (FastAPI 0.110+, asyncio) para servir queries y orquestar pipelines.
- PostgreSQL Aurora con extensión pgvector 0.7+ para el almacenamiento de embeddings y metadatos.
- Embeddings generados con OpenAI
text-embedding-3-large (1536 dims)o Anthropicclaude-3-embeddings-v1. - Redis (Upstash) opcional para caching semántico y control de consultas repetidas.
- AWS Lambda y Vercel Functions para ejecución sin servidor y autoescalado crítico.
«Una consulta típica RAG a 10 documentos candidatos consume ~0,0025$ en embeddings y LLM (OpenAI), pero cada 100ms extra y cada consulta fallida suma en costes de soporte y frustración»
Diagrama simplificado del pipeline
- Usuario realiza una consulta (prompt).
- El backend genera embedding del prompt.
- Consulta a
SELECT ... ORDER BY embedding <=> $embedding LIMIT ken pgvector para k candidatos. - Opcional: rerank semántico en backend o con LLM.
- Prompt final a LLM (OpenAI, Anthropic) con contexto top-N.
Fragmento real de consulta con pgvector
WITH query_embedding AS (
SELECT '[[embedding vector aquí]]'::vector(1536) AS embedding
)
SELECT id, content, metadata, embedding <=> query_embedding.embedding AS distance
FROM documents, query_embedding
WHERE metadata->>'status' = 'active'
ORDER BY embedding <=> query_embedding.embedding
LIMIT 10;
Chunking semántico en la indexación: errores típicos y patrones que escalan
¿Por qué importa el chunking?
- Chunks demasiado pequeños (ej. 100-200 tokens): contexto poco relevante, incremento de queries, más coste en prompt.
- Chunks grandes (>1.500 tokens): embeddings diluidos, latencias altas en postgres, pérdida de granularidad.
| Chunk size | Métrica | Ventaja | Problema típico |
|---|---|---|---|
| 100-200 tokens | ~96% recall@10 | Rápido, cheap | Contexto pobre |
| 400-600 tokens | ~91% precision@3 | Equilibrio memoria/calidad | Prompt largo si top-k grande |
| 1500-2000 tokens | ~70% recall@10 | Menos queries | Latencia y embeddings peores |
Implementar chunking semántico en Python
from nltk.tokenize import sent_tokenize
from typing import List
MAX_TOKENS = 512
def chunk_text_semantic(text: str) -> List[str]:
sentences = sent_tokenize(text)
chunks = []
buffer = []
tokens = 0
for sentence in sentences:
sentence_tokens = len(sentence.split())
if tokens + sentence_tokens > MAX_TOKENS:
chunks.append(' '.join(buffer))
buffer = [sentence]
tokens = sentence_tokens
else:
buffer.append(sentence)
tokens += sentence_tokens
if buffer:
chunks.append(' '.join(buffer))
return chunks
- La variabilidad semántica se maximiza usando puntuación y entidades como delimitadores, no cortes ciegos por token.
- Nuestros experimentos muestran reducción de 12% en queries fallidas a LLM ajustando de 200 a 500 tokens por chunk.
Rerank: cuándo y cómo aplicar reranqueado embebido y con LLM
¿Por qué rerank?
- PostgreSQL/pgvector ofrece ANN exacto o aproximado, pero ignora matices sintácticos y órdenes lógicos.
- El rerank mejora la relevancia real según el usuario y permite filtrar «falsos positivos semánticos».
Opciones de rerank aplicadas en Agente 404
| Método | Coste consulta | Latencia media | Precisión top-3 |
|---|---|---|---|
| Solo embedding | ~0,00002$ | 30ms | 72% |
| Rerank ML (bm25+ o reranker MiniLM) | ~0,00012$ | 110ms | 81% |
| LLM rerank (OpenAI v4) | ~0,00110$ | 620ms | 93% |
import { Configuration, OpenAIApi } from 'openai';
const openai = new OpenAIApi(new Configuration({ apiKey: process.env.OPENAI_API_KEY }));
async function rerankWithLLM(query: string, docs: Array<{id: string, content: string}>): Promise<string[]> {
const prompt = `Dado la consulta: "${query}", ordénalos por relevancia:\n` +
docs.map((d, i) => `${i+1}. ${d.content}`).join('\n');
const res = await openai.createChatCompletion({
model: 'gpt-4-1106-preview',
messages: [{ role: 'user', content: prompt }],
functions: [
{
name: "rank_documents",
parameters: {
type: "object",
properties: {
order: {
type: "array",
items: { type: "string" }
}
},
required: ["order"]
}
}
],
function_call: { name: 'rank_documents' },
temperature: 0.0,
max_tokens: 80
});
return res.data.choices[0].message.function_call.arguments.order;
}
- Restringimos el rerank LLM a los top-7 chunks para controlar el coste (<0,0015$/query rondando 500 tokens totales).
- Alternar entre embeddings-only o rerank LLM puede hacerse con threshold sobre score, ajustando precisión y presupuesto.
Optimización de consultas de alto volumen: claves prácticas
- Batch embeddings en backend: cada llamada a
embeddingAPI ahorra hasta 70% de latencia agrupando 10-20 queries (< 320ms OpenAI, 16ms/embedding). - Caching semántico: Redis Upstash reduce hasta un 38% el tráfico a LLM en queries repetitivas, con hit rate >70% en flujos internos.
- CTEs para filtrado y logging: añade trazabilidad y filtros de permisos en SQL en una sola consulta.
- Colas y reintentos: integración con AWS SQS o Upstash Queue, backoff exponencial de hasta 5 reintentos en errores 429/5xx.
WITH context AS (
SELECT id, content, embedding <=> $embedding AS dist
FROM documents
WHERE org_id = $org AND published = TRUE
ORDER BY embedding <=> $embedding
LIMIT 15
),
filtered AS (
SELECT * FROM context WHERE dist < 0.35
)
INSERT INTO rag_queries(query, docs, org_id, ts)
SELECT $query, array_agg(id), $org, NOW()
FROM filtered;
- Con currencía de 50+ queries/s y sizing correcto de Aurora (r6g.xlarge), mantenemos P95 < 170ms en retrieval incluyendo CTE.
Evaluación: LLM-as-judge y métricas objetivas en producción
Evaluar relevance en pipelines reales
- Recurrimos a LLM-as-judge con prompts que piden «score relevance 1-5» y explicaciones de error para 2000+ queries de test cada semana.
- Automatizamos evals con OpenAI SDK v4.77+ y script de logging continuo a S3/Aurora con anomalías >25% detectadas vía SQL y alertadas por Slack/GitHub.
import openai
async def llm_judge_eval(query, candidates, answer):
prompt = f"""
Consulta de usuario: {query}
Contexto proporcionado: {candidates}
Respuesta LLM: {answer}
Evalúa la relevancia del contexto (1-5) y justifica.
"""
completion = await openai.ChatCompletion.acreate(
model="gpt-4-1106-preview",
messages=[{"role": "user", "content": prompt}],
max_tokens=120,
temperature=0
)
return completion.choices[0].message.content
| Estrategia evaluación | Automatización | Cobertura | Insourcing necesario |
|---|---|---|---|
| Annotadores humanos | No | <25% | ~120h/mes |
| LLM-as-judge | Sí | >95% | <5h/mes |
Insight para CTO
«Automatizar la evaluación RAG con LLM reduce necesidad de insourcing hasta 40x, sacrificando < 8 puntos de correlación humana»
Impacto en la operación: cifras y riesgos de no optimizar
- Reducir el chunk size a límites óptimos (400-600 tokens) recorta prompts de LLM un 33%, baja latencias pico de >1.4s a ~700ms y reduce coste de OpenAI en ~28% (casos reales retail y seguros).
- Caching semántico y rerank solo LLM en top-5 ha bajado gasto mensual de inferencia de 960€ a 620€ en clientes con >50.000 queries/mes.
- Sin evals automatizadas con LLM, la detección de regresión de calidad llevaba semanas; ahora, minutos (< 5 min MTTR secuencia incidentes-contexto mal recuperado).
- Requisitos directos para CTO/director IT: escalar a 200QPS sin pérdidas de precisión, costes controlados bajo 0,005€/query, trazabilidad para auditoría y supervisión.
«Detectar errores de chunking o rerank tardíamente puede dilapidar hasta 2 FTEs mensuales en soporte/QA manual»
En Agente 404 implementamos diagnósticos RAG con pruebas de chunking, benchmarking y trazabilidad para sistemas sobre pgvector: encajando cifra, patrón y vínculo técnico-negocio. Consúltanos si necesitas un assessment de RAG en producción.
Te resulto util?
Compartelo con quien pueda necesitarlo



