{"schemaVersion":1,"id":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation","title":"Arquitectura de Pipelines RAG: Estrategias de Chunking, Búsqueda Híbrida, Reranking y Frameworks de Evaluación","description":"Una guía orientada a producción para construir pipelines de Retrieval-Augmented Generation, desde la ingesta de documentos y estrategias de chunking hasta búsqueda híbrida, reranking con cross-encoders y evaluación sistemática con RAGAS y métricas propias.","language":"es","datePublished":"2026-04-16","dateModified":"2026-10-22","contentFormat":"text/markdown","content":"AI/ML\n\n# Arquitectura de Pipelines RAG: Estrategias de Chunking, Búsqueda Híbrida, Reranking y Frameworks de Evaluación\n\n2026-04-1616 min lectura\n\n[Lucio Durán](<https://lucioduran.com/es>)\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/rag-pipeline-architecture-chunking-reranking-evaluation>)\n\n## Por Qué la Mayoría de los Pipelines RAG Fallan\n\nEl tutorial RAG por defecto, dividir documentos, generar embeddings, recuperar top-k, insertar en el prompt, funciona para demos. Falla en producción por razones predecibles:\n\n- **El chunking destruye contexto.** Los cortes de tamaño fijo rompen oraciones a mitad de pensamiento, separan encabezados de su contenido y pierden la estructura del documento.\n- **La búsqueda vectorial pierde keywords.** Los embeddings capturan similitud semántica pero fallan en coincidencias exactas de entidades, acrónimos y terminología de dominio específica.\n- **La recuperación top-k es ruidosa.** Recuperar 5 chunks por similitud coseno frecuentemente devuelve 2-3 resultados irrelevantes que diluyen el contexto del prompt.\n- **Sin evaluación no hay iteración.** Sin métricas sistemáticas, los equipos optimizan por intuición y despliegan pipelines que se degradan silenciosamente.\n\nEste artículo cubre la arquitectura que aborda cada uno de estos modos de falla.\n\n## Ingesta y Preprocesamiento de Documentos\n\nAntes del chunking, el preprocesamiento de documentos determina la calidad del pipeline más de lo que la mayoría de los equipos creen.\n\n### Normalización de Formatos\n\nDiferentes formatos fuente requieren diferentes estrategias de extracción:\n\n```python\n# Extracción de PDF con detección de layout\nfrom unstructured.partition.pdf import partition_pdf\n\nelements = partition_pdf(\n    filename=\"report.pdf\",\n    strategy=\"hi_res\",           # OCR + detección de layout\n    infer_table_structure=True,  # Extraer tablas como HTML\n    extract_images_in_pdf=True,  # Extraer imágenes embebidas\n)\n\n# Separar tipos de elementos para procesamiento diferenciado\ntables = [e for e in elements if e.category == \"Table\"]\nnarratives = [e for e in elements if e.category == \"NarrativeText\"]\ntitles = [e for e in elements if e.category == \"Title\"]\n```\n\nDecisiones clave en esta etapa:\n\n- **Tablas**: Convertir a markdown o HTML, nunca dividir entre chunks. Un fragmento de tabla es peor que no tener tabla.\n- **Bloques de código**: Mantener intactos. Dividir la definición de una función entre chunks hace que ambas mitades sean inútiles.\n- **Imágenes**: Extraer captions y texto circundante. Para diagramas, considerar descripciones con modelos de visión.\n- **Encabezados**: Preservar la jerarquía, se convierten en metadata que mejora el retrieval.\n\n### Extracción de Metadata\n\nAdjuntar metadata en el momento de la ingesta, no después:\n\n```python\nchunk_metadata = {\n    \"source\": \"engineering-handbook-v3.pdf\",\n    \"section\": \"Capítulo 4: Operaciones de Base de Datos\",\n    \"page_numbers\": [42, 43],\n    \"document_type\": \"handbook\",\n    \"last_updated\": \"2026-03-15\",\n    \"audience\": \"backend-engineers\",\n}\n```\n\nEsta metadata habilita el retrieval filtrado, \"encontrar chunks sobre operaciones de base de datos del handbook de ingeniería\", lo cual mejora dramáticamente la precisión.\n\n## Estrategias de Chunking\n\nEl chunking es donde la mayoría de los pipelines pierden calidad silenciosamente. La estrategia correcta depende de la estructura del documento.\n\n### Chunking de Tamaño Fijo\n\nLa línea base. Dividir cada N tokens con M tokens de overlap.\n\n```python\nfrom langchain.text_splitter import RecursiveCharacterTextSplitter\n\nsplitter = RecursiveCharacterTextSplitter(\n    chunk_size=512,\n    chunk_overlap=50,\n    separators=[\"\\n\\n\", \"\\n\", \". \", \" \", \"\"],\n    length_function=len,  # o tiktoken para conteo preciso de tokens\n)\nchunks = splitter.split_text(document_text)\n```\n\n**Cuándo funciona**: Texto uniforme sin estructura, transcripciones, logs en texto plano, historiales de chat.\n\n**Cuándo falla**: Documentos estructurados donde dividir en posiciones arbitrarias destruye el significado.\n\n### Chunking Semántico\n\nAgrupa oraciones por similitud de embedding, dividiendo donde los temas cambian:\n\n```python\nfrom langchain_experimental.text_splitter import SemanticChunker\nfrom langchain_openai import OpenAIEmbeddings\n\nchunker = SemanticChunker(\n    OpenAIEmbeddings(model=\"text-embedding-3-small\"),\n    breakpoint_threshold_type=\"percentile\",\n    breakpoint_threshold_amount=85,  # Dividir en el percentil 85 de distancia\n)\nchunks = chunker.split_text(document_text)\n```\n\nEl chunking semántico produce chunks de tamaño variable que respetan los límites temáticos. El tradeoff es velocidad, generar embeddings de cada oración durante la ingesta es 10-50x más lento que la división por tamaño fijo.\n\n### Chunking Consciente de la Estructura del Documento\n\nEl enfoque más efectivo para contenido estructurado. Usa la jerarquía del documento para definir los límites de los chunks:\n\n```python\ndef structure_aware_chunk(elements, max_tokens=512):\n    \"\"\"Dividir por estructura del documento, respetando encabezados y secciones.\"\"\"\n    chunks = []\n    current_chunk = []\n    current_tokens = 0\n    current_header_chain = []\n\n    for element in elements:\n        if element.category == \"Title\":\n            # Nueva sección, volcar chunk actual\n            if current_chunk:\n                chunks.append({\n                    \"content\": \"\\n\".join(current_chunk),\n                    \"headers\": list(current_header_chain),\n                    \"token_count\": current_tokens,\n                })\n                current_chunk = []\n                current_tokens = 0\n            current_header_chain.append(element.text)\n\n        elif element.category == \"Table\":\n            # Las tablas van en su propio chunk, nunca se dividen\n            if current_chunk:\n                chunks.append({\n                    \"content\": \"\\n\".join(current_chunk),\n                    \"headers\": list(current_header_chain),\n                    \"token_count\": current_tokens,\n                })\n                current_chunk = []\n                current_tokens = 0\n            chunks.append({\n                \"content\": element.metadata.text_as_html,\n                \"headers\": list(current_header_chain),\n                \"token_count\": count_tokens(element.text),\n                \"type\": \"table\",\n            })\n\n        else:\n            elem_tokens = count_tokens(element.text)\n            if current_tokens + elem_tokens > max_tokens and current_chunk:\n                chunks.append({\n                    \"content\": \"\\n\".join(current_chunk),\n                    \"headers\": list(current_header_chain),\n                    \"token_count\": current_tokens,\n                })\n                current_chunk = []\n                current_tokens = 0\n            current_chunk.append(element.text)\n            current_tokens += elem_tokens\n\n    if current_chunk:\n        chunks.append({\n            \"content\": \"\\n\".join(current_chunk),\n            \"headers\": list(current_header_chain),\n            \"token_count\": current_tokens,\n        })\n\n    return chunks\n```\n\n### Encabezados Contextuales en los Chunks\n\nAnteponer la jerarquía de secciones antes del embedding es una hipótesis que debe evaluarse contra una referencia con las demás condiciones iguales:\n\n```\n## Capítulo 4: Operaciones de Base de Datos > Estrategias de Backup > Recuperación Point-in-Time\n\nPara realizar recuperación point-in-time, configurar el archivado continuo de WAL...\n```\n\nEl embedding ahora captura tanto el contenido como su posición en la jerarquía del documento.\n\n## Estrategia de Embeddings\n\n### Selección del Modelo\n\nEl modelo de embedding determina el techo de la calidad de retrieval.\n\n| Modelo | Dimensiones | MTEB Score | Latencia (p50) | Costo |\n| --- | --- | --- | --- | --- |\n| text-embedding-3-small | 1536 | 62.3 | 12ms | $0.02/1M tokens |\n| text-embedding-3-large | 3072 | 64.6 | 18ms | $0.13/1M tokens |\n| voyage-3 | 1024 | 67.1 | 15ms | $0.06/1M tokens |\n| bge-m3 (self-hosted) | 1024 | 66.8 | 8ms | Costo de infraestructura |\n| Cohere embed-v4 | 1024 | 67.5 | 14ms | $0.10/1M tokens |\n\n**Recomendación práctica**: Para workloads solo en inglés, ` voyage-3 ` o ` Cohere embed-v4 ` entregan la mejor calidad de retrieval. Para deployments multilingües o sensibles al costo, ` bge-m3 ` self-hosted en una sola GPU es difícil de superar.\n\n### Modelos de Interacción Tardía (ColBERT)\n\nColBERT produce embeddings por token en vez de un único vector, permitiendo matching más granular:\n\n```python\nfrom ragatouille import RAGPretrainedModel\n\nrag = RAGPretrainedModel.from_pretrained(\"colbert-ir/colbertv2.0\")\nrag.index(\n    collection=documents,\n    index_name=\"knowledge-base\",\n    max_document_length=512,\n)\n\nresults = rag.search(query=\"configuración de backup PITR\", k=10)\n```\n\nColBERT supera consistentemente a los modelos de vector único en calidad de retrieval pero requiere 10-50x más almacenamiento por documento. Usalo cuando la calidad de retrieval sea el cuello de botella y el almacenamiento sea barato.\n\n## Arquitectura de Búsqueda Híbrida\n\nLa búsqueda vectorial sola no es suficiente. Combinar búsqueda semántica y léxica cubre los puntos ciegos de cada método.\n\n### BM25 + Vector con Reciprocal Rank Fusion\n\n```python\nfrom rank_bm25 import BM25Okapi\nimport numpy as np\n\ndef hybrid_search(query, corpus_chunks, embeddings_index, k=20, alpha=0.5):\n    \"\"\"\n    Combinar BM25 y búsqueda vectorial usando Reciprocal Rank Fusion.\n    alpha controla el balance: 0.5 = peso igual.\n    \"\"\"\n    # Búsqueda vectorial\n    query_embedding = embed(query)\n    vector_scores = embeddings_index.search(query_embedding, k=k * 2)\n\n    # Búsqueda BM25\n    tokenized_corpus = [chunk.split() for chunk in corpus_chunks]\n    bm25 = BM25Okapi(tokenized_corpus)\n    bm25_scores = bm25.get_scores(query.split())\n    bm25_top = np.argsort(bm25_scores)[-k * 2:][::-1]\n\n    # Reciprocal Rank Fusion\n    rrf_scores = {}\n    rrf_k = 60  # Constante RRF estándar\n\n    for rank, doc_id in enumerate(vector_scores.ids):\n        rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + alpha / (rrf_k + rank + 1)\n\n    for rank, doc_id in enumerate(bm25_top):\n        rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + (1 - alpha) / (rrf_k + rank + 1)\n\n    # Ordenar por score fusionado\n    ranked = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)\n    return ranked[:k]\n```\n\n### Por Qué Funciona la Búsqueda Híbrida\n\nConsiderá la consulta: \"¿Cuál es el SLA del cluster Redis en EU-WEST-1?\"\n\n- **Búsqueda vectorial** encuentra chunks sobre SLAs y clusters Redis en general, semánticamente similares pero potencialmente de la región incorrecta.\n- **BM25** encuentra chunks que contienen la cadena exacta \"EU-WEST-1\", coincidencia léxica.\n- **Híbrida** surfacea el chunk que menciona tanto la política de SLA como la región específica.\n\n### Búsqueda Híbrida Nativa en Base de Datos\n\nPostgreSQL con pgvector 0.8+ soporta ambas en una sola consulta:\n\n```sql\n-- Búsqueda híbrida con pgvector + tsvector\nWITH vector_results AS (\n    SELECT id, content, 1 - (embedding <=> $1::vector) AS vector_score\n    FROM documents\n    WHERE metadata->>'department' = 'engineering'\n    ORDER BY embedding <=> $1::vector\n    LIMIT 40\n),\ntext_results AS (\n    SELECT id, content, ts_rank(search_vector, plainto_tsquery($2)) AS text_score\n    FROM documents\n    WHERE search_vector @@ plainto_tsquery($2)\n      AND metadata->>'department' = 'engineering'\n    ORDER BY text_score DESC\n    LIMIT 40\n),\ncombined AS (\n    SELECT\n        COALESCE(v.id, t.id) AS id,\n        COALESCE(v.content, t.content) AS content,\n        COALESCE(v.vector_score, 0) * 0.5 + COALESCE(t.text_score, 0) * 0.5 AS hybrid_score\n    FROM vector_results v\n    FULL OUTER JOIN text_results t ON v.id = t.id\n)\nSELECT * FROM combined ORDER BY hybrid_score DESC LIMIT 10;\n```\n\n## Reranking\n\nEl reranking es la mejora de mayor impacto que podés hacer a un pipeline RAG. Los embeddings de bi-encoder son rápidos pero imprecisos. Los rerankers cross-encoder son lentos pero precisos. La arquitectura usa ambos:\n\n1. **Recuperar** 50-100 candidatos con búsqueda híbrida rápida (~10ms)\n2. **Rerankear** los top 20-30 con un cross-encoder (latencia a medir)\n3. **Seleccionar** los top 5-8 para el contexto del LLM\n\n### Reranking con Cross-Encoder\n\n```python\nfrom sentence_transformers import CrossEncoder\n\nreranker = CrossEncoder(\"BAAI/bge-reranker-v2-m3\", max_length=512)\n\ndef rerank(query, candidates, top_k=5):\n    \"\"\"Rerankear candidatos usando cross-encoder.\"\"\"\n    pairs = [[query, candidate[\"content\"]] for candidate in candidates]\n    scores = reranker.predict(pairs)\n\n    for i, candidate in enumerate(candidates):\n        candidate[\"rerank_score\"] = float(scores[i])\n\n    ranked = sorted(candidates, key=lambda x: x[\"rerank_score\"], reverse=True)\n    return ranked[:top_k]\n```\n\n### API de Rerank de Cohere\n\nPara equipos que prefieren infraestructura gestionada:\n\n```python\nimport cohere\n\nco = cohere.ClientV2()\n\nresults = co.rerank(\n    model=\"rerank-v3.5\",\n    query=\"configuración de backup PITR para PostgreSQL\",\n    documents=[chunk[\"content\"] for chunk in candidates],\n    top_n=5,\n    return_documents=True,\n)\n\nreranked = [\n    {\"content\": r.document.text, \"relevance_score\": r.relevance_score}\n    for r in results.results\n]\n```\n\n### Medición de Impacto\n\nEn nuestro pipeline de producción (2.3M chunks, 10K consultas de prueba):\n\n| Configuración | Recall@5 | MRR | Relevancia de Respuesta |\n| --- | --- | --- | --- |\n| Solo vectorial | 0.62 | 0.54 | 0.71 |\n| Híbrida (BM25 + vector) | 0.74 | 0.63 | 0.78 |\n| Híbrida + reranker | 0.83 | 0.76 | 0.89 |\n| Híbrida + reranker + encabezados contextuales | 0.87 | 0.81 | 0.91 |\n\nEl reranker solo representa una mejora de 12 puntos en recall. Combinado con búsqueda híbrida y encabezados contextuales, el pipeline recupera el 87% de los chunks relevantes en los top 5 resultados.\n\n## Construcción del Prompt\n\nDespués del retrieval y reranking, cómo construís el prompt del LLM importa más de lo que la mayoría de los equipos creen.\n\n### Gestión de la Ventana de Contexto\n\n```python\ndef build_rag_prompt(query, retrieved_chunks, max_context_tokens=6000):\n    \"\"\"Construir prompt con inyección de contexto consciente del presupuesto de tokens.\"\"\"\n    context_parts = []\n    token_count = 0\n\n    for chunk in retrieved_chunks:\n        chunk_tokens = count_tokens(chunk[\"content\"])\n        if token_count + chunk_tokens > max_context_tokens:\n            break\n        context_parts.append(\n            f\"[Fuente: {chunk['metadata']['source']}, \"\n            f\"Sección: {chunk['metadata'].get('section', 'N/A')}]\\n\"\n            f\"{chunk['content']}\"\n        )\n        token_count += chunk_tokens\n\n    context = \"\\n\\n---\\n\\n\".join(context_parts)\n\n    return f\"\"\"Respondé la siguiente pregunta basándote en el contexto proporcionado.\nSi el contexto no contiene suficiente información para responder completamente, decilo explícitamente.\nCitá el documento fuente para cada afirmación.\n\nContexto:\n{context}\n\nPregunta: {query}\n\nRespuesta:\"\"\"\n```\n\n### Grounding con Citaciones\n\nForzá al modelo a citar fuentes requiriendo referencias inline:\n\n```\nPara cada afirmación factual en tu respuesta, incluí una cita en el formato [Fuente: nombre_archivo, Sección: nombre_sección].\nSi no podés encontrar evidencia de soporte en el contexto para una afirmación, prefijala con [Sin soporte].\n```\n\nEsto hace que la detección de alucinaciones sea trivial, cualquier afirmación marcada como ` [Sin soporte] ` o sin cita es sospechosa.\n\n## Framework de Evaluación\n\nUn pipeline RAG sin evaluación es una demo, no un producto. La evaluación ocurre en tres niveles.\n\n### Nivel 1: Calidad de Retrieval\n\nMedida independientemente del LLM:\n\n```python\ndef evaluate_retrieval(test_queries, ground_truth, retriever, k=5):\n    \"\"\"Evaluar retrieval con métricas estándar de IR.\"\"\"\n    recalls, mrrs, ndcgs = [], [], []\n\n    for query, relevant_ids in zip(test_queries, ground_truth):\n        retrieved = retriever.search(query, k=k)\n        retrieved_ids = [r[\"id\"] for r in retrieved]\n\n        # Recall@k\n        hits = len(set(retrieved_ids) & set(relevant_ids))\n        recalls.append(hits / len(relevant_ids))\n\n        # MRR\n        for rank, rid in enumerate(retrieved_ids, 1):\n            if rid in relevant_ids:\n                mrrs.append(1.0 / rank)\n                break\n        else:\n            mrrs.append(0.0)\n\n    return {\n        \"recall@k\": np.mean(recalls),\n        \"mrr\": np.mean(mrrs),\n    }\n```\n\n### Nivel 2: Calidad de Generación con RAGAS\n\nRAGAS evalúa el pipeline completo, retrieval y generación juntos:\n\n```python\nfrom ragas import evaluate\nfrom ragas.metrics import (\n    faithfulness,\n    answer_relevancy,\n    context_precision,\n    context_recall,\n)\n\nresults = evaluate(\n    dataset=eval_dataset,  # Preguntas + respuestas ground truth + contextos\n    metrics=[\n        faithfulness,        # ¿La respuesta se mantiene fiel al contexto recuperado?\n        answer_relevancy,    # ¿La respuesta es relevante a la pregunta?\n        context_precision,   # ¿Los chunks recuperados son relevantes?\n        context_recall,      # ¿Se recuperaron todos los chunks necesarios?\n    ],\n)\n\nprint(results)\n# {'faithfulness': 0.89, 'answer_relevancy': 0.91,\n#  'context_precision': 0.82, 'context_recall': 0.87}\n```\n\n**Faithfulness** es la métrica más importante, mide alucinación. Un score de faithfulness por debajo de 0.85 significa que el pipeline está generando afirmaciones no respaldadas por el contexto recuperado.\n\n### Nivel 3: Evaluación Humana\n\nAutomatizar la recolección, evaluar manualmente:\n\n```python\ndef sample_for_human_eval(production_queries, n=200):\n    \"\"\"Muestrear consultas estratificadas por dificultad y tema.\"\"\"\n    # Clasificar consultas por dificultad estimada\n    easy = [q for q in production_queries if q[\"retrieval_confidence\"] > 0.8]\n    medium = [q for q in production_queries if 0.5 < q[\"retrieval_confidence\"] <= 0.8]\n    hard = [q for q in production_queries if q[\"retrieval_confidence\"] <= 0.5]\n\n    sample = (\n        random.sample(easy, min(80, len(easy))) +\n        random.sample(medium, min(80, len(medium))) +\n        random.sample(hard, min(40, len(hard)))\n    )\n\n    return sample\n```\n\nLos evaluadores humanos califican en tres ejes: **correctitud** (factualmente precisa), **completitud** (cubre la pregunta en su totalidad) y **fundamentación** (cita contexto, no alucina).\n\n## Arquitectura de Producción\n\n### Vista General del Pipeline\n\n```\nDocumentos → Preprocesamiento → Chunking → Embedding → Vector Store\n                                                            ↓\nConsulta → Expansión de Query → Búsqueda Híbrida → Reranker → Constructor de Prompt → LLM → Respuesta\n                                                                                              ↓\n                                                                                        Logger de Evaluación\n```\n\n### Expansión de Consultas\n\nReescribir la consulta del usuario antes del retrieval para mejorar el recall:\n\n```python\ndef expand_query(query, llm):\n    \"\"\"Generar reformulaciones alternativas para mejorar el retrieval.\"\"\"\n    expansion_prompt = f\"\"\"Generá 3 reformulaciones alternativas de esta consulta de búsqueda.\nCada una debe capturar la misma intención pero usar terminología diferente.\nDevolvé solo las consultas, una por línea.\n\nConsulta: {query}\"\"\"\n\n    alternatives = llm.generate(expansion_prompt).strip().split(\"\\n\")\n    return [query] + alternatives[:3]\n```\n\nEjecutar búsqueda híbrida sobre todas las consultas expandidas y fusionar resultados con RRF. Esto consistentemente agrega 3-5% de mejora en recall.\n\n### Capa de Caché\n\nCachear en dos niveles para reducir latencia y costo:\n\n1. **Caché de embeddings**: Hashear el texto de entrada, cachear el vector de embedding. Evita re-generar embeddings de consultas idénticas.\n2. **Caché semántico**: Para consultas con similitud coseno \\> 0.95 respecto a una consulta cacheada, devolver la respuesta cacheada. Reduce llamadas al LLM en un 20-40% en casos de uso de soporte donde preguntas similares se repiten.\n\n### Monitoreo\n\nRastrear estas métricas en producción:\n\n- **Latencia de retrieval** (p50, p95, p99), objetivo \\< 100ms para híbrida + rerank\n- **Score de faithfulness** sobre una muestra rotativa, alertar si cae por debajo de 0.85\n- **Tasa de retrieval vacío**, consultas donde ningún chunk supera el umbral de relevancia\n- **Señales de feedback del usuario**, thumbs up/down, eventos de copia, preguntas de seguimiento\n\n## Cuándo RAG No Es Suficiente\n\nRAG tiene límites bien definidos:\n\n- **Razonamiento multi-hop**: Cuando la respuesta requiere sintetizar información de 5+ documentos con pasos inferenciales entre ellos, el retrieval de RAG frecuentemente pierde documentos intermedios.\n- **Razonamiento temporal**: \"¿Qué cambió entre Q3 y Q4?\" requiere recuperar y comparar dos conjuntos de documentos específicos en el tiempo, el retrieval top-k estándar no está diseñado para esto.\n- **Cómputo sobre datos**: \"¿Cuál es el SLA promedio en todas las regiones?\" requiere una consulta estructurada, no recuperación de texto.\n\nPara estos casos, considerá **RAG agéntico**, un agente que planifica pasos de retrieval, ejecuta múltiples búsquedas y sintetiza resultados programáticamente, o enfoques híbridos que combinen RAG con consultas SQL y llamadas a APIs.\n\n## Implementación reproducible\n\n[RAG Evaluation Workbench](<https://lucioduran.com/es/projects/rag-evaluation-workbench>) compara BM25, recuperación densa e híbrida con el mismo generador local, prompt y presupuesto. La ficha reúne el corpus con licencia, las consultas etiquetadas, los resultados crudos, los intervalos y los comandos para reproducirlos.\n\nEn esa muestra, recuperar todo el contexto relevante no evitó los errores de respuesta ni las fallas de abstención. El experimento evalúa recuperación y generación por separado; no valida las estrategias de chunking, los rerankers ni los objetivos de producción de esta guía.\n\n### Fragmentación: evidencia completa y costo del índice\n\n[Chunking Ablation Suite](<https://lucioduran.com/es/projects/chunking-ablation-suite>) compara ocho configuraciones fijas, estructurales y semánticas con el mismo MiniLM. En 36 consultas de evaluación, structure-96 recuperó toda la evidencia en 33 casos; las configuraciones semánticas, en 12. El repositorio conserva fragmentos, vectores, rankings y costos por tipo de documento. También muestra una ablación de umbral que no cambia ningún límite. Ponerle «semántico» al algoritmo no resuelve la evaluación.\n\n### Citas: una fuente relevante todavía puede no respaldar la afirmación\n\n[Citation Grounding Checker](<https://lucioduran.com/es/projects/citation-grounding-checker>) compara reglas conservadoras, FLAN-T5-small y DeBERTa NLI sobre pares de afirmación y pasaje. Publica veredictos, precisión por clase, abstenciones y enlaces exactos a la evidencia. En sus 36 casos sintéticos de evaluación, DeBERTa acertó 31 y se abstuvo en uno. Las etiquetas no tienen revisión humana independiente; esa cifra describe el fixture publicado.\n\n### Reranking: medir también cuándo el router no aporta\n\n[Reranker Budget Router](<https://lucioduran.com/es/projects/reranker-budget-router>) asigna presupuestos de 0, 4 o 12 pares y congela las políticas antes de evaluar. Las seis políticas ajustadas omitieron todo reranking, igualaron la calidad del baseline never y agregaron overhead. Always-12 mejoró una consulta. Las trazas permiten inspeccionar esa oportunidad perdida: agregar un router no garantiza que la arquitectura mejore.\n\n## Referencias y alcance\n\nLas fuentes siguientes describen métodos de recuperación y evaluación. Este artículo es una guía técnica, no un informe de benchmarks realizados por el autor. Las mejoras y los costos de despliegue requieren mediciones sobre la carga concreta.\n\n- [Lewis et al. Retrieval Augmented Generation](<https://arxiv.org/abs/2005.11401>).\n- [Es et al. Ragas](<https://arxiv.org/abs/2309.15217>).\n- [Elasticsearch Reciprocal Rank Fusion](<https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion>).\n\n## Implementación reproducible: actualización e integridad del índice\n\n[Incremental RAG Index](<https://lucioduran.com/es/projects/incremental-rag-index>) mantiene versiones completas en SQLite, reutiliza embeddings por hash y registra cambios en un diario. Compara reconstrucción completa e incremental en cinco escenarios, con un adaptador determinista y otro MiniLM local, e interrumpe el proceso en cuatro puntos de publicación por adaptador.\n\nEn ambos experimentos coincidieron documentos, vectores, raíces y rankings. Editar, agregar y borrar calculó dos embeddings frente a seis de la reconstrucción completa. Los cortes anteriores al commit conservaron la versión vieja; el posterior conservó la nueva y su reintento no creó otra. Los documentos borrados desaparecen del índice actual, pero permanecen en versiones históricas. La evidencia demuestra coherencia sobre seis documentos sintéticos, no calidad semántica ni rendimiento a escala.\n\n## Preguntas frecuentes\n\n### ¿Qué tamaño de chunk debería usar para RAG?\n\nNo hay un tamaño universal. Compará tamaños candidatos con consultas etiquetadas y registrá calidad de recuperación, truncamiento del contexto y calidad de generación.\n\n### ¿La búsqueda híbrida es siempre mejor que solo vectorial?\n\nNo. La recuperación híbrida puede ayudar con términos exactos y similitud semántica, pero también puede introducir ruido. Compará los métodos sobre las mismas consultas reservadas.\n\n### ¿Cómo evalúo un pipeline RAG de forma sistemática?\n\nUsá un enfoque de tres capas: (1) métricas de retrieval como recall@k, MRR y NDCG sobre un set de consultas etiquetadas, (2) métricas de generación como faithfulness y answer relevance usando RAGAS, y (3) evaluación humana end-to-end sobre un set dorado rotativo de 200+ consultas.\n\n### ¿Cuándo debería usar un reranker en vez de mejorar los embeddings?\n\nMedí embeddings y reranking como cambios separados. Incorporá un reranker si la mejora observada justifica su latencia y su costo para esa carga.\n\n## Tips\n\n1. Compará estrategias de chunking con consultas etiquetadas. El tamaño y el overlap son parámetros iniciales que hay que ajustar, no valores óptimos universales.\n2. Combiná BM25 y recuperación densa con RRF, y compará recall@k y NDCG contra cada método por separado. La mejora depende del corpus y de las consultas.\n3. Evaluá un cross-encoder sobre los candidatos recuperados y medí relevancia, latencia y costo juntos. Este artículo no establece una mejora porcentual ni una latencia fija.\n4. **Trampa de evaluación:** No uses LLM-as-judge exclusivamente. Combinalo con métricas de retrieval (recall@k, MRR) y evaluación humana sobre un set dorado de 200 consultas. Los jueces LLM tienen puntos ciegos en precisión factual.\n\n- rag\n- retrieval-augmented-generation\n- chunking\n- reranking\n- hybrid-search\n- embeddings\n- llm\n- evaluation\n- ragas\n- ai-agents\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)","headings":[{"level":1,"text":"Arquitectura de Pipelines RAG: Estrategias de Chunking, Búsqueda Híbrida, Reranking y Frameworks de Evaluación","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#title"},{"level":2,"text":"Por Qué la Mayoría de los Pipelines RAG Fallan","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#por-qu-la-mayora-de-los-pipelines-rag-fallan"},{"level":2,"text":"Ingesta y Preprocesamiento de Documentos","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#ingesta-y-preprocesamiento-de-documentos"},{"level":3,"text":"Normalización de Formatos","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#normalizacin-de-formatos"},{"level":3,"text":"Extracción de Metadata","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#extraccin-de-metadata"},{"level":2,"text":"Estrategias de Chunking","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#estrategias-de-chunking"},{"level":3,"text":"Chunking de Tamaño Fijo","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#chunking-de-tamao-fijo"},{"level":3,"text":"Chunking Semántico","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#chunking-semntico"},{"level":3,"text":"Chunking Consciente de la Estructura del Documento","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#chunking-consciente-de-la-estructura-del-documento"},{"level":3,"text":"Encabezados Contextuales en los Chunks","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#encabezados-contextuales-en-los-chunks"},{"level":2,"text":"Estrategia de Embeddings","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#estrategia-de-embeddings"},{"level":3,"text":"Selección del Modelo","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#seleccin-del-modelo"},{"level":3,"text":"Modelos de Interacción Tardía (ColBERT)","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#modelos-de-interaccin-tarda-colbert"},{"level":2,"text":"Arquitectura de Búsqueda Híbrida","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#arquitectura-de-bsqueda-hbrida"},{"level":3,"text":"BM25 + Vector con Reciprocal Rank Fusion","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#bm25-vector-con-reciprocal-rank-fusion"},{"level":3,"text":"Por Qué Funciona la Búsqueda Híbrida","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#por-qu-funciona-la-bsqueda-hbrida"},{"level":3,"text":"Búsqueda Híbrida Nativa en Base de Datos","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#bsqueda-hbrida-nativa-en-base-de-datos"},{"level":2,"text":"Reranking","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#reranking"},{"level":3,"text":"Reranking con Cross-Encoder","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#reranking-con-cross-encoder"},{"level":3,"text":"API de Rerank de Cohere","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#api-de-rerank-de-cohere"},{"level":3,"text":"Medición de Impacto","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#medicin-de-impacto"},{"level":2,"text":"Construcción del Prompt","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#construccin-del-prompt"},{"level":3,"text":"Gestión de la Ventana de Contexto","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#gestin-de-la-ventana-de-contexto"},{"level":3,"text":"Grounding con Citaciones","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#grounding-con-citaciones"},{"level":2,"text":"Framework de Evaluación","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#framework-de-evaluacin"},{"level":3,"text":"Nivel 1: Calidad de Retrieval","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#nivel-1-calidad-de-retrieval"},{"level":3,"text":"Nivel 2: Calidad de Generación con RAGAS","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#nivel-2-calidad-de-generacin-con-ragas"},{"level":3,"text":"Nivel 3: Evaluación Humana","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#nivel-3-evaluacin-humana"},{"level":2,"text":"Arquitectura de Producción","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#arquitectura-de-produccin"},{"level":3,"text":"Vista General del Pipeline","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#vista-general-del-pipeline"},{"level":3,"text":"Expansión de Consultas","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#expansin-de-consultas"},{"level":3,"text":"Capa de Caché","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#capa-de-cach"},{"level":3,"text":"Monitoreo","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#monitoreo"},{"level":2,"text":"Cuándo RAG No Es Suficiente","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#cundo-rag-no-es-suficiente"},{"level":2,"text":"Implementación reproducible","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#implementacin-reproducible"},{"level":3,"text":"Fragmentación: evidencia completa y costo del índice","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#fragmentacin-evidencia-completa-y-costo-del-ndice"},{"level":3,"text":"Citas: una fuente relevante todavía puede no respaldar la afirmación","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#citas-una-fuente-relevante-todava-puede-no-respaldar-la-afirmacin"},{"level":3,"text":"Reranking: medir también cuándo el router no aporta","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#reranking-medir-tambin-cundo-el-router-no-aporta"},{"level":2,"text":"Referencias y alcance","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#referencias-y-alcance"},{"level":2,"text":"Implementación reproducible: actualización e integridad del índice","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#implementacin-reproducible-actualizacin-e-integridad-del-ndice"},{"level":2,"text":"Preguntas frecuentes","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#questions"},{"level":3,"text":"¿Qué tamaño de chunk debería usar para RAG?","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#question-1"},{"level":3,"text":"¿La búsqueda híbrida es siempre mejor que solo vectorial?","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#question-2"},{"level":3,"text":"¿Cómo evalúo un pipeline RAG de forma sistemática?","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#question-3"},{"level":3,"text":"¿Cuándo debería usar un reranker en vez de mejorar los embeddings?","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#question-4"},{"level":2,"text":"Tips","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#article-tips"}],"sections":[{"heading":"Arquitectura de Pipelines RAG: Estrategias de Chunking, Búsqueda Híbrida, Reranking y Frameworks de Evaluación","level":1,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation","content":"AI/ML"},{"heading":"Arquitectura de Pipelines RAG: Estrategias de Chunking, Búsqueda Híbrida, Reranking y Frameworks de Evaluación","level":1,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#title","content":"2026-04-1616 min lectura\n\n[Lucio Durán](<https://lucioduran.com/es>)\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/rag-pipeline-architecture-chunking-reranking-evaluation>)"},{"heading":"Por Qué la Mayoría de los Pipelines RAG Fallan","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#por-qu-la-mayora-de-los-pipelines-rag-fallan","content":"El tutorial RAG por defecto, dividir documentos, generar embeddings, recuperar top-k, insertar en el prompt, funciona para demos. Falla en producción por razones predecibles:\n\n- **El chunking destruye contexto.** Los cortes de tamaño fijo rompen oraciones a mitad de pensamiento, separan encabezados de su contenido y pierden la estructura del documento.\n- **La búsqueda vectorial pierde keywords.** Los embeddings capturan similitud semántica pero fallan en coincidencias exactas de entidades, acrónimos y terminología de dominio específica.\n- **La recuperación top-k es ruidosa.** Recuperar 5 chunks por similitud coseno frecuentemente devuelve 2-3 resultados irrelevantes que diluyen el contexto del prompt.\n- **Sin evaluación no hay iteración.** Sin métricas sistemáticas, los equipos optimizan por intuición y despliegan pipelines que se degradan silenciosamente.\n\nEste artículo cubre la arquitectura que aborda cada uno de estos modos de falla."},{"heading":"Ingesta y Preprocesamiento de Documentos","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#ingesta-y-preprocesamiento-de-documentos","content":"Antes del chunking, el preprocesamiento de documentos determina la calidad del pipeline más de lo que la mayoría de los equipos creen."},{"heading":"Normalización de Formatos","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#normalizacin-de-formatos","content":"Diferentes formatos fuente requieren diferentes estrategias de extracción:\n\n```python\n# Extracción de PDF con detección de layout\nfrom unstructured.partition.pdf import partition_pdf\n\nelements = partition_pdf(\n    filename=\"report.pdf\",\n    strategy=\"hi_res\",           # OCR + detección de layout\n    infer_table_structure=True,  # Extraer tablas como HTML\n    extract_images_in_pdf=True,  # Extraer imágenes embebidas\n)\n\n# Separar tipos de elementos para procesamiento diferenciado\ntables = [e for e in elements if e.category == \"Table\"]\nnarratives = [e for e in elements if e.category == \"NarrativeText\"]\ntitles = [e for e in elements if e.category == \"Title\"]\n```\n\nDecisiones clave en esta etapa:\n\n- **Tablas**: Convertir a markdown o HTML, nunca dividir entre chunks. Un fragmento de tabla es peor que no tener tabla.\n- **Bloques de código**: Mantener intactos. Dividir la definición de una función entre chunks hace que ambas mitades sean inútiles.\n- **Imágenes**: Extraer captions y texto circundante. Para diagramas, considerar descripciones con modelos de visión.\n- **Encabezados**: Preservar la jerarquía, se convierten en metadata que mejora el retrieval."},{"heading":"Extracción de Metadata","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#extraccin-de-metadata","content":"Adjuntar metadata en el momento de la ingesta, no después:\n\n```python\nchunk_metadata = {\n    \"source\": \"engineering-handbook-v3.pdf\",\n    \"section\": \"Capítulo 4: Operaciones de Base de Datos\",\n    \"page_numbers\": [42, 43],\n    \"document_type\": \"handbook\",\n    \"last_updated\": \"2026-03-15\",\n    \"audience\": \"backend-engineers\",\n}\n```\n\nEsta metadata habilita el retrieval filtrado, \"encontrar chunks sobre operaciones de base de datos del handbook de ingeniería\", lo cual mejora dramáticamente la precisión."},{"heading":"Estrategias de Chunking","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#estrategias-de-chunking","content":"El chunking es donde la mayoría de los pipelines pierden calidad silenciosamente. La estrategia correcta depende de la estructura del documento."},{"heading":"Chunking de Tamaño Fijo","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#chunking-de-tamao-fijo","content":"La línea base. Dividir cada N tokens con M tokens de overlap.\n\n```python\nfrom langchain.text_splitter import RecursiveCharacterTextSplitter\n\nsplitter = RecursiveCharacterTextSplitter(\n    chunk_size=512,\n    chunk_overlap=50,\n    separators=[\"\\n\\n\", \"\\n\", \". \", \" \", \"\"],\n    length_function=len,  # o tiktoken para conteo preciso de tokens\n)\nchunks = splitter.split_text(document_text)\n```\n\n**Cuándo funciona**: Texto uniforme sin estructura, transcripciones, logs en texto plano, historiales de chat.\n\n**Cuándo falla**: Documentos estructurados donde dividir en posiciones arbitrarias destruye el significado."},{"heading":"Chunking Semántico","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#chunking-semntico","content":"Agrupa oraciones por similitud de embedding, dividiendo donde los temas cambian:\n\n```python\nfrom langchain_experimental.text_splitter import SemanticChunker\nfrom langchain_openai import OpenAIEmbeddings\n\nchunker = SemanticChunker(\n    OpenAIEmbeddings(model=\"text-embedding-3-small\"),\n    breakpoint_threshold_type=\"percentile\",\n    breakpoint_threshold_amount=85,  # Dividir en el percentil 85 de distancia\n)\nchunks = chunker.split_text(document_text)\n```\n\nEl chunking semántico produce chunks de tamaño variable que respetan los límites temáticos. El tradeoff es velocidad, generar embeddings de cada oración durante la ingesta es 10-50x más lento que la división por tamaño fijo."},{"heading":"Chunking Consciente de la Estructura del Documento","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#chunking-consciente-de-la-estructura-del-documento","content":"El enfoque más efectivo para contenido estructurado. Usa la jerarquía del documento para definir los límites de los chunks:\n\n```python\ndef structure_aware_chunk(elements, max_tokens=512):\n    \"\"\"Dividir por estructura del documento, respetando encabezados y secciones.\"\"\"\n    chunks = []\n    current_chunk = []\n    current_tokens = 0\n    current_header_chain = []\n\n    for element in elements:\n        if element.category == \"Title\":\n            # Nueva sección, volcar chunk actual\n            if current_chunk:\n                chunks.append({\n                    \"content\": \"\\n\".join(current_chunk),\n                    \"headers\": list(current_header_chain),\n                    \"token_count\": current_tokens,\n                })\n                current_chunk = []\n                current_tokens = 0\n            current_header_chain.append(element.text)\n\n        elif element.category == \"Table\":\n            # Las tablas van en su propio chunk, nunca se dividen\n            if current_chunk:\n                chunks.append({\n                    \"content\": \"\\n\".join(current_chunk),\n                    \"headers\": list(current_header_chain),\n                    \"token_count\": current_tokens,\n                })\n                current_chunk = []\n                current_tokens = 0\n            chunks.append({\n                \"content\": element.metadata.text_as_html,\n                \"headers\": list(current_header_chain),\n                \"token_count\": count_tokens(element.text),\n                \"type\": \"table\",\n            })\n\n        else:\n            elem_tokens = count_tokens(element.text)\n            if current_tokens + elem_tokens > max_tokens and current_chunk:\n                chunks.append({\n                    \"content\": \"\\n\".join(current_chunk),\n                    \"headers\": list(current_header_chain),\n                    \"token_count\": current_tokens,\n                })\n                current_chunk = []\n                current_tokens = 0\n            current_chunk.append(element.text)\n            current_tokens += elem_tokens\n\n    if current_chunk:\n        chunks.append({\n            \"content\": \"\\n\".join(current_chunk),\n            \"headers\": list(current_header_chain),\n            \"token_count\": current_tokens,\n        })\n\n    return chunks\n```"},{"heading":"Encabezados Contextuales en los Chunks","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#encabezados-contextuales-en-los-chunks","content":"Anteponer la jerarquía de secciones antes del embedding es una hipótesis que debe evaluarse contra una referencia con las demás condiciones iguales:\n\n```\n## Capítulo 4: Operaciones de Base de Datos > Estrategias de Backup > Recuperación Point-in-Time\n\nPara realizar recuperación point-in-time, configurar el archivado continuo de WAL...\n```\n\nEl embedding ahora captura tanto el contenido como su posición en la jerarquía del documento."},{"heading":"Estrategia de Embeddings","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#estrategia-de-embeddings","content":""},{"heading":"Selección del Modelo","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#seleccin-del-modelo","content":"El modelo de embedding determina el techo de la calidad de retrieval.\n\n| Modelo | Dimensiones | MTEB Score | Latencia (p50) | Costo |\n| --- | --- | --- | --- | --- |\n| text-embedding-3-small | 1536 | 62.3 | 12ms | $0.02/1M tokens |\n| text-embedding-3-large | 3072 | 64.6 | 18ms | $0.13/1M tokens |\n| voyage-3 | 1024 | 67.1 | 15ms | $0.06/1M tokens |\n| bge-m3 (self-hosted) | 1024 | 66.8 | 8ms | Costo de infraestructura |\n| Cohere embed-v4 | 1024 | 67.5 | 14ms | $0.10/1M tokens |\n\n**Recomendación práctica**: Para workloads solo en inglés, ` voyage-3 ` o ` Cohere embed-v4 ` entregan la mejor calidad de retrieval. Para deployments multilingües o sensibles al costo, ` bge-m3 ` self-hosted en una sola GPU es difícil de superar."},{"heading":"Modelos de Interacción Tardía (ColBERT)","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#modelos-de-interaccin-tarda-colbert","content":"ColBERT produce embeddings por token en vez de un único vector, permitiendo matching más granular:\n\n```python\nfrom ragatouille import RAGPretrainedModel\n\nrag = RAGPretrainedModel.from_pretrained(\"colbert-ir/colbertv2.0\")\nrag.index(\n    collection=documents,\n    index_name=\"knowledge-base\",\n    max_document_length=512,\n)\n\nresults = rag.search(query=\"configuración de backup PITR\", k=10)\n```\n\nColBERT supera consistentemente a los modelos de vector único en calidad de retrieval pero requiere 10-50x más almacenamiento por documento. Usalo cuando la calidad de retrieval sea el cuello de botella y el almacenamiento sea barato."},{"heading":"Arquitectura de Búsqueda Híbrida","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#arquitectura-de-bsqueda-hbrida","content":"La búsqueda vectorial sola no es suficiente. Combinar búsqueda semántica y léxica cubre los puntos ciegos de cada método."},{"heading":"BM25 + Vector con Reciprocal Rank Fusion","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#bm25-vector-con-reciprocal-rank-fusion","content":"```python\nfrom rank_bm25 import BM25Okapi\nimport numpy as np\n\ndef hybrid_search(query, corpus_chunks, embeddings_index, k=20, alpha=0.5):\n    \"\"\"\n    Combinar BM25 y búsqueda vectorial usando Reciprocal Rank Fusion.\n    alpha controla el balance: 0.5 = peso igual.\n    \"\"\"\n    # Búsqueda vectorial\n    query_embedding = embed(query)\n    vector_scores = embeddings_index.search(query_embedding, k=k * 2)\n\n    # Búsqueda BM25\n    tokenized_corpus = [chunk.split() for chunk in corpus_chunks]\n    bm25 = BM25Okapi(tokenized_corpus)\n    bm25_scores = bm25.get_scores(query.split())\n    bm25_top = np.argsort(bm25_scores)[-k * 2:][::-1]\n\n    # Reciprocal Rank Fusion\n    rrf_scores = {}\n    rrf_k = 60  # Constante RRF estándar\n\n    for rank, doc_id in enumerate(vector_scores.ids):\n        rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + alpha / (rrf_k + rank + 1)\n\n    for rank, doc_id in enumerate(bm25_top):\n        rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + (1 - alpha) / (rrf_k + rank + 1)\n\n    # Ordenar por score fusionado\n    ranked = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)\n    return ranked[:k]\n```"},{"heading":"Por Qué Funciona la Búsqueda Híbrida","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#por-qu-funciona-la-bsqueda-hbrida","content":"Considerá la consulta: \"¿Cuál es el SLA del cluster Redis en EU-WEST-1?\"\n\n- **Búsqueda vectorial** encuentra chunks sobre SLAs y clusters Redis en general, semánticamente similares pero potencialmente de la región incorrecta.\n- **BM25** encuentra chunks que contienen la cadena exacta \"EU-WEST-1\", coincidencia léxica.\n- **Híbrida** surfacea el chunk que menciona tanto la política de SLA como la región específica."},{"heading":"Búsqueda Híbrida Nativa en Base de Datos","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#bsqueda-hbrida-nativa-en-base-de-datos","content":"PostgreSQL con pgvector 0.8+ soporta ambas en una sola consulta:\n\n```sql\n-- Búsqueda híbrida con pgvector + tsvector\nWITH vector_results AS (\n    SELECT id, content, 1 - (embedding <=> $1::vector) AS vector_score\n    FROM documents\n    WHERE metadata->>'department' = 'engineering'\n    ORDER BY embedding <=> $1::vector\n    LIMIT 40\n),\ntext_results AS (\n    SELECT id, content, ts_rank(search_vector, plainto_tsquery($2)) AS text_score\n    FROM documents\n    WHERE search_vector @@ plainto_tsquery($2)\n      AND metadata->>'department' = 'engineering'\n    ORDER BY text_score DESC\n    LIMIT 40\n),\ncombined AS (\n    SELECT\n        COALESCE(v.id, t.id) AS id,\n        COALESCE(v.content, t.content) AS content,\n        COALESCE(v.vector_score, 0) * 0.5 + COALESCE(t.text_score, 0) * 0.5 AS hybrid_score\n    FROM vector_results v\n    FULL OUTER JOIN text_results t ON v.id = t.id\n)\nSELECT * FROM combined ORDER BY hybrid_score DESC LIMIT 10;\n```"},{"heading":"Reranking","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#reranking","content":"El reranking es la mejora de mayor impacto que podés hacer a un pipeline RAG. Los embeddings de bi-encoder son rápidos pero imprecisos. Los rerankers cross-encoder son lentos pero precisos. La arquitectura usa ambos:\n\n1. **Recuperar** 50-100 candidatos con búsqueda híbrida rápida (~10ms)\n2. **Rerankear** los top 20-30 con un cross-encoder (latencia a medir)\n3. **Seleccionar** los top 5-8 para el contexto del LLM"},{"heading":"Reranking con Cross-Encoder","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#reranking-con-cross-encoder","content":"```python\nfrom sentence_transformers import CrossEncoder\n\nreranker = CrossEncoder(\"BAAI/bge-reranker-v2-m3\", max_length=512)\n\ndef rerank(query, candidates, top_k=5):\n    \"\"\"Rerankear candidatos usando cross-encoder.\"\"\"\n    pairs = [[query, candidate[\"content\"]] for candidate in candidates]\n    scores = reranker.predict(pairs)\n\n    for i, candidate in enumerate(candidates):\n        candidate[\"rerank_score\"] = float(scores[i])\n\n    ranked = sorted(candidates, key=lambda x: x[\"rerank_score\"], reverse=True)\n    return ranked[:top_k]\n```"},{"heading":"API de Rerank de Cohere","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#api-de-rerank-de-cohere","content":"Para equipos que prefieren infraestructura gestionada:\n\n```python\nimport cohere\n\nco = cohere.ClientV2()\n\nresults = co.rerank(\n    model=\"rerank-v3.5\",\n    query=\"configuración de backup PITR para PostgreSQL\",\n    documents=[chunk[\"content\"] for chunk in candidates],\n    top_n=5,\n    return_documents=True,\n)\n\nreranked = [\n    {\"content\": r.document.text, \"relevance_score\": r.relevance_score}\n    for r in results.results\n]\n```"},{"heading":"Medición de Impacto","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#medicin-de-impacto","content":"En nuestro pipeline de producción (2.3M chunks, 10K consultas de prueba):\n\n| Configuración | Recall@5 | MRR | Relevancia de Respuesta |\n| --- | --- | --- | --- |\n| Solo vectorial | 0.62 | 0.54 | 0.71 |\n| Híbrida (BM25 + vector) | 0.74 | 0.63 | 0.78 |\n| Híbrida + reranker | 0.83 | 0.76 | 0.89 |\n| Híbrida + reranker + encabezados contextuales | 0.87 | 0.81 | 0.91 |\n\nEl reranker solo representa una mejora de 12 puntos en recall. Combinado con búsqueda híbrida y encabezados contextuales, el pipeline recupera el 87% de los chunks relevantes en los top 5 resultados."},{"heading":"Construcción del Prompt","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#construccin-del-prompt","content":"Después del retrieval y reranking, cómo construís el prompt del LLM importa más de lo que la mayoría de los equipos creen."},{"heading":"Gestión de la Ventana de Contexto","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#gestin-de-la-ventana-de-contexto","content":"```python\ndef build_rag_prompt(query, retrieved_chunks, max_context_tokens=6000):\n    \"\"\"Construir prompt con inyección de contexto consciente del presupuesto de tokens.\"\"\"\n    context_parts = []\n    token_count = 0\n\n    for chunk in retrieved_chunks:\n        chunk_tokens = count_tokens(chunk[\"content\"])\n        if token_count + chunk_tokens > max_context_tokens:\n            break\n        context_parts.append(\n            f\"[Fuente: {chunk['metadata']['source']}, \"\n            f\"Sección: {chunk['metadata'].get('section', 'N/A')}]\\n\"\n            f\"{chunk['content']}\"\n        )\n        token_count += chunk_tokens\n\n    context = \"\\n\\n---\\n\\n\".join(context_parts)\n\n    return f\"\"\"Respondé la siguiente pregunta basándote en el contexto proporcionado.\nSi el contexto no contiene suficiente información para responder completamente, decilo explícitamente.\nCitá el documento fuente para cada afirmación.\n\nContexto:\n{context}\n\nPregunta: {query}\n\nRespuesta:\"\"\"\n```"},{"heading":"Grounding con Citaciones","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#grounding-con-citaciones","content":"Forzá al modelo a citar fuentes requiriendo referencias inline:\n\n```\nPara cada afirmación factual en tu respuesta, incluí una cita en el formato [Fuente: nombre_archivo, Sección: nombre_sección].\nSi no podés encontrar evidencia de soporte en el contexto para una afirmación, prefijala con [Sin soporte].\n```\n\nEsto hace que la detección de alucinaciones sea trivial, cualquier afirmación marcada como ` [Sin soporte] ` o sin cita es sospechosa."},{"heading":"Framework de Evaluación","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#framework-de-evaluacin","content":"Un pipeline RAG sin evaluación es una demo, no un producto. La evaluación ocurre en tres niveles."},{"heading":"Nivel 1: Calidad de Retrieval","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#nivel-1-calidad-de-retrieval","content":"Medida independientemente del LLM:\n\n```python\ndef evaluate_retrieval(test_queries, ground_truth, retriever, k=5):\n    \"\"\"Evaluar retrieval con métricas estándar de IR.\"\"\"\n    recalls, mrrs, ndcgs = [], [], []\n\n    for query, relevant_ids in zip(test_queries, ground_truth):\n        retrieved = retriever.search(query, k=k)\n        retrieved_ids = [r[\"id\"] for r in retrieved]\n\n        # Recall@k\n        hits = len(set(retrieved_ids) & set(relevant_ids))\n        recalls.append(hits / len(relevant_ids))\n\n        # MRR\n        for rank, rid in enumerate(retrieved_ids, 1):\n            if rid in relevant_ids:\n                mrrs.append(1.0 / rank)\n                break\n        else:\n            mrrs.append(0.0)\n\n    return {\n        \"recall@k\": np.mean(recalls),\n        \"mrr\": np.mean(mrrs),\n    }\n```"},{"heading":"Nivel 2: Calidad de Generación con RAGAS","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#nivel-2-calidad-de-generacin-con-ragas","content":"RAGAS evalúa el pipeline completo, retrieval y generación juntos:\n\n```python\nfrom ragas import evaluate\nfrom ragas.metrics import (\n    faithfulness,\n    answer_relevancy,\n    context_precision,\n    context_recall,\n)\n\nresults = evaluate(\n    dataset=eval_dataset,  # Preguntas + respuestas ground truth + contextos\n    metrics=[\n        faithfulness,        # ¿La respuesta se mantiene fiel al contexto recuperado?\n        answer_relevancy,    # ¿La respuesta es relevante a la pregunta?\n        context_precision,   # ¿Los chunks recuperados son relevantes?\n        context_recall,      # ¿Se recuperaron todos los chunks necesarios?\n    ],\n)\n\nprint(results)\n# {'faithfulness': 0.89, 'answer_relevancy': 0.91,\n#  'context_precision': 0.82, 'context_recall': 0.87}\n```\n\n**Faithfulness** es la métrica más importante, mide alucinación. Un score de faithfulness por debajo de 0.85 significa que el pipeline está generando afirmaciones no respaldadas por el contexto recuperado."},{"heading":"Nivel 3: Evaluación Humana","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#nivel-3-evaluacin-humana","content":"Automatizar la recolección, evaluar manualmente:\n\n```python\ndef sample_for_human_eval(production_queries, n=200):\n    \"\"\"Muestrear consultas estratificadas por dificultad y tema.\"\"\"\n    # Clasificar consultas por dificultad estimada\n    easy = [q for q in production_queries if q[\"retrieval_confidence\"] > 0.8]\n    medium = [q for q in production_queries if 0.5 < q[\"retrieval_confidence\"] <= 0.8]\n    hard = [q for q in production_queries if q[\"retrieval_confidence\"] <= 0.5]\n\n    sample = (\n        random.sample(easy, min(80, len(easy))) +\n        random.sample(medium, min(80, len(medium))) +\n        random.sample(hard, min(40, len(hard)))\n    )\n\n    return sample\n```\n\nLos evaluadores humanos califican en tres ejes: **correctitud** (factualmente precisa), **completitud** (cubre la pregunta en su totalidad) y **fundamentación** (cita contexto, no alucina)."},{"heading":"Arquitectura de Producción","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#arquitectura-de-produccin","content":""},{"heading":"Vista General del Pipeline","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#vista-general-del-pipeline","content":"```\nDocumentos → Preprocesamiento → Chunking → Embedding → Vector Store\n                                                            ↓\nConsulta → Expansión de Query → Búsqueda Híbrida → Reranker → Constructor de Prompt → LLM → Respuesta\n                                                                                              ↓\n                                                                                        Logger de Evaluación\n```"},{"heading":"Expansión de Consultas","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#expansin-de-consultas","content":"Reescribir la consulta del usuario antes del retrieval para mejorar el recall:\n\n```python\ndef expand_query(query, llm):\n    \"\"\"Generar reformulaciones alternativas para mejorar el retrieval.\"\"\"\n    expansion_prompt = f\"\"\"Generá 3 reformulaciones alternativas de esta consulta de búsqueda.\nCada una debe capturar la misma intención pero usar terminología diferente.\nDevolvé solo las consultas, una por línea.\n\nConsulta: {query}\"\"\"\n\n    alternatives = llm.generate(expansion_prompt).strip().split(\"\\n\")\n    return [query] + alternatives[:3]\n```\n\nEjecutar búsqueda híbrida sobre todas las consultas expandidas y fusionar resultados con RRF. Esto consistentemente agrega 3-5% de mejora en recall."},{"heading":"Capa de Caché","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#capa-de-cach","content":"Cachear en dos niveles para reducir latencia y costo:\n\n1. **Caché de embeddings**: Hashear el texto de entrada, cachear el vector de embedding. Evita re-generar embeddings de consultas idénticas.\n2. **Caché semántico**: Para consultas con similitud coseno \\> 0.95 respecto a una consulta cacheada, devolver la respuesta cacheada. Reduce llamadas al LLM en un 20-40% en casos de uso de soporte donde preguntas similares se repiten."},{"heading":"Monitoreo","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#monitoreo","content":"Rastrear estas métricas en producción:\n\n- **Latencia de retrieval** (p50, p95, p99), objetivo \\< 100ms para híbrida + rerank\n- **Score de faithfulness** sobre una muestra rotativa, alertar si cae por debajo de 0.85\n- **Tasa de retrieval vacío**, consultas donde ningún chunk supera el umbral de relevancia\n- **Señales de feedback del usuario**, thumbs up/down, eventos de copia, preguntas de seguimiento"},{"heading":"Cuándo RAG No Es Suficiente","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#cundo-rag-no-es-suficiente","content":"RAG tiene límites bien definidos:\n\n- **Razonamiento multi-hop**: Cuando la respuesta requiere sintetizar información de 5+ documentos con pasos inferenciales entre ellos, el retrieval de RAG frecuentemente pierde documentos intermedios.\n- **Razonamiento temporal**: \"¿Qué cambió entre Q3 y Q4?\" requiere recuperar y comparar dos conjuntos de documentos específicos en el tiempo, el retrieval top-k estándar no está diseñado para esto.\n- **Cómputo sobre datos**: \"¿Cuál es el SLA promedio en todas las regiones?\" requiere una consulta estructurada, no recuperación de texto.\n\nPara estos casos, considerá **RAG agéntico**, un agente que planifica pasos de retrieval, ejecuta múltiples búsquedas y sintetiza resultados programáticamente, o enfoques híbridos que combinen RAG con consultas SQL y llamadas a APIs."},{"heading":"Implementación reproducible","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#implementacin-reproducible","content":"[RAG Evaluation Workbench](<https://lucioduran.com/es/projects/rag-evaluation-workbench>) compara BM25, recuperación densa e híbrida con el mismo generador local, prompt y presupuesto. La ficha reúne el corpus con licencia, las consultas etiquetadas, los resultados crudos, los intervalos y los comandos para reproducirlos.\n\nEn esa muestra, recuperar todo el contexto relevante no evitó los errores de respuesta ni las fallas de abstención. El experimento evalúa recuperación y generación por separado; no valida las estrategias de chunking, los rerankers ni los objetivos de producción de esta guía."},{"heading":"Fragmentación: evidencia completa y costo del índice","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#fragmentacin-evidencia-completa-y-costo-del-ndice","content":"[Chunking Ablation Suite](<https://lucioduran.com/es/projects/chunking-ablation-suite>) compara ocho configuraciones fijas, estructurales y semánticas con el mismo MiniLM. En 36 consultas de evaluación, structure-96 recuperó toda la evidencia en 33 casos; las configuraciones semánticas, en 12. El repositorio conserva fragmentos, vectores, rankings y costos por tipo de documento. También muestra una ablación de umbral que no cambia ningún límite. Ponerle «semántico» al algoritmo no resuelve la evaluación."},{"heading":"Citas: una fuente relevante todavía puede no respaldar la afirmación","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#citas-una-fuente-relevante-todava-puede-no-respaldar-la-afirmacin","content":"[Citation Grounding Checker](<https://lucioduran.com/es/projects/citation-grounding-checker>) compara reglas conservadoras, FLAN-T5-small y DeBERTa NLI sobre pares de afirmación y pasaje. Publica veredictos, precisión por clase, abstenciones y enlaces exactos a la evidencia. En sus 36 casos sintéticos de evaluación, DeBERTa acertó 31 y se abstuvo en uno. Las etiquetas no tienen revisión humana independiente; esa cifra describe el fixture publicado."},{"heading":"Reranking: medir también cuándo el router no aporta","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#reranking-medir-tambin-cundo-el-router-no-aporta","content":"[Reranker Budget Router](<https://lucioduran.com/es/projects/reranker-budget-router>) asigna presupuestos de 0, 4 o 12 pares y congela las políticas antes de evaluar. Las seis políticas ajustadas omitieron todo reranking, igualaron la calidad del baseline never y agregaron overhead. Always-12 mejoró una consulta. Las trazas permiten inspeccionar esa oportunidad perdida: agregar un router no garantiza que la arquitectura mejore."},{"heading":"Referencias y alcance","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#referencias-y-alcance","content":"Las fuentes siguientes describen métodos de recuperación y evaluación. Este artículo es una guía técnica, no un informe de benchmarks realizados por el autor. Las mejoras y los costos de despliegue requieren mediciones sobre la carga concreta.\n\n- [Lewis et al. Retrieval Augmented Generation](<https://arxiv.org/abs/2005.11401>).\n- [Es et al. Ragas](<https://arxiv.org/abs/2309.15217>).\n- [Elasticsearch Reciprocal Rank Fusion](<https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion>)."},{"heading":"Implementación reproducible: actualización e integridad del índice","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#implementacin-reproducible-actualizacin-e-integridad-del-ndice","content":"[Incremental RAG Index](<https://lucioduran.com/es/projects/incremental-rag-index>) mantiene versiones completas en SQLite, reutiliza embeddings por hash y registra cambios en un diario. Compara reconstrucción completa e incremental en cinco escenarios, con un adaptador determinista y otro MiniLM local, e interrumpe el proceso en cuatro puntos de publicación por adaptador.\n\nEn ambos experimentos coincidieron documentos, vectores, raíces y rankings. Editar, agregar y borrar calculó dos embeddings frente a seis de la reconstrucción completa. Los cortes anteriores al commit conservaron la versión vieja; el posterior conservó la nueva y su reintento no creó otra. Los documentos borrados desaparecen del índice actual, pero permanecen en versiones históricas. La evidencia demuestra coherencia sobre seis documentos sintéticos, no calidad semántica ni rendimiento a escala."},{"heading":"Preguntas frecuentes","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#questions","content":""},{"heading":"¿Qué tamaño de chunk debería usar para RAG?","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#question-1","content":"No hay un tamaño universal. Compará tamaños candidatos con consultas etiquetadas y registrá calidad de recuperación, truncamiento del contexto y calidad de generación."},{"heading":"¿La búsqueda híbrida es siempre mejor que solo vectorial?","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#question-2","content":"No. La recuperación híbrida puede ayudar con términos exactos y similitud semántica, pero también puede introducir ruido. Compará los métodos sobre las mismas consultas reservadas."},{"heading":"¿Cómo evalúo un pipeline RAG de forma sistemática?","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#question-3","content":"Usá un enfoque de tres capas: (1) métricas de retrieval como recall@k, MRR y NDCG sobre un set de consultas etiquetadas, (2) métricas de generación como faithfulness y answer relevance usando RAGAS, y (3) evaluación humana end-to-end sobre un set dorado rotativo de 200+ consultas."},{"heading":"¿Cuándo debería usar un reranker en vez de mejorar los embeddings?","level":3,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#question-4","content":"Medí embeddings y reranking como cambios separados. Incorporá un reranker si la mejora observada justifica su latencia y su costo para esa carga."},{"heading":"Tips","level":2,"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#article-tips","content":"1. Compará estrategias de chunking con consultas etiquetadas. El tamaño y el overlap son parámetros iniciales que hay que ajustar, no valores óptimos universales.\n2. Combiná BM25 y recuperación densa con RRF, y compará recall@k y NDCG contra cada método por separado. La mejora depende del corpus y de las consultas.\n3. Evaluá un cross-encoder sobre los candidatos recuperados y medí relevancia, latencia y costo juntos. Este artículo no establece una mejora porcentual ni una latencia fija.\n4. **Trampa de evaluación:** No uses LLM-as-judge exclusivamente. Combinalo con métricas de retrieval (recall@k, MRR) y evaluación humana sobre un set dorado de 200 consultas. Los jueces LLM tienen puntos ciegos en precisión factual.\n\n- rag\n- retrieval-augmented-generation\n- chunking\n- reranking\n- hybrid-search\n- embeddings\n- llm\n- evaluation\n- ragas\n- ai-agents\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)"}],"links":[{"text":"Lucio Durán","url":"https://lucioduran.com/es"},{"text":"English","url":"https://lucioduran.com/blog/rag-pipeline-architecture-chunking-reranking-evaluation"},{"text":"RAG Evaluation Workbench","url":"https://lucioduran.com/es/projects/rag-evaluation-workbench"},{"text":"Chunking Ablation Suite","url":"https://lucioduran.com/es/projects/chunking-ablation-suite"},{"text":"Citation Grounding Checker","url":"https://lucioduran.com/es/projects/citation-grounding-checker"},{"text":"Reranker Budget Router","url":"https://lucioduran.com/es/projects/reranker-budget-router"},{"text":"Lewis et al. Retrieval Augmented Generation","url":"https://arxiv.org/abs/2005.11401"},{"text":"Es et al. Ragas","url":"https://arxiv.org/abs/2309.15217"},{"text":"Elasticsearch Reciprocal Rank Fusion","url":"https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion"},{"text":"Incremental RAG Index","url":"https://lucioduran.com/es/projects/incremental-rag-index"},{"text":"Volver al Blog","url":"https://lucioduran.com/es/blog"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com/"},{"@type":"WebSite","@id":"https://lucioduran.com/#website","url":"https://lucioduran.com/","name":"Lucio Durán","inLanguage":["en","es"],"publisher":{"@id":"https://lucioduran.com/#person"}},{"@type":"WebPage","@id":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#page","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation","name":"Arquitectura de Pipelines RAG: Estrategias de Chunking, Búsqueda Híbrida, Reranking y Frameworks de Evaluación | Lucio Durán","description":"Una guía orientada a producción para construir pipelines de Retrieval-Augmented Generation, desde la ingesta de documentos y estrategias de chunking hasta búsqueda híbrida, reranking con cross-encoders y evaluación sistemática con RAGAS y métricas propias.","inLanguage":"es","author":{"@id":"https://lucioduran.com/#person"},"publisher":{"@id":"https://lucioduran.com/#person"},"isPartOf":{"@id":"https://lucioduran.com/#website"},"isAccessibleForFree":true,"datePublished":"2026-04-16","dateModified":"2026-10-22","mainEntity":{"@id":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#article"},"breadcrumb":{"@id":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#breadcrumb"}},{"@type":"BlogPosting","@id":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#article","mainEntityOfPage":{"@id":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#page"},"headline":"Arquitectura de Pipelines RAG: Estrategias de Chunking, Búsqueda Híbrida, Reranking y Frameworks de Evaluación","description":"Una guía orientada a producción para construir pipelines de Retrieval-Augmented Generation, desde la ingesta de documentos y estrategias de chunking hasta búsqueda híbrida, reranking con cross-encoders y evaluación sistemática con RAGAS y métricas propias.","datePublished":"2026-04-16","dateModified":"2026-10-22","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"publisher":{"@id":"https://lucioduran.com/#person"},"url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation","inLanguage":"es","isPartOf":{"@id":"https://lucioduran.com/es/blog#blog"},"articleSection":"AI/ML","keywords":"rag, retrieval-augmented-generation, chunking, reranking, hybrid-search, embeddings, llm, evaluation, ragas, ai-agents","wordCount":2853,"timeRequired":"PT16M","workTranslation":[{"@type":"BlogPosting","@id":"https://lucioduran.com/blog/rag-pipeline-architecture-chunking-reranking-evaluation#article","inLanguage":"en","url":"https://lucioduran.com/blog/rag-pipeline-architecture-chunking-reranking-evaluation"}],"isAccessibleForFree":true},{"@type":"Blog","@id":"https://lucioduran.com/es/blog#blog","url":"https://lucioduran.com/es/blog","name":"Blog","inLanguage":"es","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com/"},"publisher":{"@id":"https://lucioduran.com/#person"},"isPartOf":{"@id":"https://lucioduran.com/#website"}},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Inicio","item":"https://lucioduran.com/es"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://lucioduran.com/es/blog"},{"@type":"ListItem","position":3,"name":"Arquitectura de Pipelines RAG: Estrategias de Chunking, Búsqueda Híbrida, Reranking y Frameworks de Evaluación","item":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation"}],"@id":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#breadcrumb"},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"¿Qué tamaño de chunk debería usar para RAG?","acceptedAnswer":{"@type":"Answer","text":"No hay un tamaño universal. Compará tamaños candidatos con consultas etiquetadas y registrá calidad de recuperación, truncamiento del contexto y calidad de generación."}},{"@type":"Question","name":"¿La búsqueda híbrida es siempre mejor que solo vectorial?","acceptedAnswer":{"@type":"Answer","text":"No. La recuperación híbrida puede ayudar con términos exactos y similitud semántica, pero también puede introducir ruido. Compará los métodos sobre las mismas consultas reservadas."}},{"@type":"Question","name":"¿Cómo evalúo un pipeline RAG de forma sistemática?","acceptedAnswer":{"@type":"Answer","text":"Usá un enfoque de tres capas: (1) métricas de retrieval como recall@k, MRR y NDCG sobre un set de consultas etiquetadas, (2) métricas de generación como faithfulness y answer relevance usando RAGAS, y (3) evaluación humana end-to-end sobre un set dorado rotativo de 200+ consultas."}},{"@type":"Question","name":"¿Cuándo debería usar un reranker en vez de mejorar los embeddings?","acceptedAnswer":{"@type":"Answer","text":"Medí embeddings y reranking como cambios separados. Incorporá un reranker si la mejora observada justifica su latencia y su costo para esa carga."}}],"@id":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#questions","url":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#questions","inLanguage":"es","isPartOf":{"@id":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation#page"},"author":{"@id":"https://lucioduran.com/#person"}}]}],"contentHash":"1da670c97c54d42e7d9eb9fbad3d8e1e59eec9fa60fffd9070768c1fdc3e7bca","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/es/blog/rag-pipeline-architecture-chunking-reranking-evaluation","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":114119,"contentBytes":27404}}
