Topologia
Topologia é o grafo que converte "um incidente no host edge-prod-04" em "um incidente que derruba payments e search e email". Sem ele o LLM só consegue raciocinar sobre a única série disparando.
ADR-025
Os BaseTools de topologia atuais (expand_topology, find_topology_node) entraram em 2026-05-18. Antes disso, o LLM tinha uma lista plana de devices e nenhuma forma de percorrer relações; "o que depende de X" exigia que o operador já soubesse a resposta.
O modelo de dados
Quatro tabelas, todas no MySQL:
| Tabela | Mantém |
|---|---|
topology_nodes | Uma linha por nó. Tem um type (device / service / cluster / ...), um name, e um props_json free-form. |
topology_node_types | O conjunto fechado de valores permitidos de node.type. |
topology_relations | Aresta direcionada src_node_id -> dst_node_id com um type. |
topology_relation_types | Conjunto fechado de tipos de aresta, cada um marcado com um campo semantics (hard_dep / runtime_dep / traffic / annotation / observation). |
A tag semantics é a ideia-chave. Uma aresta marcada hard_dep propaga falha (se src morre, dst é afetado); uma marcada annotation não. O walk de blast-radius usa isso para filtrar.
Veja internal/manager/biz/topology/.
De onde os dados vêm
Três fontes, em camadas:
- Auto de spans — o processor
service_graphdo Tempo emite arestasservice_a -> service_bcom a tag de semânticaroutes_to. O manager espelha essas paratopology_relationsem um tick de sync. - Auto de edges — todo edge registrado vira um nó
type=device; o id do nó é back-linkado à colunahost_devices.node_idpara queexpand_topology(device_id=X)resolva. - Manual — operadores adicionam nós
type=service/type=clustere arestas pela página/topologydo SPA. Usado para nós que você quer endereçar mas que não são diretamente observados (um banco gerenciado, uma API de terceiro).
Tools
expand_topology
Percorra para fora a partir de um nó, retorne todo nó alcançável mais como foi alcançado. BFS padrão depth 2, cap 5. Direção padrão both (o blast radius é simétrico — o que pode quebrar isso, E o que isso quebra).
{
"node_id": 142,
"depth": 2,
"only_propagating": true,
"direction": "downstream"
}Ou, quando você parte de um device id:
{ "device_id": 17, "depth": 3 }A tool resolve device_id → device.node_id automaticamente. only_propagating=true (padrão) percorre só arestas hard_dep / runtime_dep / traffic; vire para false para incluir arestas de observation / annotation (útil para casos "mostre-me tudo relacionado a X").
Hits retornados carregam os metadados de path que o LLM precisa para raciocinar sobre impacto:
{
"center": { "node_id": 142, "node_name": "payments-api", "node_type": "service", "hops": 0, "propagates_failure": false },
"max_hops": 2,
"reachable_count": 7,
"reachable": [
{ "node_id": 71, "node_name": "edge-prod-04", "node_type": "device", "hops": 1,
"relation_type": "deployed_on", "semantics_tag": "runtime_dep", "reached_via": "downstream",
"propagates_failure": true,
"via_node_id": 142, "via_node_name": "payments-api" }
]
}A lista plana (sem struct aninhada por vizinho) é intencional — mantém o JSON barato para embutir no prompt. Veja expand_topology_basetool.go.
find_topology_node
O pre-step "tenho um nome humano, me dê um node_id". A persona roda isso antes de expand_topology sempre que o prompt menciona um serviço / host por nome:
User: "what does loki-write depend on?"
Agent:
→ find_topology_node{ name: "loki-write" }
← { node_id: 219, node_type: "service", name: "loki-write" }
→ expand_topology{ node_id: 219, direction: "upstream" }
← { reachable_count: 4, ... }Ambos são registrados como BaseTools ScopeManager (sem argumento edge_id) — o DB de topologia vive do lado manager.
Walk de blast-radius na prática
O prompt da persona investigator inclui um "depois que você identifica o serviço disparando, chame expand_topology para ver o que mais está afetado". É assim que o related_alerts do report e a seção "业务影响 / Business impact" são populados — o agent percorre o grafo do device disparando para cima / para baixo e checa cruzado quais outros incidentes dispararam na mesma janela nesses nós.
O caminho de código relevante:
correlate_incidentretorna sumários de metric + log + trace mais odevice_iddo incidente.expand_topology { device_id, direction: both, depth: 2 }retorna o conjunto alcançável.- Extração Pass-2 lê a narrativa do worker e puxa
pinpointed_target(o paciente zero) +related_alerts(a cascata).