惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
T
The Blog of Author Tim Ferriss
U
Unit 42
Microsoft Security Blog
Microsoft Security Blog
WordPress大学
WordPress大学
Vercel News
Vercel News
MongoDB | Blog
MongoDB | Blog
P
Proofpoint News Feed
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
大猫的无限游戏
大猫的无限游戏
C
Check Point Blog
Blog — PlanetScale
Blog — PlanetScale
I
InfoQ
Y
Y Combinator Blog
F
Full Disclosure
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
G
Google Developers Blog
博客园_首页
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
博客园 - 叶小钗
S
SegmentFault 最新的问题
腾讯CDC
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Visual Studio Blog
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
Recent Commits to openclaw:main
Recent Commits to openclaw:main
The Register - Security
The Register - Security
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Microsoft Azure Blog
Microsoft Azure Blog
云风的 BLOG
云风的 BLOG
Last Week in AI
Last Week in AI
F
Fortinet All Blogs
C
CXSECURITY Database RSS Feed - CXSecurity.com
Hugging Face - Blog
Hugging Face - Blog
T
Threatpost
GbyAI
GbyAI
G
GRAHAM CLULEY
L
Lohrmann on Cybersecurity
T
The Exploit Database - CXSecurity.com
P
Palo Alto Networks Blog
L
LangChain Blog
T
Tenable Blog
C
Cisco Blogs
T
Threat Research - Cisco Blogs
Google Online Security Blog
Google Online Security Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Construindo um ranking da Copa pareando com o Claude Code: o relato de uma sessão
Alberto Luiz Souza · 2026-06-19 · via DEV Community

Disclaimer

Este texto é o relato de uma sessão real de trabalho minha com o Claude Code. O objetivo era montar um ranking dos melhores jogadores por posição a cada rodada da Copa do Mundo de 2026. O resultado final desse trabalho — a seleção da 1ª rodada e a régua de cada posição — foi publicado em "Cada posição tem a sua régua: a seleção da 1ª rodada da Copa". O que segue é o bastidor: reconstrói o que aconteceu na sessão, na ordem em que aconteceu, com os becos sem saída, os bugs e as decisões tomadas ao longo do caminho.

O objetivo da sessão

O ponto de partida que dei ao agente foi: criar uma estrutura de dados que permita avaliar os melhores jogadores em cada posição da rodada e montar a seleção, de forma reaproveitável para as rodadas seguintes.

Na prática, isso virou um pipeline: pegar os relatórios oficiais da FIFA da primeira rodada, cruzar com estatísticas individuais de jogadores, classificar cada um na posição que de fato ocupou em campo, criar fórmulas de avaliação por posição e, no fim, montar a seleção da rodada. Não foi linear. O relato abaixo segue o que de fato aconteceu.

O primeiro beco sem saída: os PDFs que não baixavam

A primeira tarefa era trivial no papel: baixar os 24 relatórios em PDF da fase de grupos do hub do FIFA Training Centre. O agente fez o fetch da página, listou os 24 jogos da rodada corretamente e partiu para o download. E aí veio o primeiro muro.

Todos os arquivos retornavam código HTTP 000. A primeira leitura do agente foi razoável: código 000 costuma significar conexão bloqueada por sandbox de rede. Mas a hipótese estava incompleta. O agente seguiu investigando, e o diagnóstico real só apareceu quando ele rodou um nslookup no host de onde estava tentando baixar: media.fifatrainingcentre.com retornava NXDOMAIN. O subdomínio simplesmente não existia.

O que tinha acontecido é instrutivo: a URL dos PDFs tinha sido inferida a partir do resumo da página, e o host media. foi essencialmente alucinado. A correção foi voltar ao HTML cru com curl, fazer um grep nos href e descobrir que os links reais estavam no mesmo host da página (www.fifatrainingcentre.com/media/...), inclusive com espaços nos nomes dos arquivos. A partir daí, os 24 PDFs baixaram a 200, cada um com seus cinco e poucos megabytes, renomeados para um esquema limpo do tipo M07-BRA-V-MAR.pdf.

