Skip to content

Incident investigator

incident-investigator es el más profundo de los workers de Ongrid. El coordinator despacha a él cada vez que el usuario quiere causa raíz, no solo un resumen de síntomas. La persona recorre la cadena causal desde la alerta observada hasta la fuente originaria ("patient zero" — 0 号病人 en el prompt de la persona) y devuelve un informe estructurado.

Esta es la persona detrás de RCA

El pipeline causal RCA HLD-013 (entorno de test, rollout en mayo 2026) está construido sobre esta persona. Cuando /incident/<id> → Get RCA corre, el manager lanza este worker con el incident id pre-rellenado en el prompt.

Cuándo la elige el coordinator

El archivo de persona declara los patrones de trigger. Citando el frontmatter verbatim:

yaml
when_to_use: |
  coordinator 在用户问以下场景时 spawn 本 worker:
    • "这条告警的根因是什么 / 到底是谁导致的"
    • "incident 123 怎么排查 / 受影响范围 / 持续多久"
    • "这个告警是不是误报 / 跟上次那个相关吗"
    • "这台机器 mem 飙了,看一下"

Traducción: cada vez que el usuario pregunta por qué en vez de qué. El entregable del investigator es la cadena causal hasta la fuente, no un snapshot de "estado actual".

Toolbag

Whitelisted en la persona — el filtro del runtime elimina todo lo demás:

yaml
tools:
  - query_knowledge        # KB / vault / uploads
  - get_incident_detail
  - query_incidents
  - correlate_incident     # metric+log+trace pulled together
  - query_change_events    # config / rule / device mutations
  - query_promql
  - query_logql
  - query_traceql
  - get_edge_summary
  - query_alert_rules
  - query_devices
  - get_host_load
  - get_host_processes
  - expand_topology
  - find_topology_node
  - host_find_large_files
  - host_du_summary
  - host_stat_file

disallowed_tools:
  - execute_skill
  - host_restart_service
  - run_shell

permission_mode: read-only

Consecuencias clave:

  • Read-only. Sin host_restart_service, sin execute_skill, sin run_shell. Si la investigación concluye que un servicio necesita reinicio, lo devuelve como propuesta al coordinator; el coordinator despacha specialist-ops con una intención mutadora, y el reviewer la gatea.
  • Topología y change events. La ventaja única que tiene sobre los specialists es expand_topology + find_topology_node (recorrer el grafo de servicios hacia atrás hasta fuentes upstream) y query_change_events (correlacionar síntomas con mutaciones recientes de config / regla / device alrededor del fired_at de la alerta).
  • El bash cross-host y las probes por host están ausentes. Esos viven en los specialists (specialist-network, specialist-compute, specialist-disk); el investigator coordina sobre el data plane de observabilidad.

El workflow de 5 pasos

El cuerpo de la persona codifica el workflow. Cada investigación corre:

  1. KB primero (obligatorio). Una vez con el incident_id en mano, query_knowledge exactamente una vez con el nombre de la regla + síntoma como query en lenguaje natural (p. ej. "swap_high 告警怎么 排查"). Un hit (score ≥ 0.6) significa seguir el playbook; la respuesta final lleva una citación (参考 KB: <title>). Un miss significa proceder al paso 1.
  2. Síntoma + blast radius. get_incident_detail para el nombre de la regla / severidad / target / fired_at / labels. Este es el final de la cadena causal (el efecto), no la causa raíz. No te detengas aquí.
  3. Timeline. correlate_incident jala metric + log + trace para la misma ventana del incidente en una llamada. Ordena por fired_at / primer momento de desviación. La señal más temprana es el candidato fuente; la CPU/latencia alta downstream usualmente es efecto, no causa. "Más ruidoso" ≠ "más temprano".
  4. Un salto causal upstream. Elige un tool con un propósito claro:
    • ¿Qué cambió?query_change_events(around_ts=fired_at). Los cambios del lado producto son frecuentemente patient zero.
    • ¿Dependencias?expand_topology upstream (no blast radius downstream) / find_topology_node.
    • ¿Recorrer la cadena de callers?query_traceql para encontrar el originador del span más lento.
    • ¿Primer error?query_logql grep por device_id para el ERROR / PANIC / OOM más temprano antes de fired_at.
    • ¿Quién se movió primero?query_promql para encontrar la métrica que se desvió primero.
  5. Recursa. Trata al candidato upstream como el nuevo punto actual. Repite el paso 3 hasta una de:
    • Tocar fondo — no queda upstream in-system. La hoja es un proceso / un único cambio / una dependencia externa = patient zero.
    • Señal agotada — no se puede ir más allá. Reporta "capa más profunda alcanzada + qué señal nos dejaría continuar".
  6. Validar. La causa raíz propuesta debe explicar toda la cadena downstream — temporalmente anterior al síntoma, magnitud y dirección consistentes. Si no, baja a "hipótesis" y dilo.

El presupuesto de 18 tools — cava profundo, nunca da vueltas

La persona refuerza una disciplina de iteración estricta. Del cuerpo:

