# Cómo diseñar una arquitectura RAG para producción sin alucinaciones

Aprendé a diseñar una arquitectura RAG para producción paso a paso: chunking, bases vectoriales, re-ranking y evaluación para evitar alucinaciones.

*rag · 2026-07-12 · [mauriciosneira.com](https://mauriciosneira.com/blog/rag-produccion-batalla-real)*

---

Querés armar un asistente para que tu equipo de soporte responda tickets con la documentación interna, pero el modelo inventa funciones que no existen. Databricks dice que los agentes multi-paso le ganan a RAG por 20 puntos en tareas complejas. Esos agentes razonan bien en varios pasos, pero una buena arquitectura rag sigue siendo la opción directa para mandar IA a producción hoy con datos propios y costos controlados. En este post te muestro cómo diseñar una arquitectura rag para producción, qué tradeoffs vas a enfrentar y cómo armar cada componente con el ejemplo de ese asistente de soporte.

## El mecanismo y la arquitectura rag: desarmando la magia

RAG le entrega al modelo información actual desde una base externa en vez de dejarlo responder sólo con su pre-entrenamiento, tal como propuso el paper original de [Lewis et al. en arXiv](https://arxiv.org/abs/2005.11401). El sistema reduce las alucinaciones porque ancla el contexto: le pedís al modelo que responda sólo con los fragmentos provistos.

El proceso tiene dos fases:

1. **Retrieval (Recuperación):** Tu consulta se convierte en un vector numérico (embedding) para buscar los fragmentos más relevantes en tu base. La calidad del embedding define si captura la similitud semántica con sinónimos o paráfrasis en tu dominio. La base vectorial aporta eficiencia: maneja el throughput (consultas por segundo), la latencia (tiempo de respuesta), el costo por query, la escala y la resiliencia.
2. **Generation (Generación):** El LLM recibe los fragmentos recuperados junto con la consulta original. Con ese contexto extendido, genera la respuesta anclada en tus datos reales.

## La columna vertebral: tu base de conocimiento y estrategias de chunking

Tu base de conocimiento no es una carpeta llena de PDFs. Requiere una estrategia clara de datos para procesar esa documentación de soporte.

**1. Chunking: el arte de cortar bien**

Dividir los documentos en fragmentos o chunks define la calidad del sistema. Si el chunk es enorme, metés ruido y diluís lo importante. Si es muy chico, perdés el contexto de la frase. Tenés que buscar un equilibrio donde cada bloque sea coherente por sí solo.

| Estrategia | Pros | Contras | Casos de Uso Ideales | Complejidad | Evaluación de Efectividad |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **Fijo con Overlap** | Simple de implementar, funciona con texto no estructurado | Puede cortar frases o ideas a la mitad, pierde contexto semántico | Blogs, artículos genéricos, texto plano | Baja | Recall de chunks, longitud promedio del contexto |
| **Por Estructura** | Preserva contexto semántico (sección, párrafo) | Más complejo para automatizar en contenido inconsistente | Documentación técnica, manuales, libros | Media | Context precision, faithfulness, human review |
| **Semántico** | Chunks basados en el significado, no en el tamaño | Requiere modelos de lenguaje para identificar límites, más lento | Conversaciones, resúmenes, documentos complejos | Alta | Context recall, coherencia semántica |
| **Recursivo** | Prueba diferentes tamaños hasta encontrar óptimo | Iterativo, puede ser lento para indexación masiva | Cualquier tipo de documento, adaptable | Media | Métricas Ragas (context recall, precision) |

```python
# Ejemplo de chunking con overlap (pseudocódigo en Python)
# Este es un punto de partida. En producción, considerá la semántica y la evaluación.

def chunk_document_fixed_overlap(text: str, chunk_size: int = 500, overlap_size: int = 100) -> list[str]:
    chunks = []
    start = 0
    while start < len(text):
        end = min(start + chunk_size, len(text))
        chunk = text[start:end]
        chunks.append(chunk)
        # Mover el inicio para el siguiente chunk, considerando el solapamiento
        start += (chunk_size - overlap_size)
        # Asegurar que no se cree un chunk vacío al final si el solapamiento es muy grande
        if start >= len(text) and len(chunks[-1]) < chunk_size // 2: # Heurística para evitar chunks residuales muy pequeños
            if len(chunks) > 1: # Si hay al menos un chunk anterior, unir el último con el anterior
                chunks[-2] = chunks[-2] + chunks.pop()
            else: # Si es el único chunk, dejarlo como está
                pass
    return chunks

# Para un chunking más avanzado, librerías como LangChain o LlamaIndex ofrecen RecursiveCharacterTextSplitter
# que es muy útil para probar diferentes separadores (\n\n, \n, ' ', '') y tamaños.
```

**2. La base de datos vectorial: ¿dónde guardo los embeddings?**

Para escalar a millones de documentos y sostener baja latencia, servicios como Pinecone o Weaviate resuelven varios frentes:
1. **Indexación distribuida:** dividen grandes volúmenes de datos entre varios nodos.
2. **Búsqueda ANN:** usan algoritmos de vecinos cercanos optimizados para alta dimensionalidad.
3. **Infraestructura gestionada:** resuelven alta disponibilidad y escalado sin sumar mantenimiento operativo.
4. **Tolerancia a fallos:** mantienen la continuidad del servicio ante caídas.

| Característica | pgvector (PostgreSQL) | Pinecone/Weaviate (Managed) | Chroma (Embeddings local) |
| :--- | :--- | :--- | :--- |
| **Complejidad** | Baja/Media (si ya usás PG) | Baja | Muy Baja |
| **Escalabilidad** | Media (depende del PG) | Alta (cloud nativo) | Baja (ideal para dev/POC) |
| **Costo** | Bajo/Medio (infra existente) | Medio/Alto (por uso) | Muy Bajo (solo almacenamiento) |
| **Control** | Alto | Bajo | Alto |
| **Latencia** | Media | Baja | Baja (para datasets chicos) |
| **Uso Típico** | Prototipos, datasets medios | Producción, big data | Desarrollo, testing, apps pequeñas |

## El cerebro del sistema: retrieval y re-ranking

Tener buenos chunks no alcanza si los recuperás mal. El retrieval necesita modelos de embedding precisos para el dominio de tus datos.

**1. Búsqueda inicial (Similarity Search):** buscás por cercanía vectorial con modelos como `all-MiniLM-L6-v2` o las opciones de OpenAI y Cohere. La elección balancea costo, velocidad y precisión.

**2. Re-ranking: filtrando el ruido para la precisión**

La primera búsqueda suele traer texto parecido que no responde la duda puntual. Un re-ranker ordena esos resultados con un modelo cross-encoder liviano:

* **Mecanismo:** toma la consulta del usuario junto con cada fragmento recuperado. Evalúa el par completo y calcula un puntaje de relevancia directo antes de pasárselo al LLM.
* **Tradeoff:** suma unos milisegundos de latencia, pero sube la precisión y evita que el modelo procese información irrelevante. En sistemas corporativos donde el dato exacto importa, este paso es indispensable.

## La voz: generación con LLMs y control de alucinaciones

Con los fragmentos reordenados, armás el prompt para el modelo.

**Ingeniería de prompt para RAG:**

Delimitá las instrucciones para controlar las respuestas:
1. **Instrucciones claras:** pedile que responda usando únicamente el contexto provisto y que avise si el dato no aparece.
2. **Contexto delimitado:** encerrá el texto en etiquetas como `<context>...</context>` para separar datos de instrucciones.
3. **Manejo de incertidumbre:** exigí que declare cuándo no sabe la respuesta para no perder confiabilidad.

```
Pregunta del usuario: {query}

Contexto:
<context>
{chunk1}
{chunk2}
{chunk3}
</context>

Basándote estrictamente en el contexto proporcionado, respondé la pregunta. Si la información no está disponible en el contexto, decí "No tengo suficiente información para responder a eso basándome en los documentos proporcionados."
```

**Tradeoffs del LLM:** podés usar un modelo de frontera costoso o uno abierto más chico, como vimos al comparar [modelos generalistas vs. especializados](/blog/llms-produccion-especializado-generalista). Para muchas consultas de soporte alcanza con un modelo mediano. Si hacés fine-tuning, lográs que respete el formato y use menos tokens por consulta, bajando costos y tiempos de espera.

## Orquestación: armar el rompecabezas en producción

Un RAG productivo funciona como un conjunto de microservicios coordinados.

```mermaid
graph TD
    A[Usuario/Aplicación] --> B(API Gateway/Load Balancer)
    B --> C{Servicio de Orquestación RAG}
    C --> D[Servicio de Embedding]
    C --> E[Servicio de Vector Store]
    C --> G[Servicio de Re-ranking]
    C --> H["Servicio LLM (OpenAI, HuggingFace, etc.)"]
    H --> I["Cache de Respuestas (Redis)"]
    I -- Cache Hit --> A
    H -- Cache Miss --> C
    C --> A

    subgraph Pipeline de Indexación
        J["Fuente de Datos (DB, S3, Web)"] --> K(Extracción y Limpieza)
        K --> L(Chunking Estratégico)
        L --> D
        D --> E
        E -- Metadata --> K
    end

    style C fill:#f9f,stroke:#333,stroke-width:2px
    style E fill:#f9f,stroke:#333,stroke-width:2px
```

Para operar esta arquitectura necesitás contemplar cuatro áreas:

* **Observabilidad:** guardá logs, métricas y trazas de latencia en cada paso, aplicando [distributed tracing con OpenTelemetry](/blog/distributed-tracing-agentes-opentelemetry) para ubicar cuellos de botella entre el vector store y el LLM. Podés medir alucinaciones con un segundo LLM evaluando la fidelidad (faithfulness) contra el contexto o mediante reglas heurísticas.
* **Escalabilidad:** controlá si la base vectorial aguanta la carga y si tu proveedor de LLM tiene límites de tasa (rate limits). Medí la calidad del embedding con benchmarks del dominio, recall de consultas reales o pruebas A/B.
* **Seguridad:** anonimizá datos personales (PII) antes de indexar, aplicá filtros por permisos de usuario sobre la metadata del chunk y asegurá el cifrado en tránsito y en reposo.
* **Pipeline de indexación:** armá un proceso ETL continuo que resuelva la idempotencia, la concurrencia y la actualización constante de los documentos.

## Un caso concreto: asistente de soporte para SaaS

Imaginá que sos el CTO de una empresa SaaS y necesitás un asistente interno para que el equipo de soporte resuelva tickets consultando manuales técnicos, FAQs y casos viejos.

**Solución RAG propuesta:**

1. **Fuentes de datos:** documentación interna (Confluence, Markdown), tickets resueltos (Zendesk, JIRA) y FAQs públicas.
2. **Pipeline de indexación:** un job diario procesa el texto, divide los manuales por sección y los tickets por respuesta con overlap. Genera vectores con `sentence-transformers` y los guarda en Pinecone con metadata (ID, fecha, tipo de documento y permisos).
3. **Servicio de RAG (FastAPI):** expone un endpoint `/ask` que ejecuta este flujo:
   1. Recibe la consulta del agente de soporte.
   2. Vectoriza la pregunta con el modelo de embeddings.
   3. Pide a Pinecone entre 10 y 20 chunks aplicando filtros según los permisos del usuario.
   4. Reordena los fragmentos con un cross-encoder como `cross-encoder/ms-marco-TinyBERT-L2-v2` y se queda con los 3 a 5 mejores.
   5. Envía la pregunta con los chunks a un modelo generalista de frontera o a un modelo mediano abierto con fine-tuning para soporte.
   6. Devuelve la respuesta al agente junto con los enlaces a los documentos fuente.

**Tradeoffs de este caso:**

* **Costo:** Pinecone y un modelo de frontera suben la factura mensual. Se justifica por el tiempo ganado por ticket, aunque podés arrancar con un modelo más económico.
* **Latencia:** el re-ranking y la inferencia tardan entre 1 y 3 segundos. Para un agente humano es un tiempo cómodo; para un chatbot en vivo con clientes puede sentirse lento.
* **Mantenimiento:** necesitás monitorear el pipeline ETL, evaluar el recall del embedding periódicamente con preguntas de prueba y verificar la fidelidad de las respuestas con validadores automáticos.

## Qué hacer el lunes: pasos accionables para tu primer RAG en producción

1. **Definí el caso de uso y tus KPIs:** marcá objetivos como bajar los tiempos de primera respuesta, subir la precisión del equipo de soporte o reducir alucinaciones.
2. **Empezá simple, pero pensando en grande:** arrancá con pgvector para validar rápido y proyectá la migración a Pinecone o Weaviate cuando crezca el volumen.
3. **Experimentá con chunking y embeddings:** probá distintos tamaños de bloque y solapamiento con librerías como Ragas, LlamaIndex o LangChain midiendo context precision y context recall.
4. **Implementá evals desde el día uno:** armá un set de prueba con preguntas reales y medí la fidelidad de las respuestas contra el contexto recuperado, sumando validaciones en tu pipeline como hacemos en [CI/CD para agentes de IA](/blog/arquitectura-evals-ci-cd-agentes).
    ```python
    # Pseudocódigo para evaluar 'Faithfulness' (ejemplo conceptual)
    def evaluate_faithfulness(question: str, context: str, answer: str, llm_evaluator) -> float:
        # Instruir a un LLM evaluador para que determine si cada afirmación en la 'answer'
        # está directamente soportada por el 'context'.
        prompt = f"""
        Evalúa la siguiente respuesta basada estrictamente en el contexto proporcionado.
        Para cada afirmación en la respuesta, indica si está soportada por el contexto (SÍ/NO).
        Si alguna afirmación no está soportada, la respuesta no es fiel.

        Pregunta: {question}
        Contexto: {context}
        Respuesta: {answer}

        Ejemplo de formato de salida: Soporte: SÍ/NO. Razonamiento: <explicación>
        """
        evaluation_result = llm_evaluator.generate(prompt)
        # Procesar evaluation_result para obtener una puntuación (e.g., % de afirmaciones soportadas)
        # En la práctica, se usarían librerías como Ragas o LlamaIndex para esto.
        return parse_faithfulness_score(evaluation_result)
    ```
5. **Considerá un re-ranker:** sumá un paso de reordenamiento para filtrar fragmentos irrelevantes, achicar el prompt y gastar menos en tokens.
6. **Planificá tu pipeline de indexación:** programá un ETL que actualice documentos modificados y borre los obsoletos de tu base vectorial.
7. **No olvides la observabilidad y la seguridad:** agregá métricas de latencia por paso, protegé los accesos a nivel de chunk y cifrá la información confidencial.
