1

Como fazer uma IA corrigir falhas de dbt e abrir um PR em rascunho sem dar o controle a ela.

Por Ricardo Ribeiro · Código aberto (Apache-2.0): github.com/ricardoribs/pipeline-guard

São 3 da manhã e um dbt build falha. Alguém acorda, abre o log, lê 400 linhas e descobre que o problema era uma única linha de SQL. A correção leva dois minutos. Achar a causa levou uma hora.

Foi essa hora que eu quis recuperar. Construí o Pipeline Guard, uma ferramenta de código aberto que lê a falha, diagnostica a causa com evidência que dá para conferir, corrige em um ambiente isolado, testa a correção e abre um pull request em rascunho (draft). Uma pessoa revisa e decide.

Neste artigo explico a ideia por trás dele, listo tudo o que ele entrega, mostro como cada etapa funciona e apresento dois casos reais de teste em sandbox.

O problema

Um pipeline de dados é uma sequência de etapas que transforma dados brutos em tabelas prontas para análise. O dbt é a ferramenta que cuida de boa parte dessas transformações, escritas em SQL. Quando uma etapa falha, o time precisa descobrir o que quebrou, onde e por quê.

A reação óbvia é deixar um modelo de linguagem consertar tudo sozinho. O risco é grande: o modelo pode errar a causa, inventar uma explicação convincente, editar um arquivo que não devia ou expor dados e credenciais no meio do caminho. Em dados, um erro silencioso custa mais que uma falha barulhenta.

A pergunta que guiou o projeto foi esta: como aproveitar o modelo sem entregar o controle a ele?

A ideia: separar quem raciocina de quem decide

A resposta foi dividir o trabalho em dois lados.

  • O modelo raciocina. Ele lê as evidências, classifica a falha e propõe uma correção.
  • O código determinístico decide. Determinístico quer dizer que, para a mesma entrada, a resposta é sempre a mesma, sem improviso. É esse código que valida cada citação do modelo, aplica as regras de elegibilidade, confere a autorização e controla tudo o que pode acontecer no mundo real.

Em uma frase: o modelo propõe, o código decide.

O modelo, por exemplo, não tem nenhuma operação para aprovar um PR, fazer merge, fazer deploy ou rodar de novo o pipeline que falhou. Isso não depende de ele se comportar bem: esse caminho simplesmente não existe no código.

O que o Pipeline Guard entrega

A cada falha, ele entrega um pacote completo e verificável, não só uma resposta de chat. Estas são as dez entregas, na ordem em que acontecem:

  1. Validação do incidente e da configuração. Antes de coletar qualquer evidência, ele confere se o incidente e o arquivo de configuração do projeto seguem o contrato. Se algo está inválido, a execução para com um código de saída próprio.
  2. Coleta de evidência somente leitura. Um adaptador de uma lista fechada (hoje dbt e mock) coleta só o que o incidente declarou. No dbt, são os arquivos run_results.json e manifest.json, com checagem de hash.
  3. Sanitização antes do modelo. Campos sensíveis, cabeçalhos de autenticação, cookies, chaves privadas, e-mails, URLs assinadas e formatos conhecidos de token são mascarados antes de qualquer envio ao modelo ou gravação em disco.
  4. Diagnóstico estruturado com citações. O resultado segue um contrato versionado (pipeline_diagnosis_v1): causa, família da falha, confiança, status da causa, impacto e as evidências citadas. Se a evidência for insuficiente ou conflitante, a causa vira UNKNOWN, em vez de um palpite.
  5. Decisão de elegibilidade. Código determinístico, e não o modelo, diz se a falha pode seguir para correção.
  6. Relatório neutro. Saída em JSON (para máquinas) e em Markdown (para pessoas), sem depender de Slack, Airflow, Git ou warehouse.
  7. Artefatos imutáveis e manifesto. Cada execução guarda resultado, diagnóstico, decisão e relatório, mais um manifesto com status final, versões de contrato, hashes de evidência, horários e duração. Repetir o mesmo incidente reaproveita o resultado final em vez de criar uma duplicata conflitante.
  8. Correção mínima em workspace isolado. Com autorização verificada, um agente de correção edita só o necessário, dentro de limites de caminho, extensão, número de arquivos e linhas.
  9. Validação com os comandos do seu projeto. Roda só os comandos que o projeto declarou (no dbt, deps e build) e confere o hash do patch.
  10. Push restrito e PR em rascunho. Envia só a branch do incidente, com um refspec estrito, revalida o commit enviado e cria ou reaproveita um PR em rascunho no Bitbucket Cloud ou no GitHub. A descrição traz o incidente, o diagnóstico, as evidências, os arquivos alterados e o resultado dos testes.

