Gate determinístico: o agente precisa testar o próprio output
Por que o componente determinístico do harness engineering é um dos mais necessários, e um dos menos valorizados.
- Data
- 08.10.2026
- Leitura
- 5 min
Exordium — o gancho
Desde a concepção do termo harness engineering, venho me perguntando: qual é a sua origem? Tenho uma ideia sobre isso. Sempre partimos da premissa de que o modelo não é capaz. Ora, é isso que permite a existência do harness engineering: não temos confiança no que o modelo produz. Todos os componentes derivam disso. Pretendo, em outro post, dissecar a genealogia componente a componente, mas isso é outro papo.
Narratio — a história
Em uma quarta-feira nublada, há cerca de 3 ou 4 meses, eu estava lidando com uma tarefa que, até então, parecia comum. E era. Trago esse acontecimento para elucidar na prática.
Em suma, a tarefa era: dada uma tela X, identificar os componentes e os dados necessários e retorná-los em uma API estruturada.
Nessa época, harness engineering era pouco comentado. Mas já existia o conceito de context engineering, que, na minha visão, é uma perspectiva parcial do que hoje tenho consolidado como harness engineering.
Enfim, depois de alimentar o meu contexto, ou melhor dizendo, o contexto para o agente, e seguindo os passos de planejamento e revisão, o resultado me parecia bem satisfatório. Exceto pela quantidade de iterações, que me irritou profundamente. Eu tinha que rodar o projeto, subir o frontend localmente e realizar os testes para descobrir que o type estava errado. Foram cerca de 7 iterações.
Minha primeira reação foi me irritar com o modelo. Porém, percebi que o problema não estava aí.
Propositio — a tese
O que faltava era eu entender que o agente é uma malha estática (resultado de uma série de cálculos em vetores) que comete erros, mesmo quando você preenche todas as lacunas. Era bem óbvio que era necessário algo para a LLM verificar se o que ela fez era de fato o que deveria ser feito. E, principalmente, estabelecer critérios fixos nos quais o humano (no caso, o desenvolvedor) pode confiar.
Ou seja: dados os critérios X e o output Y, o resultado será sempre Z. É o que hoje chamamos de gate determinístico, uma maneira determinística de testar o resultado. No meu caso, refazendo a tarefa com o gate determinístico, o agente até chegou perto do resultado, porém com muito menos iterações, não porque o modelo ficou melhor, mas porque ele tinha ganhado a capacidade de testar o próprio output.
Confirmatio — as provas
Todas aquelas iterações literalmente sumiram: de cerca de 7 iterações na primeira vez para 1 iteração já com esse mecanismo. Nesse caso, o critério foi o contrato do schema do JSON, mas isso pode se expandir para qualquer coisa que tenha um critério objetivo e quantitativo. Critérios qualitativos também são possíveis, mas geralmente exigem pensar mais nos trade-offs.
Mas vamos ao exemplo concreto.
Aviso: schema simplificado e diferente do original, por confidencialidade. Na época usei Zod; aqui reescrevi com ArkType.
Este é o contrato que a resposta precisa seguir (contract.ts):
import { type } from "arktype";
// Contrato: fonte única de verdade
const Item = type({
title: "string > 0",
type: "'horror' | 'comedia' | 'drama'",
});
export const HttpResponse = type({
total: "number >= 0",
data: Item.array(),
});
// Tipo TypeScript derivado do contrato, sem interface duplicada
export type HttpResponse = typeof HttpResponse.infer;Então construí um script (gate.ts):
import { type } from "arktype";
import { readFileSync } from "node:fs";
import { HttpResponse } from "./contract";
const path = process.argv[2];
if (!path) {
console.error("Uso: npm run verify:schema -- <arquivo.json>");
process.exit(2);
}
let raw: unknown;
try {
raw = JSON.parse(readFileSync(path, "utf-8"));
} catch (err) {
console.error(`GATE FALHOU: JSON inválido em ${path}\n${(err as Error).message}`);
process.exit(1);
}
const result = HttpResponse(raw);
if (result instanceof type.errors) {
console.error("GATE FALHOU: o output não respeita o contrato.\n");
console.error(result.summary);
process.exit(1);
}
console.log("GATE PASSOU.");
process.exit(0);É isso que o agente recebe quando o output não respeita o contrato:
GATE FALHOU: o output não respeita o contrato.
data[0].title must be non-empty
data[0].type must be "comedia", "drama" or "horror" (was 1)
total must be a number (was a string)Então coloquei uma regra para que, sempre após tarefas que necessitem de schema, seja entre serviços ou client-server, o agente usasse este comando como critério de DoD. No caso, coloquei no package.json como o comando verify:schema:
{
"scripts": {
"verify:schema": "tsx gate.ts"
}
}E a regra no AGENTS.md:
Ao terminar uma tarefa que envolva contratos, sempre utilize
`npm run verify:schema -- <arquivo.json>`, passando o arquivo com o output gerado.Um disclaimer: na época, eu não possuía esse conhecimento, mas hoje uma boa prática seria incorporar essa verificação de type dentro de hooks (gatilhos), como em um evento de commit. Porque, como sabemos, arquivos .md são, por definição, intenção, e não regras. Ou seja, em algumas execuções o agente pode simplesmente não executar os testes. Mas essa evolução cabe em um segundo post.
Refutatio — as objeções
“Mas um modelo melhor resolveria isso sozinho?” Talvez ele acertasse em uma porcentagem bem maior, mas isso não resolve o problema: reduzir as chances não é extingui-las. É aí que nasce o poder do gate determinístico. Todas as vezes que o input for o mesmo, o resultado será o mesmo. Com isso, temos a confiabilidade necessária.
“Ah, mas isso é só teste automatizado com nome novo.” De fato, existe uma certa verdade nisso, mas há uma distinção que acho relevante explicitar aqui. O mecanismo pode ser o mesmo; o que muda é para quem ele é escrito e quando ele roda. O teste tradicional é escrito para um humano, que lê o resultado no CI depois que o trabalho está pronto. O gate é escrito para o agente, que o executa dentro do próprio loop, enquanto ainda está trabalhando. Isso muda como ele é construído: a mensagem de erro precisa dizer exatamente o que está errado e onde, para que o agente consiga se corrigir sozinho, como na saída que mostrei acima.
E, claro, existe um limite: um JSON pode passar no schema e ainda estar mal construído, ja que o json é no fim: um artefato gerado por LLM, ou ter problemas semânticos que vão além do schema em si.
Peroratio — o fechamento
Dado isso, podemos concluir que o componente determinístico do harness engineering é extremamente necessário e importante, e talvez um dos menos valorizados por engenheiros em geral, porque parece ser algo PRÉ-AI. E isso é, na verdade, muito mais importante agora, na era dos fluxos agênticos: percebemos que os fundamentos e as práticas de antes exercem uma nova função em um sistema maior.