Oráculo de EL — Asistente conversacional del Workspace
Asistente conversacional integrado en la caja de búsqueda del header de workspace.crearack.com. Reutiliza toda la infraestructura del Bibliotecario (embeddings BGE-M3, retrieval sobre bib_chunks, síntesis con Gemma 4 26B-A4B-IT vía Google AI Studio) con un nuevo modo de síntesis 'oracle' cuyo scope abarca el universo EsfericLabs completo: CreaRack Pro, Workspace, ADRs, runbooks, claude-method (pendiente Fase 2).
Construido en 4 PRs encadenados durante la sesión 72 (s72, 2026-05-19 tarde). Fase 3 cerrada en PR#53.
Motivación
Edu leyó la guía del Bibliotecario y detectó que el 90% del backend necesario ya existía. La caja de búsqueda del header usaba solo Fuse.js local; cablearlo al grafo era la pieza que faltaba. El Tutor de CreaRack Pro servía de referencia de patrón (modo 'tutor', multi-turno con history).
Arquitectura
Detección híbrida automática
SearchInline.tsx detecta si la query debe ir al Oráculo o al índice Fuse local mediante shouldAskOracle(query, mode):
- Condición automática (modo Auto): ≥4 palabras O contiene
?/¿O palabra interrogativa (qué/cómo/dónde/por qué/cuál/quién/cuándo/what/how/where/why/which/who/when). Mínimo 8 caracteres. - Override manual: 3 chips —
Auto/Buscar(fuerza Fuse) /Preguntar(fuerza Oráculo). - Debounce 450ms + AbortController anti-tormenta.
Endpoint POST /api/oraculo/ask
functions/api/oraculo/ask.ts (~80 LOC tras PR#53):
POST /api/oraculo/ask
Body: { question: string, mode?: string, history?: TutorMessage[] }
Response: { answer, sources[], model, chunks_used, duration_ms }
Flujo interno:
- Valida
question(max 500 chars) yhistory(shape{role, content}, cap servidor 12 turnos). - Retrieval sin filtros
app/source_typesobre todo el corpus del Bibliotecario (TOP_K = 8). - Threshold relevance (PR#53): si
matches[0].score < RELEVANCE_FLOOR (0.4)yhistory.length === 0→ respuesta honesta inmediata sin invocar al modelo (ahorra ~2s + tokens). En multi-turno NO aplica. - Si pasa el threshold: llama a
synthesizeAnswer(apiKey, question, matches, 'oracle', history?)enarchivo-core.ts. - INSERT best-effort a tabla D1
oraculo_queries(telemetría, sin almacenar respuestas).
Modo 'oracle' en archivo-core.ts
buildOraclePrompts genera un system prompt distinto de 'help' (restringido a CreaRack Pro) y de 'tutor' (IT general sin grounding):
- Scope: universo EsfericLabs completo.
- Tono: coloquial, primera persona, 100-250 palabras objetivo.
- Honestidad obligada: si los chunks no cubren, responder
"No tengo info clara sobre eso en el grafo; lo que sí veo es <X>..."antes que alucinar. - Multi-turn: si llega
history, se serializa como bloque"--- CONVERSACIÓN HASTA AHORA ---"con turnosUsuario:/Oráculo:(mismo patrón que modo'tutor').
Telemetría oraculo_queries (D1)
Migración 0031_create_oraculo_queries.sql. Campos: question, sources_count, top_relevance, duration_ms, mode, created_at. Sin almacenar respuestas (no inflar BD, no duplicar contenido). El campo top_relevance permite detectar gaps de corpus (queries con score bajo habitual = área no indexada).
Chat multi-turno (SearchInline.tsx)
- Estado React:
turns: Turn[](reemplazaoracleAnswerstring único, introducido en PR#52). - Trigger: Enter (sin debounce, a diferencia del modo búsqueda automática).
- Caja secundaria
.search-inline-oracle-followupcon focus automático tras cada respuesta. - Botón Nueva conversación (visible solo cuando
inChat === true): reseteaturns[]. - Botón Cerrar (siempre visible cuando el dropdown está abierto, introducido en PR#53): dispara
setOpen(false)+inputRef.current?.blur(). - Auto-scroll al final del chat tras cada turno.
- Errores se renderizan como turn assistant con flag
is-errorpero NO se incluyen en elhistoryPayloaddel próximo turno (no contaminan el contexto del modelo). - Render markdown con
marked(mismo patrón queNoteEditor.tsx). - Fuentes clickables →
/wiki/<slug>cuandosourceToHref(path)mapea asrc/content/wiki/<slug>.md.
Threshold de relevancia (RELEVANCE_FLOOR · PR#53)
const RELEVANCE_FLOOR = 0.4;
Calibrado empíricamente sobre embeddings BGE-M3:
| Rango score | Interpretación |
|---|---|
| > 0.5 | Info clara en el grafo |
| 0.4 – 0.5 | Tangencial |
| < 0.4 | Ruido — no invocar al modelo |
Cuando topScore < 0.4 y sin history previo → respuesta honesta inmediata + top 5 fuentes débiles como chips informativos. La entrada en oraculo_queries con top_relevance bajo sirve como señal de gap de corpus.
Excepción multi-turno: en conversaciones con history, el threshold NO aplica. El contexto del chat puede dar sentido a follow-ups aunque el retrieval de la nueva pregunta sea flojo.
Cobertura del corpus (s72)
| Fuente | Estado |
|---|---|
Workspace .md (wikis) | ✅ vía Bibliotecario-Ingest |
Workspace .ts/.tsx/.mjs | ✅ vía bib-reindex-ts.yml |
CreaRack-Pro .py + .md | ✅ vía cron Hetzner bib_ast.py |
claude-method/ | ❌ sin workflow · Fase 2 pendiente |
Fase 2 — Plan concreto (Opción C recomendada): nuevo workflow .github/workflows/bib-reindex-claude-method.yml en workspace que clona claude-method via GH_PAT y corre bib_ast_ts.mjs --root external/claude-method. Cron diario 00:30 UTC (tras el cron TS de 00:15). Ejecución en sesión futura.
PRs de implementación
| PR | Commit | Descripción |
|---|---|---|
| #50 | 56463f6 | Backend: endpoint POST /api/oraculo/ask, modo 'oracle', migración oraculo_queries |
| #51 | 4e282a1 | Frontend Fase 1: UI híbrida SearchInline.tsx, chips Auto/Buscar/Preguntar, 14 clases CSS |
| #52 | a1da9a0 | Multi-turn: estado turns[], caja followup, botón Nueva conversación, cap 12 turnos |
| #53 | (cierre s72) | Cierre Fase 3: botón Cerrar, RELEVANCE_FLOOR=0.4, documentación s72 |
Validación en producción (Edu · s72)
- Velocidad: ~1-2s por turno en queries simples, ~5-7s en exploratorias.
- Tono: OK — coloquial, primera persona, sin filler intros.
- Honestidad detectada: pregunta “por qué retiramos OpenRouter” → respuesta honesta con 8 fuentes chip (no wikis, no clickables — comportamiento esperado).
- Botón Cerrar: detectado como gap por Edu tras PR#52, añadido en PR#53.
- CI: 4/4 verde en cada PR (Prettier · TypeScript · Astro · CF Pages).
Decisiones de arquitectura relevantes
- Modo
'oracle'separado de'help'y'tutor': el system prompt de'help'está hard-restricted a “in-app help assistant for CreaRack Pro”. Reutilizarlo habría bloqueado preguntas sobre Workspace o ADRs Esferic Labs. - Sin persistencia D1 de conversaciones: chat efímero en cliente. Telemetría sí (por turno, sin respuesta).
- Threshold solo en first-turn: en multi-turno el contexto del chat da sentido a follow-ups con retrieval flojo.
- Avalancha Bibliotecario-Ingest (footgun documentado): 4 PRs × 4-5 commits autónomos = ~18 deploys CF Pages. CF cancela los antiguos, solo el último queda Active. Refactor del bot para batch queda apuntado como deuda no-crítica.
Véase también
- [[workspace—que-es-workspace]] — contexto general del workspace donde está integrado el Oráculo
Page parcialmente aislada: el Oráculo es una feature nueva sin precedentes directos en el grafo wiki actual. Candidata a ampliar
relatedcuando se indexen páginas sobrearchivo-core,Bibliotecario-Ingest,synthesizeAnswery la tablaoraculo_queries.