O resultado final é versionado e legível por máquina, e vem com códigos de saída estáveis, que qualquer orquestrador sabe interpretar:

Código de saídaSignificado
0Diagnóstico validado e com evidência concluído
1Falha operacional
2Incidente ou configuração do projeto inválidos
3Parada segura com causa UNKNOWN: evidência insuficiente ou limite atingido

O código 3 é um resultado controlado. A ferramenta prefere parar a inferir uma causa que não consegue provar.

Como funciona, passo a passo

  1. Incidente normalizado. A falha entra em um formato padrão: qual pipeline, qual etapa, qual ambiente, qual commit.
  2. Evidência rastreável. O adaptador do dbt lê os arquivos run_results.json e manifest.json que o próprio dbt gera e confere se os hashes (a impressão digital de cada arquivo) batem com o que o incidente declarou. Cada evidência recebe um identificador e passa por sanitização antes do diagnóstico.
  3. Diagnóstico sem ferramentas. O agente só classifica a falha. Ele não executa nada. Cada citação que ele faz é validada contra as evidências coletadas.
  4. Elegibilidade. Uma regra fixa decide se o caso pode seguir para correção. Hoje, só falhas do tipo CODE (erro no código), com autorização verificada e ligada ao incidente, ao diagnóstico e ao commit de base.
  5. Correção em área isolada. Um workspace separado, editando só os caminhos permitidos pela configuração do projeto e rodando só os comandos de validação que o projeto declarou.
  6. PR em rascunho. Se tudo passou, a ferramenta cria (ou reaproveita) um pull request em rascunho. Quem aprova e faz o merge é uma pessoa.

Dois modos de uso

O mesmo núcleo atende quem investiga uma falha sozinho e quem quer o reparo rodando dentro do orquestrador.

Interativo, no Claude Code

O Pipeline Guard vem como um plugin com quatro skills (conjuntos de instruções que o Claude ativa conforme o que você pede):

  • pipeline-guard-setup: instala e configura o plugin em um projeto, gerando e validando o arquivo de configuração a partir de um preset.
  • diagnose: investiga uma falha e devolve diagnóstico validado, decisão de elegibilidade e relatório neutro.
  • remediate: aplica uma correção autorizada, valida o patch e envia a branch do incidente.
  • publish-draft-pr: publica ou reaproveita o PR em rascunho depois que a branch foi enviada.

Você pede em linguagem natural, por exemplo: Diagnostique o incidente descrito em ./incidents/failed-run.json. O pedido passa só o caminho do arquivo, nunca o log colado no prompt. Um gancho que falha de forma fechada bloqueia prompts de diagnóstico que contenham dados sensíveis reconhecidos antes de o Claude processá-los.

Sem supervisão, em qualquer orquestrador

Para rodar de madrugada, sem ninguém para responder perguntas, existe um comando único e neutro quanto ao host:

uv run scripts/unattended_cli.py \
  --mode unattended \
  --config ./config/pipeline-guard.yaml \
  --project-root . \
  --output-dir /retained/pipeline-guard/output \
  --authorization-state-dir /retained/pipeline-guard/authorization-state \
  ./incident.json

