CreaRack-SL

Oráculo de EL — UI híbrida de búsqueda y respuesta sintetizada (s72)

Oráculo de EL — UI híbrida de búsqueda y respuesta sintetizada (s72)

Resumen

El commit s72 (PR #51, cableado con PR #50) extiende el componente SearchInline del shell del Workspace para convertirlo en una caja de búsqueda híbrida: mantiene la búsqueda fuzzy local (Fuse.js) y añade un nuevo modo Oráculo que lanza una pregunta al endpoint POST /api/oraculo/ask, recibe una respuesta sintetizada por Gemma 4 con grounding sobre el grafo completo del Bibliotecario, y la renderiza con Markdown en un panel inline dentro del dropdown de búsqueda.

Cierra la Fase 1 del plan Oráculo de EL.


Modos de búsqueda

ModoTriggerBackendLatencia típica
searchSiempre (manual o auto cuando la query parece término corto)Fuse.js local sobre /search-index.json~0 ms (local)
oracleAuto cuando la query “parece pregunta”; o forzado por chipPOST /api/oraculo/ask → Gemma 4 + grafo Bibliotecario1–3 s
auto(default) Delega a shouldAskOracle()Cualquiera de los dos según heurísticavariable

Heurística shouldAskOracle()

Una query activa el modo Oráculo si cualquiera de estas condiciones se cumple (y tiene ≥ 8 caracteres):

  • Termina en ? o ¿.
  • Tiene ≥ 4 palabras.
  • Empieza por palabra interrogativa: qué / cómo / dónde / por qué / cuál / quién / cuándo / what / how / where / why / which / who / when (regex case-insensitive, acepta tildes y sin tilde).

El mínimo de 8 caracteres evita disparar llamadas al modelo para queries cortas como "racks" o "api".


Toggle manual (chips)

En el header del dropdown aparecen tres chips: Auto · Buscar · Preguntar.

  • Auto (default): aplica shouldAskOracle().
  • Buscar: fuerza siempre Fuse, nunca llama al Oráculo.
  • Preguntar: fuerza siempre el Oráculo (si la query tiene ≥ 8 chars).

El modo se guarda en state local de React; no persiste entre sesiones (intencional en Fase 1).


Flujo de red del Oráculo

Usuario teclea → debounce 450 ms → shouldAskOracle() → true
  → AbortController nuevo (cancela request anterior si existe)
  → POST /api/oraculo/ask { question: <query> }
  → Responde OracleResponse { answer, sources[], model, chunks_used, duration_ms }
  → marked.parse(answer) → dangerouslySetInnerHTML
  → Chips de fuentes (linkables si source_path ↔ /wiki/<slug>)

Cancelación: cada vez que el usuario sigue tecleando antes de los 450 ms, el timer se limpia y el AbortController del request anterior se llama con .abort(). Evita tormenta de llamadas a Gemma 4.

Interfaz OracleResponse

interface OracleResponse {
  answer: string;        // Markdown libre generado por Gemma 4
  sources: OracleSource[]; // Fragmentos del grafo usados como grounding
  model: string;         // e.g. "gemma-4-26b-a4b-it"
  chunks_used: number;
  duration_ms: number;
}

interface OracleSource {
  title: string;
  path: string | null;   // Ruta en el repo (puede ser null)
  chunkTitle: string;
  relevance: number;
}

Renderizado de la respuesta

  • La respuesta llega como Markdown generado por el modelo.
  • Se parsea con marked (GFM habilitado, breaks: false), mismo patrón que NoteEditor.
  • Sin DOMPurify: el corpus es interno y el modelo está constrained; no hay riesgo XSS real en este contexto.
  • Estilos propios en .search-inline-oracle-answer con soporte de: párrafos, listas, code inline, bloques pre > code, negrita, enlaces.

Chips de fuentes

La función sourceToHref(path) intenta mapear el source_path devuelto por el Oráculo a una URL navegable del Workspace:

Patrón de pathURL generada
src/content/wiki/<slug>.md/wiki/<slug>
wiki/<slug>.md/wiki/<slug>
Cualquier otronull (chip informativo sin link)

Las fuentes con link se renderizan como <a> con clase search-inline-oracle-chip linkable; las demás como <span>.


Cambios CSS (globals.css)

  • min-width de la caja: 240 px → 360 px para invitar a preguntas largas.
  • Clases nuevas añadidas bajo tokens existentes (--overlay-3/4, --accent, --fg, --fg-dim, --text-sm/xs, --border-soft, --danger):
    • .search-inline-mode-toggle / .search-inline-mode-chip / .search-inline-mode-chip.active
    • .search-inline-oracle / *-meta / *-loading / *-error / *-answer / *-sources / *-chip / *-chip.linkable
    • .search-inline-result-main / *-title / *-path / *-enter
    • .search-inline-history-term / .search-inline-empty
  • Refactorización: todos los style={{ ... }} inline de SearchInline eliminados y convertidos a clases CSS.

Estado y roadmap

FaseEstadoDescripción
Fase 1✅ Completada (s72)UI híbrida + cable con /api/oraculo/ask
Fase 2⬜ PendienteCorpus completo (todas las secciones del grafo)
Fase 3⬜ PendienteIteración de prompt según feedback de uso real

Dependencias añadidas

PaqueteUso
markedParse de Markdown en respuestas del Oráculo. Ya usado en NoteEditor.

Véase también

  • [[entity—workspace—component—search-inline]]
  • [[workspace—agentes-ia]]
  • [[workspace—que-es-workspace]]
  • [[workspace—dashboard]]