CreaRack-SL

Endpoint POST /api/oraculo/ask (Oráculo de EL · workspace)

Endpoint POST /api/oraculo/ask

Endpoint CF Pages Function que recibe una pregunta en lenguaje natural y devuelve una respuesta sintetizada por Gemini 3.8 Flash (razonamiento bajo; hasta el 13-09-2026, Gemma 4 26B), fundamentada en el corpus del Bibliotecario (embeddings BGE-M3, retrieval sobre bib_chunks). Implementado en functions/api/oraculo/ask.ts. Activo desde PR #50 (commit 56463f6, sesión s72).

Ruta y método

POST https://workspace.crearack.com/api/oraculo/ask
Content-Type: application/json

Request body

interface AskBody {
  question: string;      // Pregunta del usuario (requerido)
  history?: TutorMessage[]; // Turnos previos (opcional, cap: 12)
}

interface TutorMessage {
  role: 'user' | 'assistant';
  content: string;       // No vacío
}

history se trunca a los últimos MAX_HISTORY_MESSAGES = 12 turnos antes de pasarse al modelo. Desde el 13-09-2026 el retrieval de un seguimiento (“para qué se utiliza”) se hace con el último turno del usuario delante de la pregunta nueva; antes buscaba solo con la pregunta suelta y las fuentes del 2º turno salían de otro tema.

Response body

Respuesta normal (top_relevance ≥ 0.4 o history presente)

{
  "answer": "string",
  "sources": [
    {
      "title": "string",
      "path": "string",
      "chunkTitle": "string",
      "relevance": 0.00
    }
  ],
  "model": "gemini-3.8-flash",
  "chunks_used": 8,
  "duration_ms": 1234
}

Respuesta honesta inmediata (top_relevance < RELEVANCE_FLOOR y sin history) — PR #53

Cuando el mejor chunk tiene score < RELEVANCE_FLOOR = 0.4 y no hay conversación previa, el endpoint no invoca al modelo y devuelve:

{
  "answer": "No tengo info clara sobre eso en el grafo (las fuentes que encuentro son tangenciales). Mira los enlaces de abajo por si alguno te sirve, o reformula la pregunta con un módulo o servicio concreto.",
  "sources": [ /* top 5 chunks débiles como pistas */ ],
  "model": "gemini-3.8-flash",
  "chunks_used": 0,
  "duration_ms": 123
}

Ahorro: ~2s de latencia + tokens de Gemma 4. Queda registrado en oraculo_queries con top_relevance bajo → señal de gap de corpus.

Excepción: en multi-turno (history.length > 0) NO aplica el corte. El contexto de la conversación puede dar sentido a follow-ups con retrieval flojo.

Constantes clave

ConstanteValorDescripción
TOP_K8Chunks recuperados del retrieval
MAX_HISTORY_MESSAGES12Cap de turnos de historia aceptados
RELEVANCE_FLOOR0.4Umbral mínimo para invocar al modelo
ORACLE_SYNTHESIS_MODEL (env)gemini-3.8-flashModelo de síntesis del Oráculo (decisión de Edu 13-09-2026, [[decision—20260913—oraculo-gemini-38-flash]]). Con un modelo Gemini va thinkingLevel: low y techo 2048; con gemma-4-26b-a4b-it (marcha atrás) vuelve al comportamiento anterior. Help, Tutor, bib_ask y el chat del Correo siguen en Gemma 4
SYNTHESIS_MODELgemma-4-26b-a4b-itModelo de síntesis (Google AI Studio)

Flujo de procesamiento

1. Validar body (question requerido, history shape)
2. Retrieval: bib_chunks sin filtros app/source_type (corpus completo)
3. Si top_relevance < 0.4 && history.length === 0
   → Respuesta honesta inmediata (sin LLM)
   → logQuery(telemetría) y return
4. synthesizeAnswer(key, question, chunks, 'oracle', history)
   → buildOraclePrompts: system prompt universo EsfericLabs
   → Gemma 4 vía Google AI Studio (responseMimeType: application/json)
5. logQuery(telemetría) best-effort a D1 oraculo_queries
6. Return response con answer + sources + meta

Diferencias con otros modos de síntesis

ModoScopeRetrievalUso
'help'Hard-restricted “in-app CreaRack Pro”Sí (filtrado)Ayuda contextual in-app
'tutor'IT general sin groundingNo (sin retrieval)Tutor de red del CNS
'oracle'Universo EsfericLabs completoSí (sin filtros)Oráculo de EL en Workspace

Telemetría

Cada llamada registra una fila en oraculo_queries (D1) vía INSERT best-effort:

INSERT INTO oraculo_queries
  (question, sources_count, top_relevance, duration_ms, mode, created_at)
VALUES (?, ?, ?, ?, 'oracle', datetime('now'));

El INSERT es best-effort: si falla, la respuesta se devuelve igualmente. Rows con top_relevance bajo → señal de gap de corpus.

Manejo de errores

  • 400 Bad Request si falta question o history tiene shape inválido.
  • Errores de síntesis se propagan como respuesta de error al cliente.
  • Errores de logQuery son silenciosos (best-effort).

Archivos relacionados

  • functions/api/oraculo/ask.ts — implementación principal
  • functions/api/mcp/handlers/archivo-core.ts — synthesizeAnswer, buildOraclePrompts
  • migrations/0031_create_oraculo_queries.sql — DDL tabla telemetría
  • src/components/shell/SearchInline.tsx — cliente frontend

Véase también

  • [[entity—oraculo—endpoint—ask]]
  • [[decision—20260913—oraculo-gemini-38-flash]]
  • [[entity—oraculo—model—oraculo-queries]]
  • [[feature—workspace—oraculo-chat-multiturno]]