Nesse modo:

  • nada é perguntado nem confirmado: a autorização vem de uma política versionada do projeto e de uma identidade de serviço;
  • o trabalho acontece em um workspace isolado e gerenciado, que é preservado limpo quando a publicação precisa ser retomada com segurança;
  • a saída é um resultado versionado, legível por máquina, com um resumo para pessoas e códigos de saída estáveis;
  • o PR em rascunho sai pela própria fronteira de publicação do Pipeline Guard, sem depender de outro plugin;
  • há guias para Databricks Jobs, Bitbucket Pipelines e Airflow, sem lógica específica de cada host embutida no código;
  • um Dockerfile prepara a execução em contêiner, com usuário sem privilégios e versões fixas das dependências.

Qualquer modelo, configuração por projeto

O Pipeline Guard não prende você a um fornecedor nem a um tipo de projeto.

  • Seis perfis de modelo: anthropic (o padrão), databricks, openai, gemini, ollama e openai-compatible. Isso inclui rodar com um modelo local, sem enviar nada para a nuvem. Para o agente que escreve a correção, o endpoint precisa oferecer chamadas de função no formato do Chat Completions.
  • Ordem de escolha clara: primeiro os argumentos da linha de comando, depois o bloco runtime.model do arquivo de configuração, depois as variáveis de ambiente.
  • Credenciais por referência: o arquivo aponta para a variável de ambiente, nunca guarda a chave em texto.
runtime:
  model:
    provider: ollama
    name: llama3.3
    base_url: http://127.0.0.1:11434/v1
  • Presets de projeto: generic e dbt. A skill de setup gera o config/pipeline-guard.yaml a partir de um deles e valida o resultado.
  • Política da correção no seu arquivo: você define quais caminhos e extensões podem ser editados, quantos arquivos e linhas no máximo e quais comandos de validação rodam.
  • Adaptadores com contrato único: todo adaptador segue o mesmo contrato (pipeline_adapter_interface), o registro é fechado e os detalhes de cada ferramenta ficam isolados em adapter_details.<nome>. Hoje há dbt e mock (este para validar a instalação sem depender de nada externo).
  • Duas plataformas de PR: Bitbucket Cloud e GitHub.

As travas de segurança

  • Diagnóstico sem ferramentas. O agente que diagnostica não lê arquivos, não roda comandos, não acessa rede e não altera o projeto. Só enxerga o incidente validado e as evidências coletadas.
  • Toda afirmação cita evidência. Cada ponto relevante do diagnóstico precisa apontar para um identificador de evidência que existe, e um validador externo confere o contrato e as citações.
  • Elegibilidade por código. Quem decide se a falha pode seguir para correção é código determinístico, nunca o modelo.
  • Autorização amarrada. Ela vale para um incidente, um hash de diagnóstico e um commit de base. Se qualquer um dos três mudar, deixa de valer.
  • Agente de correção contido. Recebe só o contexto necessário, usa ferramentas restritas de leitura, escrita e edição e não tem acesso a credenciais de Git ou do provedor. Não publica, não faz merge e não faz deploy. A etapa que escreve a correção se recusa a rodar com privilégio de administrador.
  • Redação de dados sensíveis. Antes de chegar ao modelo ou ser gravado, o texto passa por um filtro que mascara campos sensíveis, cabeçalhos de autenticação, cookies, identificadores de sessão, chaves privadas, e-mails, parâmetros de URL assinada, assinaturas de acesso do Azure e formatos conhecidos de token (como os da Atlassian e da DigitalOcean). Os rótulos são reconhecidos em português e em inglês.
  • Credenciais só por variável de ambiente. Nunca aparecem em prompt, argumento de comando, log ou resultado.
  • Orçamento de execução. Há limite de chamadas de ferramenta, de tempo e de tokens. Execução inválida, com evidência insuficiente ou conflitante, ou sem orçamento, para com segurança.
  • Escrita só onde deve. O diagnóstico não escreve em projeto, Git, warehouse nem deploy. A correção só escreve no workspace isolado, envia só a branch autorizada e publica só rascunho, depois de revalidar tudo.
  • Publicador estreito. O componente que cria o PR só sabe localizar, criar, reaproveitar ou fechar o rascunho que o próprio Pipeline Guard produziu. Aprovar, revisar, fazer merge ou administrar o repositório: nada disso existe nele.

