Um agente de IA é um modelo de linguagem que trabalha em loop. Ele lê a tarefa, usa uma ferramenta como "leia este arquivo" ou "apague aquele arquivo", analisa o resultado e continua até terminar o trabalho. Cada vez que o modelo pede para usar uma ferramenta, essa solicitação é chamada de tool call (chamada de ferramenta).
O código que executa esse loop é chamado de harness. O modelo decide o que quer fazer. O harness executa a ação e também define o que o modelo tem permissão para fazer.
Um bom harness toma várias pequenas decisões ao longo do caminho. Qual modelo deve processar esta solicitação? Este tool call é seguro para executar? Esta resposta é boa o suficiente para ser entregue? A maioria dos harnesses responde a essas perguntas consultando um modelo de chat e lendo sua resposta. Isso custa uma chamada completa de modelo a cada vez, então, na prática, a maioria das verificações acaba sendo ignorada.
Jev, da TypeSafe AI, é um modelo pequeno criado exclusivamente para essas decisões. Você descreve a situação, faz algumas perguntas e ele responde a cada uma com um número. Ele nunca gera texto.
Isso faz mais diferença quando você cria um harness personalizado, ou seja, seu próprio loop de agente em vez de usar uma solução pronta de mercado. Um harness personalizado permite escolher quais modelos serão executados, o que o agente pode acessar e o que conta como "trabalho concluído". O Jev torna as verificações por trás dessas escolhas baratas o suficiente para rodar em cada etapa.
Neste tutorial, você vai construir um harness com o Pi SDK, um kit de ferramentas em TypeScript para criar agentes, e usar o Jev em três pontos. No final, você vai executar o harness pronto em um sandbox ao vivo e alterar as configurações por conta própria.
Acesse o tutorial interativo completo e o playground aqui:
https://academy.dair.ai/resources/jev-decisions-in-a-pi-sdk-harness
Este guia foi inspirado no artigo Building a Harness with Jev, de Sydney Runkle, publicado no blog da LangChain, que apresenta o roteamento de modelos e o controle de ferramentas como middlewares prontos para o LangChain. Aqui, você mesmo vai implementar esses mesmos conceitos no Pi SDK e depois adicionar dois padrões extras para lidar com falhas e verificar respostas.
O que você vai construir
O harness tem três partes. Cada uma faz uma pergunta diferente ao Jev em momentos distintos, e o restante deste guia se refere a elas por estes nomes.

O exemplo usado ao longo do texto é um agente trabalhando em uma pasta com anotações de levantamentos de trilhas, um arquivo por saída. Ele pode ler, escrever e realmente apagar essas anotações — e é por isso que o gate (portão de segurança) é tão importante.
O que é o Jev
A TypeSafe chama o Jev de modelo System One (Sistema Um). O nome vem do psicólogo Daniel Kahneman, que descreveu dois modos de pensar. O Sistema Um é rápido e automático, como saber que uma panela está quente. O Sistema Dois é lento e deliberado, como fazer uma divisão longa.
Neste harness, um modelo de linguagem comum faz o trabalho pesado de ler arquivos e escrever respostas. O Jev fica responsável pelos julgamentos rápidos ao redor disso. O Jev é barato e rápido o suficiente para ser consultado sobre cada tool call, não apenas sobre aqueles que você já esperava serem arriscados.

Cada número é uma probabilidade entre 0 e 1. Um 0,83 significa que o Jev tem bastante certeza de que a resposta é sim. Um 0,03 significa que ele tem bastante certeza de que a resposta é não.
Três tipos de perguntas
Toda requisição ao Jev tem duas partes. O state (estado) é a situação que você quer avaliar, como um tool call ou a solicitação de um usuário. As questions (perguntas) são o que você quer saber sobre aquilo. O Jev responde todas as perguntas em uma única chamada, ao mesmo tempo, então fazer três perguntas leva praticamente o mesmo tempo que fazer uma.
O Jev suporta três tipos de perguntas. A primeira, que o Jev chama de noul, é uma pergunta de sim ou não.

