Skip to content

Incident investigator

incident-investigator é o mais profundo dos workers do Ongrid. O coordinator despacha para ele sempre que o usuário quer causa raiz, não só um resumo do sintoma. A persona percorre a cadeia causal do alerta observado de volta à fonte originária ("paciente zero" — 0 号病人 no prompt da persona) e retorna um relatório estruturado.

Esta é a persona por trás do RCA

O pipeline de RCA causal do HLD-013 (ambiente de teste, rollout em maio de 2026) é construído sobre essa persona. Quando /incident/<id> → Get RCA roda, o manager dá spawn nesse worker com o incident id pré-preenchido no prompt.

Quando o coordinator a escolhe

O arquivo de persona declara os padrões de trigger. Citando o frontmatter verbatim:

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

Tradução: sempre que o usuário pergunta por quê em vez de o quê. O entregável do investigator é a cadeia causal até a fonte, não um snapshot do "estado atual".

Tool bag

Whitelisted na persona — o filtro do runtime remove tudo o mais:

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

Consequências principais:

  • Read-only. Sem host_restart_service, sem execute_skill, sem run_shell. Se a investigação concluir que um serviço precisa ser reiniciado, ela retorna isso como proposta ao coordinator; o coordinator despacha specialist-ops com uma intenção mutadora, e o reviewer faz o gate.
  • Topologia e change events. A vantagem única que ele tem sobre os specialists é expand_topology + find_topology_node (percorrer o grafo de serviços de volta às fontes upstream) e query_change_events (correlacionar sintomas com mutações recentes de config / regra / device em volta do fired_at do alerta).
  • Bash cross-host e probes por host estão ausentes. Esses vivem nos specialists (specialist-network, specialist-compute, specialist-disk); o investigator coordena sobre o plano de dados de observabilidade.

O workflow de 5 passos

O corpo da persona codifica o workflow. Toda investigação roda:

  1. KB primeiro (obrigatório). Com o incident_id em mãos, query_knowledge exatamente uma vez com o nome da regra + sintoma como query em linguagem natural (ex.: "swap_high 告警怎么排查"). Um hit (score ≥ 0.6) significa seguir o playbook; a resposta final carrega uma citação (参考 KB: <title>). Um miss significa proceder ao passo 1.
  2. Sintoma + blast radius. get_incident_detail para nome da regra / severity / target / fired_at / labels. Isto é o fim da cadeia causal (o efeito), não a causa raiz. Não pare aqui.
  3. Timeline. correlate_incident puxa metric + log + trace para a mesma janela do incidente em uma chamada. Ordene por fired_at / tempo da primeira divergência. O sinal mais antigo é o candidato a fonte; CPU/latência alta downstream geralmente é efeito, não causa. "Mais ruidoso" ≠ "mais antigo".
  4. Um salto causal upstream. Escolha uma ferramenta com um propósito claro:
    • O que mudou?query_change_events(around_ts=fired_at). Mudanças no lado do produto frequentemente são paciente zero.
    • Dependências?expand_topology upstream (não blast radius downstream) / find_topology_node.
    • Rastrear a cadeia de chamadas?query_traceql para achar o originador do span mais lento.
    • Primeiro erro?query_logql grep por device_id para o ERROR / PANIC / OOM mais antigo antes de fired_at.
    • Quem se mexeu primeiro?query_promql para achar a métrica que divergiu primeiro.
  5. Recursão. Trate o candidato upstream como o novo ponto atual. Repita o passo 3 até uma das opções:
    • Bateu no fundo — não resta upstream no sistema. A folha é um processo / uma mudança / uma dependência externa = paciente zero.
    • Sinal esgotado — não dá para ir mais fundo. Reporte "camada mais profunda alcançada + qual sinal nos deixaria continuar".
  6. Validar. A causa raiz proposta deve explicar toda a cadeia downstream — temporalmente anterior ao sintoma, magnitude e direção consistentes. Senão, rebaixe para "hipótese" e diga.

