Blog

Técnicas avanzadas de RAG parte 1: Procesamiento de datos

Discutir e implementar técnicas que puedan aumentar el rendimiento de RAG. Parte 1 de 2, centrada en el componente de procesamiento e ingesta de datos de una tubería avanzada de RAG.

Esta es la Parte 1 de nuestra exploración sobre las Técnicas Avanzadas de RAG. ¡Haz clic aquí para la Parte 2!

El reciente artículo Searching for Best Practices in Retrieval-Augmented Generation evalúa empíricamente la eficacia de diversas técnicas de mejora del RAG, con el objetivo de converger en un conjunto de mejores prácticas para el RAG.

Oleoducto RAG recomendado por Wang

El gasoducto RAG recomendado por Wang y sus colegas.

Implementaremos algunas de estas mejores prácticas propuestas, concretamente aquellas que buscan mejorar la calidad de la búsqueda (fragmentación de oraciones, HyDE, empaquetado inverso).

Por brevedad, omitiremos aquellas técnicas centradas en mejorar la eficiencia (Clasificación de Consultas y Resumen).

También implementaremos algunas técnicas que no se trataron, pero que personalmente encuentro útiles e interesantes (Inclusión de Metadatos, Incrustaciones Compuestas Multi-Campo, Enriquecimiento de Consultas).

Finalmente, realizaremos una breve prueba para ver si la calidad de nuestros resultados de búsqueda y respuestas generadas mejoró respecto a la línea base. ¡Vamos a ello!

Resumen de RAG

RAG tiene como objetivo mejorar los LLMs recuperando información de bases de conocimiento externas para enriquecer las respuestas generadas. Al proporcionar información específica de dominio, los LLM pueden adaptar rápidamente a casos de uso fuera del alcance de sus datos de entrenamiento; Significativamente más barato que el ajuste fino y más fácil de mantener actualizado.

Las medidas para mejorar la calidad de RAG suelen centrar en dos pistas:

  1. Mejorar la calidad y claridad de la base de conocimiento.

  2. Mejorar la cobertura y especificidad de las consultas de búsqueda.

Estas dos medidas lograrán el objetivo de mejorar las probabilidades de que el LLM tenga acceso a hechos e información relevantes, y así sea menos probable que alucine o se base en su propio conocimiento, que puede estar desactualizado o irrelevante.

La diversidad de métodos es difícil de aclarar en solo unas pocas frases. Vamos directamente a la implementación para aclarar las cosas.

Pipeline avanzado de RAG

Figura 1: La tubería RAG empleada por el autor.

Índice

Preparación

Todo el código puede encontrar en el repositorio de Searchlabs.

Primero lo primero. Necesitarás lo siguiente:

  1. Un despliegue de nube elástica

  2. Una API LLM - Estamos usando un despliegue GPT-4o en Azure OpenAI en este cuaderno

  3. Python Versión 3.12.4 o posterior

Ejecutaremos todo el código desde el cuaderno main.ipynb.

Adelante, clona el repositorio por git, navega a supporting-blog-content/advanced-rag-techniques y luego ejecuta los siguientes comandos:

# Create a new virtual environment named 'rag_env'
python -m venv rag_env

# Activate the virtual environment (for Unix-based systems)
source rag_env/bin/activate

# (For Windows)
.\rag_env\Scripts\activate

# Install packages listed in requirements.txt
pip install -r requirements.txt

Una vez hecho esto, crea un .env y rellenar los siguientes campos (Referenciado en .env.example). Créditos a mi coautor, Claude-3.5, por los comentarios útiles.

# Elastic Cloud: Found in the 'Deployment' page of your Elastic Cloud 
# console
ELASTIC_CLOUD_ENDPOINT=""
ELASTIC_CLOUD_ID=""

# Elastic Cloud: Created during deployment setup or in 'Security' 
# settings
ELASTIC_USERNAME=""
ELASTIC_PASSWORD=""

# Elastic Cloud: The name of the index you created in Kibana or via API
ELASTIC_INDEX_NAME=""

# Azure AI Studio: Found in 'Keys and Endpoint' section of your Azure 
# OpenAI resource
AZURE_OPENAI_KEY_1=""
AZURE_OPENAI_KEY_2=""
AZURE_OPENAI_REGION=""
AZURE_OPENAI_ENDPOINT=""