Atribuição: tudo nesta etapa foi do agente. Minha única instrução tinha sido "criei uma pasta chamada copa-2026/rodada-1, baixe os relatórios e coloque lá". O código 000, o nslookup, o diagnóstico do NXDOMAIN e a descoberta das URLs reais aconteceram sem nenhuma intervenção minha.

Escolhendo a fonte de dados: o bake-off

O PDF da FIFA é rico em estatísticas de equipe, mas pobre em dados individuais detalhados. Para avaliar jogador por jogador eu precisava de outra fonte. Em vez de eu decidir no escuro, fizemos um pequeno bake-off entre bibliotecas.

Testamos primeiro a soccerdata. O agente, para crédito dele, reconheceu um erro próprio no meio do caminho: tinha afirmado que a 1.9.0 trazia um reader do FotMob, e não trazia. Partimos para o reader do FBref, que funcionou, encontrou a partida e devolveu 32 jogadores. Mas a limitação ficou clara: para a Copa, o FBref expõe só o box score básico, sem xG, sem rating, sem mapa de calor. E ele roda em cima de Selenium com chromedriver, o que torna a primeira execução lenta.

Aí pedi para testar a mobfot. Deu 404. O agente foi sondar os endpoints e descobriu que o FotMob tinha migrado a API para outro caminho (/api/data/...), que respondia 200 e, melhor ainda, sem exigir token de anti-bot. O JSON de detalhes da partida vinha com tudo que eu queria: rating por jogador, xG, xGOT, xA, estatísticas categorizadas por bloco (ataque, defesa, duelos, passes), shotmap e coordenadas no campo.

Decisão tomada: FotMob via um cliente próprio e fino, escrito à mão sobre requests, em vez da biblioteca pronta e quebrada. Um cliente de cerca de 400 linhas que eu controlo, em vez de uma dependência de terceiro que já tinha mostrado que quebra quando o provedor muda a rota.

Atribuição: misto. Eu defini quais bibliotecas testar ("me faça um teste utilizando a soccerdata", depois "teste o fluxo usando agora o mobfot"). Toda a parte investigativa foi do agente: reconhecer que a soccerdata não tinha reader do FotMob, mapear a limitação do FBref, levar o 404 da mobfot e descobrir que o FotMob tinha migrado para /api/data/.... A escolha final pelo cliente próprio também partiu do agente. Eu apenas confirmei que era uma boa ideia.

Separação de responsabilidades: o detalhe que o humano precisa cravar

Quando pedi o cliente do FotMob, fui explícito em uma coisa: ele recebe os parâmetros de busca e devolve os dados categorizados dos jogadores, e não acopla com os PDFs. O resultado foi um módulo independente, com um schema próprio para o jogador (identidade, posição, contexto de jogo, estatísticas categorizadas, shotmap, coordenadas), testável pela linha de comando, sem nenhum conhecimento sobre a FIFA.

Esse cuidado pagou de novo logo adiante. Ao construir o extrator do relatório da FIFA, o agente começou a querer reaproveitar um script de extração que já existia no projeto. Eu interrompi mais de uma vez para cravar a fronteira: aquele script era o extrator do Wyscout, e não devia ser tocado; o relatório Post-Match da FIFA é outra coisa e precisa do seu próprio pipeline. Sem essa intervenção, dois formatos de relatório muito diferentes teriam colidido no mesmo código.

Atribuição: intervenção minha. As duas regras desta seção saíram de prompts diretos meus: "esse cliente recebe os parametros... não acople o nosso client com os pdfs" e "mantenha esse extrair_relatorio, ainda vamos ter wyscout, este é um novo pipeline". Sem isso, o agente teria reusado o script existente.

O inferno da extração de PDF

Extrair as tabelas de dados individuais do PDF da FIFA foi a parte mais áspera. Alguns dos problemas concretos que enfrentamos com pdfplumber:

  • O extract_table() enxergava a caixa do cabeçalho, que tem linhas de grade, mas não as linhas de dados, que não têm grade. E era instável de página para página: em uma página ele pegava um subtítulo de seção em vez do cabeçalho de fato.
  • A ligadura tipográfica "ff" era renderizada como \x00. A palavra "Offers" virava O\x00ers. Tivemos que casar por substrings em vez do texto completo.
  • Uma linha espúria entrava na tabela: o cabeçalho de data da partida ("13 June 2026...") tinha o número certo de tokens para se passar por uma linha de jogador.

