Query expansion y transformacion

Avanzado
LangChain
LangChain
Actualizado: 08/07/2025

¡Desbloquea el curso completo!

IA
Ejercicios
Certificado
Entrar

Multi-query retrieval con LLMs

El multi-query retrieval es una técnica que mejora significativamente la cobertura de resultados al generar múltiples versiones parafraseadas de la consulta original del usuario. En lugar de realizar una única búsqueda, esta aproximación utiliza un LLM para crear variaciones semánticamente equivalentes de la pregunta, ejecuta búsquedas independientes para cada variante y combina los resultados para obtener una respuesta más completa.

Esta técnica resulta especialmente valiosa cuando las consultas de los usuarios contienen terminología ambigua o cuando diferentes formulaciones de la misma pregunta pueden activar documentos relevantes distintos en el vector store. Por ejemplo, una consulta sobre "machine learning" podría beneficiarse de variantes como "aprendizaje automático", "inteligencia artificial" o "algoritmos de ML".

Implementación básica con MultiQueryRetriever

LangChain proporciona la clase MultiQueryRetriever que automatiza este proceso utilizando LCEL. La implementación básica requiere un retriever base y un modelo de lenguaje:

from langchain.retrievers import MultiQueryRetriever
from langchain_openai import ChatOpenAI
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

# Configuración del modelo y vector store
llm = ChatOpenAI(model="gpt-4", temperature=0)
embeddings = OpenAIEmbeddings()

# Crear vector store (asumiendo documentos ya indexados)
vectorstore = FAISS.load_local("./vector_index", embeddings)
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

# Crear multi-query retriever
multi_query_retriever = MultiQueryRetriever.from_llm(
    retriever=base_retriever,
    llm=llm
)

# Ejecutar búsqueda
query = "¿Cuáles son las mejores prácticas para optimizar modelos de deep learning?"
results = multi_query_retriever.invoke(query)

El MultiQueryRetriever genera automáticamente entre 3 y 5 variaciones de la consulta original, ejecuta búsquedas independientes y elimina duplicados antes de devolver los resultados combinados.

Personalización del prompt de generación

Para casos específicos, puedes personalizar el prompt que utiliza el LLM para generar las variaciones de consulta:

from langchain.prompts import PromptTemplate

# Prompt personalizado para generar variaciones
QUERY_PROMPT = PromptTemplate(
    input_variables=["question"],
    template="""Eres un experto en reformular consultas técnicas. 
    Genera exactamente 4 versiones diferentes de la siguiente pregunta, 
    manteniendo el significado pero variando la terminología y estructura:

    Pregunta original: {question}

    Variaciones:
    1."""
)

# Aplicar prompt personalizado
multi_query_retriever = MultiQueryRetriever.from_llm(
    retriever=base_retriever,
    llm=llm,
    prompt=QUERY_PROMPT
)

Configuración avanzada con parámetros específicos

El MultiQueryRetriever permite ajustar varios parámetros para optimizar el comportamiento según las necesidades específicas:

# Configuración detallada
multi_query_retriever = MultiQueryRetriever.from_llm(
    retriever=base_retriever,
    llm=llm,
    include_original=True,  # Incluir consulta original en los resultados
    parser_key="lines"      # Formato de parsing de las variaciones
)

# Configurar logging para ver las consultas generadas
import logging
logging.basicConfig()
logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO)

# Ejecutar con logging activo
results = multi_query_retriever.invoke(
    "¿Cómo implementar autenticación JWT en aplicaciones web?"
)

Casos de uso donde multi-query mejora el retrieval

Esta técnica demuestra particular efectividad en varios escenarios comunes:

Consultas con terminología específica: Cuando los usuarios emplean jerga técnica que puede tener múltiples denominaciones en los documentos. Una consulta sobre "containerización" se beneficia de variantes como "Docker", "contenedores" o "virtualización ligera".

# Ejemplo con terminología técnica
technical_query = "¿Qué es la containerización en DevOps?"
results = multi_query_retriever.invoke(technical_query)

# El LLM generará variaciones como:
# - "¿Cómo funcionan los contenedores Docker en desarrollo?"
# - "¿Cuáles son los beneficios de la virtualización con contenedores?"
# - "¿Qué tecnologías de containerización existen para aplicaciones?"

