Módulo 3: Query Optimization
Introducción a Query Optimization
Descripción de la cápsula
Query optimization transforma queries del usuario antes de buscar para aumentar recall (encontrar más docs relevantes) y precision (docs más específicos). Queries directas fallan frecuentemente: "fastapi auth" es ambiguo (OAuth2? JWT? Basic auth?), "how to do X" está mal formulado para semantic search.
Este módulo enseña 4 técnicas que mejoran recall +15-25%: (1) Query Expansion (generar queries relacionadas), (2) Query Rewriting (reformular para claridad), (3) Query Decomposition (dividir complejas), (4) HyDE (generar documento hipotético). Cada técnica tiene trade-offs de latency, cost, y mejora.
🎯 Objetivos de Aprendizaje
Al finalizar este módulo, podrás:
- ✅ Explicar por qué queries directas son subóptimas (-20-30% recall)
- ✅ Implementar query expansion con LLM (+20-30% recall)
- ✅ Implementar query rewriting (+10-15% precision)
- ✅ Implementar query decomposition (+15-20% precision para complejas)
- ✅ Implementar HyDE (+15-25% recall)
- ✅ Comparar técnicas con benchmarks
- ✅ Seleccionar técnica óptima según query type
- ✅ Integrar en proyecto evolutivo (+15-25% recall total)
📐 Por Qué Query Optimization Importa
Problema: Queries directas son ambiguas/incompletas
# Query directa del usuario
user_query = "fastapi auth"
# Problemas:
# 1. Ambigüedad: ¿OAuth2? ¿JWT? ¿Basic auth? ¿API keys?
# 2. Falta contexto: ¿Implementación? ¿Tutorial? ¿Best practices?
# 3. Keyword style: "fastapi auth" vs "How to implement authentication in FastAPI?"
# Semantic search con query directa
results = collection.query(
query_embeddings=[create_embedding("fastapi auth")],
n_results=5
)
# Recall: 52% (miss muchos docs relevantes)
# Precision: 68% (algunos docs no son sobre auth específicamente)
Solución: Query optimization
# Técnica 1: Query Expansion
expanded_queries = [
"How to implement OAuth2 authentication in FastAPI",
"FastAPI JWT token authentication tutorial",
"FastAPI security and authentication best practices",
"Implementing API key authentication in FastAPI"
]
# Buscar con todas las queries y merge resultados
all_results = []
for query in expanded_queries:
results = collection.query(
query_embeddings=[create_embedding(query)],
n_results=10
)
all_results.extend(results['documents'][0])
# Deduplicar y rankear
final_results = reciprocal_rank_fusion(all_results)[:5]
# Recall: 77% (+25% vs directa)
# Precision: 78% (+10% vs directa)
🗺️ Roadmap del Módulo
Cápsula 01 (Esta): Introducción
- Por qué queries directas fallan
- Overview de 4 técnicas
- Setup técnico
Cápsula 02: Problemas de Queries Directas
- Ambigüedad, incompletitud, mal formuladas
- Impacto en recall: -20-30%
Cápsula 03: Query Expansion
- Generar múltiples queries con LLM
- Merge con reciprocal rank fusion
- Mejora: +20-30% recall
Cápsula 04: Query Rewriting
- Reformular para claridad
- Casos: typos, clarificación, natural language
- Mejora: +10-15% precision
Cápsula 05: Query Decomposition
- Dividir queries complejas
- Multi-hop reasoning
- Mejora: +15-20% precision
Cápsula 06: HyDE
- Generar documento hipotético
- Buscar con doc embedding (no query)
- Mejora: +15-25% recall
Cápsula 07: Comparación de Técnicas
- Benchmark completo
- Trade-offs: Recall vs Latency vs Cost
- Decision matrix
Cápsula 08: Proyecto - Query Optimizer
- Implementar 4 técnicas
- Comparar y seleccionar
- Integrar en RAG
📊 Overview de Query Optimization Techniques
Técnica 1: Query Expansion
Concepto: Generar múltiples queries relacionadas y buscar con todas.
# Input
user_query = "fastapi auth"
# Query expansion (LLM)
expanded = [
"How to implement OAuth2 authentication in FastAPI",
"FastAPI JWT token authentication",
"FastAPI security best practices"
]
# Buscar con todas → Merge resultados
# Mejora: +20-30% recall
Cápsula: 03
Técnica 2: Query Rewriting
Concepto: Reformular query para claridad y completitud.
# Input (ambigua)
user_query = "fastapi auth"
# Query rewriting (LLM)
rewritten = "How to implement authentication in FastAPI using OAuth2 or JWT tokens"
# Buscar con rewritten query (más clara)
# Mejora: +10-15% precision
Cápsula: 04
Técnica 3: Query Decomposition
Concepto: Dividir query compleja en sub-queries simples.
# Input (compleja)
user_query = "Compare FastAPI and Flask authentication approaches"
# Decomposition (LLM)
sub_queries = [
"How does authentication work in FastAPI?",
"How does authentication work in Flask?",
"What are the differences between FastAPI and Flask?"
]
# Buscar cada sub-query → Agregar resultados
# Mejora: +15-20% precision para queries complejas
Cápsula: 05
Técnica 4: HyDE (Hypothetical Document Embeddings)
Concepto: Generar documento hipotético que respondería la query, buscar con su embedding.
# Input
user_query = "How to implement authentication in FastAPI?"
# Generar documento hipotético (LLM)
hypothetical_doc = """
To implement authentication in FastAPI, you can use OAuth2 with JWT tokens.
First, install python-jose and passlib libraries.
Then, create a User model with password hashing...
"""
# Buscar con embedding del documento (no query)
doc_embedding = create_embedding(hypothetical_doc)
results = collection.query(query_embeddings=[doc_embedding])
# Mejora: +15-25% recall (doc embeddings > query embeddings)
Cápsula: 06
🛠️ Setup Técnico
No requiere instalación nueva:
# Ya tenemos OpenAI API (Módulo 1)
# Ya tenemos ChromaDB (Módulo 1)
# Ya tenemos LangChain (Módulo 2)
# Verificar setup
python -c "from openai import OpenAI; print('✅ OpenAI ready')"
python -c "import chromadb; print('✅ ChromaDB ready')"
Configuración:
# .env (ya existe del Módulo 1)
OPENAI_API_KEY=sk-proj-...
# Usar GPT-3.5-turbo para query optimization (balance cost/calidad)
📊 Mejora Esperada por Técnica
Baseline (Queries Directas):
# Sin query optimization
user_query = "fastapi auth"
results = rag_system.query(user_query)
# Métricas baseline:
# - Recall@50: 52%
# - Precision@5: 78% (del Módulo 2 con chunking optimizado)
Query Expansion (+20-30% recall):
# Con expansion
expanded_queries = expand_query(user_query) # Genera 3-5 queries
results = search_with_multiple_queries(expanded_queries)
# Mejora esperada:
# - Recall@50: 67% (+15% vs baseline)
# - Precision@5: 80% (+2%)
# - Latency: +600ms (LLM call + multiple searches)
# - Cost: +$0.001/query
Query Rewriting (+10-15% precision):
# Con rewriting
rewritten_query = rewrite_query(user_query) # Clarifica
results = rag_system.query(rewritten_query)
# Mejora esperada:
# - Recall@50: 55% (+3%)
# - Precision@5: 88% (+10% vs baseline)
# - Latency: +500ms (LLM call)
# - Cost: +$0.0008/query
HyDE (+15-25% recall):
# Con HyDE
hypothetical_doc = generate_hypothetical_document(user_query)
doc_embedding = create_embedding(hypothetical_doc)
results = collection.query(query_embeddings=[doc_embedding])
# Mejora esperada:
# - Recall@50: 70% (+18% vs baseline)
# - Precision@5: 82% (+4%)
# - Latency: +700ms (LLM generation + embedding)
# - Cost: +$0.0015/query
🔗 Conexión con Proyecto Evolutivo
Módulo 1 (Baseline):
# retrieval.py (Módulo 1)
def retrieve(query: str, top_k: int = 5):
"""Query directa sin optimización"""
query_embedding = create_embedding(query)
return collection.query(query_embeddings=[query_embedding], n_results=top_k)
Métricas: Recall 52%
Módulo 2 (Chunking optimizado):
# indexing.py (Módulo 2)
# Mejora chunking → Mejor calidad de docs → Precision 78%
Métricas: Precision 78% (+10%), Recall 57% (+5%)
Módulo 3 (Query optimization):
# retrieval.py (Módulo 3 - actualizado)
def retrieve(query: str, top_k: int = 5, optimize_query: bool = True):
"""Retrieval con query optimization"""
if optimize_query:
# Query expansion
expanded_queries = expand_query(query)
# Buscar con todas las queries
all_results = []
for q in expanded_queries:
q_embedding = create_embedding(q)
results = collection.query(query_embeddings=[q_embedding], n_results=10)
all_results.append(results)
# Merge con reciprocal rank fusion
final_results = reciprocal_rank_fusion(all_results)[:top_k]
return final_results
else:
# Fallback a query directa
query_embedding = create_embedding(query)
return collection.query(query_embeddings=[query_embedding], n_results=top_k)
Métricas optimizadas: Precision 80% (+2%), Recall 72% (+15%)
Módulo 8 (Final):
# advanced_rag_system.py (Módulo 8)
class AdvancedRAGSystem:
def __init__(self, query_optimization: str = "expansion"):
# Módulo 2: Chunking optimizado
self.chunker = RecursiveChunker()
# Módulo 3: Query optimization
if query_optimization == "expansion":
self.query_optimizer = QueryExpansion()
elif query_optimization == "hyde":
self.query_optimizer = HyDE()
# ... módulos 4-7
Métricas finales (acumulado): Precision 93%, Recall 77%
🎯 Resumen
Conceptos clave:
- ✅ Queries directas son subóptimas: Ambiguas, incompletas, mal formuladas (-20-30% recall)
- ✅ 4 técnicas de optimization: Expansion (recall), Rewriting (precision), Decomposition (complejas), HyDE (recall máximo)
- ✅ Trade-offs: Latency (+500-800ms), Cost (+$0.001-0.002/query), Recall (+15-30%)
- ✅ Mejora combinada: +15-25% recall typical, +10-15% precision
- ✅ Decision depende de: Query type, latency budget, cost constraints
Qué sigue:
Cápsula 02 desglosa problemas específicos de queries directas con ejemplos concretos y métricas de impacto.
📚 Recursos Adicionales
- Query Understanding for RAG - Research paper
- Multi-Query Retrieval - LangChain docs
- HyDE Original Paper - Hypothetical Document Embeddings
- Query Expansion Techniques - Pinecone guide
- Query Decomposition - LlamaIndex blog
Creado: Febrero 6, 2026
Versión: 1.0