mozak.tech Engenharia de IA Corporativa 40%

Parte I — Operações e Infraestrutura Potencializadas por IA

1.12 — Projeto Integrador: Pipeline Autônomo de Resposta a Incidentes (AIOps Pipeline)

Objetivo da Aula

Ao concluir esta aula, você será capaz de:

Integrar todas as capacidades do módulo em um pipeline de agente único

Implementar audit log JSONL com registrar_audit() e EntradaAudit

Usar StatusIncidente como máquina de estados através do pipeline

Analisar o fluxo: alerta → análise → runbook → diagnóstico → remediação → verificação → postmortem

Executar o pipeline completo para 2 alertas de demo

Por que isso importa

O projeto integrador é a síntese do módulo 6. Cada unidade anterior construiu uma peça: geração de IaC, diagnóstico K8s, observabilidade, ChatOps, segurança, CI/CD, FinOps, RAG e auto-remediação. Aqui, todas essas peças se combinam em um agente que processa um incidente do início ao fim — como um SRE autônomo de plantão.

O audit log JSONL é crítico para produção: sem rastreabilidade completa de cada ação do agente, é impossível fazer postmortem, auditar compliance ou depurar comportamentos inesperados.

Conceitos Fundamentais

Máquina de Estados do Incidente

class StatusIncidente(Enum):

    ABERTO         = 'aberto'

    DIAGNOSTICANDO = 'diagnosticando'

    REMEDIANDO     = 'remediando'

    VERIFICANDO    = 'verificando'

    RESOLVIDO      = 'resolvido'

    ESCALADO       = 'escalado'

Cada tool muda o status:

analisar_alerta  → DIAGNOSTICANDO

buscar_runbook   → DIAGNOSTICANDO

executar_diagnostico → DIAGNOSTICANDO

aplicar_remediacao → REMEDIANDO

verificar_resolucao → VERIFICANDO → RESOLVIDO ou continua

gerar_postmortem → (final, status = RESOLVIDO)

escalar_incidente → ESCALADO

O loop para quando incidente.status in (RESOLVIDO, ESCALADO).

Audit Log JSONL

@dataclass

class EntradaAudit:

    incidente_id: str

    timestamp: str

    acao: str           # nome da tool

    agente: str         # 'devops-agent-v1'

    input_data: dict    # args da tool

    output_data: str    # output truncado a 500 chars

    duracao_ms: int     # latência da tool

    status: str         # status do incidente no momento



def registrar_audit(incidente: Incidente, acao: str, input_data: dict, output: str, duracao_ms: int):

    entrada = EntradaAudit(

        incidente_id=incidente.id,

        timestamp=datetime.utcnow().isoformat(),

        acao=acao,

        agente='devops-agent-v1',

        input_data=input_data,

        output_data=output[:500],

        duracao_ms=duracao_ms,

        status=incidente.status.value,

    )

    incidente.audit_entries.append(entrada)

    with open(AUDIT_FILE, 'a', encoding='utf-8') as f:

        f.write(json.dumps(asdict(entrada), ensure_ascii=False) + '\n')

mode='a': append — nunca sobrescreve o arquivo. Cada entrada é uma linha JSON completa. asdict() do dataclasses converte automaticamente para dict.

Pipeline de 6 Passos

mensagens = [{

    'role': 'user',

    'content': f'''Incidente recebido: {json.dumps(alerta)}



Execute o pipeline completo:

1. Analisar alerta

2. Buscar runbook adequado

3. Executar diagnóstico técnico

4. Aplicar remediação (dry_run primeiro)

5. Verificar resolução

6. Gerar post-mortem ou escalar se não resolvido''',

}]

O LLM recebe as 7 tools (analisar_alerta, buscar_runbook, executar_diagnostico, aplicar_remediacao, verificar_resolucao, gerar_postmortem, escalar_incidente) e executa os 6 passos em sequência.

Aprofundamento Técnico

Runbooks Rápidos por Tipo

RUNBOOKS_RAPIDOS = {

    'OOMKilled': 'Aumentar memory limit em 50% + escalar HPA',

    'HighCPU': 'Scale out +2 replicas + verificar query lenta',

    'DiskFull': 'Limpar logs antigos + verificar growth rate',

    'ConnectionPoolExhausted': 'Reiniciar connection pool + aumentar pool_size',

    'CertificateExpired': 'Renovar certificado + reload nginx',

    'CircuitBreakerOpen': 'Verificar downstream + aguardar recovery window',

}

Este dict é o “conhecimento operacional” embutido no agente. Para produção, viria do RAG sobre runbooks reais (Unidade 10).

Postmortem Automático

elif nome == 'gerar_postmortem':

    resultado = f'''POST-MORTEM — {args.get("incidente_id")}

Causa Raiz: {args.get("causa_raiz")}

Ações executadas: {", ".join(args.get("acoes", []))}

Resolvido em: {incidente.resolvido_em}

Ações preventivas: [ver exercício avançado]'''

Para postmortem real com LLM:

response = client.messages.create(

    model='claude-haiku-4-5-20251001',

    max_tokens=600,

    messages=[{

        'role': 'user',

        'content': f'''Gere um post-mortem estruturado:



Incidente: {incidente_id}

Causa raiz: {causa_raiz}

Ações executadas: {acoes}

Tempo de resolução: {tempo_min} minutos



Inclua: resumo executivo, timeline, causa raiz, impacto, ações preventivas.''',

    }],

)

Medição de Latência de Tools

