Inicio / Artículos / Comparación de RAG, RAG sin vectores y GraphRAG

Comparación de RAG, RAG sin vectores y GraphRAG

RAG vectorial clásico, recuperación sin vectores léxicos y GraphRAG: fragmentación, embeddings, BM25, grafos de múltiples saltos, y cuándo cada enfoque resulta útil.

1834 palabras

Cómo responden los modelos de lenguaje actuales a partir de material que nunca apareció en el entrenamiento.

RAG en una frase

Retrieval Augmented Generation es exactamente lo que indica la sigla. Primero se obtiene material relacionado con la pregunta del usuario. Luego un modelo escribe una respuesta utilizando ese material junto con el prompt. El patrón es primero la recuperación de información y después la generación.

¿Por qué molestarse?

Los modelos solo conocen lo que estaba incluido en su fecha límite de entrenamiento. Todo lo demás es invisible.

Imagínese un libro escaso de 3,000 páginas que apenas aparece en línea y que nunca formó parte de ningún corpus de entrenamiento. Si le hace una pregunta al modelo, obtendrá respuestas vacías. Incluso si se utilizan herramientas de extracción de datos, seguirá habiendo respuestas vacías: no hay nada que pueda ser extraído.

RAG cierra esa brecha. Carga el PDF en la pipeline. En el momento de la consulta, se extraen las secciones más relevantes y se adjuntan como contexto. La tarea del modelo se reduce a: responder a partir de este texto, utilizando sus habilidades lingüísticas para dar forma a la respuesta.

No se requiere ajuste fino. Solo el contexto adecuado en el momento oportuno.

Estructura básica de la pipeline

Primero viene el indexado, y este comienza con la división en fragmentos.

Estrategias de fragmentación

Un PDF de miles de páginas no debe almacenarse en un repositorio vectorial como un único bloque. Hay que dividirlo para que cada fragmento pueda integrarse y recuperarse por separado.

Divisiones comunes:

Por página. Una página → un fragmento (3,000 páginas → 3,000 fragmentos). Útil cuando se buscan resultados precisos y detallados.

Por párrafo. Se realizan cortes más precisos en los límites de los párrafos. Puede dar una sensación de mayor agilidad, pero los tamaños varían enormemente (200 tokens junto a 2,000). Las longitudes irregulares generan embeddings irregulares y dañan silenciosamente la recuperación de información.

Ventanas fijas. Presupuestos de tokens constantes, generalmente 512, independientemente del lugar donde se realice el corte. Los tamaños uniformes producen embeddings más comparables. Cuando no se está seguro, la mayoría de los equipos eligen esta opción por defecto.

from langchain_text_splitters import CharacterTextSplitter
from langchain_core.documents import Document

