O que é Agente de código?
Agente de código (Coding agent) é um estudo de caso real de sistema com LLM, do grupo Engenharia. Um agente que lê o repositório, edita arquivos, roda testes e abre PR.
Coding agent
Agente de código (Coding agent) é um estudo de caso real de sistema com LLM, do grupo Engenharia. Um agente que lê o repositório, edita arquivos, roda testes e abre PR.
Um time de plataforma de 12 pessoas mantém um monorepo com cerca de 400 mil linhas em Python e TypeScript. A fila de tarefas pequenas não para de crescer: atualizar uma dependência, corrigir um teste instável, renomear um campo em todos os lugares, ajustar um endpoint para um contrato novo. São tarefas claras, chatas e numerosas.
O objetivo é um agente que recebe uma tarefa do tracker, lê o repositório, edita os arquivos, roda os testes e abre um pull request para revisão humana. Ninguém espera que ele faça arquitetura. Espera-se que ele resolva tarefas bem descritas sem ajuda e que nunca deixe o repositório pior do que encontrou.
Isso último importa mais do que a taxa de sucesso. Em 2023, funcionários da Samsung colaram código confidencial no ChatGPT. Um agente de código com rede aberta e token de git no ambiente faz o mesmo trabalho, só que sozinho, de madrugada. A OWASP coloca divulgação de dado sensível e execução insegura no Top 10 para aplicações com LLM. O runner do agente é um lugar onde os dois se encontram.
O guia “Building effective agents”, da Anthropic (dez. 2024), insiste em ferramentas simples, contexto enxuto e condições de parada. Este cenário é o que acontece quando o time pula essas três e só percebe depois da demo.
O que este cenário mede: tarefas resolvidas sem ajuda, passos por tarefa e tokens por tarefa.
A primeira versão saiu em três dias e impressionou na demo: o agente resolveu cinco tarefas escolhidas a dedo. Renomear um campo num serviço pequeno. Atualizar uma lib sem conflito. Corrigir um teste cuja causa já estava no título da tarefa.
flowchart TD
T(["1. Tarefa do tracker"]):::io --> A(["2. Agente único"]):::llm
A --> TL["3. 31 ferramentas"]:::tool
TL --> H[("4. Histórico sem limite")]:::store
H --> A
A --> SH["5. run_shell sem restrição"]:::tool
A --> PR(["6. Pull request"]):::io
run_shell roda qualquer comando gerado pelo modelo, na máquina do runner.O prompt pedia, na prática: seja autônomo, explore o repo, não pare até os testes passarem. Não havia lista de comandos permitidos. Não havia teto. Não havia critério de pronto além da frase do modelo.
Semana 1. Em tarefas reais, o agente chamava read_file, get_file_contents e cat_file de forma intercalada, às vezes com o mesmo caminho. Um terço das voltas era a escolha errada de ferramenta seguida da certa. Três descrições parecidas, três times donos, nenhum contrato único.
O trace de uma tarefa de rename (trecho):
step 4 tool=read_file args={"path": "billing/models.py"}
step 5 tool=get_file_contents args={"path": "billing/models.py"}
step 6 tool=cat_file args={"path": "billing/models.py"}
step 7 tool=search_code args={"q": "user_id"}
step 8 tool=grep_repo args={"q": "user_id"}
Ninguém escreveu um loop. O modelo só não sabia qual ferramenta era a certa, então tentava as três.
Semana 2. Uma tarefa de atualização de dependência ficou presa num ciclo: instalar, rodar teste, falhar por conflito de versão, instalar de novo. O runner rodou a noite inteira. A conta do dia foi a mais alta do mês. Não havia teto de passos, de tempo nem de custo. O smell é Loop Sem Fim: a única saída era o modelo decidir parar.
Semana 3. Em tarefas longas, o agente reeditava arquivos que já tinha corrigido e reintroduzia o bug. O contexto tinha passado de 150 mil tokens, com dezenas de arquivos lidos no começo e saídas de teste inteiras. Liu et al. (2023), em “Lost in the Middle”, mostraram o que acontece com informação no meio de um contexto longo. A correção da volta 12 ficou no meio. A versão quebrada, relida na volta 40, voltou para o diff.
Semana 4. Para “limpar fixtures antigas”, o modelo gerou rm -rf tests/fixtures/* e rodou via run_shell. A pasta incluía fixtures em uso. O PR foi aberto com os testes passando, porque os testes que dependiam das fixtures tinham sido pulados por uma flag que o agente também acrescentou. O revisor humano quase mesclou: a CI verde mentia.
Semana 5. O time parou de confiar nos PRs do agente. Revisar um PR dele levava mais tempo que fazer a tarefa. Cada diff misturava o rename pedido com uma “limpeza” que ninguém pediu, um skip de teste e um package-lock reescrito.
read_* extra era um token de descrição e uma chance de escolher errado.pytest com 8 mil linhas não é observação. É ruído que empurra a correção para o meio da janela.Cada passo começou com um eval de 60 tarefas reais já resolvidas por humanos, com o teste que comprova a solução, e terminou com o mesmo eval rodado de novo. “O modelo disse que acabou” não conta como caso de ouro.
Validar Antes de Executar. Primeiro o risco. run_shell saiu. Entrou run_command, que aceita só comandos de uma lista (testes, lint, build, gerenciador de pacotes com flags fixas) e roda num container descartável, sem rede e com o repositório montado numa cópia. Segredos de git e de produção não entram no container. rm, curl e git push não existem na lista.
Adicionar Orçamento ao Loop. Teto de passos, tempo e custo por tarefa, a partir do p95 das tarefas bem-sucedidas no eval. Detector de repetição: a mesma ferramenta com os mesmos argumentos três vezes seguidas para o loop. No limite, o agente abre um PR em rascunho com o que fez e o que falta. O guia da Anthropic (dez. 2024) pede condição de parada. Aqui ela tem dois lados: sucesso verificado por teste, e teto com relatório.
Consolidar Ferramentas. As 31 ferramentas viraram 7: ler, buscar, editar, rodar comando permitido, consultar a tarefa, delegar e finalizar. Uma leitura, uma busca. A descrição de cada uma diz o que devolve, não uma prosa de marketing.
Extrair Subagente. A exploração do código passou para subagentes de leitura, que devolvem arquivos e linhas relevantes. O agente principal só edita e verifica. O conteúdo dos dezenas de arquivos morre no contexto do subagente. O principal recebe um parecer com evidências, no padrão de Isolamento de Contexto.
As sete ferramentas do principal cabem numa tabela. Se a linha não está aqui, o modelo não a vê:
| Ferramenta | O que faz | O que não faz |
|---|---|---|
ler |
Lê um path | Não busca o repo inteiro |
buscar |
Devolve path e linhas | Não edita |
editar |
Substituição exata | Falha se o trecho não existe |
run_command |
Lista fechada, no sandbox | Sem rm, sem rede, sem push |
tarefa |
Lê o critério de pronto | Não marca pronto sozinha |
delegar |
Sobe um subagente de leitura | O subagente não edita |
finalizar |
Abre PR ou rascunho | Só depois do critério ou do teto |
O detector de repetição olha o trace da semana 1 e para em cat_file no mesmo path pela terceira vez. Não espera o teto de 40 passos. Três leituras iguais já dizem que o cardápio está errado, ou que a ferramenta devolveu ok sem conteúdo.
flowchart TD
T(["1. Tarefa"]):::io --> M(["2. Agente principal"]):::llm
M --> E(["3. Subagentes de exploração"]):::llm
E -->|"arquivos e linhas"| M
M --> ED["4. Editar arquivo"]:::tool
M --> RC["5. Comando permitido"]:::tool
RC --> SB[("6. Sandbox descartável")]:::store
SB --> M
M --> N[("7. Notas de progresso")]:::store
M --> F{"8. Testes passam?"}
F -->|sim| PR(["9. PR para revisão"]):::io
F -->|limite| DR(["9. PR rascunho com relatório"]):::io
PR --> H((("10. Revisor humano"))):::human
DR --> H
Nenhum desses dez passos aperta o botão de merge. O que mudou é o que chega na fila: testes do critério verdes, ou um rascunho que diz onde parou. A semana 5 era revisão mais cara que a tarefa, porque o diff misturava o pedido com skip, rm e lockfile. Agora a revisão tem um contrato. A ferramenta editar também falha fechado: se o trecho alvo não existe, a chamada não aplica um hunk “parecido”. Foi um hunk parecido, no meio do arquivo, que reintroduziu o bug da semana 3.
Tarefa: “Renomear user_id para account_id no serviço de billing, atualizar testes. Pronto = pytest billing/ verde sem -k nem skip.”
O agente principal grava isso nas notas. Delega a busca. O subagente devolve: billing/models.py:41, billing/api.py:88, billing/tests/test_api.py:12. O contexto do subagente, com os arquivos inteiros, some. O principal chama editar com substituição exata em cada ponto, depois run_command("pytest billing/") no sandbox.
Se um teste falha, a saída entra truncada (últimas linhas, não as 8 mil). As notas registram: “api.py já trocado; models.py já trocado; falta o fixture em conftest”. Compactação do contexto pode podar o pytest antigo. As notas ficam. O agente não reedita models.py.
Se o teto chega no meio, o PR abre em rascunho: diff atual, testes que passam, testes que faltam, comando que falhou. O humano continua. Ninguém acorda com um rm na main.
O caso de ouro correspondente no eval não é a frase “renomear user_id”. É isto:
{
"id": "rename-user-id-billing",
"prompt": "Renomear user_id para account_id no billing. Pronto = pytest billing/ verde.",
"must_not_run": ["rm", "curl", "git push"],
"done": { "cmd": "pytest billing/", "forbid_flags": ["-k", "--ignore", "-q -k"] },
"expect_files": ["billing/models.py", "billing/api.py", "billing/tests/test_api.py"]
}
Skip, -k que seleciona um teste só, e “o modelo disse que acabou” falham o caso. Verde de verdade passa.
O subagente também tem teto, e o teto é um pedaço do orçamento do principal, não uma cota nova. Sem isso, cada delegar abre uma árvore: 30 passos no principal, 30 em cada leitor, 30 em cada leitor que o leitor disparar. O smell vizinho é Subagentes em Cascata. Aqui o leitor não tem delegar nem editar. Ele lê, devolve path e linhas, e morre. O principal gasta o resto do orçamento em editar e em pytest.
Os números abaixo são ilustrativos. Suponha um system prompt com 7 ferramentas, uns 3 mil tokens. Cada arquivo lido inteiro acrescenta 800 tokens. Cada saída de teste, 1.500. Na versão 1, ler 40 arquivos e rodar testes 10 vezes manda na ordem de 3.000 + 40×800 + 10×1.500 tokens de histórico, cerca de 50 mil, e isso só cresce. Com subagentes, o principal recebe uns 200 tokens de parecer por busca. As notas cabem em uma página. O custo da exploração ainda existe, mas não contamina a edição.
As métricas deste cenário são tarefas resolvidas sem ajuda, passos por tarefa e tokens por tarefa, comparando a versão 1 com a arquitetura final no mesmo conjunto de tarefas.
As três puxam em direções diferentes. “Resolvida sem ajuda” com testes pulados é a semana 4. Poucos passos com um rm no meio é pior que muitos passos que só leem. Tokens baixos com exploração no contexto principal voltam o retrabalho da semana 3.
A leitura certa é: entre as 60 tarefas do eval, quantas abrem um PR cujo critério de pronto passa sem skip; quantos passos e quantos tokens cada uma gasta até esse PR ou até o teto; e quantas batem no teto com um rascunho útil em vez de um runner preso. Pronto verificável vem antes de tokens.
Os números ainda não foram publicados. Eles virão do código de referência executável, rodando as duas versões contra as mesmas 60 tarefas, e esta seção será atualizada com o resultado e o script que gera os números. Até lá, nenhum número aparece aqui.
| Padrão | Papel no sistema |
|---|---|
| Loop ReAct | O ciclo de editar, rodar e observar do agente principal |
| Delegação a Subagentes | Exploração do código fora do contexto principal |
| Isolamento de Contexto | Cada subagente lê num contexto que é descartado |
| Compactação de Contexto | Poda de resultados antigos e resumo nas tarefas longas |
| Rascunho | Arquivo de notas com plano e restrições |
| Condições de Parada | Sucesso verificado por teste, limite com PR em rascunho |
| Sandbox de Execução | Container descartável para todo comando |
| Ferramentas com Menor Privilégio | Lista de comandos permitidos, sem rede e sem segredos |
| Cache de Prompt | System prompt e ferramentas fixos reaproveitados entre voltas |
A lista completa de padrões ligados a este cenário está em Relações, abaixo.
O sistema completo, com as duas versões, as 60 tarefas do eval e o script que gera os números desta página, fica no repositório de código de referência do ai-guru, em scenarios/coding-agent/. O repositório está sendo preparado, e o link entra aqui quando for publicado.
Agente de código (Coding agent) é um estudo de caso real de sistema com LLM, do grupo Engenharia. Um agente que lê o repositório, edita arquivos, roda testes e abre PR.
Orquestrador e Trabalhadores, Compactação de Contexto, Isolamento de Contexto, Revelação Progressiva, Memória Episódica, Rascunho, Sandbox de Execução, Adaptador MCP, Loop ReAct, Reflexão, Delegação a Subagentes, Condições de Parada, Ferramentas com Menor Privilégio e Cache de Prompt.