Harness Engineering para AI Agents: conceber ciclos de controlo
Tradução automática Este artigo foi traduzido automaticamente a partir da versão original em inglês.
O agent loop escolhe a ação seguinte. O harness fornece contexto, valida os tool calls propostos, autoriza-os, encaminha os que aceita, regista os resultados e decide se a tarefa está concluída.
Os primeiros três trabalhos já têm um artigo. Memory fornece o contexto, tool use define o que pode ser proposto e security decide o que é executado. Foram publicados como artigos separados porque são problemas de engenharia distintos. Escolher entre Qdrant e pgvector não tem qualquer relação com escrever uma regra deny de PreToolUse.
Também partilham um momento: o intervalo entre o modelo nomear uma ação e a máquina a executar. Cada um responde a uma pergunta sobre esse intervalo, e o harness é o código que o mantém aberto tempo suficiente para colocar as três.
Para os engenheiros que constroem ou revêem harnesses de coding agents, falta decidir se o trabalho concluído está realmente completo e provar que cada controlo no loop justifica o custo que introduz. A Parte 5 abordou o runtime que mantém o processo vivo por baixo.
Em resumo: trate cada controlo adicional do harness como uma hipótese sobre uma falha observada: mantenha fixos o modelo, o conjunto de tarefas, o grader e o orçamento; conserve retries, handoffs ou evaluators apenas quando a comparação melhora o resultado.
Depois da primeira acceptance check explícita, cada retry, handoff ou evaluator adicional é uma hipótese sobre uma falha observada. Só merece um lugar quando uma comparação controlada demonstra que ajuda.
A acceptance check mais simples é fácil de escrever para um pequeno research agent como o que esta série tem vindo a construir — um agent LangGraph que obtém dados de mercado e escreve um relatório de analista. Um hook fora do modelo valida o relatório contra um schema e verifica se contém realmente stock tickers; um relatório malformado mantém o run aberto. São doze linhas de código normal, e o modelo não pode declarar que o seu próprio output está bem formado. O gate do próprio repositório é mais flexível: um evaluator com fresh context que vota, seguido de um humano. A Parte 4 apresenta a versão determinística.
O que esse exemplo não mostra é a parte interessante: o que acontece quando a evidência é ambígua, quando um retry pode cobrar duas vezes a alguém ou quando o trabalho ultrapassa a sessão que o iniciou. Estes casos precisam de uma tarefa com uma fronteira de sucesso/falha mais nítida do que um research report. O research agent mantém-se como exemplo de acceptance check; um pequeno repositório fictício de uma loja junta-se aos casos de retry e handoff. A tarefa de coding consiste em baixar de $100 para $75 o limiar para um desconto automático de 10% em src/checkout.py. O repositório tem dois checks obrigatórios:
pytest tests/test_checkout.pyverifica o cálculo do desconto.pnpm playwright test tests/checkout_discount.spec.tsadiciona um artigo de $80 a uma loja de testes local e verifica que a página de checkout apresenta um desconto de $8.
O exemplo é um fixture didático, não uma aplicação real nem um benchmark. Cada tentativa começa no mesmo commit e com os mesmos dados de teste seedados. O harness só pode aceitar a alteração quando ambos os comandos passam e o trace associa esses resultados ao commit testado.
O diagrama acompanha a alteração do desconto desde a proposta até à evidência. O harness fornece a tarefa e os ficheiros, verifica os argumentos e as permissões do edit_file proposto e encaminha o call aceite. Depois de o runtime aplicar a edição, o harness executa os testes de unidade e os testes de acceptance no browser especificados. Um comando que falha regressa ao modelo como evidência para outro turno; dois comandos com sucesso tornam a alteração elegível para aceitação.
O que pertence ao harness
O percurso do Codex loop da OpenAI descreve o ciclo básico. O harness compõe um prompt, pede ao modelo a ação seguinte, envia um tool call aceite para o runtime e acrescenta o resultado. Depois pergunta novamente. Isto repete-se até o harness aceitar o resultado ou devolver o controlo ao utilizador.
As implementações podem juntar várias responsabilidades num único processo. As fronteiras de falha continuam a ser diferentes:
| Termo | Função | Exemplo num coding agent |
|---|---|---|
| Modelo | Propõe texto, um tool call ou uma resposta final | Sugere uma edição em src/checkout.py |
| Reasoning loop | Escolhe o passo seguinte a partir do contexto disponível | Inspecionar, editar, testar, voltar a inspecionar |
| Harness | Fornece contexto, valida propostas, autoriza-as, encaminha calls aceites, regista resultados e verifica a conclusão | Permite edições em src/ e exige ambos os testes nomeados |
| Runtime | Executa calls aceites e mantém o estado vivo fora do processo worker | Log da sessão, sandbox, checkpoint store, trace backend |
A linha do runtime abrange quatro coisas: sessão, sandbox, checkpoint e trace. As quatro armazenam estado ou confinam a execução. O modelo propõe a ação e o reasoning loop escolhe o passo seguinte. O harness decide se um call proposto pode ser executado e se a evidência é suficiente para terminar, razão pela qual tem o seu próprio artigo. A Parte 5 contabiliza o harness, juntamente com estas quatro componentes, como uma das cinco primitivas que é necessário posicionar antes de fazer o deploy; este artigo separa-o novamente.
Quando surge uma falha, diagnostique a fronteira que deveria responder. Um plano deficiente pode precisar de instruções melhores ou de melhor reasoning do modelo. Se edit_file apontar para um caminho fora de src/, o harness deve rejeitá-lo. Um processo sandbox que morre antes de executar a edição pertence ao runtime, que deve reiniciar o worker ou reportar o crash.
Onde se enquadram as partes anteriores
A linha do harness acima faz a maior parte do trabalho nessa tabela e é aí que acabam as Partes 2, 3 e 4. Cada uma decide uma coisa sobre um turno individual:
| Parte anterior | O que decide para este turno | Onde atua no percurso da secção seguinte |
|---|---|---|
| Parte 2 — memory | Que estado anterior entra no prompt | Passo 1, o context builder |
| Parte 3 — tool use | Que ações existem e qual é o aspeto de um resultado validado | Validação dos argumentos no passo 3 e formato do resultado no passo 4 |
| Parte 4 — security | Se este call específico pode ser executado agora | Passo 3, verificação do caminho e decisão de aprovação |
| Parte 6 — este artigo | Se a evidência resultante termina o run | Passos 5 a 7, acceptance checks e trace |
As Partes 3 e 4 partilham o passo 3, e essa sobreposição é precisamente o argumento para as tratar como um único programa. A mesma camada de código do harness que rejeita um argumento malformado rejeita também um call permitido mas ainda não aprovado. Se forem divididas por dois serviços, essas duas rejeições divergem, e um call que passa a validação do schema num local acaba autorizado noutro que nunca viu o schema.
A divisão continua a ser importante para debugging: uma edição no ficheiro errado é uma regra de caminho da Parte 4, não um problema de retrieval da Parte 2. Uma secção perto do final deste artigo transforma isso numa tabela de encaminhamento.
O próprio case study de harness engineering da OpenAI descreve uma instância da aplicação inicializável para cada worktree. A equipa integrou também browser automation no ambiente do agent e disponibilizou logs, métricas e traces.
Uma tarefa como «nenhum span nestas quatro jornadas críticas do utilizador excede dois segundos» tornou-se testável porque o agent podia executar a aplicação e consultar os mesmos sinais que um engenheiro inspecionaria. O case study é específico do produto. O que é transferível é a condição subjacente ao resultado: a aplicação e os seus sinais de desempenho tinham de estar disponíveis dentro do ambiente do agent.
Lopopolo, autor desse case study, mantém um field guide de harness engineering. Este identifica as duas alavancas usadas neste artigo: manter o modelo e o coding agent fixos como uma black box e fazer engenharia do contexto e das tools em torno deles. A sua formulação também explica por que razão grande parte do harness acaba por ser código normal.
O quality bar, os procedimentos, o histórico de exceções e as relações de autoridade de uma organização estão fora daquilo que um modelo geral pode conhecer. O harness expõe-nos como instruções do repositório, regras de permissões e acceptance checks. Cada run aceite pode devolver as suas lições a esses artefactos, em vez de depender da sessão seguinte para as redescobrir.
Acompanhar a alteração do desconto desde a proposta até à aceitação
Para a tarefa do desconto definida acima, o modelo propõe alterar calculate_discount em src/checkout.py. Várias coisas acontecem antes de essa edição contar como progresso:
- O context builder fornece a tarefa, as instruções do repositório, os ficheiros relevantes, os resultados anteriores das tools e o plano atual.
- O modelo propõe um call
edit_filecom um caminho e texto de substituição. - A fronteira da tool (o código do harness entre a proposta e a execução) valida os argumentos, verifica o caminho contra o âmbito permitido e pede aprovação se a operação assim o exigir.
- O runtime aplica a edição na sandbox e devolve um resultado estruturado.
- O harness executa
pytest tests/test_checkout.py, seguido depnpm playwright test tests/checkout_discount.spec.ts, e lê ambos os exit codes. O teste no browser verifica o desconto visível de $8 no carrinho seedado de $80. - O harness decide o significado dos resultados. Um check falhado transforma-se em contexto novo para o turno seguinte do modelo, e um run bem-sucedido torna a tarefa candidata a conclusão.
- Um resultado com sucesso só se torna evidência de conclusão depois de o harness registar no trace o comando, o exit code e a versão do artefacto testado.
Depois do passo 2, nenhum ficheiro foi alterado. O harness pode rejeitar ../../secrets.env, exigir aprovação para um comando destrutivo ou parar um run que tenha esgotado o orçamento. Esse é o último momento barato que tem. Depois de os testes serem executados, o harness lê os exit codes por si próprio. O modelo não pode marcar a sua própria edição como aprovada.
O trace deve mostrar o caminho e o texto de substituição propostos, a decisão de permissões, os ficheiros alterados, o commit testado e os resultados de ambos os comandos. Uma mensagem final done sem esses registos não prova que esta alteração passou os checks obrigatórios.
Decidir onde cada regra é aplicada
O requisito de que tests/checkout_discount.spec.ts passe pertence ao código determinístico, não ao prompt. O harness encaminha o comando Playwright para o runtime, lê o seu exit code e recusa terminar o run enquanto este falhar. Um prompt pode lembrar o modelo de executar o teste. Não pode impedir o modelo de declarar sucesso sem evidência.
Outras regras ajustam-se a camadas diferentes:
| Colocar a regra em | Utilização adequada | Exemplo |
|---|---|---|
| Prompt ou skill | Ordem de pesquisa, convenções de coding e formato do plano | Ler AGENTS.md antes de editar código de checkout |
| Fronteira da tool | Validação de argumentos, caminhos permitidos, aprovações e acesso a tools | Permitir escritas apenas em src/ |
| Código determinístico | Orçamentos, timeouts, retries, exit codes dos testes e gates de release | Manter o run aberto enquanto o teste Playwright falhar |
| Evaluator com fresh context | Revisão visual ou critérios que exigem avaliação semelhante à humana | Comparar um diagrama gerado com uma rubrica escrita |
Os contratos de tools separam proposta de permissão
A tarefa do desconto só precisa de edições de ficheiros e comandos de teste. Uma API que altera estado tem um modo de falha diferente, por isso mudemos os exemplos nesta secção. Suponha que o agent pode chamar create_test_order contra um serviço de orders de staging enquanto prepara os dados de teste. Esta tool não é uma das acceptance checks da tarefa do desconto. É útil aqui porque um timeout pode ocultar se o serviço criou uma order.
A fronteira da tool precisa de mais do que uma descrição em linguagem natural. Precisa de um contrato explícito de tool. A Parte 3 defendeu um do lado do modelo: ações claras, feedback compacto e erros recuperáveis. O harness precisa do mesmo contrato por uma razão diferente. Tem de decidir, sem perguntar ao modelo, se um call pode ser executado e se um call falhado pode ser repetido. Para create_test_order, isso significa um contrato com:
- argumentos validados, para que os dados de entrada malformados sejam rejeitados antes da execução
- um resultado estruturado como
{ "order_id": "123", "created": true }, para que os checks posteriores não tenham de analisar texto livre - uma categoria de efeitos que regista se o call apenas obtém informação ou altera um ficheiro, um registo numa base de dados ou um serviço externo. Regista também se é seguro repetir o call. Esta etiqueta informa o harness sobre se um retry automático pode duplicar trabalho. O harness pode repetir
get_order_statusquando o serviço define essa consulta como read-only. Não deve repetir cegamentecreate_test_order, porque o primeiro call pode já ter criado a order - uma política de timeout e retry, para que uma resposta perdida não desencadeie uma sequência ilimitada de calls
- uma regra de permissões que especifique a aprovação necessária. A leitura do estado de uma order pode ser executada automaticamente, enquanto a criação de uma order pode exigir confirmação
A descrição em linguagem natural é texto apresentado ao modelo. Poderá dizer: «Criar uma test order para verificar o checkout.» Essa frase ajuda o modelo a decidir quando propor create_test_order. Não autoriza o call. Neste exemplo, o cliente Model Context Protocol (MCP) do harness valida os argumentos, aplica as suas próprias regras e verifica a confiança no server, os requisitos de aprovação e a segurança do retry antes de encaminhar qualquer coisa. Essa verificação é a sequência ordenada deny/ask/hook/allow da Parte 4, com uma pergunta adicional: se um call que já falhou pode ser enviado novamente.
Um MCP server publica descrições de tools e anotações comportamentais opcionais para o cliente. Um server defeituoso ou malicioso poderia descrever uma tool que altera estado como inofensiva. Um cliente que aceitasse automaticamente essa afirmação poderia executar ou repetir create_test_order sem aprovação e criar um duplicado. Por isso, a especificação MCP exige que os clientes tratem as tool annotations como não fiáveis, salvo quando o próprio server é de confiança.
A especificação não prescreve uma única configuração universal de confiança, pelo que é necessária uma trust policy explícita para o seu deployment; um server não pode tornar fiáveis as suas próprias annotations. Essa política decide que metadados podem influenciar decisões de permissões ou retries e que annotations permanecem apenas consultivas.
Repetir um call que altera estado exige proteção contra replay
A Parte 5 coloca uma idempotency key em cada tool call com side effects. O harness é que decide quando essa chave tem de suportar esse peso. create_test_order cria a order, mas a resposta HTTP perde-se. O harness vê um timeout e não consegue saber se o server concluiu o pedido. Repetir o call pode criar uma segunda order.
Uma consulta de estado pode ser repetida quando o serviço a define como read-only. Um call de criação precisa da chave: o cliente anexa um identificador de pedido único e o serviço devolve o primeiro resultado, em vez de criar outra order, quando volta a ver esse identificador. Sem esta proteção, o harness deve verificar se a order existe ou pedir uma decisão humana antes de tentar novamente. A AWS documenta este padrão nas suas orientações sobre APIs idempotentes.
A aceitação precisa de evidência independente
Uma resposta bem-sucedida de create_test_order apenas estabelece que a tool devolveu dados. Não prova que uma tarefa de coding passou os testes. Se um teste posterior no browser depender da order staged, o harness tem de validar o schema da resposta e executar ainda esse teste antes de aceitar a alteração de código.
Alguns critérios não podem ser reduzidos a um exit code. Numa tarefa separada de design visual, um evaluator com fresh context pode comparar uma página ou um diagrama renderizado com uma rubrica escrita — «fresh context» significa uma segunda sessão do modelo que começa sem histórico do run e lê os artefactos produzidos, em vez do transcript. Valide esse evaluator contra revisões humanas antes de o deixar bloquear a conclusão.
Uma migração do adaptador de pagamentos precisa de um handoff
Mudemos novamente de tarefa, mas mantenhamo-nos no repositório fictício da loja. Agora, o agent tem de migrar o checkout do adaptador de pagamentos v1 para o v2. O trabalho abrange o checkout handler, o payment client, a configuração e os testes, pelo que pode ultrapassar uma sessão de modelo — um período contínuo de contexto do modelo, terminado por um restart ou por um fresh start deliberado, em vez de ser mantido.
Antes de a primeira sessão atingir o seu limite de contexto, modificou vários ficheiros, iniciou uma payment sandbox local e deixou tests/payment_migration.spec.ts a falhar. Esse teste de acceptance no browser conclui um pagamento através do adaptador v2 e verifica o provider ID registado. Um resumo da conversa pode orientar a sessão seguinte, mas não pode reiniciar a sandbox nem provar que ficheiros estão atualmente modificados.
A sessão seguinte tem de recuperar três coisas:
| O que tem de ser recuperado | O que inclui | Como pode falhar |
|---|---|---|
| Histórico da conversa | Mensagens, tool calls e resultados devolvidos | Detalhes antigos ocupam o lugar da tarefa atual |
| Ambiente de trabalho | Ficheiros, payment sandbox e estado do browser test | O transcript diz que um serviço está ativo depois de morrer |
| Progresso da tarefa | Plano, checks concluídos, aprovação pendente, ação seguinte | A sessão seguinte repete trabalho já concluído |
A compaction substitui mensagens antigas por um resumo mais curto para que a sessão atual possa continuar. Um progress handoff regista aquilo de que a sessão seguinte precisa: a branch atual, os ficheiros alterados, o último comando de teste e o respetivo output, e o próximo passo por resolver.
Um ficheiro de handoff é document memory escrito para um leitor específico, a sessão seguinte do modelo, e é um artefacto diferente do checkpoint que o runtime restaura. O checkpoint responde a onde a execução parou. O handoff responde ao significado do trabalho e ao que falta. Restaurar um checkpoint sem handoff dá à sessão seguinte um processo retomável, mas nenhuma indicação sobre qual das quatro áreas tocadas — handler, client, configuração, testes — já concluiu. É isso que produz trabalho duplicado.
Se a conversa antiga contiver pressupostos obsoletos, o harness pode iniciar uma sessão nova do modelo com esse handoff e o workspace atual. Substituir um worker que sofreu um crash e restaurar os seus processos é uma tarefa separada de recuperação do runtime.
Uma pequena edição de documentação pode não precisar de nenhum destes mecanismos. A migração de pagamentos precisa de um handoff quando o trabalho atravessa sessões, porque a sessão seguinte tem de reconstruir tanto o workspace como o estado da tarefa.
As experiências da Anthropic com coding agents de longa duração usaram histórico do git e um ficheiro de progresso entre sessões. O posterior relatório de harness design da Anthropic separa a compaction de um handoff com fresh context e indica que os handoffs acrescentam orchestration, utilização de tokens e tempo de execução, sem publicar valores que atribuam qualquer parte desse overhead especificamente ao handoff.
Usar traces para distinguir três falhas
As três linhas seguintes são exemplos ilustrativos de traces, não execuções medidas nem output do companion lab. Cada linha mostra uma falha diferente e, por isso, uma resposta diferente do harness.
| O que o trace regista | O que aconteceu | Resposta correta |
|---|---|---|
O call read-only get_order_status devolve 503; não há nenhum call que altere estado em curso | Uma consulta transitória falhou | Repetir a consulta com limites e backoff |
create_test_order excede o timeout; depois, uma consulta de estado encontra a order 123 associada à idempotency key checkout-42 | O serviço criou a order, mas a resposta perdeu-se | Devolver a order existente; não criar outra |
A edição e o teste de unidade passam, mas o trace não tem resultado para tests/checkout_discount.spec.ts no commit testado | Falta evidência de acceptance obrigatória | Manter o run aberto e encaminhar o teste de acceptance no browser |
Uma falha com aspeto transitório não torna todos os calls seguros para retry. A primeira linha é uma consulta read-only. A segunda é um pedido que altera estado, pelo que a idempotency key e o estado do lado do server determinam se é permitida outra tentativa de criação. A terceira nem sequer é uma falha de tool; o harness ainda não recolheu a evidência necessária para aceitar a alteração do desconto.
Um transcript de chat regista o que o modelo viu. Não consegue provar se o serviço de orders confirmou um pedido antes de a resposta desaparecer. O transcript é o relato dos acontecimentos feito pelo agent; o trace é aquilo que a máquina realmente fez. Quando os dois divergem, confie no trace. O trace tem de associar o call do cliente, a decisão de aprovação, a idempotency key, o resultado do server ou a consulta de estado, o commit testado e o resultado do acceptance test. Estes campos dizem ao harness em qual dos três caminhos se encontra.
| Sintoma repetido | Pequena alteração a experimentar | O que medir |
|---|---|---|
| Consultas read-only falham de forma transitória | Retry limitado com backoff | Taxa de recuperação, calls adicionais, tempo de execução |
| Sessões retomadas repetem trabalho concluído | Progress handoff estruturado | Ações de tools duplicadas após a retoma |
| Faltam testes obrigatórios na conclusão | Acceptance gate fail-closed | Tarefas aceites sem todos os checks obrigatórios |
| Defeitos visuais sobrevivem aos checks determinísticos | Evaluator com fresh context e rubrica | Defeitos encontrados, rejeições indevidas, tempo de revisão |
| O agent edita fora do âmbito | Permissões de tools mais restritas | Calls bloqueados e overrides manuais |
| A memory recuperada ocupa o lugar da tarefa atual | Limitar factos recuperados; ordenar antes de injetar | Tokens gastos no recall, tarefas concluídas, custo por tarefa |
Antes de adicionar uma componente, nomeie a falha repetida que deve reduzir e o número que irá acompanhar. Remova a componente se uma comparação controlada não alterar esse número o suficiente para compensar o seu custo. A maioria dos harnesses que conheci cresce no sentido oposto: alguém encontra um run problemático, adiciona uma proteção e essa proteção fica para sempre porque ninguém consegue provar que é seguro apagá-la. É assim que se acaba com um loop em que ninguém quer mexer.
Transformar essas falhas repetidas numa regression suite versionada é um trabalho autónomo. Escrevi sobre isso em AI Agent Evaluation in Production.
Medir uma alteração de cada vez
Uma ablation mede se uma componente do harness produz o efeito esperado, alterando-a ou removendo-a enquanto o resto da experiência permanece fixo. Por exemplo: o editor linting ajuda este modelo neste conjunto de tarefas?
Use o protocolo seguinte:
- Congele a versão do modelo, as instâncias das tarefas, o ambiente, o grader e os prompts que estejam fora da componente em teste.
- Dê a ambas as variantes o mesmo orçamento total de tokens, tempo e dinheiro.
- Escolha o número de ensaios ou a regra de paragem antes de executar a comparação.
- Execute as mesmas instâncias de tarefas em ambas as variantes. Como os outputs do modelo variam, repita cada tarefa várias vezes.
- Reporte a média juntamente com a dispersão ou o intervalo de confiança.
- Conte todos os ensaios iniciados, incluindo timeouts, paragens por política, crashes do harness e falhas do evaluator.
A taxa de sucesso, por si só, pode esconder uma componente dispendiosa. No mínimo, acompanhe tarefas quebradas aceites como concluídas, custo e tempo de execução por tarefa concluída, erros de tools, orders duplicadas, minutos de revisão e overrides manuais de permissões. Escolha a métrica que representa o custo real do seu produto. Um aumento de dois pontos nas tarefas concluídas é uma má troca se duplicar a sua fila de revisão.
Uma experiência emparelhada de migração de pagamentos torna mensurável o progress handoff. Cada par controlo/tratamento começa no mesmo commit do repositório e no mesmo checkpoint seedado, com o mesmo modelo, tarefa, grader e orçamento total. O handoff é a única variável. A métrica principal contabiliza ações de tools duplicadas após a retoma: uma ação é duplicada quando a sua operação e o seu artefacto correspondem a um passo que a sessão anterior já tinha concluído.
O artigo sobre o SWE-agent fixa o GPT-4 Turbo no split de 300 tarefas do SWE-bench Lite e reporta 18,0% de resolução com a sua interface completa, contra 11,0% para um agent apenas com shell ao qual foi dada uma demonstração trabalhada e 7,3% para o mesmo agent sem essa demonstração. A diferença principal de 10,7 pontos do artigo é medida contra o baseline de 7,3%; a Parte 3 trabalha os mesmos três números do ponto de vista do design da interface. O artigo também alterou funcionalidades individuais da interface:
| Alteração da interface | Resolvidas |
|---|---|
| Interface completa do SWE-agent (referência, inalterada) | 18,0% |
| Editor sem linting | 15,0% |
| Ficheiro completo em vez de um viewer de 100 linhas | 12,7% |
| Histórico completo de observações em vez das últimas cinco | 15,0% |
Estes números pertencem àquele modelo, benchmark e limite de $4 por tarefa. As três linhas abaixo da referência são os testes úteis de uma única funcionalidade: cada uma alterou uma funcionalidade da interface enquanto o modelo e a configuração de avaliação permaneceram fixos.
A LangChain publicou uma comparação mais abrangente com modelo fixo para deepagents-cli. Reporta um aumento no Terminal-Bench 2.0 de 52,8% para 66,5%, com gpt-5.2-codex fixo, enquanto a sua equipa alterava o system prompt, as tools e o middleware. O artigo combina várias alterações e omite um intervalo de confiança, uma comparação com orçamento total fixo e uma tabela de ablation por alteração. Esse resultado não consegue identificar qual das alterações ajudou. Os nomes dos modelos nesta secção são os que cada estudo fixou no momento em que foi executado; o que é transferível é o protocolo, não a lista de modelos.
O relatório de aplicação de longa duração da Anthropic é um case study qualitativo e específico do produto, não um benchmark controlado. A aplicação é o RetroForge, um criador de jogos retro 2D; no Sprint 3, o evaluator do harness verificou 27 critérios relativos ao seu editor de níveis. O trabalho começou com modelos Opus anteriores e, quando foi lançado o Opus 4.6, a equipa removeu componentes do harness uma a uma para verificar quais se tinham tornado redundantes com o modelo mais recente. O relatório indica que os evaluator calls passaram a ser overhead em tarefas que o Opus 4.6 conseguia concluir sozinho de forma fiável, mas continuavam a ajudar perto do limite do modelo. O exemplo é uma razão para revalidar scaffolding antigo quando o modelo muda; não estima um efeito geral.
Manter o harness editável depois de este justificar o seu lugar
A ablation mantém o harness pequeno, mas o seu código pode continuar a existir depois de o modelo para o qual foi afinado ter sido substituído. Um pedido como «mascarar secrets em todos os caminhos de captura» nomeia um comportamento, não um ficheiro. Num harness de produção, esse comportamento pode atravessar várias fases de execução e estado partilhado. Antes de o poder alterar com segurança, tem de encontrar todos os locais de implementação — e o coding agent a quem delega a tarefa também.
Um preprint de 2026 de Wang et al., o Harness Handbook, chama a esta pesquisa behavior localization. O handbook constrói um mapa centrado no comportamento a partir da codebase do harness. A análise estática, que não precisa de model calls, extrai um grafo do programa e um LLM organiza depois as suas unidades em fases de execução.
O maintainer ou coding agent começa com uma visão geral do sistema, abre a fase de execução relevante e desce até entradas fundamentadas no código-fonte relativas a uma função ou ficheiro. Um state register regista onde o estado partilhado é escrito e lido entre fases. Esta hierarquia mantém a visão geral pequena, preservando simultaneamente um caminho até ao código-fonte.
A atualidade é uma regra separada. Cada locator tem de resolver contra o repositório ativo. O handbook congela entradas obsoletas em vez de adivinhar, e cada diff não vazio ressincroniza as entradas que afeta.
O diagrama comprime o loop de modificação: um pedido apenas sobre comportamento desce pelos níveis do handbook, cada locator candidato é verificado contra o repositório ativo antes de o plano ser escrito e cada diff aplicado ressincroniza o mapa.
A avaliação do Handbook segue o protocolo defendido neste artigo. Abrange dois harnesses open source: Terminus-2 (seis ficheiros Python) e o monorepo do Codex (2 267 ficheiros Rust). Em cada um, um planner read-only baseado no DeepSeek-V4-Pro explorou diretamente o repositório ou foi encaminhado através do handbook. Os pedidos, o repositório, as permissões das tools e a decoding foram idênticos nos dois braços. Três juízes (GPT-5.5, Opus 4.8, DeepSeek-V4-Pro) atribuíram uma pontuação a cada plano de edição em localization, controlo de âmbito e reasoning — note-se que um dos juízes é o mesmo modelo que produziu os planos:
| Harness | Taxa de vitória do baseline | Assistido pelo Handbook | Tokens do planner |
|---|---|---|---|
| Terminus-2 (6 ficheiros) | 26,7% | 45,6% | −8,6% |
| Codex monorepo (2 267 ficheiros) | 28,3% | 38,3% | −12,7% |
O planner assistido pelo handbook venceu mais vezes e usou menos tokens de planner em ambos os repositórios. As condições permanecem associadas a esse resultado: três juízes LLM avaliaram planos de edição produzidos por um único modelo planner em dois harnesses. O estudo avaliou planos, não diffs executados nem taxas de defeitos em produção.
Experimentar o método no companion lab
O projeto harness-demo no commit 517353f3 é um exercício pequeno e determinístico com 12 tarefas sintéticas genéricas que cobrem alterações de código como fix-parser-edge-case, split-large-module e wire-browser-test. Não implementa o repositório fictício da loja.
Cada fixture de tarefa declara uma dificuldade e quatro condições booleanas: uma tool flaky, progresso perdido, uma lacuna de implementação não detetada e conclusão ambígua. O simulador deriva uma quinta condição para tarefas difíceis que também precisam de um ficheiro de progresso: sem context_reset, a compaction preserva pressupostos obsoletos. Um grader determinístico marca uma tarefa como passada apenas quando a configuração selecionada trata todas as condições aplicáveis. Não é executado qualquer modelo ou serviço externo.
Os comandos respondem a perguntas diferentes:
make checkexecuta Ruff e sete testes de unidade, incluindo o validator que rejeita qualquer par de ablation que altere mais do que uma componente.make runimprime uma matriz cumulativa didática e, depois, cinco comparações válidas leave-one-component-out.make failuresindica a condição não tratada para cada tarefa falhada. O harness completo deve terminar comall synthetic tasks pass.
make check
make run
make failures
A secção causal de make run tem o seguinte aspeto:
component control treatment delta
retry_policy 8/12 12/12 +4
progress_handoff 7/12 12/12 +5
evaluator 8/12 12/12 +4
fail_closed_acceptance 7/12 12/12 +5
context_reset 10/12 12/12 +2
Em cada linha, o controlo é a configuração completa com uma componente removida; o tratamento restaura apenas essa componente. A matriz cumulativa anterior é útil para orientação, mas algumas das suas linhas adjacentes adicionam várias componentes de uma vez e, por isso, não conseguem identificar uma causa.
O lab valida cada par declarado antes de o executar. Os seus regression tests incluem também um par intencionalmente inválido que altera simultaneamente a política de retry e o evaluator; o validator rejeita-o.
O lab compara os cinco campos de componentes ao validar um par. Este excerto executável mostra a mesma proteção num par válido de progress handoff:
from dataclasses import dataclass, fields
@dataclass(frozen=True)
class Config:
progress_handoff: bool = False
evaluator: bool = False
retry_policy: bool = False
fail_closed_acceptance: bool = False
context_reset: bool = False
def changed_components(control: Config, treatment: Config) -> tuple[str, ...]:
return tuple(
field.name
for field in fields(control)
if getattr(control, field.name) != getattr(treatment, field.name)
)
control = Config(progress_handoff=False, evaluator=True, retry_policy=True)
treatment = Config(progress_handoff=True, evaluator=True, retry_policy=True)
assert changed_components(control, treatment) == ("progress_handoff",)
Que camada abrir quando um run corre mal
A série avançou do interior para o exterior, e é aqui que essa ordem compensa. Um run falhado de um agent costuma ter um responsável:
| O que o run fez | Onde vive a correção | Parte |
|---|---|---|
| Escolheu um passo seguinte inadequado apesar de já ter a informação certa à frente | Reasoning loop ou modelo | 1 |
| Repetiu trabalho ou perdeu uma decisão tomada uma hora antes | Composição do contexto e handoffs | 2 |
| Não conseguiu expressar a ação necessária ou interpretou mal um resultado devolvido | Contrato da tool | 3 |
| Fez algo que nunca deveria ter podido fazer | Regras de permissões | 4 |
| Perdeu tudo quando um worker morreu a meio de um call | Sessão, checkpoint, sandbox | 5 |
| Declarou sucesso num trabalho que não estava concluído | Acceptance checks e traces | 6 |
Quatro destas seis linhas são código do harness. A linha 5 é o runtime subjacente, e a linha 1 é a única que um prompt ainda pode alterar.
Começar com um loop e uma acceptance check
Eu começaria um harness de coding agent com um modelo capaz, instruções do repositório, algumas tools restritas, uma sandbox e um teste de acceptance explícito. Registaria os tool calls, resultados, custos e esse teste final num único trace, para que as primeiras falhas úteis fossem visíveis sem ser necessário reconstruí-las a partir de logs do terminal e transcripts de chat. Esta é uma baseline proposta, não evidência de um sistema em produção.
A partir daí, adicione apenas aquilo que um trace justificar. Registe quem mantém cada componente, quantos tokens ou segundos acrescenta e qual o regression test que justificaria removê-la depois de uma atualização do modelo.
Seis meses mais tarde, alguém que veja progress_handoff=True deve conseguir encontrar os traces falhados que a justificaram e os casos de regressão que ainda a mantêm. Os traces explicam por que razão a componente existe; um mapa comportamental atual explica onde mexer.
Se chegou aqui através de uma pesquisa, os cinco artigos anteriores construíram um sistema em torno de um reasoning loop:
- O loop escolhe o passo seguinte.
- A Memory fornece contexto e um checkpoint store real em Postgres preserva-o.
- Os contratos de tools definem as ações e os formatos de resultados que os checks posteriores conseguem ler.
- A Security acrescenta o deny hook e o validator do stop-hook. Ambos continuam a ser apenas sketches no exemplo, mas assinalam os pontos de controlo.
- O runtime mantém o processo vivo entre sessões e falhas.
A série acrescentou também um MCP sidecar para mostrar onde pertencem os tokens dos data providers e um evaluator node que verifica o draft report antes de este chegar a um humano. São componentes normais de código em torno de um model call. O router é código do harness pela mesma razão: escolhe o padrão de reasoning antes de o reasoning loop começar.
O próximo passo é instrumentar um loop pequeno. Registe tool calls, resultados, custos e um teste de acceptance explícito. Acrescente apenas um controlo depois de um trace mostrar a falha que este aborda. Compare-o com um controlo fixo e remova-o quando o benefício medido desaparecer.
Referências
- OpenAI, Unrolling the Codex agent loop.
- OpenAI, Harness engineering: leveraging Codex in an agent-first world.
- Lopopolo, Harness engineering: anthology, field guide, and agent context bundle.
- Anthropic Engineering, Effective harnesses for long-running agents.
- Anthropic Engineering, Harness design for long-running application development.
- LangChain, Improving Deep Agents with harness engineering.
- Yang et al., SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering.
- Wang et al., Harness Handbook: Making Evolving Agent Harnesses Readable, Navigable, and Editable, arXiv:2607.13285, 2026.
- AWS, Making retries safe with idempotent APIs.
- Model Context Protocol, Tools specification.
- Market Analyst Agent Repository
O código do Market Analyst Agent está no GitHub.