O Jev só sabe o que você diz a ele, então descreva cada opção e cada nível em palavras simples. As descrições são o prompt.
Configuração
O Jev está disponível pelo OpenRouter, um serviço que oferece acesso a vários modelos de IA com uma única chave de API. Essa mesma chave cobre tanto o modelo de linguagem quanto o Jev. Instale os dois pacotes do Pi e configure sua chave.
1npm install @earendil-works/pi-agent-core @earendil-works/pi-ai
1OPENROUTER_API_KEY=...
O Jev tem seu próprio endereço web, separado do endpoint usual de chat. Sempre especifique uma versão exata, como typesafe/jev-1.13. O cliente Jev inteiro é apenas uma chamada fetch.
1const JEV = "typesafe/jev-1.13";23async function ask(state: unknown, questions: unknown) {4 const response = await fetch("https://openrouter.ai/api/alpha/decisions", {5 method: "POST",6 headers: {7 "Content-Type": "application/json",8 Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,9 },10 body: JSON.stringify({ model: JEV, state, questions }),11 signal: AbortSignal.timeout(2_000),12 });13 if (!response.ok) throw new Error(await response.text());14 return response.json();15}
O agente fica esperando enquanto o Jev responde, então a chamada é cancelada após dois segundos. Uma resposta normal leva de 200 a 400 milissegundos.
Onde o Jev entra
A classe Agent do Pi executa o loop para você. Ela permite que seu próprio código rode em momentos específicos desse loop. Esses pontos são chamados de hooks. O harness usa um hook para cada uma de suas três partes.

Um primeiro gate
Comece com a versão mais simples e útil do gate. Antes de cada tool call, faça uma pergunta de sim ou não ao Jev e bloqueie a chamada se a resposta parecer ser sim.
1import { Agent } from "@earendil-works/pi-agent-core";23const agent = new Agent({4 initialState: { systemPrompt, model, tools },5 streamFn: models.streamSimple.bind(models),67 beforeToolCall: async ({ toolCall, args }) => {8 const { answers } = await ask(9 { tool: toolCall.name, arguments: args },10 {11 destructive: {12 type: "noul",13 instructions: "This tool call destroys or overwrites data that was not created by this run.",14 criteria: {15 true: "Deletes files, truncates or overwrites existing content, drops data, or force-pushes over history.",16 false: "Reads, lists, creates a new file, or appends to a file this run already created.",17 },18 },19 },20 );2122 if (answers.destructive.noul >= 0.65) {23 return { block: true, reason: "Blocked: this looks destructive.", terminate: true };24 }25 },26});
O 0,65 é um threshold (limiar), o ponto de corte onde o harness deixa de confiar em uma chamada. Qualquer coisa que o Jev pontue nesse valor ou acima é bloqueada.
Agora, um agente que tente apagar suas anotações será interrompido antes que a exclusão aconteça. Apagar uma anotação pontua cerca de 0,83, e ler uma pontua 0,01. O gate, porém, é bruto. Criar um arquivo totalmente novo pontua cerca de 0,70, então isso também acaba sendo bloqueado.
Fazer a consulta custa muito pouco. O Jev cobra US$ 0,042 por milhão de tokens de entrada, menos de um terço do preço de entrada do GLM 5.3 Flash, o mais barato dos dois modelos que este harness utiliza.
Do primeiro gate ao harness completo
O primeiro gate funciona, mas deixa quatro lacunas.
- O threshold fica escondido dentro do hook, o que dificulta ajustes e testes.
- Toda requisição roda no mesmo modelo, seja ela fácil ou difícil.
- Não há nada definindo o que acontece se o Jev ficar inacessível.
- Nada verifica se a resposta final é realmente boa.
Cada seção numerada abaixo resolve uma dessas lacunas.
1. Mantenha os thresholds em um só lugar
Esta seção aprimora o gate.
Os thresholds decidem o que o agente pode fazer, e você vai ajustá-los com frequência assim que começar a ver resultados reais. Por isso, tire-os do hook e coloque-os em uma função simples, decideGate(). Ela recebe os números do Jev e devolve um verdict (veredito): a decisão final do harness sobre uma chamada. Manter essas regras centralizadas é o que chamamos de policy (política).
A política também adiciona uma opção intermediária. Um único threshold só pode dizer "permitir" ou "bloquear". Dois thresholds geram três vereditos.
- Em blockAt ou acima, a chamada é bloqueada.
- Abaixo de askAt, a chamada é executada.
- Entre os dois, a chamada aguarda a aprovação de uma pessoa.
Essa faixa intermediária captura as chamadas sobre as quais o Jev tem dúvida, e que um único ponto de corte classificaria errado de um jeito ou de outro.

