Help Widget · Informante del estado (s56)
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.
| Item | Datos inyectados | Reader ve | Admin/Operator ve adicional |
|---|---|---|---|
| alerts | 3 fuentes agregadas: AlertEvent activos + MonitoringTarget(last_status=down) + AIInsight pending. Etiquetado claro de cada categoría | totales + counts | top 5 devices caídos · top 3 alertas configuradas |
| racks | count por estado | count + estados | top 3 racks más llenos (U usadas/totales) |
| devices | count online/offline (MonitoringTarget) | counts | top 5 caídos con IP + “+N más sin detallar” |
| health | 3 checks operacionales | check DB | + disco libre %, uptime aprox |
| signage | SignagePlayer counts | counts | errores de deploy 24h |
| provision | DeviceProfile descubiertos 24h + pendientes aprobar | counts | top 3 candidatos por confidence |
| capacity | U totales/libres + % uso | total + libres | top 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 query | Tokens extra | DB queries extra | LLM extra |
|---|---|---|---|
| Conceptual (sin keyword) | 0 | 0 | 0 |
| Operativa 1 item | ~30-150 | 1-3 | 0 |
| Operativa N items | ~30-150 × N | N × 1-3 | 0 |
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)
| Pregunta | Resultado 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 principalbuild_state_context.core/api_help.py— integración enhelp_ask(solo modohelp, notutor).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]]