Preguntas ambiguas o con múltiples interpretaciones: Cuando la consulta original puede entenderse de diferentes maneras, las variaciones ayudan a cubrir distintas interpretaciones posibles.

# Consulta ambigua
ambiguous_query = "¿Cómo mejorar el rendimiento?"
results = multi_query_retriever.invoke(ambiguous_query)

# Variaciones posibles:
# - "¿Cómo optimizar el rendimiento de aplicaciones web?"
# - "¿Qué técnicas mejoran el performance de bases de datos?"
# - "¿Cómo acelerar la ejecución de algoritmos?"

Integración con cadenas LCEL

El MultiQueryRetriever se integra perfectamente con cadenas LCEL para crear flujos de procesamiento más complejos:

from langchain.schema.runnable import RunnablePassthrough
from langchain.prompts import ChatPromptTemplate

# Prompt para la respuesta final
answer_prompt = ChatPromptTemplate.from_template(
    """Basándote en el siguiente contexto, responde la pregunta de manera precisa:

    Contexto: {context}
    Pregunta: {question}
    
    Respuesta:"""
)

# Cadena completa con multi-query retrieval
chain = (
    {"context": multi_query_retriever, "question": RunnablePassthrough()}
    | answer_prompt
    | llm
)

# Ejecutar cadena completa
response = chain.invoke("¿Cuáles son las ventajas del edge computing?")

Esta aproximación multiplica la superficie de búsqueda sin requerir modificaciones en el vector store subyacente, proporcionando una mejora inmediata en la cobertura de resultados relevantes. La técnica resulta especialmente valiosa en dominios técnicos donde la misma información puede expresarse con terminología variada.

Query rewriting y step-back prompting

Guarda tu progreso

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

El query rewriting representa una evolución natural del multi-query retrieval, donde en lugar de generar múltiples variaciones paralelas, transformamos estratégicamente la consulta original para mejorar su capacidad de recuperación. Esta técnica resulta especialmente efectiva cuando las preguntas de los usuarios son demasiado específicas o contienen detalles que pueden limitar la búsqueda en el vector store.

El step-back prompting constituye una implementación particular del query rewriting que reformula preguntas específicas en consultas más abstractas y generales. Esta aproximación se basa en el principio de que las preguntas generales suelen activar documentos con información fundamental que posteriormente puede aplicarse a casos específicos.

Implementación de step-back prompting

Para implementar step-back prompting en LangChain, creamos una cadena LCEL que primero reformula la consulta y luego ejecuta la búsqueda:

from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnablePassthrough
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

# Configuración inicial
llm = ChatOpenAI(model="gpt-4", temperature=0)
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.load_local("./vector_index", embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 6})

# Prompt para step-back prompting
stepback_prompt = ChatPromptTemplate.from_template(
    """Eres un experto en reformular consultas técnicas. Tu tarea es transformar 
    preguntas específicas en consultas más generales y abstractas que puedan 
    recuperar información fundamental relevante.

    Pregunta específica: {question}
    
    Reformula esta pregunta de manera más general, enfocándote en conceptos 
    fundamentales y principios subyacentes. Responde solo con la nueva consulta:"""
)

# Cadena para generar consulta step-back
stepback_chain = stepback_prompt | llm

Ejemplo práctico de transformación

Veamos cómo funciona la transformación step-back con ejemplos concretos:

# Consulta específica sobre un modelo particular
specific_query = "¿Cuál es la velocidad máxima del Tesla Model 3 Performance?"

# Generar consulta step-back
stepback_query = stepback_chain.invoke({"question": specific_query})
print(f"Consulta original: {specific_query}")
print(f"Consulta step-back: {stepback_query.content}")

# Resultado esperado:
# "¿Cuáles son las especificaciones de rendimiento de los vehículos eléctricos Tesla?"

La consulta transformada tiene mayor probabilidad de recuperar documentos con información técnica general sobre vehículos Tesla que pueden contener las especificaciones específicas del Model 3.

Implementación completa con retrieval dual

Una estrategia efectiva combina tanto la consulta original como la reformulada para maximizar la cobertura:

from langchain.schema.runnable import RunnableLambda

