HyDE Opcional en Retrieval (ArcRift cherry-pick #1)
Qué es
HyDE (Hypothetical Document Embeddings) es una técnica de retrieval que, en vez de embeber la pregunta del usuario directamente, genera un pasaje hipotético que la respondería — redactado como si fuera un fragmento de la documentación — y embebe ese pasaje. El vector resultante cae más cerca de los chunks relevantes en el espacio BGE-M3, mejorando el recall.
Ventaja principal
- Recall mejorado: La pregunta “¿Cómo hago X?” genera un pasaje como “Para hacer X, siga estos pasos: 1. …”, que vectoriza más cerca de documentación real que la pregunta cruda.
- Anclaje robusto: Se embebe
pregunta + pasajeconcatenados, de modo que la pregunta ancla el vector si la hipótesis alucina.
Costo
- +1 llamada Gemma (~1-2s) por query.
- OFF por defecto: la decisión de activación (y dónde: Oráculo sí, Help Widget probablemente no) se toma tras medir recall vs latencia con el harness.
Arquitectura
Flujo de query
- Pregunta del usuario →
buildQueryVec(env, question, { hyde: true }) buildQueryVecllama agenerateHyDE(apiKey, question)sihyde=truegenerateHyDEusa Gemma conresponseSchema(JSON limpio, descarta scratchpad CoT) para generar 2-4 frases de pasaje hipotético- Si
generateHyDEfalla → fallback a embedding normal de la pregunta (degradation path) - El vector resultado se usa en
searchChunksyretrievecomo siempre
Lugar de integración
archivo.ts: handler MCP/ask—buildQueryVecen línea 172oraculo/ask.ts: Oráculo público —buildQueryVecen línea 99- Help Widget (no modificado en este PR): se activaría con flag
RETRIEVAL_HYDE=1
Evaluación
El harness bib_eval_retrieval se amplia con variantes:
baseline: embedding directo de preguntahyde: embedding depregunta + pasaje hipotéticotrim: trim sentence-level (feature anterior)hyde+trim: HyDE + trim combinados
La métrica overlap_vs_baseline (proporción de chunks en común con baseline) se generaliza para todas las variantes.
Decisiones de diseño
¿Por qué responseSchema en HyDE?
Gemma 4 tiende a generar CoT (chain-of-thought) scratchpad antes de la respuesta. El responseSchema fuerza una salida JSON limpia y descarta el scratchpad automáticamente, evitando el “footgun s52” (concatenar scratchpad confunde el vector). Mismo patrón que en synthesizeAnswer.
¿Por qué concatenación y no solo pasaje?
- Pasaje puro: si alucina completamente, el vector pierde anclaje a la pregunta real.
- Pregunta + pasaje: la pregunta actúa como ancla semántica. Si el pasaje es malo, el vector sigue siendo cercano a preguntas/temas similares.
¿Por qué OFF por defecto?
Latencia sensible en Help Widget (cliente en navegador). El toggle (RETRIEVAL_HYDE=1) permite A/B testing en stage antes de rollout.
Implementación
generateHyDE(apiKey, question): Promise<string>
Ubicación: functions/api/mcp/handlers/archivo-core.ts, línea 641
Parámetros:
apiKey: API key de Google AI Studioquestion: pregunta del usuario (detecta idioma automáticamente)
Proceso:
- Detecta idioma (ES o EN)
- Genera prompt bilingüe pidiendo pasaje hipotético (2-4 frases, concreto, sin disclaimers)
- Llama a Gemma (
SYNTHESIS_MODEL) conresponseSchemapara forzar JSON - Timeout: 15s
- Parsea JSON; si falla, retorna texto crudo; si está vacío, retorna cadena vacía
Salida: string de pasaje (o vacío si falla)
buildQueryVec(env, question, opts): Promise<{vec, hyde?}>
Ubicación: functions/api/mcp/handlers/archivo-core.ts, línea 694
Parámetros:
env: entorno CloudFlare (conenv.AI,env.GOOGLE_AI_API_KEY)question: pregunta del usuarioopts:{ hyde?: boolean; apiKey?: string }
Lógica:
- Si
opts.hyde=truey hay API key:- Llama a
generateHyDE(apiKey, question) - Si éxito y pasaje no vacío: embebe
"${question}\n\n${hyp}"→ retorna{vec, hyde: hyp} - Si falla: catch silencioso, fallback a paso 2
- Llama a
- Caso por defecto (sin HyDE o fallo): embebe solo la pregunta → retorna
{vec}
Salida: {vec: number[], hyde?: string}
Flag RETRIEVAL_HYDE
Ubicación: functions/types.ts, línea 27
Tipo: RETRIEVAL_HYDE?: string
Comportamiento:
- No definido o
"0"→ HyDE desactivado "1"→ HyDE activo en all handlers
Testing y validación
Antes de merge
✅ tsc --noEmit pasa
Post-merge (harness)
bib_eval_retrieval({
variants: ["baseline", "hyde", "hyde+trim"],
top_k: 5,
trim_threshold: 0.38
})
Métricas a recopilar:
overlap_vs_baselinepara hyde vs baseline (si ↑ recall, entonces éxito)top_score(máxima similitud del primer chunk) para medir seguridadtokens(latencia del embedding)- Comparar
hyde+trimvstrimpuro (¿suma sinergias o solo suma latencia?)
Criterio de activación
Si overlap_hyde > baseline + 5% Y latencia acceptable → considerar activar en Oráculo (backend menos sensible a +1-2s que Help Widget).
Véase también
- [[entity—functions—service—generate-hyde]]
- [[entity—functions—service—build-query-vec]]
- [[concept—biblioteca—retrieval-augmented-generation]]
- [[feature—biblioteca—trim-sentence-level]]
- [[runbook—biblioteca—measure-retrieval-quality]]