def perform_fixed_size_chunking(document, chunk_size=1000, chunk_overlap=200😞
    """
    Performs fixed-size chunking on a document with specified overlap.

    Args:
        document (str): The text document to process
        chunk_size (int): The target size of each chunk in characters
        chunk_overlap (int): The number of characters of overlap between chunks

    Returns:
        list: The chunked documents with metadata
    """
    # Create the text splitter with optimal parameters
    text_splitter = CharacterTextSplitter(
        separator="\n\n",
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        length_function=len
    )

    # Split the text into chunks
    chunks = text_splitter.split_text(document)
    print(f"Document split into {len(chunks)} chunks")

    # Convert to Document objects with metadata
    documents = []
    for i, chunk in enumerate(chunks):
        doc = Document(
            page_content=chunk,
            metadata={
                "chunk_id": i,
                "total_chunks": len(chunks),
                "chunk_size": len(chunk),
                "chunk_type": "fixed-size"
            }
        )
        documents.append(doc)

    return documents

# Example usage
if __name__ == "__main__":

    # Create the dummy document
    document = create_dummy_document()

    # Process with fixed-size chunking
    chunked_docs = perform_fixed_size_chunking(
        document,
        chunk_size=1000,
        chunk_overlap=200
    )

    # Display results
    print("\n----- CHUNKING RESULTS -----")
    print(f"Total chunks: {len(chunked_docs)}")

    # Print an example chunk
    print("\n----- EXAMPLE CHUNK -----")
    middle_chunk_idx = len(chunked_docs) // 2
    example_chunk = chunked_docs[middle_chunk_idx]
    print(f"Chunk {middle_chunk_idx} content ({len(example_chunk.page_content)} characters):")
    print("-" * 40)
    print(example_chunk.page_content)
    print("-" * 40)
    print(f"Metadata: {example_chunk.metadata}")

    # For integration with Databricks Vector Search
    print("\nThese documents are ready for embedding and storage in Databricks Vector Search")
    print("Example next steps:")
    print("1. Create embeddings using the Databricks embedding endpoint")
    print("2. Store documents and embeddings in Delta table")
    print("3. Create Vector Search index for retrieval")

Embeddings

Un embedding convierte texto (palabra, oración, página) en un vector denso de alta dimensión que codifica el significado, de modo que las ideas similares se agrupen.

Cuatro fases dentro de RAG

  1. Lado del corpus. Insertar embedding en cada fragmento; almacenar el índice y el vector.
  2. Lado de la consulta. Insertar embedding en el mensaje del usuario para que la solicitud tenga una huella semántica.
  3. Búsqueda. Buscar en el almacenamiento los vecinos más cercanos, generalmente mediante similitud de coseno.
  • Aumento. Adjunte los fragmentos recuperados como contexto adicional; el modelo responde a partir de la instrucción más el contexto.
  • Dónde se almacenan los vectores

    Se trata de un almacenamiento especializado para embeddings, no de una base de datos relacional u objetiva general. AlloyDB, Pinecone y Qdrant son ejemplos comunes; muchos equipos utilizan pgvector con PostgreSQL.

    Dónde tiene dificultades el RAG vectorial tradicional

    1. Los recortes pueden ignorar el significado. Las ventanas relacionadas pueden separarse; una mayor superposición ayuda a veces, pero no es una solución universal.
    2. La similitud puede pasar por alto las paráfrasis: frases como “las ventas disminuyeron” y “la empresa está en declive” podrían no quedar cerca en el espacio vectorial.
    3. Los hechos de múltiples pasos fallan cuando la causa y el efecto se encuentran en fragmentos diferentes y solo se recupera uno de ellos.
    4. Crear, almacenar, indexar y volver a indexar embeddings es costoso.

    También: una base de datos vectorial no es sinónimo de RAG. Es un tipo de backend de recuperación. El RAG sin vectores mantiene el enfoque “obtener luego generar”, eliminando la búsqueda mediante embeddings.

    ¿Por qué evitar los vectores? El costo de creación/reindexación de embeddings, un comportamiento deficiente en búsquedas exactas para IDs/números/códigos de error, y la necesidad de infraestructura adicional para su funcionamiento.

    El enfoque sin vectores es una familia de métodos, no una única solución:

    1. Búsqueda léxica. BM25, Postgres tsvector, Elasticsearch: los términos exactos son más eficaces que las semánticas difusas para SKUs, citas y líneas de registro.
    from rank_bm25 import BM25Okapi
    
    def vectorless_retrieve(query, corpus_chunks, top_k=3):
        """
        Lexical retrieval over raw text chunks - no embeddings, no vector DB.
        """
        tokenized_corpus = [chunk.lower().split() for chunk in corpus_chunks]
        bm25 = BM25Okapi(tokenized_corpus)
    
        tokenized_query = query.lower().split()
        scores = bm25.get_scores(tokenized_query)
    
        ranked = sorted(zip(corpus_chunks, scores), key=lambda x: x[1], reverse=True)
        return [chunk for chunk, score in ranked[:top_k]]
    
    1. Recuperación mediante agentes/herramientas. Sin indexación previa; el modelo busca, llama a APIs de búsqueda o abre secciones según sea necesario, al igual que un agente de programación que explora un repositorio. Búsqueda en tiempo real basada en razonamiento.
  • Relleno de contexto largo. Con ventanas de contexto enormes, los corpus pequeños pueden incluirse en la petición. No se trata de una recuperación clásica, pero se obtienen resultados similares con corpus modestos.
  • Reclasificación híbrida. Se crea una lista corta léxica económica y luego un modelo reordena los elementos según su relevancia: velocidad con palabras clave y cierta nuance semántica, sin necesidad de un índice de embeddings completo desde el principio.
  • Límites de los métodos sin vectores

    Las palabras clave siguen sin capturar las paráfrasis, a veces incluso peor que los embeddings. Los bucles de tipo agente añaden latencia y más tokens por consulta. Los corpus enormes siguen favoreciendo un índice de vectores bien estructurado. Los métodos sin vectores suelen ser más eficaces a escala pequeña o media, o cuando la exactitud es más importante que la vaguedad.

    GraphRAG

    El RAG clásico puede separar la causa del efecto dentro de los diferentes fragmentos. Los métodos sin vectores sustituyen los embeddings por palabras clave o contexto largo. Ninguno de estos enfoques modela cómo se relacionan las ideas. Graph RAG busca subsanar esa deficiencia.

    En lugar de preguntar “¿qué fragmento está más cerca?”, pregúntese “¿cómo se conectan estos conceptos?”. La respuesta es un grafo de conocimiento.

    Indexación: hacer crecer el grafo

    Omite primero los fragmentos o incrustaciones. Pasa el documento por un modelo que extrae entidades y relaciones. El resultado son nodos y aristas.

    En ese largo libro teórico podrías obtener nodos como Marx, Engels, El Capital, El Manifiesto Comunista, plusvalía, materialismo dialéctico, y aristas como escribió, coescribió, introduce-concepto.

    Las entidades se convierten en nodos; las relaciones, en aristas. Estás mapeando el significado, no dividiendo páginas.

    Agrupa los nodos estrechamente vinculados en comunidades (Leiden es popular), luego resume cada comunidad con otra pasada del modelo.

    Opera en dos niveles:

    • Nodos para hechos específicos y enlaces directos
    • Comunidades para temas y resúmenes

    Preguntas específicas → nodos. Preguntas generales sobre “ideas centrales” → resúmenes comunitarios. Un índice, dos modos de recuperación.

    Consultas: recorrer el grafo

    Pregunta: “¿Quiénes coescribieron con Marx y qué escribieron juntos?”

    RAG tradicional tiene dificultades: la coautoría está en un mismo fragmento, mientras que las obras se encuentran a cientos de páginas de distancia; hay que confiar en que los embeddings coincidan.

    RAG basado en grafo recorre:

    1. Detectar a Marx como punto de anclaje
    2. Cargar el nodo y las aristas de Marx
    3. Siguir la relación coescribió → Engels
    4. Siguir las obras escritas por Engels → Manifiesto, La condición de la clase obrera y otras obras relacionadas

    Las aristas direccionales mantienen intactas las relaciones. Las causas y efectos que en el RAG clásico se separan se convierten en nodos conectados. Ese patrón de múltiples saltos es difícil de manejar de forma limpia tanto en el RAG básico como en aquel sin vectores.

    Se reúnen nodos, aristas y resúmenes comunitarios para formar el contexto; luego se genera el resultado como de costumbre.

    from graphrag import GraphRAGPipeline
    
    # Indexing — runs once
    pipeline = GraphRAGPipeline(llm="claude-3", graph_store="neo4j")
    pipeline.index(documents=["book.pdf"])
    # Under the hood: entity extraction → graph build → community detection → summaries
    
    # Querying
    result = pipeline.query(
        "Who co-wrote with Marx and what did they write together?",
        mode="global"   # uses community summaries for broad questions
        # mode="local"  # uses node-level traversal for specific facts
    )
    print(result.answer)
    print(result.sources)   # returns actual nodes + edges used, fully traceable
    

    mode no es algo estético. Las implementaciones (incluida la pila de código abierto de Microsoft) distinguen entre enfoques globales y locales porque se trata de estrategias diferentes.

    Costos de GraphRAG

    La extracción es costosa. Es necesario leer todo el documento; los libros largos consumen muchos tokens; las referencias implícitas (“como se discutió anteriormente”) podrían no convertirse nunca en conexiones entre nodos.

    La calidad del grafo limita la calidad del resultado. Una mala extracción genera un grafo deficiente, lo que a su vez afecta negativamente la recuperación de información. Las soluciones suelen requerir volver a leer todo, lo cual resulta problemático a gran escala.

    En la práctica, elija el método de recuperación según el tipo de consulta en lugar de forzar siempre el mismo enfoque para cada pregunta.

    Resumen: utilice RAG clásico para búsquedas semánticas, sin vectores cuando necesite precisión o menos recursos infraestructurales, y Graph RAG cuando las relaciones entre datos sean el elemento clave.

    Elegir un estilo de recuperación sin dogmas

    Una secuencia útil para tomar decisiones se ve así.

    Comience con el RAG vectorial clásico cuando su corpus sea grande, el lenguaje varíe mucho y lo que suele necesitar son vecinos semánticos aproximados. Invierta desde el principio en la calidad del fragmentado y en los procesos de actualización de embeddings; esos son los factores que determinan la calidad.

    Opte por técnicas sin vectores cuando los identificadores exactos sean más importantes que las paráfrasis, cuando el corpus sea lo suficientemente pequeño como para permitir búsquedas léxicas o contextos extensos, o cuando no quiera asumir los costos de la infraestructura de embeddings. BM25 y similares no son “obsoletos”: son la herramienta adecuada para códigos de producto, citas y cadenas de errores.

    Utilice Graph RAG cuando la pregunta sobre el producto sea relacional: quién se conectó con quién, qué concepto introdujo qué idea, qué comunidad resume un tema. Espere costos de indexación más altos y trate la calidad de la extracción como una dependencia de primer orden.

    Muchos equipos terminan adoptando un enfoque híbrido: un primer paso léxico, un segundo paso vectorial y un modelo gráfico para un subconjunto de dominios con múltiples saltos. El objetivo no es elegir una única metodología, sino adaptar el mecanismo de recuperación al tipo de fallos que realmente se presentan.

    Notas operativas que las demostraciones omiten

    Los horarios de reindexación son importantes. Los embeddings obsoletos deterioran silenciosamente el rendimiento del RAG vectorial, incluso cuando el modelo no ha cambiado. Los ajustes de superposición son cruciales cuando las ventanas adyacentes comparten significado. Los filtros de metadatos son esenciales cuando los usuarios no deben ver nunca los fragmentos de otros. Los conjuntos de evaluación son importantes cuando se afirma que algo es “mejor” sin etiquetas.

    Para Graph RAG, hay que planificar intentos repetidos de extracción, actualizaciones parciales del grafo y resúmenes comunitarios cuando los documentos cambian. En el caso de la recuperación agente, es necesario establecer límites en cuanto al uso de herramientas y políticas de tiempo de espera, para que un modelo curioso no agote toda la reserva de tokens en una sola consulta.

    Ninguna de estas notas es glamorosa. Representan la diferencia entre un diagrama y un sistema que puede resistir un mes de tráfico real.

    Lecturas relacionadas

  • RAG explicado: Cómo evitar que los chatbots inventen hechos de la empresa — Una guía práctica sobre el pipeline RAG: cargadores, fragmentación, embeddings, almacenes vectoriales, reclasificación, búsqueda híbrida y RRF; además, cuándo no utilizar la recuperación de información en absoluto.
  • La fragmentación es todo el sistema de recuperación: la trampa Spring AI 800 vs 128 — Cuando el divisor genera fragmentos de 800 tokens pero el generador de embeddings solo procesa 128, la recuperación no puede ver la parte truncada. El tamaño del fragmento, el ventana y la superposición constituyen el sistema de recuperación.
  • Por qué el RAG empresarial necesita búsqueda híbrida y no solo vectores — Las representaciones incrustadas omiten los IDs de facturas y códigos de error; BM25 combinado con recuperación densa mediante fusión soluciona el problema de los identificadores empresariales.