def dual_retrieval(inputs):
    """Ejecuta retrieval tanto con consulta original como step-back"""
    original_query = inputs["question"]
    stepback_query = inputs["stepback_query"]
    
    # Recuperar documentos con ambas consultas
    original_docs = retriever.invoke(original_query)
    stepback_docs = retriever.invoke(stepback_query)
    
    # Combinar y eliminar duplicados
    all_docs = original_docs + stepback_docs
    unique_docs = []
    seen_content = set()
    
    for doc in all_docs:
        if doc.page_content not in seen_content:
            unique_docs.append(doc)
            seen_content.add(doc.page_content)
    
    return unique_docs[:8]  # Limitar a 8 documentos más relevantes

# Cadena completa con retrieval dual
dual_chain = (
    {
        "question": RunnablePassthrough(),
        "stepback_query": stepback_chain
    }
    | RunnableLambda(dual_retrieval)
)

Casos específicos donde step-back mejora el retrieval

Preguntas sobre productos o versiones específicas: Cuando los usuarios preguntan sobre características de productos particulares, la reformulación hacia categorías generales mejora la recuperación:

# Ejemplo con software específico
specific_software_query = "¿Cómo configurar SSL en Apache 2.4.52?"
stepback_result = stepback_chain.invoke({"question": specific_software_query})

# Transformación esperada:
# "¿Cómo configurar certificados SSL en servidores web Apache?"

Consultas con fechas o números específicos: Las preguntas que incluyen datos temporales o numéricos específicos se benefician de abstracciones conceptuales:

# Ejemplo con datos temporales
temporal_query = "¿Qué cambios introdujo Python 3.11 en el manejo de excepciones?"
stepback_result = stepback_chain.invoke({"question": temporal_query})

# Transformación esperada:
# "¿Cómo funciona el manejo de excepciones en Python?"

Integración con cadenas de respuesta

Para crear un sistema completo, integramos el query rewriting con la generación de respuestas:

# Prompt para respuesta final
answer_prompt = ChatPromptTemplate.from_template(
    """Basándote en el contexto proporcionado, responde la pregunta específica 
    del usuario de manera precisa y detallada.

    Contexto: {context}
    Pregunta original: {question}
    
    Proporciona una respuesta completa que aborde específicamente lo que pregunta el usuario:"""
)

# Cadena completa con step-back prompting
complete_chain = (
    {
        "context": dual_chain,
        "question": RunnablePassthrough()
    }
    | answer_prompt
    | llm
)

# Ejecutar sistema completo
response = complete_chain.invoke(
    "¿Cuánta memoria RAM requiere entrenar un modelo BERT-large?"
)

Personalización avanzada del step-back prompting

Para dominios específicos, puedes personalizar el comportamiento del step-back prompting:

# Prompt especializado para consultas técnicas
technical_stepback_prompt = ChatPromptTemplate.from_template(
    """Como experto en {domain}, reformula la siguiente pregunta específica 
    en una consulta más general que capture los principios fundamentales:

    Dominio: {domain}
    Pregunta específica: {question}
    
    Enfócate en conceptos, metodologías y principios generales del dominio.
    Consulta reformulada:"""
)

# Cadena especializada
specialized_stepback = technical_stepback_prompt | llm

# Uso con dominio específico
domain_response = specialized_stepback.invoke({
    "domain": "machine learning",
    "question": "¿Qué learning rate usar para fine-tuning GPT-3.5 con LoRA?"
})

Esta técnica de reformulación estratégica complementa perfectamente el multi-query retrieval al abordar limitaciones diferentes: mientras que multi-query amplía la superficie de búsqueda con variaciones semánticas, el step-back prompting eleva el nivel de abstracción para capturar conocimiento fundamental que puede aplicarse a casos específicos.

Aprendizajes de esta lección

  • Comprender el concepto y ventajas del multi-query retrieval con LLMs.
  • Aprender a implementar y personalizar MultiQueryRetriever en LangChain.
  • Conocer la técnica de query rewriting y su implementación mediante step-back prompting.
  • Saber combinar consultas originales y reformuladas para optimizar la recuperación de documentos.
  • Integrar estas técnicas en cadenas LCEL para crear sistemas de búsqueda y respuesta más efectivos.

Completa LangChain y certifícate

Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración