Verificando acesso...

Início / Trilha 2 — Dicas e Procedimentos Técnicos / Módulo 2.4
MÓDULO 2.4

📊 Tabelas-Resumo + DuckDB — Implementação Passo a Passo

Como transformar dados brutos em tabelas que um agente consegue consultar sem gastar uma fortuna em tokens. Do grain design ao SQL rodando em produção.

6
Tópicos
35
Minutos
Inter.
Nível
DuckDB
Tipo
1

📊 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
2

🕐 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.

3

📐 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.

4

⚙️ 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.

1

Instalar e conectar

pip install duckdb
import duckdb

# Cria o arquivo .duckdb se não existir
conn = duckdb.connect('engagement.duckdb')
2

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()
3

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
""")
4

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()
5

💬 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
6

🤖 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

1.

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.

2.

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.

3.

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

Tabela-resumo = VIEW pré-computada — agente lê 50 linhas em vez de 18.000
Grain design por pergunta do cliente — "qual foi a receita do trimestre?" → grain mensal
Fatos vs Dimensões separados — evita fanout e somas matematicamente incorretas
DuckDB em 4 passos — instalar, conectar, carregar CSV, criar tabela-resumo
SQL para perguntas de negócio — window functions, GROUP BY, LAG para tendências
Arquitetura mínima — LLM + tool execute_sql + schema no system prompt

Próximo Módulo:

2.5 — Monitoramento e Gestão: não ser pego de surpresa quando o pipeline quebra silenciosamente