A solução que se mostrou robusta foi abandonar a tentativa de inferir a estrutura página a página e cravar os schemas de cada seção em código, já que o template da FIFA é estável. Cada seção passou a ter sua lista exata de colunas, e cada linha só era aceita se batesse a contagem de tokens esperada e os valores fossem numéricos por regex. A linha espúria da data morreu nesse filtro. Resultado final: 32 jogadores por jogo, três seções completas.

Atribuição: do agente. Dentro da fronteira que eu já tinha cravado (pipeline novo, separado do Wyscout), toda a engenharia de extração — o diagnóstico do extract_table(), a ligadura \x00, a decisão de cravar os schemas em código e o filtro por contagem de tokens — foi do agente, sem direcionamento meu.

Rodando a rodada inteira: o off-by-one e os nomes divergentes

Com o jogo do Brasil funcionando, mandei rodar para os 24 PDFs. A extração da FIFA passou em todos. O cruzamento com o FotMob falhou em 12. Dois bugs explicavam quase tudo:

Primeiro, um off-by-one de fuso horário. A data na capa do relatório da FIFA estava um dia antes da data que o FotMob usa para a mesma partida. A correção foi fazer a busca da partida com uma janela de mais ou menos um dia em torno da data informada.

Segundo, nomes de seleções divergentes entre as fontes. "Korea Republic" contra "South Korea", "IR Iran" contra "Iran", "Côte d'Ivoire" contra "Ivory Coast", "Cabo Verde" contra "Cape Verde". Construímos um mapa de aliases com normalização. E aí um detalhe fino quase passou: o apóstrofo de "Côte d'Ivoire" normaliza para espaço, então a chave do alias precisava ser exatamente "cote d ivoire", com o espaço, não "cote divoire". Sem perceber esse detalhe, a Costa do Marfim continuava falhando depois de todo o resto consertado.

Fechamos em 24 de 24 jogos, 753 jogadores, 100% casados, zero avisos. E aqui entrou uma etapa que eu valorizo muito: a validação. O agente cruzou os sobrenomes da FIFA contra os do FotMob em todos os jogadores casados e reportou "123 jogadores casados, zero cruzamentos de nome". O casamento era feito por (lado, número da camisa), e essa checagem independente confirmou que a junção estava correta.

Atribuição: do agente. Eu só dei a partida ("próxima etapa do pipeline é rodar a extração do json para todos os jogos") e deleguei explicitamente a decisão de paralelizar — o agente escolheu rodar sequencial. O off-by-one de fuso, o mapa de aliases, o detalhe do apóstrofo de "Côte d'Ivoire" e a validação cruzada de sobrenomes foram todos do agente.

Classificação de posição: onde o "taste" virou arquitetura

A "posição" do FotMob é grossa demais para o meu objetivo: ela só diferencia goleiro, defensor, meio e atacante. Eu queria a seleção por posição fina, (GK, CB, LB, RB, DM, CM, AM, W, CF). A estratégia que combinamos foi em camadas: um motor que decodifica a posição fina a partir do position_id e das coordenadas reais dos jogadores; um fallback por zona do campo para casos não vistos; e uma camada de override editorial, um JSON onde eu corrijo manualmente o que a máquina errou, com a maior precedência.

O ponto mais interessante foi a fronteira entre primeiro volante (DM) e meia (CM). A lógica puramente posicional colocava só 14 jogadores como volantes, e jogava verdadeiros primeiros volantes, como Casemiro, na categoria de meia, porque eles jogam adiantados no campo. A ideia de como resolver isso foi minha, mas a execução foi do agente: e se a gente escolher os jogadores de meio de campo e fizer a inferência via LLM? Para cada um, perguntar se ele é primeiro volante, segundo volante ou armador, com base no dossiê de ações dele naquele jogo, e gravar esse mapeamento em um arquivo de consulta.