def executar_tool_integrador(nome: str, args: dict, incidente: Incidente) -> str:

    inicio = datetime.utcnow()

    

    # ... execução da tool ...

    

    duracao = int((datetime.utcnow() - inicio).total_seconds() * 1000)

    registrar_audit(incidente, nome, args, resultado, duracao)

    return resultado

Medir latência de cada tool no audit log permite identificar gargalos: se executar_diagnostico demora 5s consistentemente, pode ser o LLM lento ou a tool em si.

Exemplos Anotados

Exemplo 1: Trace do Pipeline para OOMKilled

[DEVOPS AGENT] Processando INC-20240115-143200: OOMKilled em checkout-service



[aberto] → analisar_alerta({'alerta': {'tipo': 'OOMKilled', ...}})

← Alerta analisado: tipo=OOMKilled, serviço=checkout-service



[diagnosticando] → buscar_runbook({'tipo_incidente': 'OOMKilled'})

← Runbook para OOMKilled: Aumentar memory limit em 50% + escalar HPA



[diagnosticando] → executar_diagnostico({'servico': 'checkout-service', 'verificacoes': ['logs', 'metricas', 'hpa']})

← Diagnóstico de checkout-service: logs=OK | metricas=OK | hpa=OK



[diagnosticando] → aplicar_remediacao({'tipo': 'aumentar_memory_limit', 'servico': 'checkout-service', 'modo': 'dry_run'})

← [DRY_RUN] Remediação aumentar_memory_limit em checkout-service: executado



[remediando] → aplicar_remediacao({'tipo': 'aumentar_memory_limit', 'servico': 'checkout-service', 'modo': 'live'})

← [LIVE] Remediação aumentar_memory_limit em checkout-service: executado



[remediando] → verificar_resolucao({'servico': 'checkout-service', 'metrica_alvo': 'memory_restarts'})

← Verificação de checkout-service: RESOLVIDO ✓



[verificando] → gerar_postmortem({'incidente_id': 'INC-20240115-143200', 'causa_raiz': 'Memory limit muito baixo', 'acoes': [...]})

← POST-MORTEM — INC-20240115-143200 ...



[RESULTADO] INC-20240115-143200: resolvido

  Ações: ['aumentar_memory_limit (dry_run)', 'aumentar_memory_limit (live)']

  Audit entries: 7

Exemplo 2: Lendo o Audit Log

# Após execução, ler o audit log

with open('devops_audit.jsonl') as f:

    entries = [json.loads(line) for line in f]



# Estatísticas

for entry in entries:

    print(f'{entry["timestamp"]} | {entry["acao"]:<30} | {entry["duracao_ms"]}ms | {entry["status"]}')



# Análise de latência

import statistics

duracoes = [e['duracao_ms'] for e in entries]

print(f'Latência média: {statistics.mean(duracoes):.1f}ms')

print(f'Latência max: {max(duracoes)}ms — {max(entries, key=lambda e: e["duracao_ms"])["acao"]}')

Padrões e Armadilhas

Padrões

Padrão 1: Loop com condição de saída dupla

for i in range(12):

    response = client.messages.create(...)

    

    # Saída 1: LLM decidiu terminar

    if response.stop_reason == 'end_turn':

        break

    

    # Saída 2: incidente resolvido/escalado via tool

    if incidente.status in (StatusIncidente.RESOLVIDO, StatusIncidente.ESCALADO):

        break

Padrão 2: Audit de cada tool, não do loop

# Audit dentro de executar_tool_integrador, não fora

# Isso garante que TODA chamada de tool é registrada

duracao = int((datetime.utcnow() - inicio).total_seconds() * 1000)

registrar_audit(incidente, nome, args, resultado, duracao)

Padrão 3: asdict() para serialização de dataclass

# dataclasses.asdict() converte Enum para valor string automaticamente

f.write(json.dumps(asdict(entrada), ensure_ascii=False) + '\n')

# ensure_ascii=False → preserva caracteres PT-BR no log

Armadilhas

⚠️ Armadilha 1: Audit file truncado em vez de append

# ERRADO: sobrescreve o log a cada execução

with open(AUDIT_FILE, 'w') as f: ...



# CORRETO: append — preserva histórico de incidentes anteriores

with open(AUDIT_FILE, 'a') as f: ...

⚠️ Armadilha 2: Loop sem saída por status

# Sem verificar incidente.status, o loop continua mesmo após RESOLVIDO

# O agente pode chamar mais tools desnecessárias (e custar mais tokens)

for i in range(12):

    ...

    # FALTA: if incidente.status in (RESOLVIDO, ESCALADO): break

⚠️ Armadilha 3: output_data sem truncamento

# Output de tools pode ser longo (logs, YAML, trace)

# Sem truncar, o audit log pode crescer rapidamente

output_data=output[:500],  # truncar é importante
⚗ Laboratório prático — mozak.tech
Se não for realizar o laboratório, pule para o próximo capítulo.

Ponte para o Lab

Esta unidade não tem TODOs — o código está completo. O lab é exploratório:

Rode python starter.py e observe os 2 alertas sendo processados

Abra devops_audit.jsonl e analise o trail de auditoria

Modifique verificar_resolucao para sempre retornar resolvido=False — observe o agente escalar

Adicione um 3º alerta com tipo CertificateExpired e veja o agente buscar o runbook correto

Conecte funcionalidades reais: substitua o mock de diagnóstico por chamadas reais ao kubectl ou Prometheus

O pipeline integrador é a base para um agente DevOps de produção real. Cada substituição de mock por implementação real aproxima o lab da produção.

Agora você está pronto para o lab.