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
| Constante | Valor | Descripción |
|---|---|---|
TOP_K | 8 | Chunks recuperados del retrieval |
MAX_HISTORY_MESSAGES | 12 | Cap de turnos de historia aceptados |
RELEVANCE_FLOOR | 0.4 | Umbral mínimo para invocar al modelo |
ORACLE_SYNTHESIS_MODEL (env) | gemini-3.8-flash | Modelo 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_MODEL | gemma-4-26b-a4b-it | Modelo 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
| Modo | Scope | Retrieval | Uso |
|---|---|---|---|
'help' | Hard-restricted “in-app CreaRack Pro” | Sí (filtrado) | Ayuda contextual in-app |
'tutor' | IT general sin grounding | No (sin retrieval) | Tutor de red del CNS |
'oracle' | Universo EsfericLabs completo | Sí (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 Requestsi faltaquestionohistorytiene 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 principalfunctions/api/mcp/handlers/archivo-core.ts—synthesizeAnswer,buildOraclePromptsmigrations/0031_create_oraculo_queries.sql— DDL tabla telemetríasrc/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]]