# Azure AI Studio: Found in 'Deployments' section of your Azure OpenAI 
# resource
AZURE_OPENAI_DEPLOYMENT_NAME=""

# Using BAAI/bge-small-en-v1.5 because I think it is a good balance of 
# resource efficiency and performance. 
HUGGINGFACE_EMBEDDING_MODEL="BAAI/bge-small-en-v1.5"

A continuación, elegimos el documento a ingerir y lo colocaremos en la carpeta de documentos. Para este artículo, emplearemos el Reporte Anual 2023 de Elastic N.V. Es un documento bastante exigente y denso, perfecto para poner a prueba nuestras técnicas RAG.

Reporte Anual de Elastic 2023

Reporte Anual de Elastic 2023

Ahora que estamos listos, vamos a ingestión. Abre main.ipynb y ejecuta las dos primeras celdas para importar todos los paquetes e iniciar todos los servicios.

Volver arriba

Ingestión, procesamiento e incrustación de documentos

Ingesta de datos

  • Nota personal: Me sorprende la comodidad de LlamaIndex. En la antigüedad, antes de los LLMs y LlamaIndex, ingerir documentos de varios formatos era un proceso doloroso de recopilar paquetes esotéricos de todas partes. Ahora se reduce a una sola llamada de función. Salvaje.

El SimpleDirectoryReader cargará todos los documentos del directory_path. Para .pdf archivos, devuelve una lista de objetos documento, que convierto a diccionarios de Python porque me resultan más fáciles de manejar.

# llamaindex_processor.py
from llama_index.core import SimpleDirectoryReader

class LlamaIndexProcessor:
   def __init__(self):
       pass 
   
   def load_documents(self, directory_path):
       ''' 
       Load all documents in directory
       '''
       reader = SimpleDirectoryReader(input_dir=directory_path)
       return reader.load_data()

# main.ipynb
llamaindex_processor=LlamaIndexProcessor()
documents=llamaindex_processor.load_documents('./documents/')
documents=[dict(doc_obj) for doc_obj in documents]

Cada diccionario contiene el contenido clave en el campo text . También contiene metadatos útiles como número de página, nombre de archivo, tamaño y tipo.

{
  'id_': '5f76f0b3-22d8-49a8-9942-c2bbab14f63f',
  'metadata': {'page_label': '5',
   'file_name': 'Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf',
   'file_path': '/Users/han/Desktop/Projects/truckasaurus/documents/Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf',
   'file_type': 'application/pdf',
   'file_size': 3724426,
   'creation_date': '2024-07-27',
   'last_modified_date': '2024-07-27'},
   'text': 'Table of Contents\nPage\nPART I\nItem 1. Business 3\n15 Item 1A. Risk Factors\nItem 1B. Unresolved Staff Comments 48\nItem 2. Properties 48\nItem 3. Legal Proceedings 48\nItem 4. Mine Safety Disclosures 48\nPART II\nItem 5. Market for Registrant's Common Equity, Related Stockholder Matters and Issuer Purchases of \nEquity Securities49\nItem 6. [Reserved] 49\nItem 7. Management's Discussion and Analysis of Financial Condition and Results of Operations 50\nItem 7A. Quantitative and Qualitative Disclosures About Market Risk 64\nItem 8. Financial Statements and Supplementary Data 66\nItem 9. Changes in and Disagreements With Accountants on Accounting and Financial Disclosure 100\n100\n101Item 9A. Controls and Procedures\nItem 9B. Other Information\nItem 9C. Disclosure Regarding Foreign Jurisdictions That Prevent Inspections 101\nPART III\n102\n102\n102\n102Item 10. Directors, Executive Officers and Corporate Governance\nItem 11. Executive Compensation\nItem 12. Security Ownership of Certain Beneficial Owners and Management, and Related Stockholder Matters  \nItem 13. Certain Relationships and Related Transactions, and Director Independence\nItem 14. Principal Accountant Fees and Services 102\nPART IV\n103\n105Item 15. Exhibits and Financial Statement Schedules  \nItem 16. Form 10-K Summary\nSignatures 106\ni',
   ...
}

Volver arriba

Fragmentación a nivel de frase, por fichas

