Da reunião ao notebook: como construímos a camada semântica da CERC
TL;DR — Dois times. Mesma tabela. Mesma pergunta. Dois números diferentes — e os dois errados. Isso acontecia toda semana na CERC, em 5 domínios financeiros com bilhões de transações por dia. A solução não foi mais um dashboard: foi um pipeline de ponta a ponta com +10 agentes de IA — de gravação de reunião com quem entende os dados até notebook Databricks pronto para produção, passando por tese de domínio, wishes de negócio e clusters semânticos. E o próximo domínio já sai em 2–3 semanas.
Sumário
- O problema que não aparecia nos dashboards
- Por que Diamond e não Gold padrão?
- A resposta: um framework orientado por agentes
- Os 3 grandes blocos do processo
- Arquitetura & Contratos: entenda a invariante
- Padrões Estratégicos que Sustentam o Framework
- O ecossistema de agentes em detalhe
- O artefato final: o que o Odin entrega
- O papel dos hooks e guardrails
- Análise estática de Spark — performance como primeira classe
- O resultado: 3 meses de trabalho, 5 domínios, dezenas de tabelas
- O que aprendemos sobre automação com agentes
- O que vem a seguir
- Para quem quer replicar isso
1. O problema que não aparecia nos dashboards
Duas pessoas. Mesma tabela. Mesma pergunta: quantas unidades ativas a gente tem hoje?
Cada uma responde um número diferente. Ninguém está mentindo — os dois rodaram a query “certa”, só que com um filtro diferente, numa janela de tempo diferente. E o pior: nenhum dos dois números estava certo.
Isso não era um caso isolado. Acontecia toda semana, em qualquer um dos domínios financeiros da CERC — com bilhões de transações por dia rodando por trás de cada um. Cada domínio com regras próprias, fluxos próprios, e — consequentemente — queries próprias.
O problema não era falta de dado. O problema era que o mesmo dado era consumido de formas incompatíveis ao mesmo tempo.
Não porque os dados estavam sujos, mas porque ninguém tinha definido a semântica correta da pergunta. O que é uma unidade ativa? Depende de quem você pergunta. O que é uma agenda consolidada? Depende de quando.
💸 Processamento duplicado
Time A roda query com filtro X
Time B roda a mesma query com filtro X+Y
Resultado: dois números, dois jobs de processamento, zero consenso
📊 Divergência analítica
"Quantas unidades ativas temos?"
Depende: você considerou empresas com menos de 1 ano? Sim? Não?
Pronto: você e eu temos respostas que nunca vão bater
O problema não era técnico. Era que a informação não chega linear. Uma reunião de negócio tem:
- Um expert que sabe a regra em prosa (narrativa não-linear)
- Um analista que tira 3 queries diferentes daquela conversa
- Um engenheiro que não sabe qual das 3 é a “certa”
- Um sistema que precisa de UMA definição, não de 3 interpretações
Ninguém consegue linearizar essa conversa com um diagrama. A gente precisava de agentes que transformassem conversa de domínio em especificação executável. Não com prompts vazios — com uma tese de domínio que funciona como ponte semântica entre o que o negócio fala e o que o código entende.
A solução era criar uma camada analítica oficial — o que todo mundo chama de Gold, e o que a gente decidiu chamar de Diamond. Mas por trás de cada Diamond há sempre uma tese.
2. Por que Diamond e não Gold padrão?
No nosso datalake, a arquitetura segue o padrão clássico de camadas:
flowchart LR
BR["BRONZE (bruto)"] --> SI["SILVER (limpo)"]
SI -.->|cada time a sua forma| GO["Gold ad hoc (zona cinza, pre-Diamond)"]
SI ==>|contrato explicito| DI["DIAMOND (oficial)"]
style BR fill:#78350f,stroke:#f59e0b,color:#fff
style SI fill:#374151,stroke:#9ca3af,color:#fff
style GO fill:#7f1d1d,stroke:#ef4444,color:#fff,stroke-dasharray: 5 5
style DI fill:#1e3a5f,stroke:#3b82f6,color:#fff
A distinção importa. Antes da Diamond, existia a zona cinza acima: tabelas Gold criadas ad hoc por times diferentes, sem nomenclatura consistente, sem grão documentado. Cada time definia sua própria “Gold” — e ninguém sabia onde a outra terminava. Todas saiam da Silver e a cada processamento uma visão diferente contando uma história diferente.
Diamond é diferente. É a camada oficial: semântica, deliberada, com contrato de qualidade explícito — criada exatamente para ocupar o espaço que a zona cinza deixava em aberto.
O problema era: como construir isso em escala, para 5 domínios com alta complexidade regulatória, sem virar um projeto de 2 anos?
O que surpreendeu: a resposta não foi “mais um dashboard” nem “documentação melhor” nem “mais validação manual”. Foi automatizar a transformação de conversa em tese em código — com agentes que entendem linguagem natural e uma tese de domínio como ponte semântica.
3. A resposta: um framework orientado por agentes
Para que isso não demorasse anos de desenvolvimento, construímos um framework com 3 blocos sequenciais — cada um entregando um artefato concreto que alimenta o próximo.
O framework roda inteiro em GitHub Copilot Agents dentro do VS Code, com agentes, hooks, skills e instructions que levam de gravações .vtt até Notebooks Databricks prontos para produção. O ponto de entrada sempre é o Black Belt — um agente hub que classifica o pedido do engenheiro e delega para o agente especializado certo.
Os 3 blocos são:
4. Os 3 grandes blocos do processo
O mapa geral, antes de entrar em cada bloco:
flowchart LR
subgraph B1["4.1 — Da gravação à tese"]
direction LR
b1a[".vtt"] --> b1b["tese de domínio"]
end
subgraph B2["4.2 — Da tese aos wishes"]
direction LR
b2a["wishes brutos"] --> b2b["cluster semântico"]
end
subgraph B3["4.3 — Do wish ao artefato"]
direction LR
b3a["table_spec.yaml"] --> b3b["notebook.py"]
end
B1 --> B2 --> B3
style B1 fill:#14532d,stroke:#22c55e,color:#bbf7d0
style B2 fill:#2e1065,stroke:#a78bfa,color:#ddd6fe
style B3 fill:#1e3a5f,stroke:#3b82f6,color:#bfdbfe
4.1 Bloco 1 — Da gravação à tese
4.1.1 “Vtt bronze”
O ponto de partida é o arquivo .vtt — a transcrição bruta gerada pelo Microsoft Teams. Ele se parece com isso:
WEBVTT
00:02:09.561 --> 00:02:13.996
<v Pessoa1>Você pode ter dois times querendo a mesma
informação, fazendo a mesma query,</v>
00:02:10.054 --> 00:02:13.996
<v Pessoa2>Sim</v>
00:02:15.154 --> 00:02:17.682
<v Pessoa1>dois momentos diferentes,
gastando duas vezes no processamento.</v>
Impossível usar diretamente. O agente precisa de algo linearizado, organizado, com o ruído removido.
4.1.2 “Vtt silver”
O _parsed.md é o que trata e tira impurezas do texto. Stop words, afirmações, suspiros, etc não importam para o contexto. Apenas o que é dito. Frases curtas ou com poucos segundos não ajudam.
Um script Python retira deterministicamente efeitos de linguagem que de nada ajudam no processo.
Ele se parece com isso:
WEBVTT
00:02:09.561 --> 00:02:17.682
<v Pessoa1>Você pode ter dois times querendo a mesma
informação, fazendo a mesma query dois momentos diferentes,
gastando duas vezes no processamento.</v>
4.1.3 “Vtt gold”
O _parsed_summary.md é o que alimenta o resto do pipeline. Um exemplo real de output:
## Como o time X usa dados do domínio Y atualmente
O time executa 3-4 joins manuais toda vez que precisa de uma visão
consolidada: histórico de eventos + registros complementares + consentimentos.
**Dor principal**: toda análise recoça do zero. Não existe uma tabela
"mágica" que dê a visão final pronta.
**Wish implícito**: tabela com granularidade pela entidade principal,
contendo fluxo recebido, fluxo enviado, participante solicitante e status de liquidação.
4.1.4 Pipeline
O primeiro pipeline transforma isso em 3 camadas:
.vtt (bronze)
└── _parsed.md (silver)
grupos de fala consolidados
stopwords removidas
"aham", "sim", "certo" eliminados
└── _parsed_summary.md (gold)
insights de negócio extraídos
decisões capturadas
dores identificadas
wishes implícitos e explícitos sinalizados
Para os 5 domínios que trabalhamos, a gente tinha dezenas de reuniões. Cada uma de 1 hora. Seria impossível revisitá-las sempre. O pipeline de VTT resolve isso: a gente processa uma vez, e a camada summary vira a fonte de verdade para os próximos passos (com revisão de cada um que participou da reunião para evitar alucinações).
Agentes envolvidos nessa etapa:
flowchart LR
VTT(["📼 .vtt"])
ORCH["vtt-orchestrator"]
SPEC["vtt-spec-transcript"]
OUT(["📄 _parsed_summary.md"])
VTT --> ORCH
ORCH --> SPEC
SPEC --> OUT
style VTT fill:#166534,stroke:#22c55e,color:#fff
style ORCH fill:#166534,stroke:#22c55e,color:#fff
style SPEC fill:#166534,stroke:#22c55e,color:#fff
style OUT fill:#166534,stroke:#22c55e,color:#fff
O vtt-orchestrator detecta o tipo de reunião (deepdive técnico? discovery de negócio? hands-on com dados?) e roteia para o agente certo. O vtt-spec-transcript processa e extrai o sumário estruturado.
4.2 Bloco 2 — Da tese aos wishes
Com o _parsed_summary.md em mãos, o próximo problema é: como organizar dezenas de reuniões de múltiplos domínios de forma que o Odin (o agente que vai gerar as tabelas) consiga trabalhar com isso?
Cada reunião conta um pedaço da história. Um time está preocupado com fluxos de consentimento. Outro quer a relação entre participantes do mercado. Um terceiro quer rastrear liquidação. Todos falam do mesmo produto, mas de ângulos completamente diferentes.
O problema de fundo é sempre o mesmo: a informação não chega linear. A solução foi criar uma tese por domínio — um documento que lineariza todo esse conhecimento disperso em uma narrativa coerente.
docs/knowledge_base/cerc_systems/{dominio}/wiki/thesis/
├── 01_intro.md
├── 02_glossary.md
├── 03_background.md
├── 04_participants.md
├── 05_business_rules.md
├── 06_legal_and_regulatory.md
├── 07_flow.md
├── 08_billing.md
├── 09_consumers.md
├── 10_risks_and_limitations.md
├── appendix_01_open_questions.md
├── appendix_02_pains.md
└── appendix_03_operational_queries.md
O que entra em cada capítulo da tese
A tese tem 13 capítulos — cada um responde uma pergunta específica:
| Capítulo | Pergunta Central | Conteúdo | Quem Valida |
|---|---|---|---|
| 01_intro.md | Qual é este domínio? | Nome, abreviação, propósito, stakeholders principais, contexto histórico | PO, Domain Lead |
| 02_glossary.md | O que cada termo significa? | Dicionário de 50-200 termos técnicos e de negócio específicos do domínio (ex: “consentimento”, “liquidação”, “entidade”, etc) | Especialista técnico + PO |
| 03_background.md | Por que este domínio existe? | História, evolução, regulamentações que criaram a necessidade, contexto macro (mercado, concorrência, tecnologia) | Domain Lead |
| 04_participants.md | Quem é quem? | Mapeamento de personas: qual role, qual responsabilidade, qual informação usa, qual dor tem | Domain Lead + Operações |
| 05_business_rules.md | Como funciona? | 50-300 regras de negócio estruturadas (condições, lógica, exceções, citadas diretamente das reuniões) | PO + Agente PHD |
| 06_legal_and_regulatory.md | Que leis/regulações se aplicam? | Compliance requirements, SLAs, janelas de processamento, retenção de dados, auditoria | Legal + Operações |
| 07_flow.md | Qual é a sequência? | Diagrama temporal: eventos, estados, transições, dependências entre etapas (ex: ordem de chegada, precedência) | Especialista técnico |
| 08_billing.md | Quem paga e como? | Modelos de cobrança, margem, custos de processamento, volume drivers (ex: por transação? por entidade? por mês?) | Finance + PO |
| 09_consumers.md | Quem usa e por quê? | Mapeamento de quem consome dados: qual time, qual query pattern, qual dor específica que resolvemos | Times clientes |
| 10_risks_and_limitations.md | O que pode quebrar? | Limites conhecidos, riscos operacionais, edge cases documentados, scale limits | Engenharia + PO |
| appendix_01_open_questions.md | O que não sabemos? | Perguntas em aberto, hipóteses a validar, lacunas de conhecimento ainda não resolvidas | Agente PHD |
| appendix_02_pains.md | Quais são as dores? | Lista estruturada de dores: processing overhead (duplicação), data quality, latência, custos, compliance risk | Compilado de reuniões |
| appendix_03_operational_queries.md | Quais queries existem hoje? | Exemplos reais de queries SQL que times rodam manualmente, com contexto de por que existem | Operações |
Cada capítulo é escrito no markdown com exemplos concretos (não templates genéricos). O agente PHD escreve usando as reuniões como fonte; o PHD Supervisor valida que a tese faz sentido; times de domínio revisam para veracidade.
A tese é chamada de tese por uma razão: ela pode estar errada e pode ser refutada.
Isso vem do rigor acadêmico. Em um doutorado, você escreve uma tese. A banca examina. Te refutam. Você defende ou reconhece o erro. Aqui é a mesma coisa.
A refutação é amiga. Encontrar o erro na tese custa 1 hora. Encontrá-lo em produção custa semanas.
Exemplo real: em um dos domínios, escrevemos que um evento chegava num determinado horário (tese). A validação com os times mostrou que o horário estava errado (refutação). A tese foi atualizada. Esse ciclo de hipótese → validação → refinamento é explícito no processo. Sem a tese documentada, esse erro teria sido descoberto 6 meses depois no código de produção, causando retrabalho massivo. Com a tese, foi descoberto e corrigido na hora.
Ninguém precisa voltar à reunião de 2 horas para descobrir “ah, na verdade o horário é outro”. Tá lá. Tá refutado. Tá documentado por que foi refutado. Próximo.
A tese como fundação permanente
Aqui está a verdade incômoda: o notebook Databricks tem ciclo de vida atrelado aos sistemas de origem. Quando uma tabela SILVER muda de schema, quando uma regra de negócio é revisada, quando um novo campo passa a existir — o código acompanha essa cadência.
A tese também evolui — mas em um ritmo diferente. Ela captura o porquê do domínio existir — suas regras, seus participantes, seus fluxos — e esse raciocínio tende a ser mais estável do que qualquer implementação específica. Quando o sistema muda, o notebook muda junto; quando a premissa de negócio muda, a tese é revisada — e o notebook segue a tese, não o contrário.
Quando um engenheiro entra no time, não precisa entrar em 20 reuniões. Lê a tese em 3–4 horas e está up to speed. Ninguém na empresa tinha feito uma curadoria assim — documentado cada pedaço de como o sistema funciona, validado por todas as áreas. Isso é raro. Isso é valioso.
O ativo central — o artefato mais poderoso desse processo não é o notebook Databricks. É a tese. Ela concentra conhecimento de produto que normalmente fica na cabeça de 3–4 pessoas que participaram das reuniões certas. É versionada, coexiste com código (cada regra de negócio tem um capítulo correspondente na tese), e é refutável — pode estar errada, pode ser melhorada. E em tempos de agentes, ela vale ainda mais: é a tese que dá contexto ao agente. Sem tese, o agente pode gerar artefatos — mas não consegue raciocinar sobre o porquê de cada regra existir.
O agente PHD (quem escreve tese, afinal, é doutor) constrói e mantém esses capítulos. O PHD Supervisor revisa em 17 dimensões e reporta findings de qualidade sem editar diretamente — separação clara de responsabilidades.
Paralelamente, o djinn extrai os wishes.
O wish é a unidade mínima de desejo de negócio. É o que uma pessoa expressou, de forma explícita ou implícita, que precisava de uma tabela para fazer.
Um exemplo de wish (simplificado, sem dados internos):
wish_id: "0001"
title: Tabela Consolidada de Eventos do Domínio X
description: >
Tabela "mágica" que consolida visão de eventos orientada ao grão principal do domínio.
origin: explicit
ambition: incremental
tipo: table
grain_hint: >
1 linha = 1 entidade com evento conciliado, rastreada em 1 data de referência
cadence_hint: daily
warehouse_hint: fact_transactional
consumers:
- time_analítico
- time_operacional
- time_produto
business_function: entity_lifecycle
need: |-
Criar uma tabela Diamond com granularidade por entidade principal, contendo:
- Evento recebido (chave composta pelos campos de negócio relevantes)
- Tipo de relação entre os participantes
- Status consolidado do ciclo de vida
- Rastreabilidade temporal
expected_value: |-
- Eliminar 3-4 joins manuais repetidos por análise
- Permitir group by e filtro simples sem refazer lógica complexa
evidence: >
Reunião de discovery com time consumidor — _parsed_summary.md
No primeiro run em um domínio/sistema específico, foram detectados 150 wishes. Era demais. Muitos duplicados, muitos que eram variações do mesmo problema.
E aqui virou um ponto crítico do framework — por dois motivos que valem a pena separar: o que acontece quando se automatiza demais, e como resolver o excesso de wishes sem perder rigor.
O erro que não cometemos duas vezes: automação total
A tentação era clara: jogar os 150 wishes no agente e pedir para ele gerar as tabelas.
Tentamos. Falhou miseravelmente.
Os motivos foram múltiplos:
- A tese, por mais rica que fosse, tinha hipóteses não validadas
- O Unity Catalog tinha dados que contradiziam o que as reuniões diziam
- Muitos wishes eram de fato a mesma necessidade, expressa por times diferentes com vocabulários diferentes
- O agente não conseguia discernir qual PKs fazia sentido sem confirmação humana
A lição foi: o agente certo para uma tarefa não-determinística não é substituto de julgamento humano. É amplificador dele.
Mudamos a abordagem:
❌ Antes: 150 wishes → agente → 150 tabelas (automático, catastrófico)
✅ Depois: 150 wishes → curadoria humana → 65 wishes → script determinístico → N tabelas → human-in-the-loop → decision
Etapa 1: Curadoria Humana (150 → 65 wishes)
Os 150 wishes brutos tinham um problema crítico: 5 times, mesma pergunta, 5 vocabulários diferentes, 5 interpretações.
Exemplo real:
- Time A: “Tabela de eventos consolidados por data”
- Time B: “Visão de fluxo por entidade”
- Time C: “Histórico de participante”
- Todos queriam a mesma coisa, mas ninguém sabia.
Isso exigia curadoria humana pesada:
- Ler cada wish em contexto (reunião, domínio, persona)
- Identificar duplicatas semânticas (mesma coisa, vocabulários diferentes)
- Eliminar ambiguidade (o que “consolidado” significa para você? E para ele?)
- Consolidar em uma única definição clara
Resultado: 150 → 65 wishes consolidados. Cada um com definição unívoca, sem duplicatas, sem conflitos de interpretação.
Etapa 2: Clusterização Determinística (65 → N tabelas)
Com os 65 wishes limpos, o próximo passo era agrupar em tabelas lógicas. Aqui sim entra o script.
Tentamos deixar o agente fazer isso. Não funcionou:
- Agente não consegue garantir reprodutibilidade (roda 2x, dá 2 resultados diferentes)
- Não consegue controlar threshold semântico (qual similarity score vira um cluster?)
- Não consegue explicar por que juntou X com Y
A solução: um cluster builder determinístico (script + regras explícitas).
O script:
- Vetoriza cada wish usando embeddings semânticos
- Calcula distância entre pares com threshold explícito (ex: 0.35 no espaço coseno)
- Agrupa em clusters usando algoritmo hierárquico
- Output: N clusters consolidados, cada um candidato a 1 tabela
Resultado: 65 wishes → ~8-15 cluster candidates (antes teríamos 65 tabelas — absurdo).
Etapa 3: Human-in-the-Loop Final (clusters → tabelas aprovadas)
O script gerou os clusters, mas ainda há decisão humana crítica:
- Grain tradeoff: você quer 1 tabela wide (muitas colunas, grão fino) ou 3 tabelas narrow (grão grosso, poder maior)?
- Volume vs complexity: juntar 3 clusters economiza computação, mas complica queries?
- Consumer fit: qual configuração serve melhor os times que consomem?
Exemplo:
Cluster A: 4 wishes sobre eventos
Cluster B: 3 wishes sobre participantes
Cluster C: 2 wishes sobre fluxo
Opção 1: Mantém 3 tabelas (grão fino, cada time vê só suas colunas)
Opção 2: Junta em 1 tabela wide (grão cruzado, mais poder, mais complexidade)
Decisão: Depende da arquitetura que você quer no Diamond.
→ Human-in-the-loop decide qual caminho.
# Exemplo simplificado do cluster builder interno
from sentence_transformers import SentenceTransformer
import numpy as np
from sklearn.cluster import AgglomerativeClustering
def build_clusters(wishes: list[dict], weights: dict) -> list[list[str]]:
"""
Clusteriza wishes usando embeddings semânticos com pesos por campo.
Pesos calibrados empiricamente para o domínio Diamond:
- title: 0.3 (nomenclatura pode divergir muito entre times)
- need: 0.4 (a necessidade real é o sinal mais forte)
- grain_hint: 0.3 (grão diferente = tabela diferente, sem negociação)
"""
model = SentenceTransformer("sentence-transformers/paraphrase-multilingual-mpnet-base-v2")
embeddings = []
for wish in wishes:
combined = (
weights["title"] * model.encode(wish["title"]) +
weights["need"] * model.encode(wish["need"]) +
weights["grain_hint"] * model.encode(wish.get("grain_hint", ""))
)
embeddings.append(combined / np.linalg.norm(combined))
clustering = AgglomerativeClustering(
n_clusters=None,
distance_threshold=0.35, # calibrado para domínio financeiro
metric="cosine",
linkage="complete"
)
labels = clustering.fit_predict(np.array(embeddings))
clusters = {}
for wish, label in zip(wishes, labels):
clusters.setdefault(label, []).append(wish["wish_id"])
return list(clusters.values())
O script é determinístico. Dado o mesmo input, sempre produz o mesmo output. O agente então recebe os clusters e os revisa — ele não decide os clusters, ele os interpreta.
Essa distinção é fundamental para o que a gente chama de “agentic engineering”: saber onde o agente agrega valor (interpretação semântica, geração de artefatos, raciocínio sobre regras de negócio) e onde um script determinístico é mais confiável (clusterização, validação de schema, geração de DDL).
4.3 Bloco 3 — Do wish ao artefato Databricks
Com os wishes clusterizados e revisados, o Odin entra em ação.
Odin é o agente mais complexo do pipeline. Ele:
- Lê os wishes de um cluster e propõe um
table_spec.yamlintermediário - Analisa grão, fontes, joins, regras de negócio e PKs
- Gera o
config.yamlcom os metadados da tabela e configuração do workflow Databricks - Gera o
notebook.pycom a transformação completa em PySpark - Roda a validação de cobertura — todos os wishes do cluster estão respondidos?
O Odin é munido com diversas skills: desde consultas no Databricks SQL para validar dados até um analisador estático de notebook PySpark para garantir boas práticas antes de cada geração.
Ele é como se fosse o grande construtor e sábio de todo o framework. Sem as etapas anteriores, o Odin não se sustenta — não tem contexto suficiente para criar artefatos que façam sentido.
4.4 Fluxograma geral
O fluxo completo fica assim:
flowchart TD
A(["📼 .vtt"])
subgraph VTT_PIPELINE["Stage 1 — VTT"]
V1["vtt-orchestrator"] --> V2(["_parsed_summary.md"])
end
subgraph KNOWLEDGE_PIPELINE["Stage 2 — Knowledge"]
K1["phd"] --> K2(["tese atualizada"])
K3["djinn"] --> K4(["wish_NNNN.yaml"])
K4 --> K5["spoiler-batch"]
K5 --> K6(["wish com SQL resolvido"])
end
subgraph ODIN_PIPELINE["Stage 3 — Diamond"]
O1["🧠 Odin"] --> O2(["table_spec.yaml"])
O1 --> O3(["config.yaml"])
O1 --> O4(["notebook.py"])
end
CONF(["Confluence / Xray / PDFs"])
A --> V1
V2 --> K1
V2 --> K3
CONF --> K1
K2 --> O1
K6 --> O1
style A fill:#166534,stroke:#22c55e,color:#fff
style V1 fill:#166534,stroke:#22c55e,color:#fff
style V2 fill:#166534,stroke:#22c55e,color:#fff
style K1 fill:#5b21b6,stroke:#a78bfa,color:#fff
style K2 fill:#5b21b6,stroke:#a78bfa,color:#fff
style K3 fill:#1e40af,stroke:#3b82f6,color:#fff
style K4 fill:#1e40af,stroke:#3b82f6,color:#fff
style K5 fill:#1e40af,stroke:#3b82f6,color:#fff
style K6 fill:#1e40af,stroke:#3b82f6,color:#fff
style O1 fill:#92400e,stroke:#f59e0b,color:#fff
style O2 fill:#92400e,stroke:#f59e0b,color:#fff
style O3 fill:#92400e,stroke:#f59e0b,color:#fff
style O4 fill:#92400e,stroke:#f59e0b,color:#fff
style CONF fill:#374151,stroke:#9ca3af,color:#fff
style VTT_PIPELINE fill:#14532d,stroke:#22c55e,color:#bbf7d0
style KNOWLEDGE_PIPELINE fill:#2e1065,stroke:#a78bfa,color:#ddd6fe
style ODIN_PIPELINE fill:#1e3a5f,stroke:#3b82f6,color:#bfdbfe
5. Arquitetura & Contratos: entenda a invariante
Antes de ver cada agente em detalhe, é importante entender por que este framework funciona — e o que torna impossível replicá-lo sem as duas alavancas que seguem.
5.1 O Princípio Central: Human-in-the-Loop
A camada semântica precisa de contexto robusto para funcionar. Esse contexto não vem de tabelas — vem de pessoas.
Agentes de IA são excelentes em consolidar informação dispersa (reuniões, PDFs, Confluence, investigações de data warehouse) em conhecimento estruturado. Mas eles não conseguem validar a semântica de forma independente, porque:
- As regras de negócio mudam — o que era verdade em janeiro pode ser falso em março sem aviso
- As definições são ambíguas — “ativo” pode significar 5 coisas diferentes dependendo do domínio
- Os dados no Databricks Unity Catalog frequentemente contradizem o que as pessoas dizem — ninguém está mentindo, mas há divergência entre o mapa (UC) e o território (realidade operacional)
Por isso, agentes amplificam julgamento humano, não o substituem. Sem human-in-the-loop em cada stage crítico, você gera lixo com mais velocidade.
O design reflete isso explicitamente:
✅ Stage 1 — VTT (gravações em sumários): 95% determinístico, 5% validação
✅ Stage 2 — Knowledge (evidências em tese + wishes): 60% agente, 40% humano
✅ Stage 3 — Diamond (wishes em tabelas): 70% agente, 30% validação de PK + cobertura
Essa proporção varia porque cada stage tem confiabilidade diferente. Stage 1 é linguístico, praticamente determinístico. Stage 2 é onde mais erros acontecem — hipóteses da tese que não batem com o UC real.
5.2 Contratos de Input/Output
Cada stage é independente, com contrato claro:
| Stage | Input | Output | Tempo | Esforço Humano | Taxa de Sucesso |
|---|---|---|---|---|---|
| 1 — VTT | .vtt bruto (gravações) | _parsed_summary.md estruturado | 2–4h / 1h de gravação | Mínimo (1 engenheiro valida) | ~95% (falhas = áudio ruim) |
| 2 — Knowledge | _parsed_summary.md + evidências | Tese de domínio + 20–100 wishes | 3–5 dias | Alto (2–3 pessoas escrevendo tese, 1–2 iterações) | ~70% primeira vez (3–4 ciclos até convergência) |
| 3 — Diamond | 50–80 wishes clusterizados | table_spec.yaml + config.yaml + notebook.py | 4–6h / tabela | Médio (validação de PK, cobertura de wishes, performance) | ~85% primeira vez (bloqueios = sources erradas, business rules incompletas) |
O que “sucesso” significa em cada stage:
- Stage 1: Sumário está legível, tópicos principais capturam a reunião, sem hallucinations
- Stage 2: Tese refletida no UC, wishes não são duplicados, hipóteses não são contraditas por dados reais
- Stage 3: PKs validam sem erro, 100% dos wishes têm resposta na tabela, performance < 5min em volume
5.3 Taxa de Sucesso e Retrabalho Real
Esses números vêm de 5 domínios rodados:
Stage 1 — VTT:
- Taxa de sucesso: ~95%
- Retrabalho: ~10% dos sumários (incompletos, refazem 1–2x)
- Causa de falha: áudio ruim, múltiplas línguas, barulho de fundo extremo
Stage 2 — Knowledge:
- Taxa de sucesso primeira vez: ~70%
- Retrabalho: ~3–4 ciclos até convergência (tese é refinada iterativamente)
- Causa de falha: hipótese da tese contradita por dados reais do UC, definições ambíguas que só o time operacional consegue resolver
- Exemplo real: a tese dizia que evento X chegava sempre antes de evento Y. Dados mostraram inversão em 2% dos casos — tese foi atualizada
Stage 3 — Diamond:
- Taxa de sucesso primeira vez: ~65%
- Retrabalho: ~4–5 ciclos (bugs em joins, PK incorreta, business rule não capturada)
- Causa de falha: source table não existe, business rule do wish não está documentada na tese, performance não validada antes do merge, validação de join errada, omissão de informações relevantes
- Tempo de debug: ~8h / tabela para resolver bloqueios de PK ou lógica
Inversamente, se você não tiver human-in-the-loop:
- Taxa de erro cresce para ~40–50% (tabelas que ninguém usa porque não responderam a pergunta certa)
- Tempo de debug sobe para 8–16h / tabela (você descobrir o erro em produção)
6. Padrões Estratégicos que Sustentam o Framework
Uma última camada antes dos agentes: as decisões estruturais que sustentam a qualidade e a rastreabilidade de todo o sistema.
Não foram decisões acidentais. Cada uma resolve um problema específico que apareceu quando o framework começou a escalar.
| Padrão Estratégico | Por quê? | Benefício Estratégico |
|---|---|---|
| Contrato de dois artefatos por tabela (config + notebook) | Separar contrato técnico da implementação evita confusão entre intenção, metadado e lógica executável. | Clareza arquitetural: cada informação tem lugar certo e a manutenção fica mais previsível. |
| Dono único da transformação por tabela | Lógica espalhada entre arquivos e fluxos diferentes cria ambiguidade de responsabilidade. | Accountability técnica: quando algo diverge, existe um ponto único para corrigir e auditar. |
| Grain-first com decisão explícita de tipo de artefato (fato, dimensão, OBT, MV) | A maior fonte de inconsistência analítica é granularidade implícita ou mal definida. | Contrato semântico estável: todos entendem o que uma linha representa, sem interpretação subjetiva. |
| SCD como decisão contextual (não padrão automático) | Nem toda dimensão precisa histórico completo; aplicar SCD2 em tudo aumenta custo e complexidade sem ganho real. | Equilíbrio entre governança e eficiência: histórico quando necessário, simplicidade quando suficiente. |
| Rastreabilidade ponta a ponta (wish → spec → implementação → tese/regras) | Sem origem explícita da demanda, produção acumula débito técnico difícil de justificar. | Governança forte: cada artefato em produção prova por que existe e qual decisão de negócio atende. |
| Guardrails de hardblock (hooks + CI) | Em escala, revisão manual não consegue evitar deriva estrutural com consistência. | Qualidade sistemática: problemas críticos são bloqueados antes de chegar ao catálogo produtivo. |
| Runtime canônico com derivação determinística de destino | Hardcode de ambiente, catálogo e path gera drift silencioso entre execuções. | Reprodutibilidade operacional: mesmo contrato se comporta de forma consistente em qualquer ambiente. |
| DataFrame como padrão e SQL como exceção justificada | Pipeline opaco ou excessivamente declarativo dificulta revisão e evolução das regras. | Legibilidade e manutenibilidade: engenharia e negócio conseguem revisar a transformação com confiança. |
| Ownership explícito + human-in-the-loop | Premissas mudam com o tempo; automação sem validação humana propaga erro rapidamente. | Responsabilidade real: decisões críticas passam por validação consciente antes de virar produção. |
Como esses padrões se encaixam
- Contrato de artefato + dono único + hardblocks garantem execução confiável em escala.
- Grain-first + SCD contextual + rastreabilidade garantem semântica confiável para consumo.
- Runtime determinístico + human-in-the-loop garantem evolução segura sem perder governança.
Resultado estratégico
Esses padrões transformam o framework de gerador de tabelas em camada semântica oficial: a diferença entre tabela difícil de confiar e ativo de dados com contrato, dono e trilha de decisão.
7. O ecossistema de agentes em detalhe
O design final ficou com 12 agentes com responsabilidades claramente delimitadas. Antes do diagrama de relações, o resumo de quem faz o quê:
| Agente | Grupo | Papel em uma frase |
|---|---|---|
| 💎🥋 Black Belt | Hub | Classifica o pedido do engenheiro e delega para o agente certo |
| djinn | Diamond | Extrai wishes (desejos de negócio) das teses e reuniões |
| spoiler-batch | Diamond | Resolve o SQL de cada wish com evidência do Xray |
| Odin | Diamond | Gera table_spec.yaml, config.yaml e notebook.py |
| vtt-orchestrator | VTT | Detecta o tipo de reunião e roteia para o agente certo |
| vtt-spec-transcript | VTT | Processa o .vtt e extrai o _parsed_summary.md |
| phd | Knowledge | Escreve e mantém os capítulos da tese de domínio |
| phd_supervisor | Knowledge | Revisa a tese em 17 dimensões, sem editar diretamente |
| xray | Xray | Investiga o Unity Catalog: grão real, volume, PII |
| business-rules | Xray | Extrai regras de negócio a partir do que o Xray encontrou |
| 🌙 Moonshot | Desafiador | Questiona modelagens tímidas demais |
| 🧒 ELI5 | Desafiador | Explica qualquer conceito da tese em linguagem simples |
O diagrama abaixo mostra como eles se conectam:
flowchart LR
BB(["💎 Black Belt"])
subgraph DIAMOND["Diamond"]
DJ["djinn"]
SB["spoiler-batch"]
OD["Odin"]
end
subgraph VTT["VTT"]
VO["vtt-orchestrator"]
VS["vtt-spec-transcript"]
end
subgraph KNOWLEDGE["Knowledge"]
PHD["phd"]
PHDS["phd_supervisor"]
end
subgraph XRAY["Xray"]
XR["xray"]
BR["business-rules"]
end
MS["🌙 moonshot"]
E5["🧒 eli5"]
BB -->|extrai wishes| DJ
BB -->|preenche spoilers pendentes| SB
BB -->|gera tabela Diamond| OD
BB -->|transcreve| VO
BB -->|escreve tese| PHD
BB -->|investiga UC| XR
BB -->|é desafiado por| MS
BB -->|explica domínio| E5
DJ -->|UC offline - pendente| SB
XR -->|xray lido por| SB
BB -->|table_spec + config + notebook| OD
VO -->|transcreve| VS
PHD -->|supervisiona| PHDS
XR -->|extrai regras| BR
classDef dmnd fill:#1e40af,stroke:#3b82f6,color:#fff
classDef vttCls fill:#166534,stroke:#22c55e,color:#fff
classDef knowledgeCls fill:#5b21b6,stroke:#a78bfa,color:#fff
classDef xrayCls fill:#92400e,stroke:#f59e0b,color:#fff
classDef moonCls fill:#78350f,stroke:#fbbf24,color:#fff
classDef hub fill:#1d4ed8,stroke:#60a5fa,color:#fff,stroke-width:3px
class BB hub
class OD,DJ,SB dmnd
class VO,VS vttCls
class PHD,PHDS knowledgeCls
class XR,BR xrayCls
class MS,E5 moonCls
style DIAMOND fill:#1e3a5f,stroke:#3b82f6,color:#bfdbfe
style VTT fill:#14532d,stroke:#22c55e,color:#bbf7d0
style KNOWLEDGE fill:#2e1065,stroke:#a78bfa,color:#ddd6fe
style XRAY fill:#451a03,stroke:#f59e0b,color:#fef3c7
O Black Belt — o entrypoint único
O Black Belt é o único agente que o engenheiro chama diretamente. Ele classifica o intent e delega:
@Diamond Black Belt, preciso de uma nova tabela de safra para o domínio X
↓
Classifica como: "nova tabela"
↓
💎🥋🤖 using agent Odin
O Black Belt não edita arquivos diretamente. Toda escrita passa pelo agente especializado. Isso garante que cada agente tem um escopo claro e que não há “criatividade descontrolada” de um único agente com poder demais.
O Moonshot — o desafiador estratégico
Um dos agentes mais interessantes é o Moonshot. Ele é invocado quando o Black Belt detecta que o time pode estar pensando pequeno.
Exemplo real: estávamos modelando um fato de fluxo bidirecional como tabela única, com um campo direction. O Moonshot foi invocado e apontou: com filtro obrigatório WHERE direction = 'receiving' em toda análise, o time vai errar mais, não menos. Propôs dividir em duas tabelas especializadas por direção de fluxo.
Dividimos em duas. Era o certo.
No recorte principal de um dos domínios, o resultado consolidado ficou em tabelas como:
databricks/notebooks/{dominio}/
├── tb_fct_entidade_principal/
├── tb_fct_fluxo_entrada/ ← veio do split proposto pelo Moonshot
├── tb_fct_fluxo_saida/ ← idem
├── tb_obt_visao_consolidada/
├── tb_obt_consentimentos/
└── tb_dim_contrato/
O conjunto varia por domínio e etapa de evolução. Novos produtos geram novos padrões de tabela dentro da mesma estrutura.
O Xray — investigação no Unity Catalog
Antes de gerar qualquer tabela, o Xray investiga o Unity Catalog: quais tabelas existem na camada SILVER? Qual o grão real? Quantas linhas? Quais colunas têm PII?
Isso alimenta os wishes com SQL concreto — o campo sql_spoiler de cada wish — para que o Odin não precise inferir joins do zero. Ele parte de evidência real.
O ELI5 — democratizando a tese
Um dos agentes mais usados no dia a dia: o ELI5 (Explain Like I’m 5). Ele vai na tese do domínio e explica qualquer conceito em linguagem simples.
💬 Engenheiro: o que é [conceito do domínio]?
🧒 ELI5: Quando você registra uma entidade desse tipo, as regras que se aplicam são...
A tese virou uma fonte de onboarding viva. Todo engenheiro novo no time consegue se situar no domínio em horas, não semanas.
O Conselho da CERC — validação por personas
Um experimento que surgiu no final do projeto: o Conselho da CERC. A ideia é criar personas baseadas nos VTTs de reuniões de diferentes times.
# personas geradas dos VTTs de reunião
personas = {
"executivo": {"role": "Executivo", "focus": "ROI e eficiência de custo"},
"analista": {"role": "Analista de dados", "context": vtt_time_analítico},
"operações": {"role": "Analista Operacional", "context": vtt_time_operacional},
"engenheiro": {"role": "Engenheiro de dados", "context": vtt_time_engenharia}
}
Cada persona avalia a tabela candidata e “discute” com as outras. O resultado é um relatório de cobertura multi-perspectiva — essencialmente o equivalente de rodar um review com stakeholders reais, mas de forma assíncrona e a qualquer hora.
8. O artefato final: o que o Odin entrega
Já vimos que o Odin fecha o Bloco 3 gerando artefatos prontos para o Databricks. Na prática, para cada tabela, ele entrega 3 arquivos:
1. config.yaml — o contrato da tabela
table:
system: {dominio}
catalog: diamond
schema: {dominio}
name: tb_fct_{entidade_principal}
type: fct
warehouse_hint: fact_transactional
merge_keys:
- DataReferencia
- ChavePrimaria1
- ChavePrimaria2
- ChavePrimaria3
description: >
Fato diário da entidade principal no grão definido pela PK composta.
Consolida valores, estágio de ciclo de vida e relacionamentos de negócio.
ownership:
consumers:
- time_analitico
- time_operacional
- time_produto
workflow:
name: dmnd-{dominio}-tb-fct-{entidade}
compute:
schedule_cron_expression: 0 10 8 * * ?
spark_version: <runtime_version>
runtime_engine: PHOTON
node_type_id: <node_profile>
fixed_workers: <n_workers>
spark_conf:
spark.sql.adaptive.enabled: "true"
spark.sql.adaptive.skewJoin.enabled: "true"
output_schema:
- name: DataDeReferencia
type: DATE
is_primary_key: true
nullable: false
description: "Data de referencia da tabela."
tags: ["PK"]
- name: ChaveNotaFiscal
type: STRING
is_primary_key: true
nullable: false
description: "Chave de acesso única de 44 dígitos da NF-e. PK (parte 1/3) e chave de merge. Join com tabela de cabeçalho da NF-e pai."
tags: ["PK"]
- name: EmitenteCNPJ
type: STRING
is_primary_key: false
nullable: true
description: "CNPJ do emitente da NF-e. Use para correspondência de vendedor e identificação de credor."
- name: DestinatarioCNPJ
type: STRING
is_primary_key: false
nullable: true
description: "CNPJ do destinatário da NF-e (devedor). Use para identificação de cliente e análise de risco de crédito."
Esse arquivo é o contrato. Ele alimenta o workflow builder que cria o job no Databricks, e é validado por um schema executável antes de qualquer deploy.
2. notebook.py — a transformação
# Databricks notebook source
# fmt: off
# COMMAND ----------
# MAGIC %md
# MAGIC # Diamond — tb_fct_{entidade_principal}
# MAGIC Fato diário da entidade principal do domínio.
# MAGIC Grain: DataReferencia + ChavePrimaria1 + ChavePrimaria2 + ...
# COMMAND ----------
from diamond.runtime_functions import get_context, load_output_schema, execute_write
output_schema = load_output_schema(DEFAULT_CONFIG_PATH)
# COMMAND ----------
# MAGIC %md ## Sources
df_eventos = spark.table(
cfg,
"SILVER.{dominio}.{tabela_eventos_principais}"
)
df_complementar = spark.table(
cfg,
"SILVER.{dominio}.{tabela_complementar}"
)
# COMMAND ----------
# MAGIC %md ## Regra de negócio BR-001
# MAGIC Aplica filtro de direção de fluxo conforme definido na tese
# MAGIC Referência: thesis/05_business_rules.md § ENTITY_LIFECYCLE
df_filtrado = (
df_eventos
.filter(col("direction") == "FLUXO_PRINCIPAL")
.filter(col("DataReferencia").isNotNull())
)
# COMMAND ----------
# MAGIC %md ## Enriquecimento
df_enriched = (
df_filtrado
.join(
df_complementar.select("ChavePrimaria1", "ChavePrimaria2", "status"),
on=["ChavePrimaria1", "ChavePrimaria2"],
how="left"
)
)
# COMMAND ----------
# MAGIC %md ## Write
execute_write(cfg, df_enriched, output_schema)
3. O relatório de cobertura
O Odin também roda uma checagem de cobertura após gerar o notebook: todos os wishes do cluster estão respondidos pelo output_schema da tabela?
📊 Coverage Report — tb_fct_{entidade}
Wishes cobertos (7/9):
✅ wish_0001: visão consolidada por entidade principal
✅ wish_0007: tipo de relação entre participantes
✅ wish_0012: participante solicitante do evento
✅ wish_0013: status equivalente no fluxo de saída
✅ wish_0022: status de liquidação
✅ wish_0024: rastreabilidade de constituição
✅ wish_0026: relação entre os dois tipos de participante
Wishes não cobertos (2/9):
⚠️ wish_0042: drill-down por dimensão adicional → candidato a amendment
⚠️ wish_0046: histórico de renegociação → requer tabela de dim separada
KPI answerability:
Hoje (sem amendment): 78%
Com amendment: 89%
Moonshot (nova dim): 95%
9. O papel dos hooks e guardrails
Os 3 arquivos que o Odin gera não vão para produção sem passar por um portão de qualidade antes. Um dos aprendizados mais valiosos foi que agentes sem guardrails divergem. Não por má-vontade, mas porque o espaço de soluções possíveis é grande demais.
flowchart LR
OD["Odin gera notebook.py"] --> H["Hooks (.github/hooks/diamond.py)"]
H -->|passa| M["Merge liberado"]
H -->|exit code 2| B["Bloqueado - Odin nao avanca"]
style OD fill:#92400e,stroke:#f59e0b,color:#fff
style H fill:#374151,stroke:#9ca3af,color:#fff
style M fill:#166534,stroke:#22c55e,color:#fff
style B fill:#7f1d1d,stroke:#ef4444,color:#fff
O contrato de notebook: liberdade com padrão
O config.yaml que vimos na seção anterior é o contrato de dados — grão, PKs, merge keys, metadados. Mas existe um segundo contrato, tão importante quanto: o contrato de escrita do notebook.py. Toda tabela Diamond, em qualquer domínio, segue a mesma espinha dorsal de células — título e grão em Markdown, leitura de sources, regras de negócio referenciando a tese, enriquecimento, write final.
Isso é proposital. O engenheiro tem liberdade total para resolver o problema de negócio da forma que fizer mais sentido; o que não é negociável é como isso é escrito:
| O contrato garante (fixo) | O engenheiro decide (livre) | |
|---|---|---|
| Estrutura de células | Título, Sources, Regras de negócio, Enriquecimento, Write — sempre nessa ordem | — |
| Leitura de fontes | Sempre via sources.read_SILVER(cfg, ...) | Quais tabelas SILVER usar, quais joins fazer |
| Referência à tese | Toda regra de negócio cita o capítulo da tese (thesis/05_business_rules.md § ...) | Qual regra de negócio se aplica, e por quê |
| Qualidade de join | Sempre qualificado (df_a["campo"]), nunca ambíguo | A lógica do join em si |
| Write final | Sempre via execute_write(cfg, df, output_schema) | A transformação que chega até ali |
O resultado prático: um engenheiro que nunca viu o domínio X consegue abrir o notebook do domínio Y e navegar exatamente da mesma forma — mesmo esqueleto, mesmo vocabulário de funções, mesma ordem de raciocínio. E esse contrato não depende de disciplina ou revisão manual: quem garante que ele nunca é violado são os hooks.
A validação automática dos hooks
O framework tem um sistema de hooks que roda automaticamente. Entre as validações mais relevantes:
.github/hooks/diamond.py
├── validação de ambiguidade em joins
├── validação de coerência config ↔ notebook
├── validação de práticas mínimas de SQL/PySpark
└── bloqueio de violações críticas antes do merge
Qualquer violação retorna exit code 2 e bloqueia o agente. O Odin não pode avançar sem passar por todos os checks.
Essa foi uma lição cara: na versão inicial, o Odin às vezes gerava JOINs sem qualificação de tabela (o famoso col("campo") ambíguo). Isso causava bugs silenciosos. O hook check_join_ambiguity() surgiu exatamente desse episódio.
# RUIM: ambíguo após join — qual tabela tem "campo"?
df_joined = df_a.join(df_b, "chave").select(col("campo"))
# BOM: qualificado — inequívoco
df_joined = df_a.join(df_b, "chave").select(df_a["campo"])
A regra agora é automática. O agente não consegue commitar código com join ambíguo.
10. Análise estática de Spark — performance como primeira classe
Guardrails garantem correção. Mas correção não é o mesmo que eficiência — e é aí que entra o segundo sistema de qualidade do framework. Um dos recursos mais úteis é a análise estática de notebooks Spark. Ela analisa o notebook.py sem executar nada e produz um relatório de risco de performance.
A motivação é pessoal: Spark é complexo. É fácil escrever um notebook que funciona corretamente mas queima 10x mais compute do que precisa.
🔍 Análise Estática — tb_fct_{entidade}/notebook.py
Risco 1/7 — [MEDIUM-5] Broadcast join ausente
AS-IS: df_principal.join(df_complementar, on=["ChavePrimaria1", "ChavePrimaria2"])
UC Metrics: tabela pequena (~18MB estimado)
→ Broadcast reduz shuffle de O(n*m) para O(n).
TO-BE:
```diff
- df_principal.join(df_complementar, ...)
+ df_principal.join(broadcast(df_complementar), ...)
```
Risco 2/7 — [LOW-3] Repartição desnecessária antes do write
AS-IS: df.repartition(200).write(...)
→ Default parallelism > 200 → repartition reduz paralelismo sem ganho.
TO-BE: Remover repartition ou usar coalesce().
Cada recomendação vem com:
- O risco classificado numa escala de 1 a 7
- O AS-IS com evidência do código atual
- O TO-BE com diff exato
- A métrica do Unity Catalog que fundamenta a recomendação
A regra é rígida: nenhuma recomendação sobre join strategy ou broadcast é feita sem métricas reais do UC. Se não tem métricas, o report é bloqueado até o engenheiro confirmar.
11. O resultado: 3 meses de trabalho, 5 domínios, dezenas de tabelas
Guardrails e análise de performance garantem a qualidade de cada tabela individual. Mas qual foi o resultado agregado depois de 3 meses com esse processo rodando nos 5 domínios?
O escopo eram 5 sistemas de negócio com alta complexidade regulatória — os domínios financeiros centrais da CERC.
Os números:
| Métrica | Antes da Diamond | Com Diamond |
|---|---|---|
| Domínios com camada analítica oficial | 0 | 5 |
| Tabelas analíticas padronizadas | ~dezenas (ad hoc) | Dezenas (contrato formal) |
| Times precisando reconciliar queries | todo trimestre | queda forte com fonte padrão por domínio |
| Tempo estimado manual | 10+ meses | 3 meses com agentes |
| Tempo para próximo domínio (ex: depositária) | - | 2–3 semanas |
A maior economia não é de tempo, é de qualidade. A camada SILVER tinha centenas de tabelas de diferentes sistemas. A Diamond cobre as necessidades analíticas de 80–90% dos casos de uso com dezenas de tabelas com contrato claro.
12. O que aprendemos sobre automação com agentes
Os números acima vieram acompanhados de lições sobre onde automação com agentes realmente funciona — e onde não funciona.
1. Scripts ainda são superiores em tarefas determinísticas
A clusterização de wishes com ML é mais confiável do que deixar o agente clusterizar semanticamente. O agente agrega valor onde há interpretação; o script agrega valor onde há computação reprodutível.
2. Human in the loop é inegociável em modelagem semântica
Não existe modelo que substitua a validação com o negócio. A tese pode estar errada. O Unity Catalog pode mostrar algo que contradiz o que a reunião indicou. Sem validação humana, a camada Diamond seria apenas mais uma camada de suposições.
3. O agente precisa de guardrails antes de ser útil
A primeira versão do Odin não tinha hooks. Ele gerava código tecnicamente válido mas com problemas de qualidade que só apareciam depois. Os hooks transformaram a experiência: o agente sabe o que não pode fazer, e foca no que agrega valor.
4. Separar responsabilidades entre agentes reduz alucinação
Um agente que sabe tudo e faz tudo é mais propenso a inventar. O Black Belt classifica mas não executa. O Odin executa mas não decide grão sem evidência. O PHD escreve tese mas não valida. Essa separação é intencional e crucial.
5. A tese é o ativo mais valioso
No final, o artefato mais poderoso não é o notebook. É a tese. Ela concentra o conhecimento de produto que normalmente fica na cabeça de 3–4 pessoas que participaram das reuniões certas. Com a tese versionada, qualquer engenheiro que entrar no time pode se situar em horas.
13. O que vem a seguir
Agentes com domínio: o próximo passo natural
Toda organização vai querer um agente que entenda seu produto. A questão não é se, mas quando — e o que vai separar um agente útil de um bot genérico é a qualidade do contexto que ele carrega.
O que dá contexto a um agente? A tese.
A CERC tem algo que a maioria não tem: teses linearizadas por domínio — documentos que capturam as invariantes de cada sistema, validados por quem entende o negócio, versionados no repositório.
Cada tese pode virar a constituição de um agente específico. Um agente de Crédito que conhece os fluxos de Crédito. Um agente de Depositária que entende as regras da Depositária. Não um bot genérico com instruções vagas — um agente que leu a tese do domínio e entende por que cada regra existe.
O material já está pronto:
- Linearizado — glossário, conceitos, regras, validação. Estrutura que o agente consegue usar.
- Versionado — quando uma regra muda, a tese muda. O agente fica atualizado.
- Validado por humanos — não é especulação. Cada capítulo passou por times de domínio.
- Acessível — não está em reuniões perdidas ou na cabeça de 3 pessoas. Está escrito.
Com agentes específicos por domínio, esse ativo fica exponencialmente mais valioso.
A tese é a fundação. O agente é o que você constrói em cima dela.
Próximas evoluções do framework
Institucionalização — ao apresentar os resultados para as lideranças, a conversa final foi sobre transformar esse material em ativo corporativo. A tese não deveria morar só no repositório Diamond. Ela deveria alimentar o CoE de Arquitetura, virar referência para onboarding, ser consumida por qualquer agente que precise entender um produto da CERC.
CI/CD para a tese — quando uma regra muda em SILVER, qual tabela Diamond é impactada? Hoje isso é manual. A próxima versão do framework terá rastreabilidade automática de impacto.
Métricas de cobertura — hoje medimos coverage por wish. A próxima iteração mede coverage por tipo de pergunta de negócio: análise de safra, monitoramento de mercado, billing. Isso vai permitir priorizar os próximos amendments com dados.
14. Para quem quer replicar isso
O framework inteiro roda em GitHub Copilot Agents dentro do VS Code, aproveitando o stack já adotado pelo time (repositório, Databricks e catálogo). Os agentes são arquivos .agent.md com instruções estruturadas. As skills são arquivos SKILL.md que os agentes invocam.
Por onde começar: o maior bloqueio inicial não é técnico — é escolher o domínio certo para a primeira tese. O perfil ideal: um domínio onde já há reuniões gravadas, um especialista disponível para validar, e pelo menos 2–3 times que reclamam de divergência analítica. Com esse ponto de partida, a primeira tese fica pronta em 1–2 semanas. O primeiro notebook segue em alguns dias.
A estrutura de pastas que o engenheiro precisa criar para um novo domínio:
docs/knowledge_base/cerc_systems/{domínio}/
├── BRONZE/
│ └── meetings/
│ └── {data}_{nome_reuniao}.vtt
└── wiki/
└── thesis/
├── 01_intro.md
├── 02_glossary.md
└── ...
docs/knowledge_base/datalake/wiki/diamond/{domínio}/
├── system_tables.json ← tabelas SILVER de origem
├── system_tables.yaml ← gerado automaticamente
└── wishes/
└── wish_NNNN.yaml ← gerados pelo djinn
E então: @Diamond Black Belt, e o processo começa.
Conclusão
Construir a camada Diamond não foi um projeto de dados. Foi um projeto de epistemologia aplicada.
O problema original não era técnico — era que o conhecimento de como os produtos financeiros da CERC funcionam estava disperso em reuniões, cabeças e queries mal documentadas. A solução não foi apenas criar tabelas. Foi criar um processo que captura, organiza e preserva esse conhecimento de forma que agentes possam usá-lo para construir artefatos técnicos de qualidade.
O framework VTT → Tese → Wishes → Clusters → Diamond é reutilizável, documentado e já está em uso para novos domínios.
Aquelas duas pessoas do começo — mesma tabela, mesma pergunta, dois números diferentes — hoje têm a mesma resposta. Porque existe uma tese que define o que a pergunta significa. E a tese não está na cabeça de ninguém: está no repositório, versionada, pronta para ser lida por qualquer engenheiro — ou agente — que precise entender o domínio.
O que fazíamos na mão agora tem um processo. E o processo roda com agentes.
Este post foi escrito por: André Tayer, Davi Campos, Guilherme Oliveira, José Roling, Robson Sampaio e Marcel Duarte.