📊 O que é uma Tabela-Resumo e Por que ela Existe
Uma tabela-resumo é uma VIEW pré-computada. Em vez de o agente calcular "receita por cliente por mês" toda vez que perguntado, você pré-calcula e salva. O agente só lê o resultado — sem processar 18.000 linhas a cada consulta.
💡 O Conceito Central
Imagine que o cliente pergunta todos os dias: "Qual foi a receita do mês passado por cliente?" Sem tabela-resumo, o agente lê 18.000 linhas do Stripe, filtra, agrupa, soma — repetindo esse trabalho a cada consulta. Com a tabela-resumo, essa query já está calculada e armazenada. O agente lê 50 linhas e responde em segundos.
❌ Sem tabela-resumo
- • Agente lê 18.000 linhas por consulta
- • Custo alto de tokens a cada pergunta
- • Risco de exceder contexto do modelo
- • Lentidão em perguntas recorrentes
✓ Com tabela-resumo
- • Agente lê 50-200 linhas por consulta
- • Custo de tokens mínimo
- • Sempre cabe no contexto
- • Resposta instantânea para perguntas recorrentes
🕐 Grain Design — Semana, Mês, Trimestre
Grain é o nível de granularidade de cada linha da tabela-resumo. A regra prática: escolha o grain mais granular que o cliente vai perguntar — e não mais fino que isso.
Como Escolher o Grain Correto
Pergunta do cliente: "Qual foi a receita do trimestre?"
→ Grain mínimo: mensal. Uma linha por cliente por mês permite agregar em qualquer nível superior (trimestral, anual).
Pergunta do cliente: "Qual consultor foi mais produtivo esta semana?"
→ Grain mínimo: semanal. Uma linha por consultor por semana ISO permite comparações semanais.
Pergunta do cliente: "Quantas transações aconteceram ontem?"
→ Grain mínimo: diário. Mas cuidado — grains diários geram tabelas grandes. Considere se mensal não resolve 90% dos casos.
💡 Regra de Ouro do Grain
Você pode sempre agregar um grain mais fino para obter um mais grosso (diário → semanal → mensal). Você nunca pode desagregar um grain mais grosso para obter um mais fino (mensal → diário é impossível sem os dados originais). Então escolha o mais granular que faz sentido.
📐 Dimensões vs Fatos — Estrutura de Cada Tabela
Fatos são o que você mede (receita, horas, quantidade). Dimensões são os contextos em que você mede (cliente, projeto, período, consultor). Misturar os dois gera resultados matematicamente incorretos.
Tabela de Fatos — O que medir
- •Medidas numéricas aditivas (soma faz sentido): revenue, horas, quantidade
- •Chaves estrangeiras para as dimensões (client_id, project_id, period_id)
- •Data/período da medição
- •Medidas derivadas (se recalculáveis): revenue_per_hour
Tabela de Dimensão — O contexto
- •Atributos descritivos: nome, categoria, status, segmento
- •Chave primária única (client_id, project_id)
- •Atributos que mudam raramente (SCD Type 1 ou 2)
- •Hierarquias: cliente → conta → contrato
⚠️ Non-additive measures: Taxa de conversão, NPS, e percentuais NÃO são aditivos — você não pode somar duas taxas de conversão. Para esses casos, armazene o numerador e denominador separados e calcule o percentual na query.
⚙️ Passo a Passo: Carregar CSVs no DuckDB
DuckDB é o SQLite analítico: zero configuração, roda localmente em Python, suporta arquivos de gigabytes sem problema. Do CSV ao banco em menos de 10 linhas de código.
Instalar e conectar
pip install duckdb
import duckdb
# Cria o arquivo .duckdb se não existir
conn = duckdb.connect('engagement.duckdb')
Carregar CSVs
# DuckDB infere schema automaticamente
conn.execute("""
CREATE OR REPLACE TABLE stripe_payments AS
SELECT * FROM read_csv_auto('processed/stripe_clean.csv')
""")
conn.execute("""
CREATE OR REPLACE TABLE hubspot_contacts AS
SELECT * FROM read_csv_auto('processed/hubspot_clean.csv')
""")
# Verificar
conn.execute("SHOW TABLES").fetchdf()
Criar Tabela-Resumo
conn.execute("""
CREATE OR REPLACE TABLE revenue_by_client_month AS
SELECT
customer_email,
DATE_TRUNC('month', payment_date) AS month,
SUM(amount_usd) AS total_revenue_usd,
COUNT(*) AS transaction_count,
NOW() AS generated_at
FROM stripe_payments
WHERE status = 'succeeded'
GROUP BY customer_email, DATE_TRUNC('month', payment_date)
ORDER BY month DESC, total_revenue_usd DESC
""")
Validar
# Conferir shape
print(conn.execute("SELECT COUNT(*) FROM revenue_by_client_month").fetchone())
# Top 5 clientes
conn.execute("""
SELECT customer_email, SUM(total_revenue_usd)
FROM revenue_by_client_month
GROUP BY customer_email
ORDER BY 2 DESC LIMIT 5
""").fetchdf()
💬 Rodando SQL para Responder Perguntas de Negócio
Traduzir perguntas de negócio em SQL é a habilidade central. Com DuckDB + tabelas-resumo, o agente escreve o SQL e o banco retorna o resultado — sem jogar 18.000 linhas no contexto.
Perguntas de Negócio → SQL
Pergunta: "Quais são os 5 clientes com maior receita no último trimestre?"
SELECT customer_email,
SUM(total_revenue_usd) AS quarterly_revenue
FROM revenue_by_client_month
WHERE month >= DATE_TRUNC('quarter', CURRENT_DATE - INTERVAL 1 QUARTER)
GROUP BY customer_email
ORDER BY quarterly_revenue DESC
LIMIT 5
Pergunta: "Qual consultor teve mais horas faturadas em fevereiro?"
SELECT consultor_nome,
SUM(horas_faturadas) AS total_horas
FROM hours_by_consultant_week
WHERE DATE_TRUNC('month', week_start) = '2026-02-01'
GROUP BY consultor_nome
ORDER BY total_horas DESC
LIMIT 1
Pergunta: "A receita está crescendo mês a mês?"
SELECT month,
SUM(total_revenue_usd) AS monthly_revenue,
LAG(SUM(total_revenue_usd)) OVER (ORDER BY month) AS prev_month,
ROUND((SUM(total_revenue_usd) /
LAG(SUM(total_revenue_usd)) OVER (ORDER BY month) - 1) * 100, 1) AS growth_pct
FROM revenue_by_client_month
GROUP BY month
ORDER BY month DESC
LIMIT 12
🤖 Conectando o Agente ao DuckDB
A arquitetura mínima funcional: agente recebe pergunta → gera SQL → executa no DuckDB → formata resultado → responde. Três componentes: LLM + DuckDB + schema description no system prompt.
Os 3 Componentes da Arquitetura
Schema Description no System Prompt
Descrição das tabelas disponíveis no DuckDB: nomes, colunas, tipos e exemplos de valores. Sem isso, o LLM inventa tabelas que não existem.
Tool Use para Execução de SQL
O LLM tem acesso a uma tool "execute_sql(query: str) → DataFrame". Ele gera o SQL, chama a tool, recebe o resultado e formula a resposta em linguagem natural.
DuckDB Local como Backend
Zero custo de infraestrutura, latência de milissegundos, sem servidor para manter. O arquivo engagement.duckdb fica junto com o código do agente.
System Prompt — Schema Description
Você tem acesso a um banco DuckDB com as seguintes tabelas:
**revenue_by_client_month**
- customer_email (VARCHAR): email do cliente
- month (DATE): primeiro dia do mês de referência
- total_revenue_usd (DOUBLE): receita total em USD naquele mês
- transaction_count (INTEGER): número de transações
- generated_at (TIMESTAMP): quando esta tabela foi calculada
Use a tool execute_sql para responder perguntas sobre receita.
Sempre mencione o período dos dados (generated_at) na resposta.
✅ Resumo do Módulo
Próximo Módulo:
2.5 — Monitoramento e Gestão: não ser pego de surpresa quando o pipeline quebra silenciosamente