Multi-query: descomposición de preguntas
El embedding promedia los temas
Sección titulada «El embedding promedia los temas»Una pregunta como:
“Comparame Clean Architecture con Hexagonal en términos de testabilidad.”
cubre tres temas: Clean Architecture, Hexagonal Architecture, y testabilidad. Cuando embedeás esa pregunta, el embedding promedia las direcciones semánticas de los tres. Resultado: un vector que está “en el medio” — no apunta fuerte a ninguno de los tres temas.
El retriever te trae chunks que también están en el medio: chunks ambiguos que tocan los temas pero no son el “core” de ninguno.
La solución: buscar tres veces, una por tema, y unir. Es exactamente lo que hace multi-query.
El pipeline
Sección titulada «El pipeline»pregunta original ↓ LLM (decompose)3 sub-preguntas ↓ retrieve cada una (dense, o hybrid)3 listas de top-K ↓ unión deduplicada por chunk_idcandidatos únicos ↓ (rerank si --rerank también)top-K final ↓ generaterespuestaCosto: 1 LLM call extra para descomponer + N retrieves. Para corpus chicos y queries simples es overkill. Para queries multi-tema es win casi seguro.
El archivo
Sección titulada «El archivo»packages/01-vercel-ai-sdk/src/query/multi-query.ts — un solo export:
export async function decomposeQuery(query: string, n: number): Promise<string[]>;Devuelve [query] (la original) si n <= 1 o si la descomposición falla. Esa es la propiedad importante: fail-soft. Si el decompose tira error o el LLM devuelve algo malparseable, multi-query degrada a single-query — no te rompe la pipeline.
El prompt de descomposición
Sección titulada «El prompt de descomposición»const prompt = `Descomponé la siguiente PREGUNTA en exactamente ${n} sub-preguntas más simples y específicas que cubran distintas facetas o partes de la pregunta original. Cada sub-pregunta debe ser autocontenida y buscable por sí sola.
Respondé EXACTAMENTE con este JSON, sin texto adicional, sin bloques de código:["sub-pregunta 1", "sub-pregunta 2", ...]
PREGUNTA:${query}
JSON:`;Tres detalles:
autocontenida y buscable por sí solaes la frase mágica. Sin esto, el LLM tiende a hacer sub-preguntas tipo “¿qué dice el primer punto?” — útil para él, inútil para el retriever.exactamente ${n}: si pedís 3, querés 3. Sin esto, el modelo a veces devuelve 5 o 2.- JSON cerrado: simplifica el parser. Si no parsea, fallback a
[query].
Unión deduplicada
Sección titulada «Unión deduplicada»Cada sub-query devuelve top-K chunks. Algunos chunks pueden aparecer en múltiples sub-queries (típicamente los más relevantes a la pregunta original). El orquestador los dedupica por chunk.id:
const merged = new Map<string, { chunk: Chunk; score: number }>();for (const sq of queries) { const dense = await denseRetrieve(sq, wide); for (const d of dense) { const cur = merged.get(d.chunk.id); if (!cur || cur.score < d.score) { merged.set(d.chunk.id, { chunk: d.chunk, score: d.score }); } }}Para cada chunk visto, guardamos el mejor score de las distintas sub-queries. Eso premia chunks que son top-1 en alguna sub-pregunta — son los que más probablemente respondan algún aspecto del problema.
El comando
Sección titulada «El comando»pnpm vercel:query:adv "<pregunta>" --multi-query 3Combinable con todo:
pnpm vercel:query:adv "<pregunta>" --multi-query 3 --hybrid --rerankPipeline interno: descompone → 3 retrieves (dense + BM25 + RRF cada una) → merge → rerank a top-4 → generate. Es lento (4 LLM calls + 3 retrieves) pero brutalmente fuerte para preguntas complejas.
Probá el contraste:
# Pregunta multi-tema, single-querypnpm vercel:query:adv "Comparame Clean Architecture con Hexagonal Architecture en términos de testabilidad y separación de capas"
# Misma pregunta, multi-querypnpm vercel:query:adv "Comparame Clean Architecture con Hexagonal Architecture en términos de testabilidad y separación de capas" --multi-query 3En la salida del segundo, mirá las sub-queries que generó el LLM en el bloque [multi-query] decomposed into 3:. Algo así:
[multi-query] decomposed into 3: 1. ¿Qué establece Clean Architecture sobre testabilidad y separación de capas? 2. ¿Qué establece Hexagonal Architecture sobre testabilidad y separación de capas? 3. ¿Cuáles son las diferencias clave entre Clean Architecture y Hexagonal Architecture en cuanto a estructura de capas?Cada una busca un aspecto distinto. La unión te trae chunks de ambos archivos y de los puntos de contraste — algo que el embedding promedio no podía traer.
Cuándo NO usar multi-query
Sección titulada «Cuándo NO usar multi-query»- Preguntas single-tema. “¿Qué es la regla de dependencia?” no necesita descomposición.
- Latencia crítica. Multi-query agrega 1 LLM call al inicio + N veces el costo de retrieve. Si tu SLA es < 500ms total, no entra.
- Corpus chico y temáticamente homogéneo. Si todos los chunks hablan de variantes del mismo tema, las sub-queries van a traer los mismos chunks deduplicados — multi-query no aporta.
Lo que viene
Sección titulada «Lo que viene»Multi-query asume que la pregunta es completa y bien planteada. Pero las preguntas reales de usuarios son sucias: pronombres, referencias a contexto previo, telegráficas. El siguiente capítulo: query rewriting — limpiar la pregunta antes de buscar.