1export function decideGate(signals: GateSignals, policy = DEFAULT_GATE_POLICY): GateVerdict {2 const worst = Math.max(3 signals.destructive.noul,4 signals.irreversible.noul,5 signals.outsideWorkspace.noul,6 );7 if (worst >= policy.blockAt) return { action: "block", ... };8 if (worst >= policy.askAt) return { action: "ask", ... };9 return { action: "allow", ... };10}
Por ser uma função simples, você pode testá-la passando números como os do exemplo acima, sem precisar envolver um modelo ao vivo.
2. Escolha um modelo por requisição
Esta seção adiciona o roteador.
Algumas requisições são fáceis, como ler um único arquivo. Outras são difíceis, como investigar por que algo quebrou. Rodar tudo no modelo mais poderoso desperdiça dinheiro, e rodar tudo em um modelo barato gera respostas fracas para as requisições complexas. O roteador associa cada requisição ao tier (nível) certo, ou seja, ao modelo rápido e barato ou ao modelo poderoso e caro.
Antes de uma requisição começar, o roteador faz duas perguntas ao Jev em uma única chamada. Uma escolha seleciona o tier, e uma pontuação avalia a complexidade da requisição.

1const ROUTER_QUESTIONS = {2 tier: choice("Which model tier should handle this request?", {3 fast: "Reading one file, pulling a fact out of it, or a small edit in a single place.",4 powerful: "Work that spans several files, or a failure with no obvious cause.",5 }),6 complexity: score("How much reasoning does this request need?", [7 "Mechanical. One step, no judgement.",8 "Localized. A few steps inside one area.",9 "Architectural. Many moving parts or an unknown root cause.",10 ]),11};
A política do roteador usa as duas respostas assim:
- Se a pontuação de complexidade for alta, use o modelo poderoso, mesmo que o Jev tenha escolhido o rápido.
- Se o Jev não estiver confiante na própria escolha, use o modelo poderoso por segurança.
- Caso contrário, use o modelo que o Jev escolheu.
1if (complexity.score >= policy.escalateAtComplexity) return powerful;2if (tierAnswer.confidence < policy.minConfidence) return policy.fallbackTier;3return tierAnswer.choice;
Este é apenas um exemplo de política. A sua pode ponderar as respostas de forma diferente para se adequar ao seu domínio — por exemplo, priorizando o modelo barato para um job em lote de alto volume ou sempre escalando qualquer coisa que toque a produção. O Jev só fornece as respostas; seu código decide o que fazer com elas.
Por que escolher apenas uma vez
O roteador escolhe um modelo uma única vez, quando a requisição começa, e mantém essa escolha até o fim. Isso acontece por causa do prompt caching (cache de prompt).
Cada vez que o agente dá um passo, o modelo relê toda a conversa até aquele momento. Os provedores de IA armazenam conversas lidas recentemente para que relê-las saia barato. Mas cada modelo tem seu próprio armazenamento. Troque de modelo no meio do caminho e o novo modelo terá que ler tudo de novo, a preço cheio.
O criador do Jev detalha esses cálculos em um documento de design sobre agentes de programação. Em uma sessão longa, alternar do Claude Opus para o Sonnet (mais barato) e voltar custou cerca de 50% a mais do que simplesmente permanecer no Opus o tempo todo. Portanto, escolha o modelo no início, enquanto a conversa ainda é curta, e mantenha-o.
3. Prepare-se para a indisponibilidade do Jev
Esta seção altera tanto o gate quanto o roteador.
A partir do momento em que o harness consulta o Jev sobre cada tool call, o agente passa a depender dele. Como qualquer serviço online, o Jev pode ficar lento ou fora do ar. Decida antecipadamente o que cada parte fará quando não obtiver resposta. A escolha certa varia para cada uma delas.
O gate bloqueia a chamada. Se o gate não consegue consultar o Jev, ele não tem como saber se a chamada é segura. Deixá-la passar poderia apagar arquivos, então o gate recusa. Engenheiros chamam isso de failing closed (falhar fechado), como uma porta que tranca automaticamente quando acaba a energia.
O roteador usa o modelo poderoso. Se o roteador não consegue consultar o Jev, ele não sabe quão difícil é a requisição. O modelo poderoso dá conta de qualquer coisa, então a requisição ainda recebe uma boa resposta, e você paga um pouco mais por isso. Isso é failing open (falhar aberto), deixando o trabalho seguir em frente.

