Como construir agentes com limites claros: um módulo, uma decisão
Colocar um agente numa pasta separada não cria uma fronteira. A fronteira aparece quando o módulo responde a uma pergunta, tem contrato versionado, pode recusar legalmente e deixa evidência verificável. Neste texto eu organizo essa arquitetura em seis artefatos locais e um coordenador que valida a transferência sem refazer o trabalho em silêncio.
Um módulo, uma pergunta, seis artefatos locais e um coordenador que sabe onde o sistema pode falhar
O que este artigo cobre
- Por que a estrutura de pastas não cria uma fronteira real sem decisões, entradas, saídas, proibições e evidências explícitas.
- Como módulos com uma pergunta só contêm erros e tornam as recusas diagnosticáveis.
- Quais seis artefatos locais transformam um módulo numa unidade de trabalho verificável.
- O que um coordenador deve validar, o que ele não deve reinterpretar e onde a liberação humana continua necessária.
- Quando dividir o trabalho melhora o isolamento de falhas e quando o custo de coordenação piora o sistema.
Na primeira parte eu expliquei por que um contexto enorme e um modelo caro não salvaram um projeto anonimizado de otimização de rotas. O agente escrevia código. O sistema não conseguia explicar qual decisão tinha eliminado uma rota viável.
Agora vem a parte de engenharia.
A versão curta é simples: não divida uma aplicação de agentes por substantivos. Divida por decisões.
"Tudo sobre pedidos" vira monólito. Coleta de pedidos, aceitação, tratamento de duplicados, busca de rotas, economia e liberação da sessão são decisões diferentes. Elas podem ler as mesmas linhas. Não devem dividir a responsabilidade.
A pasta não é a fronteira
Criar pasta, sozinho, não muda nada. Se todo agente pode ler toda pasta, alterar toda tabela, reinterpretar todo número e chamar qualquer ferramenta, o projeto continua sendo uma sala enorme. As paredes são decoração.
Uma pasta vira uma fronteira real quando define quatro coisas:
- A pergunta respondida pelo módulo.
- A entrada e a saída exatas.
- As decisões que o agente local não pode tomar.
- A evidência necessária pra verificar o resultado.
Isso é engenharia de software antiga com um novo consumidor. No trabalho de 1972 On the Criteria To Be Used in Decomposing Systems into Modules, David Parnas defendeu que módulos deveriam esconder decisões de projeto sujeitas a mudança e permitir que uma parte fosse entendida com pouco conhecimento das outras. Ele alertou contra dividir o sistema apenas pela sequência das etapas de processamento.
Com agentes, esse princípio ganha uma forma física. Cada arquivo extra, decisão vizinha, regra antiga e ferramenta sem relação consome contexto e cria mais uma ação que o agente pode tentar.
Então a pasta precisa funcionar como uma bancada. O agente vê a peça, o desenho, os instrumentos, as ferramentas permitidas. Ele não recebe a chave da fábrica inteira.
Regra um: um módulo responde a uma pergunta
O módulo de aceitação pergunta:
Este cartão está legível e é fisicamente admissível?
O módulo de economia pergunta:
Esta rota pronta passa pelos critérios econômicos de liberação?
O montador de rotas responde a outra pergunta. O coletor responde a outra. O coordenador não responde tudo de novo. Ele verifica se as saídas podem ser montadas sem contradição.
Isso dá um teste prático. Se a pergunta do módulo precisar de um "e", olha a frase de novo. "Leia o cartão e decida se a rota dá lucro" contém duas decisões, usa evidências diferentes e falha por motivos diferentes.
A fronteira acompanha a decisão, não a entidade de dados. Juntar tudo que tocava num pedido produziu mais de 8.000 linhas de SQL, busca, ranking, economia e ciclo de vida da sessão. O código estava perto porque as linhas da base estavam perto. O raciocínio não.
Regra dois: o módulo precisa ter o direito de errar localmente
Aqui está o melhor teste de fronteira que eu conheço:
Este módulo pode errar sem deixar todos os outros errados?
A aceitação pode aprovar um pedido economicamente péssimo e continuar correta. Um pedido de cerca de US$ 350 por quase 900 milhas é fisicamente admissível. A taxa isolada não importa porque o módulo econômico pode combinar esse pedido com outro veículo depois.
Essa independência local ajuda. Se o agente de aceitação rejeita o cartão, a gente olha visibilidade da evidência, tipo de veículo, quantidade, resolução de local ou capacidade. Não olha custo de combustível nem receita da rota.
O monólito não consegue errar localmente. A resposta dele mistura decisões, então uma premissa errada contamina o resultado final e apaga o lugar onde o erro nasceu.
O objetivo não é eliminar o erro. É conter o erro.
Regra três: número compartilhado precisa de um dono
Se dois módulos precisam concordar sobre um valor calculado, esse valor pertence a um terceiro componente compartilhado, importado pelos dois.
A gente aprendeu isso com um bug em que a mesma grandeza era calculada em três lugares, de três formas. Os resultados divergiam nos últimos bits. Cada cálculo parecia razoável. Juntos, quebravam identidade e comparação de estado.
Não resolva isso com um parágrafo pedindo que três agentes "calculem de forma consistente". Dê ao cálculo uma implementação, uma versão, uma suíte de testes, um dono.
Números são contratos.
Os seis artefatos locais de um módulo limitado
Uma pasta de módulo tem seis artefatos locais. Nenhum entrou por estética. Cada um corresponde a uma perda real.
order_acceptance/
CONTRACT.md
AGENT.md
INVARIANTS.md
DECISIONS.md
FAILURES.md
tests/
CONTRACT.md
Este arquivo define a entrada e a saída exatas, incluindo as versões dos contratos.
Sem ele, um valor se espalha por três implementações e ninguém percebe que as definições são diferentes. O contrato precisa dizer quais campos são obrigatórios, quais podem faltar, como partial é representado, quais códigos de motivo são permitidos e se a saída aceita apenas acréscimos.
O texto deve ser preciso o bastante pra gerar validação a partir dele.
AGENT.md
Esta é a instrução operacional local. Ela lista decisões permitidas, verificações obrigatórias e decisões proibidas.
A lista de proibições é a parte mais importante.
Na aceitação, o agente pode comparar as evidências sobre a quantidade de veículos. Pode rejeitar um tipo de transportador incompatível. Pode devolver partial quando as fontes entram em conflito. Não pode alterar preço, milhas ou composição dos veículos. Não pode montar pares, calcular rota, pedir outra consulta ao quadro ou adivinhar qual fonte contraditória está certa.
Uma instrução é obrigatória:
Você tem permissão pra responder "eu não sei".
Sem um estado de recusa definido, o agente costuma completar o padrão e produzir uma resposta. Num sistema de produção, incerteza precisa ser um tipo de saída.
INVARIANTS.md
Este arquivo contém propriedades que sempre devem ser verdadeiras, escritas num formato que possa virar teste.
"A rota deve terminar dentro da zona de destino" é documentação. Um teste que falha quando as coordenadas finais ficam fora dessa zona é uma invariante.
O projeto carregou essa frase por muito tempo. Nada verificava. Até o sistema liberar uma rota terminando em outra cidade.
Se uma invariante não pode ser testada, provavelmente ainda é só uma intenção.
tests/
Os testes ficam ao lado do módulo, não apenas numa pilha central.
Numa auditoria, havia dezenas de arquivos de teste reunidos. Mesmo assim, vários módulos não tinham cobertura nenhuma. Um deles tinha derrubado uma sessão real pouco antes. Outro gerava o texto de confirmação mostrado ao operador.
Testes locais deixam a falta de cobertura visível na própria árvore de diretórios. Também permitem que o agente local verifique sua decisão sem carregar fixtures e histórias que pertencem ao resto do sistema.
DECISIONS.md
Este arquivo registra por que uma regra ou um limite existe, quando entrou e qual decisão posterior pode ter eliminado o motivo original.
Um mínimo antigo de cerca de 100 milhas para um trecho carregado sobreviveu depois que o aplicativo ganhou uma verificação econômica da rota inteira. O motivo deixou de existir. O limite ficou. Ele bloqueou uma rota viável porque um trecho tinha aproximadamente 60 milhas.
O código mostra o que a regra faz. O registro de decisão diz ao próximo agente se a regra ainda merece existir.
FAILURES.md
Este arquivo guarda falhas reais, códigos de motivo e referências para sessões reproduzíveis.
Muita equipe guarda exemplos de sucesso e apaga os candidatos rejeitados. Eu mantenho as recusas em formato append-only, sem apagar o histórico. Foi isso que permitiu a uma única consulta revelar mais de 1,4 milhão de rejeições ligadas a um limite de ocupação única.
A saída não dizia apenas "nenhuma rota encontrada". Ela mostrava qual regra tinha matado cada candidato.
Isso é observabilidade.
O que o agente local vê
O agente local de aceitação recebe:
- a pasta completa do próprio módulo
- dados de entrada compatíveis com o contrato declarado
- a especificação confirmada do processamento
- o resolvedor de ZIP necessário para as verificações de localização.
Ele não recebe os módulos vizinhos, a economia da rota, o estado atual da busca nem permissão pra escrever mudanças arbitrárias na base compartilhada.
Isso não é desconfiança do modelo. Uma decisão tomada com contexto desnecessário é difícil de reproduzir, porque a influência escondida desse contexto também é difícil de isolar.
O resultado da aceitação deve ser o mesmo se a rota futura der lucro ou prejuízo. Se a economia da rota mudar a aceitação, a fronteira vazou.
O que o coordenador deve fazer
O coordenador não é um agente maior com permissão pra improvisar no repositório inteiro. Ele é um ponto controlado de síntese.
No fluxo que eu uso, cada módulo é analisado separadamente depois de uma execução da nova versão. O coordenador coleta os relatórios locais, verifica o estado combinado e produz uma análise única do sistema. Depois eu reviso o resultado manualmente. As correções entram em outra versão.
O coordenador deve receber contratos, resumos de resultados, distribuição de falhas, identificadores de versão e invariantes entre módulos. Ele não precisa de cada token de cada investigação local.
O trabalho dele se parece mais com despacho que com produção:
- confirmar que cada módulo obrigatório devolveu um resultado
- confirmar que as versões dos contratos combinam
- detectar contradições entre resultados locais
- executar invariantes de sistema que não pertencem a um único módulo
- preservar a evidência usada na liberação
- parar quando um módulo obrigatório devolve
partialou conflito sem solução.
Essa estrutura também combina com a evidência mais recente sobre sistemas multiagente. O estudo de escala do Google mostrou que um coordenador central conteve melhor a amplificação dos erros que agentes independentes. O estudo MAST encontrou falhas recorrentes em especificação, alinhamento entre agentes, verificação e encerramento. Coordenação ajuda quando funciona como gargalo de validação. Ela atrapalha quando vira outra fonte de decisões impossíveis de rastrear.
Commits e releases acompanham as etapas de decisão
As mudanças são commitadas por etapa, e as etapas não são misturadas sem necessidade.
Toda tarefa informa explicitamente se a mudança altera o comportamento observável. Isso importa porque uma limpeza aparentemente interna pode alterar a identidade do estado.
Uma vez, a mesma grandeza era calculada em vários lugares. Substituir esses cálculos por uma função única parecia refatoração. Só que também mudava assinaturas de estado armazenadas num índice único. Se a mudança saísse como "refatoração sem alteração de comportamento", a retomada das sessões poderia quebrar sem caminho de migração.
Commits separados não são cerimônia. Eles preservam causa e efeito. Quando uma versão muda o comportamento, a gente precisa saber qual decisão mudou junto.
Até onde vale dividir um módulo?
Não existe um número universal de linhas, tokens ou níveis de pasta.
A pesquisa traz um aviso, não um número mágico. O estudo do Google mostrou que sistemas multiagente ajudaram em tarefas paralelizáveis, mas pioraram o planejamento sequencial entre 39% e 70%. Tarefas com muitas ferramentas também pagaram um custo maior de coordenação. Se todo resultado precisa passar por vários agentes antes do próximo passo, a arquitetura gasta o orçamento conversando com ela mesma.
Eu uso quatro testes:
- A pergunta do módulo cabe numa frase?
- A saída pode ser validada sem ler a implementação dos módulos vizinhos?
- O módulo pode falhar com um código específico sem corromper o resto do sistema?
- Outro módulo pode consumir a saída por meio de um contrato versionado?
Se a resposta for não, o módulo está grande demais ou a fronteira está no lugar errado.
Um módulo pode estar pequeno demais quando não consegue produzir um resultado significativo e testável sozinho, quando gasta quase todo o contexto reconstruindo a etapa anterior ou quando o trabalho exige uma cadeia contínua de raciocínio. Nesse caso, dividir cria mensagens e perde estado sem isolar falha nenhuma.
O menor módulo útil não é o menor pedaço de código. É a menor decisão completa.
A prática de engenharia está indo na mesma direção
Esse padrão já aparece em sistemas sérios de agentes.
O relatório da OpenAI de 2026 sobre harness engineering descreve um repositório em que um AGENTS.md curto funciona como mapa e a documentação estruturada é a fonte de verdade. O sistema entrega contexto por camadas, impõe direções rígidas de dependência, roda testes de arquitetura e verifica automaticamente a documentação antiga. A equipe relata que um arquivo gigante de instruções falhou porque ocupava o espaço da tarefa, fazia todas as regras parecerem igualmente importantes e era difícil de verificar.
O relatório da Anthropic de março de 2026 sobre harness design for long-running application development descreve a divisão de builds em partes tratáveis, transferência de estado por artefatos estruturados e separação entre um agente gerador e um avaliador. O avaliador não existe pra escrever mais código. Ele é dono de outra decisão.
Em outro experimento, a Anthropic usou dezesseis agentes em quase 2.000 sessões para construir um compilador C com 100 mil linhas, capaz de compilar o Linux 6.9 para x86, ARM e RISC-V. Os agentes reservavam tarefas por arquivos explícitos de bloqueio e trabalhavam em falhas separadas. O projeto custou perto de US$ 20 mil em uso de API, então não é receita pra qualquer aplicativo. Ele mostra uma coisa útil: trabalho paralelo com agentes exige propriedade explícita, testes compartilhados e um mecanismo que impeça vários agentes de resolver o mesmo problema ao mesmo tempo. Building a C compiler with a team of parallel Claudes
Os fornecedores são diferentes. O padrão de engenharia não: mapas no lugar de enciclopédias, responsabilidade local, transferências estruturadas, testes, estado visível, avaliação independente.
A arquitetura numa imagem
O aplicativo está caminhando pra esta estrutura:
coletor do quadro
-> base de evidências selada
-> aceitação de pedidos
-> busca e combinação de candidatos
-> construção da rota
-> economia da rota completa
-> análise do coordenador
-> revisão humana
-> release versionado
Cada seta é um contrato. Cada módulo é dono de uma decisão. Cada recusa continua disponível para análise. O coordenador enxerga o suficiente pra montar o sistema, não o suficiente pra reescrever em silêncio cada resultado local.
É a mesma lógica operacional que eu uso hoje ao construir agentes, aplicativos, sistemas de tratamento de informação e processos grandes de seleção. Divida o fluxo quando as decisões forem diferentes. Mantenha o trabalho determinístico como código determinístico. Use IA quando a evidência precisa ser interpretada. Dê um estado legal pra incerteza. Guarde os caminhos rejeitados. Coloque um coordenador acima dos módulos locais e mantenha uma pessoa na fronteira de liberação.
O melhor agente não é o que conhece o projeto inteiro.
Ele sabe exatamente qual pergunta é dele.
Aplicação prática
- Escreva a pergunta do módulo numa frase. Se ela exigir duas decisões, divida o módulo.
- Versione o contrato de entrada e saída, incluindo estados válidos de recusa e
partial. - Dê a cada número derivado compartilhado uma implementação, um dono e uma suíte de testes.
- Mantenha invariantes, testes, histórico de decisões e evidências de falha ao lado do módulo responsável.
- Use o coordenador para validar contratos e estado entre módulos. Não deixe que ele refaça decisões locais em silêncio.
- Mantenha uma pessoa na fronteira final de liberação quando o resultado altera operações reais.
Em resumo
- Um agente limitado é definido pela fronteira da decisão, não pelo nome da pasta.
- Erros locais são úteis quando permanecem locais, visíveis e reproduzíveis.
- Recusa e incerteza são tipos de saída, não falhas de conversa.
- A coordenação só compensa o custo quando reduz contradições e preserva evidências.
- O menor módulo útil é a menor decisão completa.
Tags
Agentes de IA · Arquitetura de Agentes · Engenharia de Software · Observabilidade · Operações
Fontes
Pesquisas e relatórios de engenharia citados na série:
- Lost in the Middle: How Language Models Use Long Contexts, Liu et al., 2023.
- RULER: What's the Real Context Size of Your Long-Context Language Models?, Hsieh et al., 2024.
- NoLiMa: Long-Context Evaluation Beyond Literal Matching, Modarressi et al., ICML 2025.
- Context Length Alone Hurts LLM Performance Despite Perfect Retrieval, Du et al., preprint, 2025.
- Why Do Multi-Agent LLM Systems Fail?, Cemri et al., preprint, 2025.
- Towards a Science of Scaling Agent Systems, Kim et al., preprint, 2025. Veja também o resumo do Google Research, janeiro de 2026.
- On the Criteria To Be Used in Decomposing Systems into Modules, D. L. Parnas, Communications of the ACM, 1972.
- How we built our multi-agent research system, Anthropic Engineering, junho de 2025.
- Harness engineering: leveraging Codex in an agent-first world, OpenAI, 2026.
- Harness design for long-running application development, Anthropic Engineering, março de 2026.
- Building a C compiler with a team of parallel Claudes, Anthropic Engineering, fevereiro de 2026.
Como vocês definem o menor limite útil de um agente sem transformar a coordenação em outro monólito?
Versão canônica e referências: https://denisostapenko.com/pt/blog/how-to-build-bounded-agents
#AgentesDeIA #ArquiteturaDeSoftware #EngenhariaDeSoftware
Denis Ostapenko · ORCID: https://orcid.org/0009-0006-2630-7280