Foi o que fizemos. Geramos um dossiê por meio-campista (coordenadas, ações defensivas, quebras de linha, chances criadas, xA, toques) e despachamos seis subagentes em paralelo para classificar cada um em DM, CM ou AM, com nível de confiança e justificativa. A distribuição saiu realista: 46 volantes, 74 meias, 45 armadores. A precedência final do classificador ficou: override manual, depois papel inferido por LLM, depois position_id, depois zona, depois a linha grossa.

Tem uma indecisão minha aí que eu acho honesto registrar. Em um momento perguntei se o mapeamento de papéis não valeria para a Copa inteira em vez de por rodada. O agente mudou para Copa inteira. Eu repensei e voltei atrás: melhor por rodada, porque o papel de um jogador pode mudar de jogo para jogo. O agente reverteu sem ruído, e o arquivo de papéis passou a viver dentro da pasta da rodada.

Atribuição: misto, e este é o ponto. A taxonomia de siglas foi proposta pelo agente e aprovada por mim. A estratégia em camadas era uma recomendação que o próprio agente tinha feito antes e que eu resgatei ("lá atrás você tinha sugerido isso aqui: A como motor, B para validar, C para correções"). A ideia de inferir o papel do meio-campo via LLM foi minha ("e se a gente escolher esses jogadores de meio de campo e fizermos a inferência via llm"), assim como a definição do escopo e a escolha de fazer por rodada. A implementação dos dossiês e dos subagentes foi do agente.

O bug sistemático que só uma pergunta humana pegou

Esse episódio é o que melhor resume a tese. Em algum momento eu perguntei, simplesmente: "Paquetá ficou onde?". O agente foi olhar e ele tinha sido classificado como lateral-direito, ranqueado entre os laterais. Paquetá é ponta. Errado.

A investigação revelou que não era um caso isolado. O slot largo da direita misturava laterais (defensores) e pontas (meias e atacantes) sob o mesmo position_id. Trinta e seis jogadores estavam no balde errado, incluindo Raphinha classificado como lateral-esquerdo. A correção foi desambiguar os slots largos ambíguos pela linha do jogador: se a linha é meio ou ataque, é ponta; se é defesa, é lateral. O pool de pontas saltou de 80 para 116, e Hakimi, que joga de fato como lateral, corretamente continuou lateral.

A natureza do erro vale o registro. Não era um crash nem um teste vermelho. Era um ranking que estava perfeitamente "verde", rodando, gerando JSON bonito, e silenciosamente errado para 36 jogadores. Nenhum assert teria pego isso. O que pegou foi uma pergunta de sanidade sobre um caso que eu conhecia.

Atribuição: misto, com papéis bem separados. Quem expôs o bug fui eu, com uma única pergunta ("paquetá ficou onde?"). Quem descobriu que eram 36 jogadores no balde errado e implementou a desambiguação por linha foi o agente.

A pontuação: camada editorial em JSON, mecânica em Python

Para a avaliação, a arquitetura que combinamos separa de propósito duas coisas. A camada editorial fica em JSON: os pesos de cada métrica e o tempo mínimo de jogo por posição, que são decisões de gosto e podem mudar a cada conversa. A mecânica fica em Python: o registro de métricas e o motor de pontuação, que não muda.

A normalização padrão é por percentil dentro do grupo da posição naquela rodada, com soma ponderada de 0 a 100. O agente foi honesto sobre o trade-off do percentil já na explicação: ele mede ordem, não magnitude, e por isso achata os extremos. Construímos as fórmulas uma posição de cada vez, e cada uma foi calibrada contra o ranking real, comigo olhando se o resultado fazia sentido.

O momento mais didático foi o dos pontas. Eu reclamei: "Messi fez 3 gols. Como é que alguém fica na frente dele no ranking?". O agente investigou e encontrou uma falha de método, não de peso. Sob percentil, três gols ficavam quase empatados com um gol, porque o percentil só enxerga a ordem: quem fez mais, fez mais, sem importar o quanto a mais. A correção foi tornar a normalização configurável por métrica e aplicar min-max (que preserva magnitude) a gols e assistências, mantendo percentil no resto. Messi subiu para primeiro. E o agente apontou o custo simétrico da escolha: quem fez um gol só perdeu posições.