4. Verifique a resposta
Esta seção adiciona o verificador. Em harnesses de agentes, um verifier (verificador) é a etapa que checa o trabalho do agente antes de considerá-lo concluído.
Um agente pode terminar com uma resposta que deixou algo de fora ou que afirma coisas que ele nunca chegou a verificar nos arquivos. Quem lê muitas vezes não percebe. Checar a resposta antes de devolvê-la pega esses erros enquanto o agente ainda pode tentar de novo.
O verificador envia ao Jev a resposta finalizada junto com os arquivos e resultados de ferramentas nos quais ela se baseou. O Jev pontua a qualidade da resposta e diz se as afirmações são grounded (fundamentadas), ou seja, respaldadas pelo que o agente realmente leu.

1const VERIFY_QUESTIONS = {2 quality: score("How well does the answer satisfy the request?", [3 "Does not answer the request.",4 "Partly answers it, with a gap the reader would notice.",5 "Fully answers the request.",6 ]),7 grounded: noul("Every factual claim is supported by the files or tool results in the transcript."),8};
Duas regras impedem que o agente fique tentando para sempre. Ele tem, no máximo, duas tentativas no total. E quando o Jev não está confiante na própria nota, o harness aceita a resposta em vez de pagar por outra tentativa.
Segurança e logs
O Jev te dá uma probabilidade, e probabilidades podem estar erradas. Então, tudo o que um código simples puder verificar com certeza deve ser verificado em código. Neste harness, toda ferramenta de arquivo rejeita qualquer caminho fora da pasta do projeto, não importa o que o Jev diga. Deixe o Jev para os julgamentos que o código não consegue fazer.
O gate analisa apenas o tool call em si, ou seja, o nome da ferramenta e seus parâmetros. Isso ajuda contra prompt injection, quando um texto escondido em um arquivo ou página web engana o modelo para fazê-lo realizar algo prejudicial. O gate nunca vê o truque, mas enxerga a chamada maliciosa que ele provoca.
Registre em log cada decisão junto com os números que a embasaram. O log explica por que algo foi bloqueado e mostra dados reais para calibrar os thresholds. O harness escreve uma linha por decisão em um arquivo chamado decisions.jsonl. Como o gate vê tudo o que o agente tenta fazer, o log oculta endereços de e-mail e chaves, além de encurtar entradas longas.
Experimente
O sandbox abaixo executa o harness finalizado deste tutorial, conectado ao Jev real. Ele trabalha na pasta de anotações de levantamento de trilhas, e sua ferramenta delete_path realmente pode apagá-las.
Teste aqui: https://academy.dair.ai/resources/jev-decisions-in-a-pi-sdk-harness
Por que criar seu próprio harness
Agentes prontos tomam essas decisões usando suas próprias regras internas. Um harness personalizado coloca essas decisões no seu código. Você escolhe os modelos, define os thresholds, decide quando uma pessoa deve intervir e lê no log exatamente por que cada chamada foi feita.
O Jev é o que torna isso viável. Cada decisão leva algumas centenas de milissegundos e custa uma fração mínima de centavo, então você pode adicionar uma verificação onde quer que seu fluxo precise, e não apenas onde você possa bancar uma chamada completa de modelo. As três partes deste tutorial são um ponto de partida. Um harness personalizado para o seu domínio pode fazer ao Jev qualquer pergunta que seja relevante ali.
Outros usos
Essas mesmas três partes funcionam fora dos agentes de programação.
- Bots de revisão de código. Pontue cada alteração sugerida e mostre a um humano apenas aquelas que valem a leitura.
- Limpeza de registros. Pergunte se dois registros descrevem a mesma coisa antes de mesclá-los e deixe os pares duvidosos para uma pessoa.
- Pipelines de documentos. Pontue cada página extraída e reprocesse apenas as que tiverem notas baixas.
- Filas de aprovação. Somente as chamadas que caem na faixa de "perguntar a uma pessoa" chegam a um revisor humano.
Em todos os casos, o modelo de linguagem faz o trabalho aberto e criativo, e o Jev responde às pequenas perguntas ao redor dele.
As perguntas, thresholds e políticas deste tutorial são exemplos didáticos, e não configurações prontas para produção. Estamos fazendo benchmarks de como essas mudanças no harness afetam o custo e a qualidade das respostas, e um guia complementar com esses resultados sai em breve.
Passei uma noite com o Opus 5.5 montando este guia e o sandbox. Se encontrar algum problema, me mande uma DM. Fique à vontade para copiar o artigo e enviá-lo aos seus agentes para continuar experimentando com essas ideias.





