CreaRack-SL

Help Widget · Informante del estado (s56)

Funcionalidadactivecreado Mon May 11#help#crearack-pro#feature#ai#ux

Qué hace

El Informante del estado del Help Widget detecta preguntas operativas del usuario (“¿hay alertas?”, “¿cuántos racks tengo?”, “¿qué devices están offline?”) y inyecta datos en vivo de su organización al system prompt del modelo (Gemma 4 via google-genai), de modo que las respuestas contienen información real y no inventada.

Para preguntas conceptuales sigue el flujo Help estándar sin coste extra de tokens.

Motivación

Tras cerrar el Help Widget en s53-s55 con corpus user-facing y chat multi-turn, quedaba sin resolver el caso “¿hay alertas?”: el modelo solo dispone del corpus de la wiki user-facing, no del estado vivo de la app. Sin contexto factual, inventaba números o se limitaba a explicar qué son las alertas en general.

Edu decidió en s56 (11-05-2026 PM) implementar un mecanismo lazy + selective + por perfil + coste cero por defecto.

Estrategia · “Lazy + selective + regex puro”

User pregunta en Help Widget
  ↓
POST /api/help/ask (Django proxy)
  ↓
[1] core.services.help_intent.detect_state_intent(question)
    ├── Regex contra catálogo de keywords (sin LLM, coste 0).
    ├── Devuelve lista de items que matchean (p. ej. ["alerts", "racks"]).
    └── Lista vacía → flujo Help normal, sin contexto extra, 0 tokens.
  ↓
[2] core.services.help_state.build_state_context(items, user)
    ├── Para cada item, llama su builder.
    ├── Builders agregan datos en vivo de la org del user (RLS Postgres).
    └── Devuelve bloque "[CONTEXTO]" compacto (~30-150 tokens).
  ↓
[3] question = "[CONTEXTO]\n- ...\n[/CONTEXTO]\n\n" + question
  ↓
[4] _mcp_call("bib_ask", {question, ...}) → workspace
  ↓
[5] Gemma 4 responde usando los datos reales del bloque

Catálogo de items operativos (7)

Cada item tiene su lista de regex de keywords disparadores y un builder asociado.

ItemDatos inyectadosReader veAdmin/Operator ve adicional
alerts3 fuentes agregadas: AlertEvent activos + MonitoringTarget(last_status=down) + AIInsight pending. Etiquetado claro de cada categoríatotales + countstop 5 devices caídos · top 3 alertas configuradas
rackscount por estadocount + estadostop 3 racks más llenos (U usadas/totales)
devicescount online/offline (MonitoringTarget)countstop 5 caídos con IP + “+N más sin detallar”
health3 checks operacionalescheck DB+ disco libre %, uptime aprox
signageSignagePlayer countscountserrores de deploy 24h
provisionDeviceProfile descubiertos 24h + pendientes aprobarcountstop 3 candidatos por confidence
capacityU totales/libres + % usototal + librestop 3 racks más llenos

Saltados del scope original (decisión Edu charla Fase 0 s56):

  • Sesiones SSH: el Local Agent es in-memory por conexión, no multi-tenant.
  • Network BW: datos en VictoriaMetrics, no Postgres. Reabrir si se justifica un wrapper sobre VM.

Granularidad por perfil

Filtrado en cada builder según user.role:

  • readonly → version reducida (solo counts + estados generales).
  • operator + admin + superuser → vista extendida con listas top-N y detalles operativos.

Decisión Edu s56: operator ve igual que admin (solo readonly recibe la versión reducida).

Coste

Tipo de queryTokens extraDB queries extraLLM extra
Conceptual (sin keyword)000
Operativa 1 item~30-1501-30
Operativa N items~30-150 × NN × 1-30

Sin mini-call LLM clasificador. Sin HTTP extra al workspace. El regex de intent corre local en Django (microsegundos).

Multi-tenancy

TenantRLSMiddleware aplica Row-Level Security a nivel Postgres por organización. Los builders filtran adicionalmente por FK explícita cuando el modelo no la tiene directa (caso AlertEvent → vía alert.organization).

Test test_build_context_multi_tenant: alertas de org A NO se cuelan al contexto de un user de org B.

Modo Tutor

El modo IT Tutor del Help Widget (s55) explícitamente skippea el Informante. Razón: el Tutor es asistente de conocimiento general IT/networking (Cisco, OSPF, VLAN, RFC, etc.), no del estado de la app. Inyectar datos en vivo aquí confunde al modelo y rompe el modo.

Failure mode

Si cualquier builder lanza excepción, build_state_context la captura con logger.warning y devuelve cadena vacía. El Help Widget sigue funcionando con el flujo normal sin inyección. El Informante NO puede romper el Help.

Fix post-deploy s56

Validación en PROD reveló que _build_alerts_context original solo contaba alertas configuradas manualmente (AlertEvent), pero el operador espera ver también devices caídos (MonitoringTarget.last_status=down) y AIInsights pendientes (CNS Sentinel). Fix commit 06a228a8 añade las 3 fuentes con etiquetado claro:

“Alertas activas: 133 (devices caídos: 128, alertas configuradas: 0, insights IA pendientes: 5).”

Métricas de éxito (validadas en PROD, 11-05-2026)

PreguntaResultado en PROD
“¿hay alertas?”133 con desglose 3 categorías ✅
“¿cuántos racks tengo y cuánto espacio libre?”“80 racks y 3177U libres” ✅
“¿qué devices están offline?”Lista top 5 + tail explícito ✅
“¿cómo creo un rack?” (falso positivo)Respuesta conceptual correcta (modelo ignora contexto) ✅
“¿qué es CNS Sentinel?” (conceptual)Respuesta del corpus sin contexto inyectado ✅
Modo Tutor con “¿hay alertas?”Sin contexto, respuesta general IT ✅

Archivos clave

  • core/services/help_intent.py — catálogo regex de 7 items.
  • core/services/help_state.py — 7 builders + función principal build_state_context.
  • core/api_help.py — integración en help_ask (solo modo help, no tutor).
  • tests/api/test_help.py — 27 tests (intent puro + builders + integración).

Releases

  • PR #24 (v1.0.72) — MVP base con 7 items y tests.
  • commit 06a228a8 (v1.0.73) — fix _build_alerts_context con 3 fuentes.

Véase también

  • [[entity—blueprints—service—google-genai-driver]]
  • [[entity—monitoring—service—google-genai-provider]]
  • [[feature—ai—google-genai-cns-tutor-mib]]