Lo primero que hay que hacer es reducir nuestros documentos a fragmentos de una longitud estándar (para garantizar la coherencia y la manejabilidad). Los modelos de incrustación tienen límites únicos de tokens (tamaño máximo de entrada que pueden procesar). Los tokens son las unidades básicas de texto que procesan los modelos. Para evitar la pérdida de información (truncamiento u omisión de contenido), deberíamos proporcionar texto que no exceda esos límites (dividiendo textos más largos en segmentos más pequeños).

El chunking tiene un impacto significativo en el rendimiento. Idealmente, cada fragmento representaría una pieza de información autónoma, capturando información contextual sobre un único tema. Los métodos de fragmentación incluyen el fragmento a nivel de palabra, donde los documentos se dividen por el recuento de palabras, y el fragmento semántico, que emplea un LLM para identificar puntos de interrupción lógicos.

El fragmento a nivel de palabra es barato, rápido y sencillo, pero corre el riesgo de fragmentar las frases y así romper el contexto. El fragmento semántico se vuelve lento y caro, especialmente si se trata de documentos como el Reporte Anual de Elastic de 116 páginas.

Elijamos un enfoque intermedio. El fragmento a nivel de oración sigue siendo sencillo, pero puede preservar el contexto de forma más eficaz que el fragmento a nivel de palabra, siendo significativamente más barato y rápido. Además, implementaremos una ventana deslizante para capturar parte del contexto circundante y aliviar el impacto de dividir los párrafos.

# chunker.py 

import uuid
import re


class Chunker: 
    def __init__(self, tokenizer):
        self.tokenizer = tokenizer 
    
    def split_into_sentences(self, text):
        """Split text into sentences."""
        return re.split(r'(?<=[.!?])\s+', text)
 
    def sentence_wise_tokenized_chunk_documents(self, documents, chunk_size=512, overlap=20, min_chunk_size=50):
        '''
        1. Split text into sentences.
        2. Tokenize using the provided tokenizer method.
        3. Build chunks up to the chunk_size limit.
        4. Create an overlap based on tokens - to preserve context.
        5. Only keep chunks that meet the minimum token size requirement.
        '''
        chunked_documents = []

        for doc in documents:
            sentences = self.split_into_sentences(doc['text'])
            tokens = []
            sentence_boundaries = [0]

            # Tokenize all sentences and keep track of sentence boundaries
            for sentence in sentences:
                sentence_tokens = self.tokenizer.encode(sentence, add_special_tokens=True)
                tokens.extend(sentence_tokens)
                sentence_boundaries.append(len(tokens))

            # Create chunks
            chunk_start = 0
            while chunk_start < len(tokens):
                chunk_end = chunk_start + chunk_size

                # Find the last complete sentence that fits in the chunk
                sentence_end = next((i for i in sentence_boundaries if i > chunk_end), len(tokens))
                chunk_end = min(chunk_end, sentence_end)

                # Create the chunk
                chunk_tokens = tokens[chunk_start:chunk_end]

                # Check if the chunk meets the minimum size requirement
                if len(chunk_tokens) >= min_chunk_size:
                    # Create a new document object for this chunk
                    chunk_doc = {
                        'id_': str(uuid.uuid4()),
                        'chunk': chunk_tokens,
                        'original_text': self.tokenizer.decode(chunk_tokens),
                        'chunk_index': len(chunked_documents),
                        'parent_id': doc['id_'],
                        'chunk_token_count': len(chunk_tokens)
                    }

                    # Copy all other fields from the original document
                    for key, value in doc.items():
                        if key != 'text' and key not in chunk_doc:
                            chunk_doc[key] = value

                    chunked_documents.append(chunk_doc)

                # Move to the next chunk start, considering overlap
                chunk_start = max(chunk_start + chunk_size - overlap, chunk_end - overlap)

        return chunked_documents

# main.ipynb 
# Initialize Embedding Model
HUGGINGFACE_EMBEDDING_MODEL = os.environ.get('HUGGINGFACE_EMBEDDING_MODEL')
embedder=EmbeddingModel(model_name=HUGGINGFACE_EMBEDDING_MODEL)

# Initialize Chunker
chunker=Chunker(embedder.tokenizer)