Essa sequência teve várias intervenções minhas que mudaram o método, não só os números. Quando sugeri valorizar drible, e depois percebemos que drible isolado não diz muita coisa porque o que importa é o progresso do jogo, tiramos a métrica de drible solto. Quando perguntei se existia métrica para bola perdida no drible, adicionamos uma taxa de insucesso de drible contando negativamente.

Atribuição: misto. As decisões editoriais foram minhas, uma a uma — a divisão de pesos por posição, valorizar gol feito, tirar o drible solto, punir bola perdida. A análise que mostrou por que três gols quase empatavam com um gol sob percentil, e que levou ao min-max, foi do agente, em resposta à minha reclamação sobre o Messi. A mecânica do motor (registro de métricas, normalização configurável) foi do agente.

Duas óticas: por 90 minutos e dados brutos

Perto do fim, ao olhar Casemiro ranqueado entre os volantes, desconfiei de um efeito de amostra: ele jogou 45 minutos, e as métricas por 90 inflavam o volume dele, projetando seis ações defensivas como doze. Pedi para a seleção operar em duas óticas: por 90 minutos e considerando só os dados brutos. Implementamos um modo global em que só as métricas de volume mudam entre as óticas; taxas como porcentagem de passe e totais absolutos como gols evitados não mudam. Na ótica bruta, Casemiro caiu para 27 de 41 e Enzo Fernández assumiu a vaga de volante, confirmando a suspeita.

Atribuição: a desconfiança e o pedido foram meus, a partir de "casemiro tá em 5 de 41?" e "tem como a gente ter o script que extrai os jogadores da seleção operando por duas óticas". A implementação do modo global e a decisão de quais métricas mudam entre as óticas foram do agente.

Empacotando a jornada em skills

No fim, transformamos o pipeline inteiro em skills reaproveitáveis do Claude Code, porque o objetivo sempre foi servir as próximas rodadas, não só a primeira. Ficaram três: uma para preparar a rodada (que orquestra a extração, a classificação de posição, o dossiê do meio e a inferência de papéis por LLM), uma para gerar o ranking dado o minuto de corte e a ótica, e uma para filtrar um time específico de um ranking. A configuração editável ficou em JSON; a mecânica, em Python; o extrator do Wyscout, intocado.

A seleção da primeira rodada, com corte de 45 minutos, saiu com Beach no gol, Hakimi e Castagne nas laterais, Singo e Olivera na zaga, Schlager de volante, Bentancur de meia, Pedri de armador, e Messi, Haaland e Luis Díaz na frente.

Atribuição: o pedido de transformar o pipeline em skills foi meu, skill por skill. A estrutura de cada uma e o código foram do agente.

Onde o humano importou

Se eu tivesse que resumir onde o meu trabalho foi insubstituível nessa jornada, não seria em escrever código. O agente escreveu mais e mais rápido do que eu escreveria, e diagnosticou problemas de rede e de API numa velocidade que eu não tenho. O meu papel foi outro:

  • Não aceitar o primeiro diagnóstico plausível e deixar a investigação descer até a causa real.
  • Cravar fronteiras de design que o agente, otimizando para a tarefa imediata, tende a borrar (o extrator do Wyscout que não se mistura com o da FIFA, o cliente do FotMob que não acopla com PDF).
  • Fazer perguntas de sanidade ancoradas em domínio que expõem erros sistemáticos e silenciosos ("Paquetá ficou onde?").
  • Tomar as decisões editoriais sobre o que importa (o que é ser um bom ponta, percentil ou magnitude, por 90 ou bruto) e julgar se o resultado bate com a realidade.

Nenhuma dessas coisas é sobre digitar código. Todas são sobre julgamento, fronteira e taste. É exatamente esse tipo de habilidade que fica em primeiro plano quando se trabalha sério pareando com um agente de código: você para de ser quem digita e passa a ser quem decide, interroga e responde por estar certo. E essa, para mim, é a competência que vale a pena treinar de propósito agora.

Abraço,
Alberto


PS: este post foi gerado pelo agente de marketing a partir de um log exportado de uma sessão de trabalho minha com o Claude Code, e revisado por mim.