你有 ~18 个工具调用预算(够上溯 4-6 层)。深挖允许,但死分支立刻砍

  • 工具返回空(result:[] / streams:[]):第一次空可换思路;第二次 空立刻停这条线,换方向或就此上溯为止。
  • 同一工具失败 / 空 ≥2 次 → 必须换工具或换方向,禁止反复换表达式空转 (v0.7.51-55 的失败都栽在这).
  • 每一步都要朝"再上溯一层"前进 — 调之前问自己 "这步能让我更接近源头吗"。
  • 上溯到 4-5 层仍未触底、或预算用到 ~15:停,输出"目前最深一层 + 缺失 信号",别为凑满空转。

El tope max_turns: 40 en el frontmatter es el techo duro — el grafo de eino cuenta MaxStep = MaxIterations*2+2, así que 40 → MaxStep=82 → aproximadamente 41 turnos de ChatModel. El presupuesto de 18 tools es la guía más suave en el prompt; el tope es la red de seguridad del runtime.

Los dead branches no son negociables

El prompt del investigator lo hace una regla explícita de "no hagas" porque las primeras evals v0.7.51-55 vieron al worker quemar 30+ turnos re-permutando expresiones PromQL intentando hacer que una range query vacía devolviera data. Un solo resultado vacío es información; un segundo resultado vacío significa que ya aprendiste lo que ibas a aprender — pivota o asciende.

Formato de salida

La respuesta final al coordinator es Markdown verbatim de esta forma:

markdown
**根因(0 号病人)**
{One-line patient zero — process / change / upstream service+node /
config. Concrete identifiers (pid + cmdline / service name / change
key) — this is the source of pinpoint_target. If we didn't hit bottom:
"未触底,最深到 X;要继续需 Y 信号".}

**因果链**
{Source → … → alert symptom. One line per hop, each with "why this
caused the next" plus evidence (PromQL / LogQL / trace span /
process line).}

**现象**
{1-2 sentences: when did it start / which host / what crossed
threshold / for how long.}

**置信度与验证**
{High / medium / low + reason. Plus: "what query or action would
further validate or falsify this root cause".}

El coordinator sintetiza esto en la respuesta hacia el usuario (un idioma, un párrafo, la citación si hubo hit de KB). El Markdown crudo del investigator también se persiste como el Result del worker en la sesión — la UI de RCA lo muestra verbatim bajo el desplegable Razonamiento.

El test e2e F1

F1 es la eval end-to-end que ejercita esta persona contra un incidente sembrado en el entorno de test. Forma:

  1. Sembrar un incidente sintético swap_high en node-01 (device_id=7) con un proceso searxng fijado al 95% RSS durante 30 minutos.
  2. Cablear el pipeline de alerta para que el incidente dispare con las labels esperadas + fired_at.
  3. Despachar al investigator con prompt = "rca incident 1234".
  4. Asegurar que la respuesta final contenga:
    • regla swap_high mencionada en 现象.
    • proceso searxng identificado en 根因(0 号病人) con un pid + línea de comando.
    • Al menos 2 saltos causales en 因果链 (síntoma → upstream).
    • Citación (参考 KB: …) SI el KB sembrado tiene un playbook matcheando.

La primera versión de HLD-013 falló F1 porque default_provider no estaba seteado en la DB — el resolver cayó a openai con un nombre de modelo glm, el chat model erroró y el worker devolvió un análisis vacío. La lección: F1 también cubre el resolver de LLM como side-effect, por lo cual es el gate canónico "¿RCA realmente cabló end-to-end?".

Razones comunes por las que se queda corta

La persona devuelve "patient zero no alcanzado" honestamente cuando:

  • La causa está fuera del cluster — proveedor DNS, API upstream, energía. query_change_events no ve cambios de infra fuera del scope del manager.
  • Faltan datos de trazas — las queries TraceQL no devuelven spans para el servicio relevante. El investigator no puede recorrer una cadena de callers sin trazas; reporta "缺失 trace 信号".
  • La línea de log que apuntaría al trigger ha rotado. La retención de Loki < el time-to-first-investigate. El investigator lo dice y recomienda extender la retención para investigaciones recurrentes.

Esta honestidad es por diseño. Una respuesta equivocada con confianza es peor que "tocamos la señal X y necesitamos Y para continuar".

Tuning

Cosas que realísticamente cambiarías en un fork de esta persona:

  • Añadir hits de KB específicos del dominio — escribe playbooks para tus modos de falla comunes, distribúyelos vía el vault, el paso KB first del investigator los descubrirá.
  • Ajustar la whitelist de tools — añade variantes de query_traceql si tu stack de tracing no es Tempo, o quita host_du_summary si no quieres que el investigator despache inspección de disco inline (el default es delegar a specialist-disk vía la propia salida del worker — pero la persona lleva las tools para inspeccionar él mismo cuando es rápido).
  • Apretar el presupuesto — si el costo por-token de tu modelo importa, baja max_turns a 25-30. El presupuesto blando de 18 llamadas en el cuerpo de la persona ya mantiene la mayoría de investigaciones por debajo de eso.

Ver Agentes custom para cómo montar tu fork sobre la integrada.