A rotina que se repete toda segunda
Você é analista de macro e, antes da reunião das 10h, alguém precisa baixar o Focus daquela segunda, ler o boletim, identificar o que mudou em relação à semana anterior e escrever um parágrafo para o Slack ou o e-mail do time. São 20 minutos toda segunda numa tarefa cujo roteiro nunca muda: mesmo site, mesmo PDF, mesma estrutura de variáveis (IPCA, PIB, Selic, câmbio).
Tarefas repetitivas que envolvem texto não estruturado são onde a automação com modelo de linguagem começa a fazer sentido, e a pergunta correta deixa de ser “dá para automatizar?” e passa a ser “com qual ferramenta?”.
Como automatizar isso
O leque de opções é maior do que parece, e cada alternativa cobre um trade-off diferente entre agendamento, capacidade de ler texto livre e dependência da sua presença.
| Como | Agenda? | Lê texto? | Precisa de você? |
|---|---|---|---|
| Script + cron (Python, R) | ✅ | ❌ | só se quebrar |
| Chatbot no navegador (ChatGPT, Claude.ai) | ❌ | ✅ | presente, todo dia |
| n8n / Zapier + LLM | ✅ | ✅ | configurar visualmente |
| Routine (Claude Code) | ✅ | ✅ | revisar PR depois |
Script + cron é imbatível quando o dado já vem estruturado por uma API, e chatbot resolve quando o trabalho é pontual. n8n encaixa bem em fluxos que orquestram serviços externos sem código próprio. Routine ganha quando o output precisa virar arquivo versionado no Git e o agente raciocina sobre os arquivos do repositório — o caso de um boletim que entra para revisão antes de chegar ao time.
Determinístico, LLM ou híbrido
Antes de adotar qualquer ferramenta, vale classificar a tarefa em um de três regimes.
Código puro serve quando o dado já vem estruturado: API REST, CSV bem formado, regra que não muda, sem texto livre para interpretar. Um script Python com requests + pandas ou um nó HTTP no n8n resolvem sem precisar de modelo de linguagem.
LLM puro entra quando o trabalho é ler texto livre, seja para resumir um documento, classificar comentários ou redigir um e-mail a partir de bullets. Um chatbot ou um nó Anthropic/OpenAI dentro do n8n cobrem o caso.
Híbrido com código (Routine) aparece quando, além de interpretar texto, você precisa rodar análise, gerar gráfico ou versionar o resultado, com o agente decidindo o próximo passo a cada etapa. É aqui que Routine entra, e o resumo semanal do Focus se encaixa nesse regime.
O que é uma Routine
Uma Routine é uma configuração salva do Claude Code: um prompt, um ou mais repositórios e um conjunto de conectores, empacotados uma vez e executados automaticamente. A execução acontece na infraestrutura em nuvem da Anthropic, então a rotina segue funcionando mesmo com seu notebook desligado.
Em uma frase, é o agente do Claude Code rodando num servidor remoto, no horário que você definir, com acesso ao seu repositório.
Há três formas de criar, todas escrevendo na mesma conta na nuvem. Pelo site claude.ai/code/routines, com formulário completo e suporte aos três tipos de trigger; pelo aplicativo desktop, na seção Routines da barra lateral; ou pelo CLI, com /schedule dentro da pasta do projeto, exigindo um repositório Git conectado.
A feature está em research preview, então limites e comportamento podem mudar. O /schedule no CLI cria apenas trigger do tipo schedule — para os outros tipos, edite a Routine pela web.
Como uma Routine dispara
Existem três formas de iniciar a execução.
| Trigger | Quando dispara | Caso típico |
|---|---|---|
| Schedule | Cron recorrente ou data única no futuro | Resumo do Focus toda segunda às 10h |
| API | POST autenticado num endpoint próprio da Routine | Alerta do Sentry chama Claude para investigar |
| GitHub event | Eventos do repo (pull_request.opened, release.published) |
Revisão automática de PR com checklist do time |
Uma mesma Routine pode combinar os três, rodando toda noite, reagindo a cada novo PR e ainda aceitando disparo manual via API. Nesta postagem usamos apenas schedule, mas vale saber que os outros existem.
Anatomia de uma Routine
Toda Routine se decompõe em quatro decisões.
O ponto de partida define se a Routine recebe só um prompt (tarefa de texto pura), prompt + repositório pronto (caso do Focus) ou prompt + repositório que o próprio Claude vai escrever do começo.
Os conectores decidem com o que ela conversa. GitHub é obrigatório quando há repositório, e o Environment customizado entra se a rotina precisa de dependências instaladas ou acesso de rede específico. Connectors MCP (Slack, Linear, Drive, Notion, GitHub) cobrem o que estiver fora do repo.
O trigger determina quando ela dispara — schedule, API ou GitHub event.
A entrega define para onde vai o resultado: PR no GitHub (default quando há repo), e-mail, Slack/Discord/Teams, Notion/Docs/Sheets via connector. Os destinos são combináveis, e você pode abrir PR e postar o mesmo conteúdo no Slack na mesma execução.
Três perguntas ajudam a desenhar uma Routine nova. Tem código no fluxo? Se sim, precisa de repositório e Environment. Roda sozinha ou reage a algo? Sozinha pede schedule; reativa pede API ou GitHub event. Quem precisa ler o resultado? Você sozinho aceita PR ou e-mail; o time inteiro pede Slack ou Notion via connector.
O projeto: resumo semanal do Focus
O Focus é a pesquisa semanal do Banco Central com cerca de 100 instituições financeiras, consolidando expectativas para IPCA, PIB, Selic e câmbio. É publicado toda segunda às 8h30 BRT em bcb.gov.br/publicacoes/focus, e quando segunda é feriado, o BCB publica na terça.
A entrega que vamos automatizar é um markdown com duas seções: um resumo executivo de até 200 palavras, começando pelas medianas-chave (IPCA do ano corrente, Selic fim de ano, PIB, câmbio); e uma lista das três principais revisões da semana, cada uma com hipótese de motivo. O markdown chega como Pull Request no GitHub, e você revisa o diff antes de compartilhar com o time.
Por que duas peças, não uma
A primeira tentativa intuitiva é colocar tudo dentro da própria Routine: baixar o PDF, extrair o texto e resumir. Não funciona, porque o BCB bloqueia IPs de cloud (Anthropic, AWS, GCP) — a Routine roda numa dessas faixas e o download falha.
A saída é separar o pipeline em duas peças, com responsabilidades claras.
A primeira peça é um GitHub Action que roda às 9h15 BRT, baixando o PDF do Focus mais recente e extraindo o texto, para então commitar data/focus_AAAA-MM-DD.{pdf,txt} em main. Roda em IPs do GitHub, que o BCB não bloqueia, sem nenhum LLM no caminho — apenas Python com requests e pdfplumber.
A segunda peça é a Routine do Claude Code que entra às 10h BRT, lê o .txt mais recente, gera o resumo executivo + três revisões e abre PR na branch claude/focus-AAAA-MM-DD. Interpretação de texto livre é o tipo de tarefa que justifica usar um modelo de linguagem em vez de regex.
A folga de 45 minutos entre as duas garante que o .txt já esteja em main quando a Routine começar a procurar.
Estrutura do projeto
meu-projeto/
├── CLAUDE.md # briefing que o agente lê a cada execução
├── routine-prompt.md # os passos que ele executa toda segunda
├── requirements.txt # requests, pdfplumber, pytest
├── pytest.ini # configura marker `network`
├── .github/workflows/
│ └── focus-download.yml # Action que baixa o PDF (9h15 BRT segunda)
├── src/
│ ├── baixar_focus.py # descobre a data certa e baixa o PDF
│ └── extrair_texto.py # extrai o texto do PDF e salva como .txt
├── tests/
│ └── test_baixar_focus.py
├── demo.py # roda baixar + extrair localmente
└── output/focus/ # onde a Routine salva o markdown final
Os scripts Python rodam no GitHub Action, não na Routine, cuidando só da parte determinística (baixar e extrair). O resumo em si é gerado pelo próprio agente Claude Code que executa a Routine, lendo o .txt que o Action commitou em main.
Etapas até a Routine rodar sozinha
São seis etapas, na ordem em que precisam acontecer. Pré-requisitos: Python 3.12+, GitHub CLI autenticado (gh auth login), Claude Code instalado, conta na Anthropic com acesso a Routines e um repositório no GitHub onde o bot do Claude possa abrir PR.
Estrutura local
mkdir -p meu-projeto && cd meu-projeto
mkdir -p src tests data output/focus .github/workflows
touch CLAUDE.md routine-prompt.md requirements.txt demo.py
requirements.txt:
requests
pdfplumber
pytest
O CLAUDE.md do projeto
O CLAUDE.md é o briefing permanente, lido pelo agente no início de toda execução. Coloque ali objetivo, fonte, convenções de arquivo e branch, e regras inegociáveis (nunca inventar número, nunca dar push direto em main).
# Resumo Focus — Routine semanal
## Objetivo
Toda segunda-feira, baixar o boletim Focus do BCB em PDF, gerar resumo
executivo + análise das três principais revisões da semana, abrir PR com
o markdown pronto.
## Fonte
- Página: https://www.bcb.gov.br/publicacoes/focus
- PDF: https://www.bcb.gov.br/content/focus/focus/R{AAAAMMDD}.pdf
## Convenções
- Saída: `output/focus/focus_AAAA-MM-DD.md` (nunca sobrescreve)
- Branch: `claude/focus-AAAA-MM-DD`
- Push direto em `main` é proibido
## Regras
- Nunca inventar número. Toda mediana citada deve estar no PDF.
- Citação literal entre aspas é preferível quando o boletim traz
número-chave atualizado.
- Quando segunda é feriado, o script retrocede dia a dia até 7 dias antes
de desistir.
O script de download
Este script roda dentro do GitHub Action, e não na Routine, porque o BCB bloqueia IPs de cloud da Anthropic. Como o Focus às vezes empurra para terça em feriado, o script parte da última segunda e recua dia a dia até encontrar um PDF válido.
from __future__ import annotations
import sys
from datetime import date, timedelta
from pathlib import Path
import requests
URL = "https://www.bcb.gov.br/content/focus/focus/R{ymd}.pdf"
UA = "Mozilla/5.0 (compatible; routine-focus/1.0)"
def ultima_segunda(hoje: date) -> date:
return hoje - timedelta(days=(hoje.weekday() or 7))
def baixar(dest: Path) -> tuple[date, Path]:
dest.mkdir(parents=True, exist_ok=True)
candidato = ultima_segunda(date.today())
for _ in range(7):
ymd = candidato.strftime("%Y%m%d")
url = URL.format(ymd=ymd)
resp = requests.get(url, headers={"User-Agent": UA}, timeout=30)
if resp.status_code == 200 and resp.content[:4] == b"%PDF":
arq = dest / f"focus_{candidato}.pdf"
arq.write_bytes(resp.content)
return candidato, arq
candidato -= timedelta(days=1)
raise RuntimeError("Nenhum PDF do Focus nos últimos 7 dias.")
def main() -> int:
dest = Path(__file__).parent.parent / "data"
data_pub, arq = baixar(dest)
print(f"Focus de {data_pub} salvo em {arq} ({arq.stat().st_size // 1024} KB)")
return 0
if __name__ == "__main__":
sys.exit(main())
A função ultima_segunda usa o truque hoje.weekday() or 7 para que, quando hoje é segunda (weekday() == 0), o resultado seja a segunda anterior — caso contrário, retornaria o próprio dia, antes do boletim daquela manhã estar publicado.
O script de extração de texto
Segunda etapa do Action: abre o PDF com pdfplumber, concatena o texto das páginas e salva como .txt ao lado do PDF. Nenhuma chamada de LLM acontece aqui, e é esse material que a Routine lerá 45 minutos depois.
from __future__ import annotations
import argparse, sys
from pathlib import Path
ROOT = Path(__file__).parent.parent
DATA = ROOT / "data"
def extrair(pdf_path: Path) -> Path:
import pdfplumber
with pdfplumber.open(pdf_path) as pdf:
texto = "\n".join(p.extract_text() or "" for p in pdf.pages)
txt = pdf_path.with_suffix(".txt")
txt.write_text(texto, encoding="utf-8")
return txt
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--pdf", type=Path, help="caminho do PDF (default: último em data/)")
args = parser.parse_args()
if args.pdf:
pdf = args.pdf
else:
candidatos = sorted(DATA.glob("focus_*.pdf"))
if not candidatos:
print("Nenhum PDF em data/. Rode src/baixar_focus.py primeiro.", file=sys.stderr)
return 1
pdf = candidatos[-1]
txt = extrair(pdf)
print(f"Texto salvo em {txt}")
return 0
if __name__ == "__main__":
sys.exit(main())
Validar localmente antes de agendar
Antes de agendar qualquer coisa, confirme que o pipeline determinístico funciona na sua máquina. Como o resumo é trabalho do agente da Routine, localmente você só valida baixar + extrair.
python3 -m pip install -r requirements.txt
python3 demo.py --abrir
Saída esperada:
[1/2] PDF baixado: focus_2026-05-18.pdf (768 KB)
[2/2] Texto extraído: data/focus_2026-05-18.txt
Abra o .txt e confira que ele traz as tabelas de IPCA, Selic, PIB e câmbio com os marcadores ▲▼ de revisão — é esse material que o agente lerá na Routine.
Subir o repositório
git init -b main
git add .
git commit -m "Projeto inicial: Routine Focus"
gh repo create resumo-focus --private --source=. --remote=origin --push
Em Settings → Actions → General do repositório, marque Allow all actions and reusable workflows e, em Workflow permissions, Read and write permissions. Sem isso, o Action não consegue commitar o .txt de volta em main e a Routine não consegue abrir PR.
Workflow do GitHub Action
Este workflow é a outra ponta do pipeline: toda segunda às 9h15 BRT (12h15 UTC) ele baixa o PDF, extrai o texto e commita ambos em main. O workflow_dispatch permite disparar manualmente pela aba Actions, o que ajuda no primeiro teste.
.github/workflows/focus-download.yml:
name: Focus — Download e Extração
on:
schedule:
# Toda segunda-feira às 12:15 UTC = 09:15 BRT (UTC-3)
- cron: '15 12 * * 1'
workflow_dispatch:
jobs:
baixar:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Instalar dependências
run: pip install -r requirements.txt
- name: Baixar PDF do Focus
run: python src/baixar_focus.py
- name: Extrair texto do PDF
run: python src/extrair_texto.py
- name: Commit dos arquivos gerados
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add data/focus_*.pdf data/focus_*.txt
if git diff --cached --quiet; then
echo "Nenhum arquivo novo — Focus já estava em data/."
else
DATE=$(ls data/focus_*.txt | sort | tail -1 | grep -oP '\d{4}-\d{2}-\d{2}')
git commit -m "data: Focus ${DATE} — PDF e texto extraído"
git push
fi
Em Actions → Focus — Download e Extração → Run workflow, dispare a primeira execução manual e confira que data/focus_AAAA-MM-DD.{pdf,txt} aparece commitado em main.
Conectores GitHub: App + MCP
A Routine precisa de dois acessos ao GitHub, que não são a mesma coisa.
O GitHub App é instalado em github.com/apps/claude, selecionando o repositório resumo-focus, e autoriza a Anthropic a clonar e ler o repo. O MCP do GitHub é adicionado durante o /schedule, no campo MCP connectors, e é obrigatório, porque é por ele que a Routine cria a branch claude/focus-*, commita o markdown e abre o PR. Sem MCP, a Routine não tem ferramenta para entregar.
Se o conector visual não estiver disponível na criação da Routine, adicione manualmente via URL do servidor MCP: https://api.githubcopilot.com/mcp.
O routine-prompt.md
Este é o prompt que a Routine executa a cada disparo. Estruture em passos numerados com regras explícitas de aborto, porque o agente roda sem você — cenários de falha precisam estar escritos, não implícitos.
Você está executando a Routine de resumo semanal do Focus.
O download do PDF e a extração do texto já foram feitos por um GitHub
Action mais cedo (segunda 9h15 BRT). Os arquivos
`data/focus_AAAA-MM-DD.{pdf,txt}` já estão commitados em `main` quando
a Routine inicia. Sua tarefa é ler o `.txt` mais recente, gerar o
resumo executivo e abrir um PR.
## Passos
1. **Localize o `.txt` mais recente.** Liste `data/focus_*.txt` e pegue
o de data mais alta. Se não houver nenhum, pare sem abrir PR — o
Action não rodou.
2. **Verifique frescor.** Extraia a data do nome e compare com hoje:
- 0 a 3 dias: está fresco, siga.
- 4 a 7 dias: marque o PR como `[REVISAR]`.
- Mais de 7 dias: pare sem abrir PR.
3. **Sanity check do texto.** Confirme: pelo menos 2 000 caracteres e
presença das palavras `IPCA`, `Selic`, `PIB`. Se falhar, o layout do
PDF pode ter mudado — pare sem abrir PR.
4. **Leia o texto** e gere markdown com:
- **Resumo executivo** em até 200 palavras, em prosa corrida.
Comece pelas medianas das principais variáveis (IPCA do ano,
Selic fim de ano, PIB, câmbio). Cite literalmente entre aspas
quando houver número-chave.
- **Três principais revisões da semana** em bullets:
`- **Variável (ano):** anterior → atual. *Hipótese:* motivo.`
- Nunca invente número. Se não houver hipótese sólida, escreva
"sem hipótese clara — pode ser ruído amostral".
5. **Salve** em `output/focus/focus_AAAA-MM-DD.md` com cabeçalho YAML
(`data`, `fonte`).
6. **Inspecione** o markdown gerado: medianas batem com o `.txt`, ao
menos uma citação literal entre aspas, hipóteses plausíveis.
7. **Abra o PR.** Branch `claude/focus-AAAA-MM-DD` a partir de `main`,
commit `"Focus AAAA-MM-DD: resumo semanal"`, título do PR
`"Focus — AAAA-MM-DD"`.
8. **Corpo do PR:** o markdown inteiro + link para o PDF original no
site do BCB.
## Falhas
Em qualquer cenário abaixo, pare sem abrir PR. O motivo aparece no
transcript da Routine.
- Nenhum `.txt` em `data/` (Action não rodou).
- `.txt` com mais de 7 dias (Action quebrado).
- Sanity check do texto falhou (mudança de layout do PDF).
Nunca dê push direto em `main`. Nunca invente número.
Criar a Routine com /schedule
Abra o Claude Code local na pasta do projeto e rode /schedule. Preencha:
| Campo | Valor |
|---|---|
| Nome | resumo-focus |
| Repositório | analisemacro/resumo-focus (com allow_unrestricted_git_push: true) |
| Prompt | cole o conteúdo de routine-prompt.md |
| Trigger (cron) | 0 13 * * 1 |
| Modelo | claude-sonnet-4-6 |
| MCP connectors | github |
O cron 0 13 * * 1 é segunda às 13h UTC, ou 10h BRT, 45 minutos depois do Action das 9h15 BRT. Essa folga garante que o .txt já esteja em main quando a Routine começar.
A branch claude/ e por que ela existe
Por padrão, a Routine só consegue dar push em branches prefixadas com claude/, como proteção contra sobrescrever a main por engano. Na prática, o PR que chega no repositório vem de uma branch tipo claude/focus-2026-05-18, e você revisa o diff e dá merge normalmente.
Para liberar push em qualquer branch, marque Allow unrestricted branch pushes na edição da Routine. Para a maioria dos casos, manter o default é mais seguro.
Primeiro disparo
Pela web (claude.ai/code/routines → sua Routine → Run now) ou pelo CLI (/schedule run resumo-focus), dispare a primeira execução manual e acompanhe o transcript: a Routine clona o repo, lista data/focus_*.txt, lê o mais recente, faz o sanity check, gera o markdown em output/focus/focus_AAAA-MM-DD.md, cria a branch claude/focus-AAAA-MM-DD, commita e abre o PR.
Você recebe a notificação do GitHub com o PR, abre, revisa o diff e faz merge se estiver correto. A partir da segunda seguinte, tudo roda sozinho às 10h.
O primeiro disparo é onde você ajusta o prompt: se o resumo veio sem citação literal, reforce no routine-prompt.md; se a hipótese sobre uma revisão soou inventada, adicione um exemplo do que considera hipótese aceitável. Cada erro vira uma linha nova no prompt, e em duas ou três semanas a Routine estabiliza.
Onde entregar o resumo
O markdown chega como PR no GitHub, mas o time precisa ler o resumo onde já trabalha. A Routine pode postar o mesmo texto em vários destinos no fim do prompt, bastando acrescentar um passo extra que dispara um webhook ou usa um connector já autorizado na sua conta.
| Destino | Quando usar |
|---|---|
| Slack / Discord | Time trabalha em chat; histórico fica no canal |
| E-mail corporativo | Escalação formal; arquivo pessoal |
| Teams / Mattermost | Corporativo com plataforma própria |
| Google Docs / Sheets | Auditoria e histórico fora do git |
| Notion | Knowledge base integrado |
Mantenha o PR como ponto de revisão obrigatório, e o destino externo recebe apenas o que você aprovou no merge — isso preserva o controle editorial sem perder velocidade de distribuição.
Observabilidade
Uma Routine que trabalha sem supervisão precisa deixar rastro. O transcript da execução fica em claude.ai/code/routines e mostra cada chamada de ferramenta com input e output, o histórico de PRs aparece em gh pr list --author "@claude" com toda semana publicada, e os logs do Action ficam na aba Actions do repositório, com retenção padrão de 30 dias.
Configure notificação por e-mail quando o Action falhar (Settings → Notifications no GitHub), porque sem isso você só percebe o problema na segunda em que o PR não chegar.
Quando a Routine não basta
A Routine automatiza o fluxo, mas não substitui você na interpretação econômica de fundo — o resumo cita “mediana do IPCA subiu para 4,5%”, e quem decide se é ruído ou mudança de regime continua sendo o analista. Decisões editoriais finais passam pelo seu merge, e mudanças de escopo (o boletim virar nota de política monetária) pedem reescrita do prompt, já que a Routine não se reinventa sozinha.
Reaproveitando o padrão
O padrão Action determinístico baixa + Routine interpretativa resume + PR para revisão serve, com pequenas trocas, para outros boletins.
| Produto | Ajustes mínimos |
|---|---|
| Ata do Copom | Trocar URL e cron (8 reuniões ao ano em vez de semanal) |
| Relatório Trimestral de Inflação | Trocar fonte e expandir o prompt para gráficos comparativos |
| Boletim Macro Fiscal (STN) | Trocar fonte; manter o resto |
| Release IBC-Br | Action baixa o XLS; Routine compara mês contra mês |
A primeira Routine custa o tempo de entender os conectores, a branch claude/ e o ciclo Action → Routine → PR; a segunda custa metade, porque a arquitetura já está resolvida. Trate o projeto do Focus como template.