Dois casos reais de teste

Os dois casos abaixo rodaram em um ambiente de teste (sandbox) no Databricks Jobs, a partir de cenários de referência: falhas plantadas de propósito para exercitar o caminho completo. Não são incidentes de produção. Os PRs foram publicados em um repositório Bitbucket.

Caso 1: o teste invertido

  • Falha: o dbt build terminou com código de saída 1 na etapa dbt_build.
  • Diagnóstico: um teste falhou porque retornou um resultado quando estava configurado para esperar zero. Família da falha: CODE. Confiança: alta. Causa: confirmada.
  • Evidência: o registro de evidência do dbt informava um registro com falha.
  • Correção: um arquivo alterado, tests/scenario_singular_inverted.sql, com 1 linha adicionada e 1 removida.
  • Validação: deps passou em 3,9 s e build passou em 5,1 s. Status: todas aprovadas.
  • Resultado: o PR #20 foi criado como rascunho pelo publicador do Pipeline Guard. As duas pipelines do próprio repositório passaram. Eu revisei e fiz o merge na branch dev.

Caso 2: a coluna que sumiu

  • Falha: o mesmo tipo de erro, na mesma etapa, mas por outro motivo.
  • Diagnóstico: a transformação usava um campo que não estava disponível na hora da execução. Família: CODE. Confiança: alta. Causa: confirmada.
  • Evidência: a evidência de execução informava que um campo referenciado estava indisponível.
  • Correção: um arquivo, models/scenarios/scenario_missing_column.sql, com 1 linha adicionada e 1 removida.
  • Validação: deps em 4,3 s e build em 5,2 s, todas aprovadas.
  • Resultado: o PR #21, também criado como rascunho, revisado e com merge feito por mim.

Um detalhe de que gosto: o commit de base do segundo incidente é exatamente o commit do merge do primeiro. O ciclo funciona em cadeia: a correção aprovada de um caso vira a base do seguinte.

O que os dois casos mostram

CasoTipo da falhaDiagnósticoArquivos alteradosValidaçãoDecisão final
1. Teste invertidoCODECorreto, confiança alta1 (1 linha mudou)Todas aprovadas (cerca de 9 s)Merge por pessoa
2. Coluna ausenteCODECorreto, confiança alta1 (1 linha mudou)Todas aprovadas (cerca de 9,5 s)Merge por pessoa

Os dois casos percorreram o caminho completo, de ponta a ponta: da falha ao rascunho revisável, sem nenhuma etapa manual no meio, exceto a que deve ser manual, a decisão final.

O que aprendi

  1. Controle vale mais que inteligência. O ganho não veio de um modelo melhor, e sim de limitar o que o modelo consegue fazer.
  2. Evidência que se confere muda a conversa. Quando cada afirmação do diagnóstico aponta para um arquivo com hash, revisar fica rápido e o erro do modelo fica fácil de ver.
  3. O rascunho é a interface certa. Um PR em rascunho dá ao time o que ele já sabe usar: revisão, histórico e testes automáticos. Ninguém precisa aprender uma ferramenta nova.
  4. Parar é uma resposta válida. Quando a evidência é insuficiente ou conflitante, o diagnóstico vira UNKNOWN e a execução para, em vez de chutar uma causa. Um sistema que sabe dizer que não sabe é mais fácil de confiar.

Teste você mesmo

O código está em github.com/ricardoribs/pipeline-guard, com licença Apache-2.0. Se você usa dbt, o que mais me ajudaria é:

  • rodar em um projeto de teste e abrir uma issue com o que quebrou;
  • contar qual tipo de falha seu time mais enfrenta, para eu priorizar o próximo adaptador;
  • sugerir ou testar o cenário do Airflow.

Estou construindo na área de engenharia de dados com IA generativa e aberto a conversar sobre oportunidades. Pode me encontrar no LinkedIn.

Carregando publicação patrocinada...