Volver a la wiki

Servicio buildQueryVec — Orquestador de Embedding con Fallback

Resumen

buildQueryVec(env, question, opts): Promise<{vec, hyde?}>

Orquesta la construcción del vector de query para retrieval. Si HyDE está activo, genera un pasaje hipotético y embebe pregunta + pasaje concatenados. Si HyDE falla, cae automáticamente al embedding normal de la pregunta. Retorna el vector y (opcionalmente) el pasaje generado para instrumentación.


Firma y ubicación

Archivo: functions/api/mcp/handlers/archivo-core.ts

Línea: 694

Tipo: async function

export async function buildQueryVec(
  env: Env,
  question: string,
  opts: { hyde?: boolean; apiKey?: string },
): Promise<{ vec: number[]; hyde?: string }>

Parámetros

NombreTipoPropósito
envEnvEntorno CloudFlare (contiene env.AI, env.GOOGLE_AI_API_KEY)
questionstringPregunta del usuario
opts.hydeboolean?Si true, intenta activar HyDE (default: false)
opts.apiKeystring?API key para Gemma (p.ej. env.GOOGLE_AI_API_KEY)

Flujo de ejecución

┌─ buildQueryVec(env, question, { hyde: true, apiKey })
│
├─ if opts.hyde && opts.apiKey && env.AI
│  │
│  ├─ try:
│  │  ├─ hyp = await generateHyDE(apiKey, question)
│  │  ├─ if hyp (no vacío):
│  │  │  ├─ text = "${question}\n\n${hyp}"
│  │  │  ├─ [vec] = await embedTexts(env.AI, [text])
│  │  │  └─ return { vec, hyde: hyp }
│  │  │
│  │  └─ (HyDE vacío: continúa a fallback)
│  │
│  └─ catch:
│     └─ (fallback silencioso a paso siguiente)
│
└─ // Fallback: embedding normal
   ├─ [vec] = await embedTexts(env.AI, [question])
   └─ return { vec }

Paso 1: Tentativa de HyDE

  1. Verifica condiciones para activar HyDE:

    • opts.hyde === true (explícitamente activado)
    • opts.apiKey proporcionado (necesario para Gemma)
    • env.AI disponible (binding CloudFlare Workers AI)
  2. Si todas se cumplen, entra en bloque try:

    • Llama generateHyDE(opts.apiKey, question) → retorna string (puede ser vacío)
    • Si pasaje no vacío:
      • Concatena: text = "${question}\n\n${hyp}"
      • Embebe text → embedTexts(env.AI, [text]) retorna array de vectores
      • Extrae primer vector: [vec]
      • Retorna { vec, hyde: hyp } con evidencia del pasaje
  3. Si generateHyDE lanza excepción (timeout, HTTP error, etc.):

    • Catch silencioso (no lanza)
    • Continúa a fallback
  4. Si pasaje está vacío (p.ej. JSON parse falló):

    • Fallback silencioso (no retorna)
    • Continúa a fallback

Paso 2: Fallback a embedding normal


Salida

Tipo: Promise<{ vec: number[]; hyde?: string }>

CampoTipoPresente siEjemplo
vecnumber[]Siempre[0.001, -0.023, ..., 0.456] (dim BGE-M3)
hydestring?HyDE exitoso"Para configurar multi-tenancy..."

Integración en handlers

archivo.ts (línea 172)

const hydeEnabled = args.hyde === true || env.RETRIEVAL_HYDE === '1';
const { vec: queryVec } = await buildQueryVec(env, question, {
  hyde: hydeEnabled,
  apiKey: env.GOOGLE_AI_API_KEY,
});

Lógica:

oraculo/ask.ts (línea 99)

const { vec: queryVec } = await buildQueryVec(env, question, {
  hyde: env.RETRIEVAL_HYDE === '1',
  apiKey: env.GOOGLE_AI_API_KEY,
});

Lógica:

bib_eval_retrieval (harness, línea 530)

let qv = baseQv;
if ((v === 'hyde' || v === 'hyde+trim') && env.GOOGLE_AI_API_KEY) {
  const bv = await buildQueryVec(env, q, { hyde: true, apiKey: env.GOOGLE_AI_API_KEY });
  qv = bv.vec;
}

Lógica:


Decisiones de diseño

¿Por qué concatenación pregunta + pasaje?

Alternativas:

  1. Solo pasaje: vector más cercano a docs reales, pero pierde anclaje si alucina
  2. Pregunta + pasaje (elegido): pregunta ancla el vector; si pasaje es falso, vector sigue cercano al tema
  3. Promedio: complejidad innecesaria

Justificación: robustez a alucinaciones sin perder recall.

¿Por qué try-catch silencioso?

¿Cómo se instrumenta HyDE?

¿Por qué dos puntos de entrada (args vs env)?


Casos de uso

ScenarioFlujoResultado
hyde=true, API válida, Gemma rápidoHyDE → pasaje → concat → embed✅ {vec (de concat), hyde: "..."}
hyde=true, API válida, Gemma timeoutHyDE lanza → catch → fallback✅ {vec (pregunta)}
hyde=true, API inválidaHyDE lanza → catch → fallback✅ {vec (pregunta)}
hyde=falseSalta HyDE → fallback directo✅ {vec (pregunta)}
hyde=true pero opts.apiKey undefinedSalta condición → fallback✅ {vec (pregunta)}

Performance

Implicación: +1-1.5s sensible en Help Widget; aceptable en Oráculo backend.


Testing

Con HyDE activo (harness)

bib_eval_retrieval({variants: ["baseline", "hyde", "hyde+trim"]})

Compara:

Métrica: overlap_vs_baseline (% chunks en común) → si ↑, HyDE aporta recall.

Unitario (mock)

const mock_vec = await buildQueryVec({AI, GOOGLE_AI_API_KEY}, "¿Qué es X?", {
  hyde: false,
});
expect(mock_vec.vec.length).toBe(1024); // dim BGE-M3
expect(mock_vec.hyde).toBeUndefined();

Véase también

Subir