O budget de 18 ferramentas — cave fundo, nunca rode em círculo

A persona impõe disciplina estrita de iteração. Do corpo:

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

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

O cap max_turns: 40 no frontmatter é o teto rígido — o grafo do eino conta MaxStep = MaxIterations*2+2, então 40 → MaxStep=82 → aproximadamente 41 turns de ChatModel. O budget de 18 ferramentas é a orientação mais branda no prompt; o cap é a rede de segurança do runtime.

Dead branches são inegociáveis

O prompt do investigator faz disso uma regra explícita de "não fazer" porque as evals iniciais de v0.7.51-55 viram o worker queimar 30+ turns re-permutando expressões PromQL tentando fazer uma range query vazia retornar dado. Um único resultado vazio é informação; um segundo resultado vazio significa que você já aprendeu o que vai aprender — pivote ou suba.

Formato de saída

A resposta final ao coordinator é Markdown verbatim deste formato:

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".}

O coordinator sintetiza isso na resposta voltada ao usuário (um idioma, um parágrafo, a citação se houve hit de KB). O Markdown bruto do investigator também é persistido como o Result do worker na sessão — a UI de RCA o exibe verbatim sob o disclosure de Reasoning.

O teste e2e F1

F1 é a eval end-to-end que exercita essa persona contra um incidente semeado no ambiente de teste. Formato:

  1. Semeia um incidente sintético swap_high em node-01 (device_id=7) com um processo searxng fixado a 95% RSS por 30 minutos.
  2. Conecta o pipeline de alerta para que o incidente dispare com as labels esperadas + fired_at.
  3. Despacha o investigator com prompt = "rca incident 1234".
  4. Asserta que a resposta final contém:
    • Regra swap_high mencionada em 现象.
    • Processo searxng identificado em 根因(0 号病人) com pid + command line.
    • Pelo menos 2 saltos causais em 因果链 (sintoma → upstream).
    • Citação (参考 KB: …) SE a KB semeada tem um playbook correspondente.

A primeira versão do HLD-013 falhou no F1 porque default_provider não estava setado no DB — o resolver caiu para openai com um nome de modelo glm, o chat model deu erro, e o worker retornou uma análise vazia. A lição: F1 também cobre o resolver de LLM como efeito colateral, e por isso é o gate canônico de "o RCA foi conectado end-to-end de verdade?".

Razões comuns para ele parar curto

A persona retorna "paciente zero não alcançado" honestamente quando:

  • A causa está fora do cluster — provedor DNS, API upstream, energia. query_change_events não vê mudanças de infra fora do escopo do manager.
  • Dado de trace está ausente — queries TraceQL não retornam spans para o serviço relevante. O investigator não pode percorrer a cadeia de callers sem traces; ele reporta "缺失 trace 信号".
  • A linha de log que apontaria para o trigger já foi rotacionada. Retenção do Loki < tempo até a primeira investigação. O investigator diz isso e recomenda estender a retenção para investigações repetidas.

Essa honestidade é por design. Uma resposta errada confiante é pior que "batemos no sinal X e precisamos de Y para continuar".

Tuning

Coisas que você realisticamente mudaria num fork dessa persona:

  • Adicione hits de KB específicos do seu domínio — escreva playbooks para seus modos de falha comuns, distribua via vault, o passo KB first do investigator vai descobri-los.
  • Ajuste a whitelist de tools — adicione variantes de query_traceql se sua stack de tracing não for Tempo, ou remova host_du_summary se você não quer que o investigator despache inspeção de disco inline (o padrão é delegar a specialist-disk via a própria saída do worker — mas a persona carrega as tools para se autoinspecionar quando é rápido).
  • Aperte o budget — se o custo por token do seu modelo importa, baixe max_turns para 25-30. O budget soft de 18 chamadas do corpo da persona já mantém a maioria das investigações abaixo disso.

Veja Agents customizados para como montar seu fork sobre o built-in.