La clase Chunker incorpora el tokenizador del modelo de incrustación para codificar y decodificar texto. Ahora construiremos fragmentos de 512 tokens cada uno, con una superposición de 20 tokens. Para ello, dividiremos el texto en frases, tokenizaremos esas frases y luego agregaremos las frases tokenizadas a nuestro fragmento actual hasta que no podamos agregar más sin superar nuestro límite de tokens.

Finalmente, decodifica las frases de nuevo al texto original para incrustarlas, almacenándola en un campo llamado original_text. Los chunks se almacenan en un campo llamado chunk. Para reducir el ruido (es decir, documentos inútiles), descartaremos cualquier documento de menos de 50 tokens de longitud.

Vamos a repasarla por nuestros documentos:

chunked_documents=chunker.sentence_wise_tokenized_chunk_documents(documents, chunk_size=512)

Y que me devolvan fragmentos de texto que se parezcan a esto:

print(chunked_documents[4]['original_text'])

[CLS] the aggregate market value of the ordinary shares held by non - affiliates of the registrant, 
based on the closing price of the shares of ordinary shares on the new york stock exchange on 
october 31, 2022 ( the last business day of the registrant 's second fiscal quarter ), was 
approximately $ 6. 1 billion. [SEP] [CLS] as of may 31, 2023, the registrant had 97, 390, 886 
ordinary shares, par value €0. 01 per share, outstanding. [SEP] [CLS] documents incorporated by 
reference portions of the registrant 's definitive proxy statement relating to the registrant 's 2
023 annual general meeting of shareholders are incorporated by reference into part iii of this annual 
...
...

Volver arriba

Inclusión y generación de metadatos

Dividimos nuestros documentos. Ahora es el momento de enriquecer los datos. Quiero generar o extraer metadatos adicionales. Estos metadatos adicionales pueden emplear para influir y mejorar el rendimiento en las búsquedas.

Definiremos una clase DocumentEnricher , cuyo papel es incluir una lista de documentos (diccionarios de Python) y una lista de funciones del procesador. Estas funciones se ejecutarán sobre la columna original_text de los documentos y almacenarán sus salidas en nuevos campos.

Primero, extraemos las frases clave usando TextRank. TextRank es un algoritmo basado en gráficos que extrae frases clave y oraciones del texto clasificando su importancia en función de las relaciones entre palabras.

A continuación, generaremos potential_questions usando GPT-4o.

Finalmente, extraeremos entidades usando Spacy.

Dado que el código de cada uno de estos es bastante extenso y complejo, me abstendré de reproducirlo aquí. Si te interesa, los archivos están marcados en los ejemplos de código que aparecen a continuación.

Vamos a ejecutar el enriquecimiento de datos:

# documentenricher.py
from tqdm import tqdm

class DocumentEnricher:

    def __init__(self):
        pass 

    def enrich_document(self, documents, processors, text_col='text'):
        for doc in tqdm(documents, desc="Enriching documents using processors: "+str(processors)): 
            for (processor, field) in processors: 
                metadata=processor(doc[text_col])
                if isinstance(metadata, list):
                    metadata='\n'.join(metadata)
                doc.update({field: metadata})
 
# main.ipynb
# Initialize processor classes 
nltkprocessor=NLTKProcessor() // nltk_processor.py
entity_extractor=EntityExtractor() // entity_extractor.py
gpt4o = LLMProcessor(model='gpt-4o') // llm.py

# Initialize LLM
documentenricher=DocumentEnricher()

# Create new fields in the documents - These are the outputs of the processor functions.
processors=[
    (nltkprocessor.textrank_phrases, "keyphrases"),
    (gpt4o.generate_questions, "potential_questions"),
    (entity_extractor.extract_entities, "entities")
    ]

# .enrich_document() will modify chunked_docs in place. 
# To view the results, we'll print chunked_docs in the next few cells!
documentenricher.enrich_document(chunked_docs, text_col='original_text', processors=processors)

Y echa un vistazo a los resultados:

Frases clave extraídas por TextRank

Estas frases clave son un sustituto de los temas centrales del fragmento. Si una consulta tiene que ver con ciberseguridad, el puntaje de este segmento se incrementará.

print(chunked_documents[25]['keyphrases'])

'elastic agent stop', 'agent stop malware', 
'stop malware ransomware', 'malware ransomware environment', 
'ransomware environment wide', 'environment wide visibility', 
'wide visibility threat', 'visibility threat detection', 
'sep cl key', 'cl key feature'

Posibles preguntas generadas por GPT-4o

Estas posibles preguntas pueden coincidir directamente con las consultas de los usuarios, ofreciendo un aumento en el puntaje. Pedimos a GPT-4o que genere preguntas que pueden responder usando la información encontrada en el fragmento actual.

print(chunked_documents[25]['potential_questions'])

1. What are the primary functions that Elastic Agent provides in terms of cybersecurity?
2. Describe how Logstash contributes to data management within an IT environment.
3. List and explain any key features of Logstash mentioned in the document.
4. How does Elastic Agent enhance environment-wide visibility in threat detection?
5. What capabilities does Logstash offer for handling data beyond simple collection?
6. In what ways does the document suggest that Elastic Agent stops malware and ransomware?
7. Can you identify any relationships between the functionalities of Elastic Agent and Logstash in an integrated environment?
8. What implications might the advanced threat detection capabilities of Elastic Agent have for organizational security policies?
9. Compare and contrast the roles of Elastic Agent and Logstash based on their described functions.
10. How might the centralized collection ability of Logstash support the threat detection capabilities of Elastic Agent?

Entidades extraídas por Spacy

Estas entidades cumplen un propósito similar al de las frases clave, pero capturan los nombres de organizaciones e individuos, que la extracción de frases clave puede pasar por alto.

print(chunked_documents[29]['entities'])

'appdynamics', 'apm data', 'azure sentinel', 
'microsoft', 'mcafee', 'broadcom', 'cisco', 
'dynatrace', 'coveo', 'lucidworks'

Volver arriba

Incrustaciones compuestas multicampo

Ahora que enriquecimos nuestros documentos con metadatos adicionales, podemos aprovechar esta información para crear incrustaciones más robustas y conscientes del contexto.

Repasemos nuestro punto actual en el proceso. Tenemos cuatro campos de interés en cada documento.

{
    "chunk": "...",
    "keyphrases": "...", 
    "potential_questions": "...", 
    "entities": "..." 
}

Cada campo representa una perspectiva diferente sobre el contexto del documento, lo que puede destacar un área clave en la que el LLM debe centrar.

Pipeline de Enriquecimiento de Metadatos en RAG

Pipeline de Enriquecimiento de Metadatos

El plan es incrustar cada uno de estos campos y luego crear una suma ponderada de las incrustaciones, conocida como Incrustación Compuesta.

Con suerte, esta Incrustación Compuesta permitirá que el sistema sea más consciente del contexto, además de introducir otro hiperparámetro ajustable que controla el comportamiento de búsqueda.

Primero, embebamos cada campo y actualicemos cada documento en su lugar, usando nuestro modelo de incrustación definido localmente importado al inicio del cuaderno main.ipynb.

# EmbeddingModel defined in embedding_model.py
embedder=EmbeddingModel(model_name=HUGGINGFACE_EMBEDDING_MODEL)

cols_to_embed=['keyphrases', 'potential_questions', 'entities']

embedding_cols=[]
for col in cols_to_embed:
    # Works on text input
    embedding_col=embedder.embed_documents_text_wise(chunked_documents, text_field=col)
    embedding_cols.append(embedding_col)
# Works on token input
embedding_col=embedder.embed_documents_token_wise(chunked_documents, token_field="chunk")
embedding_cols.append(embedding_col)

Cada función de incrustación devuelve el campo de la incrustación, que es simplemente el campo de entrada original con un _embedding postfijo.

Ahora definamos las ponderaciones de nuestra incrustación compuesta:

embedding_cols=[
                'keyphrases_embedding',
                'potential_questions_embedding',
                'entities_embedding',
                'chunk_embedding']
combination_weights=[
                    0.1,
                    0.15,
                    0.05,
                    0.7
                ]

Las ponderaciones te permiten asignar prioridades a cada componente, basándote en tu caso de uso y la calidad de tus datos. Intuitivamente, el tamaño de estos pesos depende del valor semántico de cada componente. Como el texto en fragmentos en sí es, con diferencia, el más rico, asigno un peso del 70%. Como las entidades son las más pequeñas, siendo solo una lista de nombres de organizaciones o personas, le asigno un peso del 5%. La configuración precisa de estos valores debe determinar empíricamente, caso de uso por caso.

Finalmente, escribamos una función para aplicar los pesos y creemos nuestra incrustación compuesta. También eliminaremos todas las incrustaciones de componentes para ahorrar espacio.

from tqdm import tqdm 
def combine_embeddings(objects, embedding_cols, combination_weights, primary_embedding='primary_embedding'):
    # Ensure the number of weights matches the number of embedding columns
    assert len(embedding_cols) == len(combination_weights), "Number of embedding columns must match number of weights"
    
    # Normalize weights to sum to 1
    weights = np.array(combination_weights) / np.sum(combination_weights)
    
    for obj in tqdm(objects, desc="Combining embeddings"):
        # Initialize the combined embedding
        combined = np.zeros_like(obj[embedding_cols[0]])
        
        # Compute the weighted sum
        for col, weight in zip(embedding_cols, weights):
            combined += weight * np.array(obj[col])
        
        # Add the new combined embedding to the object
        obj.update({primary_embedding:combined.tolist()})
        
        # Remove the original embedding columns
        for col in embedding_cols:
            obj.pop(col, None)

combine_embeddings(chunked_documents, embedding_cols, combination_weights)

Con esto, completamos el procesamiento de los documentos. Ahora tenemos una lista de objetos documento que se ven así:

{ 'id_': '7fe71686-5cd0-4831-9e79-998c6dbeae0c', 'chunk': [2312, 14613, ...], 'original_text': 'if an emerging growth company, indicate by check mark if the registrant has elected not to use the extended ...', 'chunk_index': 3, 'chunk_token_count': 399, 'metadata': {'page_label': '3', 'file_name': 'Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf', ... 'keyphrases': 'sep cl unk\ncheck mark registrant\ncl unk indicate\nunk indicate check\nindicate check mark\nprincipal executive office\naccelerate filer unk\ncompany unk emerge\nunk emerge growth\nemerge growth company', 'potential_questions': '1. What are the different types of registrant statuses mentioned in the document?\n2. Under what section of the Sarbanes-Oxley Act must registrants file a report on the effectiveness of their internal ...', 'entities': 'the effe ctiveness of\nsection 13\nSEP\nUNK\nsection 21e\n1934\n1933\nu. s. c.\nsection 404\nsection 12\nal', 'primary_embedding': [-0.3946287803351879, -0.17586839850991964, ...] }

Indexación a Elastic

Subamos nuestros documentos en masa a Elastic Search. Para este propósito, hace mucho tiempo definí un conjunto de funciones auxiliares elásticas en elastic_helpers.py. Es un código muy largo, así que vamos a centrarnos en las llamadas a funciones.

es_bulk_indexer.bulk_upload_documents funciona con cualquier lista de objetos de diccionario, aprovechando los convenientes mapeos dinámicos de Elasticsearch.

# Initialize Elasticsearch
ELASTIC_CLOUD_ID = os.environ.get('ELASTIC_CLOUD_ID')
ELASTIC_USERNAME = os.environ.get('ELASTIC_USERNAME')
ELASTIC_PASSWORD = os.environ.get('ELASTIC_PASSWORD')
ELASTIC_CLOUD_AUTH = (ELASTIC_USERNAME, ELASTIC_PASSWORD)
es_bulk_indexer = ESBulkIndexer(cloud_id=ELASTIC_CLOUD_ID, credentials=ELASTIC_CLOUD_AUTH)
es_query_maker = ESQueryMaker(cloud_id=ELASTIC_CLOUD_ID, credentials=ELASTIC_CLOUD_AUTH)

# Define Index Name
index_name=os.environ.get('ELASTIC_INDEX_NAME')


# Create index and bulk upload 
index_exists = es_bulk_indexer.check_index_existence(index_name=index_name)
if not index_exists:
    logger.info(f"Creating new index: {index_name}")
    es_bulk_indexer.create_es_index(es_configuration=BASIC_CONFIG, index_name=index_name)

success_count = es_bulk_indexer.bulk_upload_documents(
    index_name=index_name, 
    documents=chunked_documents, 
    id_col='id_',
    batch_size=32
)

Ve a Kibana y verifica que todos los documentos fueron indexados. Deberían ser 224. ¡No está mal para un documento tan extenso!

Índice Kibana

Documentos de Reportes Anuales Indexados en Kibana

Volver arriba

Ruptura de gato

Vamos a hacer una pausa, el artículo es un poco pesado, lo sé. Mira a mi gato:

Gasoducto Han

Mira lo enojada que está

Adorable. El sombrero desapareció y sospecho que lo robó y escondió en algún sitio :(

¡Enhorabuena por llegar hasta aquí :)

¡Únete a mí en la Parte 2 para probar y evaluar nuestra cadena RAG!

Apéndice

Definiciones

1. Fragmentación de frases

  • Una técnica de preprocesamiento empleada en sistemas RAG para dividir el texto en unidades más pequeñas y significativas.

  • Proceso:

    1. Entrada: Gran bloque de texto (por ejemplo, documento, párrafo)

    2. Salida: Segmentos de texto más pequeños (normalmente oraciones o pequeños grupos de oraciones)

  • Propósito:

    • Crea segmentos de texto granulares y específicos de contexto

    • Permite una indexación y recuperación más precisas

    • Mejora la relevancia de la información recuperada en sistemas RAG

  • Características:

    • Los segmentos tienen significado semántico

    • Puede ser indexado y recuperado de forma independiente

    • A menudo preserva cierto contexto para garantizar la comprensibilidad independiente

  • Beneficios:

    • Mejora la precisión de la recuperación

    • Permite una ampliación más enfocada en las canalizaciones RAG

2. HyDE (Embedding de Documentos Hipotéticos)

  • Una técnica que emplea un LLM para generar un documento hipotético para la expansión de consultas en sistemas RAG.

  • Proceso:

    1. Consulta de entrada a un LLM

    2. El LLM genera un documento hipotético que responde a la consulta

    3. Incrustar el documento generado

    4. Emplea la incrustación para búsqueda vectorial

  • Diferencia clave:

    • RAG tradicional: Empareja la consulta con documentos

    • HyDE: Empareja documentos con documentos

  • Propósito:

    • Mejorar el rendimiento de la recuperación, especialmente para consultas complejas o ambiguas

    • Captura un contexto semántico más rico que una consulta corta

  • Beneficios:

    • Aprovecha el conocimiento de LLM para ampliar las consultas

    • Puede mejorar potencialmente la relevancia de los documentos recuperados

  • Desafíos:

    • Requiere inferencia adicional de LLM, aumentando la latencia y el costo

    • El rendimiento depende de la calidad del documento hipotético generado

3. Empaquetado inverso

  • Una técnica empleada en sistemas RAG para reordenar los resultados de búsqueda antes de pasarlos al LLM.

  • Proceso:

    1. El motor de búsqueda (por ejemplo, Elasticsearch) devuelve los documentos en orden descendente de relevancia.

    2. El orden se invierte, colocando el documento más relevante al final.

  • Propósito:

    • Aprovecha el sesgo de actualidad de los LLM, que tienden a centrar más en la información más reciente en su contexto.

    • Garantiza que la información más relevante esté "más fresca" en la ventana de contexto del LLM.

  • Ejemplo: Orden original: [Más relevante, Segundo más, Tercero más, ...] Orden invertido: [..., Tercero más, segundo más relevante]

4. Clasificación de consultas

  • Una técnica para optimizar la eficiencia del sistema RAG determinando si una consulta requiere RAG o puede ser respondida directamente por el LLM.

  • Proceso:

    1. Desarrollar un conjunto de datos personalizado específico para el LLM en uso

    2. Capacitar un modelo de clasificación especializado

    3. Emplea el modelo para categorizar las consultas entrantes

  • Propósito:

    • Mejorar la eficiencia del sistema evitando el procesamiento innecesario de RAG

    • Consulta directa al mecanismo de respuesta más adecuado

  • Requisitos:

    • Conjunto de datos y modelo específicos de LLM

    • Refinamiento continuo para mantener la precisión

  • Beneficios:

    • Reduce la sobrecarga computacional para consultas simples

    • Potencialmente mejora el tiempo de respuesta para consultas que no son RAG

5. Resumen

  • Una técnica para condensar documentos recuperados en sistemas RAG.

  • Proceso:

    1. Recuperar documentos relevantes

    2. Genera resúmenes concisos de cada documento

    3. Emplea resúmenes en lugar de documentos completos en la tubería RAG

  • Propósito:

    • Mejora el rendimiento de RAG centrándote en la información esencial

    • Reducir el ruido y las interferencias de contenido menos relevante

  • Beneficios:

    • Potencialmente mejora la relevancia de las respuestas de los LLM

    • Permite incluir más documentos dentro de los límites del contexto

  • Desafíos:

    • Riesgo de perder detalles importantes en la resumen

    • Sobrecarga computacional adicional para la generación de resúmenes

6. Inclusión de metadatos

  • Una técnica para enriquecer documentos con información contextual adicional.

  • Tipos de metadatos:

    • Frases clave

    • Títulos

    • Fechas

    • Detalles de la autoría

    • Resumen

  • Propósito:

    • Aumentar la información contextual disponible para el sistema RAG

    • Proporcionar a los LLMs una comprensión más clara del contenido y la relevancia del documento

  • Beneficios:

    • Potencialmente mejora la precisión de la recuperación

    • Mejora la capacidad del LLM para evaluar la utilidad de los documentos

  • Implementación:

    • Se puede hacer durante el preprocesamiento de documentos

    • Puede requerir pasos adicionales de extracción de datos o generación

7. Incrustaciones compuestas multicampo

  • Una técnica avanzada de incrustación para sistemas RAG que crea incrustaciones separadas para diferentes componentes del documento.

  • Proceso:

    1. Identificar campos relevantes (por ejemplo, título, frases clave, resúmenes, contenido principal)

    2. Genera incrustaciones separadas para cada campo

    3. Combina o almacena estos embeddings para su uso en la recuperación

  • Diferencia con el enfoque estándar:

    • Tradicional: Embedding único para todo el documento

    • Compuesto: Múltiples incrustaciones para diferentes aspectos del documento

  • Propósito:

    • Crear representaciones documentales más matizadas y conscientes del contexto

    • Captura información de una mayor variedad de fuentes dentro de un documento

  • Beneficios:

    • Potencialmente mejora el rendimiento en consultas ambiguas o multifacéticas

    • Permite una ponderación más flexible de los diferentes aspectos del documento en la recuperación

  • Desafíos:

    • Mayor complejidad en los procesos de incrustación, almacenamiento y recuperación

    • Puede requerir algoritmos de emparejamiento más sofisticados

8. Enriquecimiento de consultas

  • Una técnica para ampliar la consulta original con términos relacionados para mejorar la cobertura de búsqueda.

  • Proceso:

    1. Analizar la consulta original

    2. Generar sinónimos y frases semánticamente relacionadas

    3. Complementa la consulta con estos términos adicionales

  • Propósito:

    • Ampliar el rango de posibles coincidencias en el corpus documental

    • Mejorar el rendimiento de recuperación para consultas con lenguaje específico o técnico

  • Beneficios:

    • Potencialmente recupera documentos relevantes que no coinciden exactamente con los términos originales de la consulta

    • Puede ayudar a superar la discrepancia de vocabulario entre consultas y documentos

  • Desafíos:

    • Riesgo de deriva de consulta si no se implementa cuidadosamente

    • Puede aumentar la sobrecarga computacional en el proceso de recuperación

Volver arriba

Contenido relacionado

LINQ a Elasticsearch ES|QL: escribir en C#, buscar en Elasticsearch

Florian Bernd

Mejorando las capacidades de los chatbots con PLN y búsqueda vectorial en Elasticsearch

Priscilla Parodi

Elasticsearch vs. OpenSearch: Comparación del rendimiento de búsqueda vectorial

Ugo Sangiorgi

Plagio por IA: detección de plagio con Elasticsearch

Priscilla Parodi

Técnicas avanzadas de RAG parte 2: Consultas y pruebas

Han Xiang Choong

¿Estás listo para crear experiencias de búsqueda de última generación?

No se logra una búsqueda suficientemente avanzada con los esfuerzos de uno. Elasticsearch está impulsado por científicos de datos, operaciones de ML, ingenieros y muchos más que son tan apasionados por la búsqueda como tú. Conectemos y trabajemos juntos para crear la experiencia mágica de búsqueda que te dará los resultados que deseas.

Pruébalo tú mismo