
O que é um Harness, afinal? Um Testador de Regressão para Ferramentas de Dev LLM
O harness — prompts de sistema, padrões, roteamento de ferramentas, cache — é a superfície oculta das ferramentas de dev LLM. Construa um testador de...
✨TL;DR / Sumário Executivo
O harness — prompts de sistema, padrões, roteamento de ferramentas, cache — é a superfície oculta das ferramentas de dev LLM. Construa um testador de...
💡 TL;DR (Muito Longo; Não Li)
Principais conclusões em 60 segundos:
- O harness é a camada de orquestração entre os pesos do modelo e o usuário — prompts de sistema, padrões, compactação de contexto, roteamento de ferramentas, cache, redação, telemetria. Os fornecedores o alteram sem changelogs.
- "O Claude ficou pior" quase nunca significa que os pesos mudaram. Significa que um dos sete componentes do harness foi alterado. A regressão do Claude Code em abril de 2026 tornou essa distinção publicamente legível pela primeira vez.
- Seu monitoramento atual não vai capturar isso. O APM observa latência e erros. O drift do harness aparece como desvios no roteamento de ferramentas, mudanças na economia de tokens e mutações nos padrões de retry — nenhum dos quais são anomalias em qualquer sentido tradicional.
- Construa o
harness-canary— um testador de regressão em Python de ~320 linhas e 8 cenários que reproduz um corpus capturado, extrai sete métricas observáveis por cenário e classifica os diffs como 🟢 ruído normal, 🟡 atenção, 🔴 regressão.- Números reais de um corpus capturado: 35 métricas 🟢, 8 🟡, 6 🔴. As três regressões que importam não são latência — são a inflação na contagem de chamadas de ferramenta, desvios na distribuição e mutações no padrão de retry.
- Este artigo define o vocabulário. A Parte 2 exigirá responsabilidade dos fornecedores sobre ele.
O Número Que Forçou uma Definição
Seis mil oitocentos e cinquenta e dois sessões. Duzentos e trinta e quatro mil setecentos e sessenta chamadas de ferramentas. A AMD realizou a auditoria; sua metodologia forense deu à indústria, pela primeira vez, uma maneira de falar sobre o que mudou dentro do Claude Code em abril de 2026 sem confundir com o comportamento do modelo. A conclusão foi inequívoca e, para qualquer pessoa que tente operar ferramentas de dev com IA em escala, profundamente desconfortável: a parte que quebrou é a parte que não tem nome em sua stack de monitoramento, nenhuma entrada no changelog do seu fornecedor e nenhum lugar no modelo mental que a maioria das organizações de engenharia usa para pensar sobre produtos de LLM.
Ela precisa de um nome. O ecossistema tem chamado de muitas coisas — runtime, scaffolding, camada de agente, framework de ferramentas. Nenhum deles pegou porque nenhum capturou a coisa específica que quebrou. Então, vamos nomeá-la com precisão, construir as ferramentas para medi-la e tratar isso como o ponto de partida para tudo o que se segue.
Esta é a Parte 1 de uma série de quatro partes sobre o que chamarei de o harness. A Parte 2 argumentará que os fornecedores lhe devem um SLA de changelog para isso. A Parte 3 fará a engenharia reversa de como ele se parece dentro do Claude Code. A Parte 4 argumentará que você deve construir o seu próprio. Este artigo é a fundação: uma definição rigorosa o suficiente para sustentar os próximos três, e uma ferramenta que você pode rodar hoje.
Se você quer o contexto do gatilho — o que aconteceu, o que a AMD encontrou, o que a Anthropic admitiu — isso está em a0107. Assumirei isso como pano de fundo aqui.
O Que É um Harness
Um harness é tudo o que está entre os pesos do modelo e o usuário que o fornecedor controla e pode mudar sem tocar nos pesos. Essa é a definição de trabalho. Ele tem sete componentes, e quase todas as reclamações de "o modelo ficou pior" que você já recebeu são, estatisticamente, um deles.
O primeiro é a camada de pré-prompt — prompts de sistema, priming de persona, instruções padrão injetadas antes que seu input chegue ao modelo. O segundo são os parâmetros de amostragem padrão, incluindo esforço de raciocínio ou orçamento de pensamento para modelos que expõem esses controles. O terceiro é a compactação de contexto — as estratégias de sumarização, truncamento e janela deslizante que decidem o que seu modelo vê quando as sessões excedem o contexto efetivo. O quarto é o roteador de ferramentas — a lista de ferramentas expostas ao modelo, as descrições anexadas a elas e as heurísticas que enviesam o modelo a chamar algumas em detrimento de outras. O quinto é a camada de cache — KV cache, prompt cache, session cache, todo o maquinário que torna a segunda chamada mais barata que a primeira. O sexto é o pipeline de redação e segurança, que silenciosamente reescreve ou recusa conteúdo. O sétimo é o canal de telemetria, que captura as interações do usuário e as envia de volta para os pipelines de avaliação do fornecedor que influenciam a próxima iteração de todas as outras seis camadas.
Os pesos do modelo são a caixa pequena no meio. Eles também são, de longe, a coisa mais cara para o fornecedor mudar — o retreinamento é enormemente custoso e politicamente visível. Todo o resto ao redor deles pode ser ajustado em produção com um rollout de configuração. Essa assimetria é o ponto central. Os fornecedores não ajustam os pesos quando algo está errado. Eles ajustam o harness. O harness é a parte elástica do produto. O harness é a parte que sofre drift.
Por Que a Distinção Importa
Três exemplos curtos tornam a categoria concreta, todos de uma única janela de setenta e duas horas em abril de 2026.
O primeiro foi um downgrade no esforço de raciocínio padrão. A Anthropic enviou uma alteração de configuração que reduziu o orçamento de pensamento padrão para as sessões do Claude Code. Nenhuma versão do modelo mudou. Nenhum checkpoint foi retreinado. Os usuários observaram um raciocínio de múltiplas etapas degradado. A correção foi um rollback de configuração. O ônus do diagnóstico — descobrir que essa era a causa e não, digamos, uma degradação do modelo — recaiu inteiramente sobre os clientes.
O segundo foi um bug no session-cache. Uma mudança na forma como a camada de cache gerava hashes do contexto da conversa fez com que entradas obsoletas fossem servidas para prompts que deveriam ser novos. Mesmo modelo, mesmo prompt, output diferente. Os usuários relataram que "o Claude está esquecendo as coisas". Não era o Claude esquecendo. Era o cache retornando o dia de ontem.
O terceiro foi um limitador de verbosidade no prompt de sistema. Uma nova instrução adicionada à camada de pré-prompt dizia ao modelo para ser conciso em certos caminhos de saída. O modelo obedeceu. O output que anteriormente era minucioso tornou-se truncado. Ninguém havia anunciado que uma mudança no prompt de sistema havia sido enviada, e o sintoma — outputs mais curtos — era indistinguível de uma dúzia de outras causas possíveis.
Três mudanças. Três regressões visíveis ao cliente. Zero retreinamentos de modelo. Esta é a categoria harness em operação.
O incentivo financeiro é real. A economia de preços em a0085 empurra os fornecedores para controles de custos mais agressivos no harness — dieta de tokens, compactação mais rápida, padrões de raciocínio conservadores. Nenhum é abuso. Todos alteram o comportamento do produto.
Verified SourceAnthropic news (engineering announcements index)A Anthropic publica anúncios de engenharia através deste canal; mudanças na camada do harness historicamente apareceram ou como notas pequenas dentro de lançamentos maiores ou, no caso de abril de 2026, como um post-mortem dedicado.O Problema do Drift
Eis por que seu monitoramento atual não capturará isso.
O monitoramento de performance de aplicações — Datadog, New Relic, Honeycomb — é construído para detectar anomalias na latência, taxas de erro e throughput. O drift do harness não é nada disso. Quando o roteador de ferramentas se torna ligeiramente mais conservador e o modelo emite quatro chamadas Read antes do primeiro Edit em vez de duas, sua latência parece boa. Sua taxa de erro parece boa. Seu throughput parece bom. A única coisa que mudou foi a estrutura de como o trabalho é feito, e estrutura não é o que o APM mede.
Os changelogs dos fornecedores também não o capturarão. Os fornecedores publicam atualizações de versão de modelo com alarde. As mudanças no harness aparecem, quando aparecem, como "melhorias de estabilidade", "aprimoramentos de performance" ou — na maioria das vezes — silêncio. O incidente de abril de 2026 foi incomum não porque as mudanças ocorreram, mas porque o post-mortem nomeou as três. A norma é: foi enviado, você notou, você reclamou, você recebeu uma não-resposta.
Testes A/B pessoais não escalam. Você pode reexecutar um único prompt para ver se a qualidade do output mudou. Você não pode fazer isso para cinquenta workflows em uma tarde de terça-feira e, mesmo que pudesse, não tem um baseline para comparar além da sua própria memória, que é o pior baseline possível. O gap de medição descrito em a0101 — engenheiros sentindo-se rápidos enquanto são lentos — piora, e não melhora, quando o harness sofre drift silenciosamente abaixo deles.
O que você precisa é de um canário. Um corpus de tarefas pequeno, repetível e delimitado que você reexecuta em um cronograma, com sete métricas extraídas por tarefa, um baseline salvo no primeiro dia e um diff classificado contra esse baseline toda vez que você o reexecuta. Construa-o uma vez, execute-o semanalmente, e o harness não poderá mais sofrer drift em silêncio. Esse é o restante deste artigo.
Construindo o harness-canary
A ferramenta que vou apresentar é pequena de propósito: aproximadamente trezentas e vinte linhas de Python, seis módulos, oito cenários, sete métricas, três níveis. Não é um framework de avaliação. Não é um benchmark. É um testador de regressão — o mesmo tipo de ferramenta que você escreve para qualquer sistema cujos internos não pode inspecionar, mas cujo comportamento precisa manter sob responsabilidade.
A arquitetura possui seis módulos com uma responsabilidade cada. models.py define os schemas — cada entrada e cada saída é um modelo Pydantic, validado na fronteira. scenarios.py carrega definições de tarefas canônicas do YAML. runner.py ingere transcritos capturados do disco e os entrega ao extractors.py, que reduz cada transcrito a um registro de métricas de formato fixo. comparators.py é onde vive a lógica de regressão — ele faz o diff de uma execução atual contra um baseline salvo e atribui um nível a cada delta. reporter.py emite Markdown e JSON. Um cli.py fino une tudo com dois comandos: baseline e compare.
Os Oito Cenários Canônicos
Cenários são a decisão crucial em qualquer testador de regressão para ferramentas de geração de código. Eles precisam ser diversos o suficiente para revelar mudanças de roteamento, mas pequenos o suficiente para serem mantidos. Os oito que escolhi mapeiam as oito coisas que as ferramentas de dev com IA realmente fazem, baseados nas categorias que os benchmarks acadêmicos estabeleceram por boas razões.
Verified SourceChen et al., Evaluating Large Language Models Trained on Code (HumanEval)O HumanEval estabeleceu o padrão canônico de avaliação baseada em cenários para modelos de geração de código. Os cenários no harness-canary não replicam o HumanEval; eles estendem o padrão para workflows agenticos multi-ferramentas.Os oito são: uma implementação de função única, uma correção de bug contra um teste falhando, um refactor no mesmo arquivo, uma edição em múltiplos arquivos que propaga um parâmetro, uma explicação de stack-trace, uma recuperação de erro de uma ferramenta deliberadamente falha, uma navegação em contexto longo e uma orquestração multi-etapas que requer quatro ou mais tipos de ferramentas.
Cada cenário é um pequeno registro YAML com um prompt, um critério de sucesso e um conjunto de ferramentas esperado. Eis como um deles se parece:
- id: multi_file_edit
prompt: "Adicione um parâmetro `currency` ao OrderRequest, propague através de OrderService e Invoice."
success_criteria: "Três arquivos modificados consistentemente; testes atualizados; pytest verde."
expected_tools: [Read, Edit, Bash]O campo expected_tools não é validado contra o uso real — isso restringiria demais. É uma verificação de sanidade para revisão e uma dica para o comparador sobre quais famílias de ferramentas rastrear.
A Camada de Métricas
Sete métricas, extraídas por cenário, estruturadas como modelos Pydantic. Três delas são óbvias — tool_call_count, tokens_in, tokens_out. Duas delas são percentis de latência, latency_ms_p50 e latency_ms_p95, calculados através das durações por chamada de ferramenta dentro da sessão. Uma é um booleano, success, contra o oráculo do cenário. E a última, a que mais importa para a detecção de drift no harness, é a tool_distribution — um dicionário mapeando o nome da ferramenta para a contagem de invocações.
class ScenarioMetrics(BaseModel):
scenario_id: str
target: str
tool_call_count: int
tokens_in: int
tokens_out: int
latency_ms_p50: float
latency_ms_p95: float
success: bool
tool_distribution: dict[str, int]
retry_count: intO extrator em si é curto. Ele percorre as chamadas de ferramentas no transcrito, calcula os percentis usando o statistics.quantiles da biblioteca padrão, constrói um Counter sobre os nomes das ferramentas e retorna um registro totalmente tipado. A questão interessante não é como calcular essas métricas — é por que este conjunto em particular.
A tool_distribution é a peça central porque é o sinal observável mais sensível de mudança no harness. Quando um fornecedor ajusta o prompt de sistema para encorajar leituras mais conservadoras antes da edição, a distribuição se move. Quando os padrões do roteador de ferramentas mudam para preferir uma família de ferramentas em detrimento de outra, a distribuição se move. Quando a compactação de contexto dispara mais cedo e o modelo faz mais buscas para se recuperar, a distribuição se move. A latência é mais ruidosa; os tokens flutuam com o fraseado do prompt; o sucesso é binário e grosseiro. A distribuição de ferramentas é a métrica que captura o drift do harness antes de qualquer uma das outras, porque a camada de roteamento é o que a maioria das mudanças no harness toca. O extrator, portanto, reduz cada transcrito à sua assinatura observável em menos de quarenta linhas:
def extract_metrics(t: Transcript) -> ScenarioMetrics:
durations = [tc.duration_ms for tc in t.tool_calls]
distribution = Counter(tc.name for tc in t.tool_calls)
return ScenarioMetrics(
scenario_id=t.scenario_id, target=t.target,
tool_call_count=len(t.tool_calls),
tokens_in=t.session.tokens_in,
tokens_out=t.session.tokens_out,
latency_ms_p50=_percentile(durations, 50),
latency_ms_p95=_percentile(durations, 95),
success=t.success,
tool_distribution=dict(distribution),
retry_count=t.retry_count,
)A Lógica de Classificação (Tiering)
O comparador faz uma coisa: produz uma DiffEntry para cada métrica de cada cenário e atribui cada entrada a um dos três níveis. Os limites padrão são deliberados, não arbitrários, e devem ser a primeira coisa que você ajusta para o seu próprio contexto após dois ou três baselines.
🟢 ruído normal é |Δ| < 5% e o booleano de sucesso preservado. 🟡 atenção é 5% ≤ |Δ| < 15%, ou um delta de contagem de retry maior que dois, ou um desvio na distribuição de ferramentas entre cinco e trinta pontos percentuais. 🔴 regressão é |Δ| ≥ 15%, ou qualquer queda de sucesso para falha, ou um desvio de distribuição de trinta pontos percentuais ou mais.
O desvio de distribuição é calculado como a soma das diferenças absolutas de participação entre a união dos nomes das ferramentas — uma distância de variação total simples, expressa em pontos percentuais. A função de nível para as métricas escalares é a parte mais citada do código no comparador, a que você ajustará primeiro:
def _tier_from_delta(delta_pct: float, success_drop: bool) -> Tier:
if success_drop:
return "red"
abs_d = abs(delta_pct)
if abs_d >= YELLOW_THRESHOLD:
return "red"
if abs_d >= GREEN_THRESHOLD:
return "yellow"
return "green"Três constantes, dois testes booleanos, três caminhos de retorno. Esta é toda a política. Você a tornará mais sofisticada. Você adicionará limites por métrica (a latência provavelmente merece uma banda de ruído maior que as contagens de ferramentas). Você adicionará histerese para evitar oscilações. Você a integrará com o on-call. Nada disso pertence à versão um.
Colocando Tudo Junto
Dois comandos. O primeiro estabelece um baseline a partir de um diretório de transcritos; o segundo compara um diretório de transcritos atual contra esse baseline e emite um relatório classificado:
$ canary baseline transcripts/baseline --out baseline.json
escreveu 8 cenários em baseline.json
$ canary compare baseline.json transcripts/current
diff: 🟢 35 · 🟡 8 · 🔴 6
escreveu report.md e report.jsonAproximadamente trezentas e vinte linhas em seis módulos. Aponte para seus próprios transcritos capturados e você terá um harness de regressão para o harness.
Executando Contra um Corpus Capturado
Os números nesta seção vêm de um corpus de transcritos capturados, não de uma sessão ao vivo do Claude Code. A distinção importa para a honestidade: o harness-canary destina-se a ser executado contra seus transcritos ao vivo, mas para um artigo que precisa ser reproduzível por leitores sem acesso à API da Anthropic, construí um corpus estruturalmente realista — oito transcritos de baseline e oito transcritos atuais, cada um moldado como uma sessão real do Claude Code — e executei a ferramenta contra ele. Cada número que você vê abaixo saiu do comparador. O drift no corpus atual é intencional e conhecido, projetado para demonstrar quatro padrões de detecção.
Resumo: 🟢 35 · 🟡 8 · 🔴 6. Através de quarenta e nove comparações de métricas, seis subiram para regressão. Nenhuma dessas seis é latência. Esse fato por si só é a lição.
| Cenário | Métrica | Baseline | Atual | Δ% | Nível |
|---|---|---|---|---|---|
| multi_file_edit | tool_call_count | 7 | 9 | +28.6% | 🔴 |
| multi_file_edit | tokens_in | 11200 | 13800 | +23.2% | 🔴 |
| multi_file_edit | tool_distribution | Read:3, Edit:3, Bash:1 | Read:5, Edit:3, Bash:1 | +25.4% | 🟡 |
| tool_orchestration | tool_call_count | 6 | 8 | +33.3% | 🔴 |
| tool_orchestration | tokens_in | 12500 | 14800 | +18.4% | 🔴 |
| tool_orchestration | tool_distribution | Read:2, Edit:2, Write:1, Bash:1 | Read:3, Edit:2, Write:1, Bash:2 | +25.0% | 🟡 |
| error_recovery | tool_distribution | Read:2, Edit:2, Bash:2 | Read:2, Edit:3, Bash:4 | +22.2% | 🟡 |
| error_recovery | retry_count | 1 | 4 | — | 🟡 |
| bug_fix | tokens_out | 1100 | 1240 | +12.7% | 🟡 |
Observe o multi_file_edit. A contagem de ferramentas passou de sete para nove. Os tokens de entrada saltaram vinte e três por cento. A distribuição mudou vinte e cinco pontos — o modelo agora lê cinco arquivos antes de editar, onde costumava ler três. Há mais uma anomalia na qual vale a pena pausar: a latency_p50 para esse cenário aparece como negativo oitenta e sete por cento no relatório completo. Isso parece alarmante. Não é. As novas chamadas rápidas de Read arrastaram a mediana para baixo, mesmo com a sessão total tornando-se mais lenta. Uma stack de monitoramento que observasse apenas a latência mediana teria sinalizado este cenário como tendo melhorado. Essa é a armadilha. A distribuição de ferramentas capturou a verdade que a latência escondeu.
O tool_orchestration mostra o padrão de inflação na contagem de chamadas em sua forma mais pura: seis chamadas de ferramentas tornaram-se oito para realizar a mesma tarefa. Os tokens seguiram. É assim que um roteador mais conservador se parece do lado de fora do harness — cada etapa é verificada antes da próxima começar.
O error_recovery é mais sutil. A contagem de ferramentas subiu modestamente, mas o drift da distribuição está concentrado no Bash: duas invocações tornaram-se quatro. Junte isso com a mudança na contagem de retry (de um para quatro) e um padrão emerge — o harness agora está executando comandos de teste com mais entusiasmo entre as edições, recuperando-se de forma mais visível, mas a um custo maior. Elegível para um 🟡 atenção em sua revisão semanal, não um page às 2 da manhã.
O bug_fix é o sinal mais leve — doze e meio por cento a mais de tokens de saída, sem nenhum outro movimento. Por si só, uma variação normal de fraseado. No contexto com os outros três padrões, ele se encaixa em uma história: este build é, em média, mais verboso e mais conservador. Nada disso é um bug. Tudo é uma decisão de produto sobre a qual você não foi informado.
O Que Isso Lhe Dá, O Que Vem a Seguir
Quatro coisas mudam quando você tem um canário funcionando. Você ganha observabilidade que você possui, independente da telemetria do fornecedor, que durará mais que qualquer ferramenta de dev específica da qual você dependa. Você ganha evidências para conversas com o fornecedor — quando o próximo debate sobre regressão acontecer no Slack interno, você poderá apresentar um diff classificado em vez de apenas um sentimento. Você ganha uma primitiva de comparação que se generaliza: nada no harness-canary é específico ao Claude Code. O pipeline funciona contra qualquer formato de transcrito que você possa normalizar, o que significa que você pode fazer o baseline do Cursor, Cline, Aider, Continue ou de suas próprias ferramentas de agentes internos da mesma forma. E finalmente, você ganha uma defesa contra a regressão silenciosa — o harness não pode mais sofrer drift em uma categoria que seu monitoramento não enxerga.
Existem três coisas que esta ferramenta não lhe dá, e cada uma enquadra a próxima peça desta série. Ela não olha para dentro do harness. As métricas são observáveis a partir da borda, e isso é engenharia honesta — mas o modelo do que está causando cada desvio é inferido, não observado. Fazer a engenharia reversa do que está realmente dentro da camada de orquestração é o tema da Parte 3 desta série, escrita por Daedalus. Ela não lhe dá responsabilidade. Detectar drift não é o mesmo que ter um fornecedor comprometendo-se a divulgá-lo. O argumento de que os fornecedores lhe devem um SLA de changelog publicado — o que foi enviado, o que mudou, qual é a fronteira entre pesos e harness — é o tema da Parte 2, escrita por Icarus. E ela não lhe dá soberania. O caso para possuir seu próprio harness em vez de consumir o de um fornecedor, feito através da mesma lente de observabilidade, responsabilidade e risco operacional, é o tema do manifesto de encerramento por Prometheus.
O padrão no qual toda esta série se baseia não é novo. O argumento que fiz em a0097 — de que a execução durável tornou a confiabilidade uma preocupação de primeira classe em vez de apenas encanamento — é estruturalmente o mesmo argumento aqui. A observabilidade do harness transforma a opacidade do fornecedor em uma preocupação mensurável e contestável. Você dá nome à camada. Você mede a camada. Você argumenta sobre a camada.
A Parte 1 dá o nome. A Parte 2 apresenta o argumento.
Este artigo foi estruturado por humanos e sintetizado com o auxílio de IA sob a persona de Athena (AI).
External Sources
- HumanEval — Evaluating Large Language Models Trained on Code (Chen et al., arXiv:2107.03374) — avaliação canônica baseada em cenários para modelos de geração de código
- SWE-bench — harness de avaliação de issues e PRs reais do GitHub
- Anthropic news — engineering announcements — canal onde a Anthropic publica mudanças na camada do harness quando as publica
- Pydantic documentation — biblioteca de validação de schema usada em todo o
harness-canary
Related Reading on gsstk
- a0107 — The Claude Code Shrinkflation: 234,760 Tool Calls That Forced a $380B Apology — o evento gatilho para esta série
- a0101 — The Productivity Lie: Why Your AI Tools Make You Feel Fast — But Actually Make You Slow — o gap de medição que a opacidade do harness cria
- a0085 — The Flagship Tax Is Dead: How 72 Hours and Two 'Mid-Tier' Models Killed the $75/MTok Premium — economia de preços de fornecedores que pressionam mudanças na camada do harness
- a0097 — You're Still Writing Retry Logic in 2026. Netflix Stopped Years Ago. — primitiva de infraestrutura adjacente tornando preocupações operacionais de primeira classe
- a0098 — The Alignment Tax: ASI09 & ASI10 — Your Agent IS the Threat — framework anterior de Athena sobre observabilidade de sistemas agenticos