Arquitetura de Agentes

Harness baseado em IA

Um guia completo sobre a infraestrutura que transforma um modelo de linguagem em um sistema agêntico de produção — cada componente explicado do zero, com a implementação em Python + LangGraph e a leitura de negócio de cada decisão.

Conceitos essenciais

Antes do harness em si, quatro conceitos que todo o resto pressupõe. Se você já domina LLMs, pule para a próxima seção.

O que um LLM realmente faz

Um LLM (Large Language Model) é uma função estatística gigante: recebe uma sequência de tokens (pedaços de texto — palavras, sílabas, símbolos) e devolve, token a token, a continuação mais provável dessa sequência. Só isso. Ele não "executa" nada, não acessa a internet, não lembra da conversa anterior, não tem estado entre chamadas.

Ponto central

O modelo é stateless: cada chamada à API é independente. Se parece que ele "lembra" da conversa, é porque alguém reenviou o histórico inteiro dentro da requisição. Esse "alguém" é o harness — e essa única observação explica metade da arquitetura deste guia.

Janela de contexto

Cada modelo tem um limite de tokens que consegue processar por chamada — a janela de contexto (context window). Tudo o que o modelo "sabe" sobre a tarefa atual precisa caber ali: instruções, histórico, documentos, resultados de ferramentas. Estourou o limite, a chamada falha ou informação é cortada. Administrar esse espaço finito é uma das funções mais críticas do harness.

Function calling (tool use)

Modelos modernos aceitam, junto do prompt, uma lista de ferramentas (tools) descritas em JSON Schema: nome, descrição, parâmetros. Em vez de responder com texto, o modelo pode responder com um objeto estruturado dizendo "chame a função X com os argumentos Y". Importante: o modelo não executa a função — ele apenas pede. Quem executa, valida e devolve o resultado é o software em volta dele. De novo: o harness.

Agente

Um agente é um sistema em que o LLM decide, iterativamente, quais ações tomar para cumprir um objetivo — chamando ferramentas, observando resultados e ajustando o plano — em vez de apenas responder uma pergunta de uma vez. A diferença entre um chatbot e um agente não está no modelo: está no software que permite (e controla) esse ciclo de decisão.

O que é um harness

Harness (literalmente "arnês" ou "arreio"; em engenharia, "andaime de execução") é toda a infraestrutura de software que envolve o LLM e o transforma em um sistema útil e confiável. A analogia canônica:

Analogia

O modelo é o motor; o harness é o resto do carro. Um motor sozinho não vai a lugar nenhum — precisa de chassi, transmissão, freios, painel e airbag. Dois carros com o mesmo motor podem ter desempenhos e níveis de segurança completamente diferentes. Com agentes é idêntico: o mesmo modelo, em dois harnesses diferentes, produz sistemas radicalmente distintos.

O termo aparece em dois contextos que compartilham a mesma mecânica:

TipoObjetivoExemplos
Harness de agente (produção) Executar tarefas reais com confiabilidade: montar contexto, rodar o loop, executar tools, aplicar guardrails. LangGraph, Claude Code, OpenAI Agents SDK, um supervisor customizado em FastAPI
Harness de eval (avaliação) Rodar o agente contra um dataset de tarefas, capturar trajetórias e medir desempenho de forma reprodutível. SWE-bench harness, harness internos de benchmark, pipelines de trajectory evals
Regra de ouro

Avalie com o mesmo harness que roda em produção. Um benchmark medido em um harness diferente do seu mede outra coisa — os resultados não transferem, porque grande parte da performance vem do scaffolding, não do modelo.

Leitura de negócio

É por isso que dois times com acesso ao mesmo modelo entregam produtos com qualidade radicalmente diferente. O modelo é uma commodity acessível por API; o harness é onde mora o diferencial competitivo — e também onde mora a maior parte do custo de engenharia, do risco operacional e da dívida técnica de um produto de IA.

Anatomia de um harness de IA Um usuário envia uma mensagem que entra no container do harness. Dentro dele: montagem de contexto e loop agêntico alimentam o LLM, que aciona a execução de tools; guardrails e telemetria cortam todas as camadas na base. Harness Montagem de contexto Prompt, memória, RAG Loop agêntico Iteração e parada LLM Tokens in, tokens out Execução de tools Parsing, validação, retry Guardrails e telemetria ACLs, injection, tracing Usuário
Fig. 1 — Anatomia do harness: entrada do usuário, montagem de contexto e loop agêntico alimentando o LLM, execução de tools, e guardrails/telemetria como camada transversal na base.

Parte 1 · Entrada do usuário

técniconegócio

No primeiro diagrama, a seta "Usuário" entrando no container do harness parece trivial — mas é a primeira fronteira de segurança e de contrato do sistema.

O que acontece tecnicamente

  • Autenticação e identidade. Antes de qualquer token chegar ao modelo, o harness estabelece quem está pedindo: valida o JWT, extrai claims (usuário, tenant, escopos) e materializa isso em um objeto de contexto tipado. Em sistemas multiagente, essa identidade precisa se propagar por toda a cadeia de chamadas (padrão de token propagation).
  • Normalização da entrada. A mensagem crua vira uma estrutura interna: texto, anexos, metadados de sessão (thread_id), idioma, canal de origem.
  • Primeira camada de defesa. Filtros de entrada rodam aqui: limites de tamanho, sanitização, detecção precoce de prompt injection (instruções maliciosas embutidas na mensagem tentando sequestrar o comportamento do agente).
Erro comum

Tratar a entrada do usuário como "apenas texto" e concatená-la direto no prompt. Toda entrada é dado não confiável — inclusive documentos anexados e resultados de RAG. O harness precisa distinguir estruturalmente instruções do sistema (confiáveis) de conteúdo do usuário (não confiável), porque o modelo, sozinho, não distingue.

Leitura de negócio

Essa fronteira define o contrato do produto: quem pode usar, com quais dados, sob qual isolamento entre clientes (multi-tenancy). Falhas aqui não são bugs — são incidentes de segurança e de compliance (LGPD/GDPR), com custo reputacional e jurídico. É a camada mais barata de acertar no design e a mais cara de corrigir depois.

Parte 1 · Montagem de contexto

técniconegócio

Esta é a etapa que decide o que o modelo "vê" a cada chamada. Como o modelo é stateless e a janela de contexto é finita, cada requisição precisa ser montada do zero — e a qualidade dessa montagem determina, mais do que qualquer outra variável, a qualidade do agente. A disciplina que estuda isso hoje se chama context engineering.

Os ingredientes do contexto

ComponenteO que éResponsabilidade do harness
System promptInstruções fixas: papel do agente, regras, formato de saída, políticas.Versionar, testar e proteger (o usuário não pode sobrescrevê-lo).
Histórico da conversaMensagens anteriores do usuário, do modelo e resultados de tools.Persistir por sessão (thread_id), truncar ou resumir quando crescer demais.
Memória de longo prazoFatos sobre o usuário/domínio que sobrevivem entre sessões.Decidir o que gravar, como indexar e o que injetar em cada chamada.
RAG (Retrieval-Augmented Generation)Trechos de documentos recuperados de uma base vetorial, relevantes à pergunta atual.Chunking, embedding, busca, re-ranking e citação de fontes.
Definições de toolsOs JSON Schemas das ferramentas disponíveis.Escrever descrições claras (o modelo escolhe a tool pela descrição) e filtrar por permissão do usuário.

O problema central: espaço finito

Tudo isso disputa a mesma janela de tokens. As estratégias clássicas do harness:

  • Truncamento por janela deslizante — descartar mensagens antigas, mantendo as N mais recentes. Simples, mas perde informação.
  • Sumarização (compactação) — quando o histórico cruza um limiar, o harness chama o próprio LLM para resumir o trecho antigo e substitui as mensagens pelo resumo. Custa uma chamada extra, preserva o essencial.
  • Recuperação seletiva — em vez de carregar tudo sempre, guardar em storage externo e recuperar sob demanda (memória como RAG).
  • Prompt caching — provedores permitem cachear o prefixo estável do contexto (system prompt + tools), reduzindo custo e latência das chamadas repetidas. O harness precisa ordenar o contexto para maximizar o prefixo cacheável: o que é estável vem primeiro, o que muda vem por último.
Boa prática

Trate a montagem de contexto como uma função pura e testável: build_context(state) -> list[Message]. Isso permite fazer snapshot testing do prompt final, medir tokens por componente e auditar exatamente o que o modelo viu em cada decisão — essencial para depurar comportamento estranho em produção.

Leitura de negócio

Contexto é custo direto: você paga por token de entrada em toda chamada, e o loop reenvia o histórico a cada iteração. Um contexto 2× maior pode significar uma conta de API 2× maior com ganho marginal de qualidade. Context engineering é, na prática, uma alavanca de margem: as maiores reduções de custo unitário em produtos de IA vêm de compactação, caching e curadoria do que entra no prompt — não de trocar de modelo.

Parte 1 · Loop agêntico (visão geral)

técnico

O loop é o coração do harness — o mecanismo que transforma um modelo que só completa texto em um sistema que age. Em uma frase: um while que alterna entre chamar o modelo e executar o que ele pediu, realimentando os resultados, até a tarefa terminar.

while não_terminou:
  resposta = chamar_llm(contexto)
  if resposta.tool_calls → executar e realimentar
  else → resposta final, break

Esse padrão tem nome na literatura: ReAct (Reasoning + Acting) — o modelo raciocina, age, observa o resultado e raciocina de novo. Cada volta do laço é uma iteração; a sequência completa de decisões e observações de uma execução chama-se trajetória (conceito central em avaliação de agentes).

A visão geral fica aqui; a Parte 2 disseca cada etapa interna do loop, mostrada na Fig. 2 mais abaixo.

Por que um loop e não uma chamada só?

Porque tarefas reais exigem observar antes de decidir. O modelo não sabe o que tem no banco antes de consultá-lo, nem se o deploy passou antes de rodar o teste. O loop permite que cada decisão seja condicionada ao resultado da anterior — é isso que separa "gerar um texto plausível" de "resolver um problema".

Parte 1 · O LLM dentro do harness

técniconegócio

No diagrama, o LLM é uma caixa entre as outras — e isso é proposital. Dentro do harness, o modelo é um componente com um contrato estreito: tokens entram, tokens saem. Todo o resto é responsabilidade do software em volta.

O contrato do componente

  • Entrada: lista de mensagens + definições de tools + parâmetros de amostragem (temperatura, máximo de tokens de saída).
  • Saída: texto e/ou uma lista de tool calls estruturadas. Nada mais.
  • Garantias que ele NÃO dá: determinismo (a mesma entrada pode gerar saídas diferentes), veracidade (pode alucinar), aderência perfeita a schema (pode gerar argumentos inválidos), disponibilidade (a API pode falhar ou limitar taxa).

O harness existe, em grande parte, para compensar cada uma dessas não-garantias: validação compensa a falta de aderência a schema; retries e fallback de provedor compensam a disponibilidade; guardrails e citação de fontes mitigam alucinação; temperatura baixa + testes estatísticos lidam com o não-determinismo.

Abstração de provedor

Um bom harness isola o modelo atrás de uma interface (no LangChain/LangGraph, BaseChatModel). Trocar de GPT para Claude, ou rotear tarefas simples para um modelo barato e tarefas complexas para um modelo forte (model routing), vira uma decisão de configuração — não uma reescrita.

Leitura de negócio

Tratar o LLM como componente substituível é uma decisão estratégica: reduz lock-in de fornecedor, permite arbitrar preço entre provedores (os preços caem e mudam constantemente) e habilita otimização de custo por rota — usar o modelo caro só onde ele paga o próprio preço. Empresas que acoplam o produto a um provedor específico perdem essa alavanca de negociação.

Parte 1 · Execução de tools

técniconegócio

As tools são as "mãos" do agente: consultas a banco, chamadas HTTP, buscas, escrita de arquivos, envio de mensagens — ou, em arquiteturas multiagente, outros agentes inteiros expostos como ferramentas (via protocolos como MCP). Esta camada do harness é o runtime de execução dessas ações.

Responsabilidades

  1. Registro e descoberta. Manter o catálogo de tools disponíveis, com schema e descrição. Em setups com MCP, isso inclui conectar-se a servidores externos e importar as tools que eles expõem dinamicamente.
  2. Dispatch. Receber a tool call do modelo (nome + argumentos em JSON) e rotear para a implementação correta.
  3. Validação de argumentos. Checar o JSON contra o schema (Pydantic em Python) antes de executar. O modelo erra: inventa campos, troca tipos, esquece obrigatórios.
  4. Execução protegida. Rodar com timeout, retry com backoff exponencial + jitter para falhas transitórias, e circuit breaker para dependências degradadas (parar de insistir em um serviço que está fora, falhando rápido em vez de acumular timeouts).
  5. Autorização. Verificar se este usuário, com estas permissões, pode executar esta tool com estes argumentos (ACLs por escopo). O modelo pedir não é autorização.
  6. Serialização do resultado. Converter o retorno (objeto, DataFrame, erro) em texto/JSON que caiba no contexto — inclusive truncando resultados gigantes.
Princípio de segurança

O modelo propõe; o harness dispõe. Nenhuma tool call vai direto para execução pela vontade do modelo. Entre a intenção e o efeito existem validação, autorização e, para ações destrutivas ou irreversíveis (pagamentos, deleções, e-mails), aprovação humana explícita (human-in-the-loop). Um agente comprometido por prompt injection só causa o dano que essa camada permitir.

Leitura de negócio

As tools definem o raio de ação — e o raio de estrago — do produto. A pergunta de negócio não é "o que o agente consegue fazer?", e sim "qual é o pior efeito de uma ação errada, e quem responde por ele?". A granularidade das permissões e os pontos de aprovação humana são decisões de risco/produto, não detalhes de implementação: determinam desde o preço do seguro cibernético até o que o jurídico deixa ir para produção.

Parte 1 · Guardrails e telemetria

técniconegócio

A faixa horizontal na base do primeiro diagrama é proposital: guardrails e observabilidade são transversais — cortam todas as outras camadas, da entrada do usuário à execução de tools.

Guardrails (trilhos de proteção)

  • Na entrada: detecção de prompt injection, filtros de conteúdo, limites de tamanho e taxa (rate limiting) por usuário/tenant.
  • No meio: separação estrutural entre instruções confiáveis e dados não confiáveis; allowlists de tools por contexto; validação de que o agente permanece dentro do escopo da tarefa.
  • Na saída: checagem de vazamento de dados sensíveis (PII, segredos), validação de formato, filtros de conteúdo e, em domínios críticos, verificação factual contra as fontes recuperadas.
  • Defesa em profundidade: nenhuma camada é suficiente sozinha; a segurança vem do empilhamento (tipicamente 3–4 camadas independentes entre a entrada e o efeito colateral).

Telemetria (observabilidade)

Sistemas agênticos são não-determinísticos e distribuídos — sem instrumentação, são caixas-pretas impossíveis de depurar. O harness precisa registrar, para cada execução:

  • Traces distribuídos (OpenTelemetry): cada chamada ao LLM, cada tool call e cada hop entre serviços como spans correlacionados, propagando o trace context (W3C TraceContext) e o thread_id da sessão por toda a cadeia.
  • Métricas: tokens de entrada/saída por chamada, custo estimado, latência por etapa, taxa de erro por tool, iterações por tarefa, taxa de acionamento de guardrails.
  • Trajetórias completas: a sequência de decisões do agente, persistida — é a matéria-prima tanto do debugging quanto das avaliações offline (trajectory evals).
Boa prática

Instrumente antes de precisar. A pergunta "por que o agente fez isso ontem às 3h?" só tem resposta se a trajetória e o contexto exato daquela execução foram gravados. Logs de aplicação comuns não bastam: você precisa do prompt completo que o modelo viu e da resposta completa que ele deu, por iteração.

Leitura de negócio

Guardrails são o que permite vender para empresas: auditoria, trilha de decisão e controles são requisitos de procurement em setores regulados (financeiro, saúde, jurídico). E a telemetria é o que torna o custo gerenciável — sem métricas de tokens por rota, o produto tem uma conta de API que ninguém explica. Observabilidade em agentes não é engenharia de plataforma opcional; é pré-requisito de unit economics e de compliance.

Loop agêntico em detalhe Chamada ao LLM leva ao parsing da resposta, que bifurca entre executar uma tool (realimentando o histórico e voltando à chamada ao LLM) ou emitir a resposta final. Saídas forçadas por budget interrompem o ciclo a qualquer momento. Chamada ao LLM Contexto acumulado Parsing da resposta Tool call ou texto? Executar tool Validação, timeout, retry Realimentar histórico tool_result ↻ nova iteração Resposta final Sem tool calls → fim Saídas forçadas: max iterações, timeout, custo, guardrail
Fig. 2 — O loop agêntico em detalhe: chamada ao LLM → parsing → execução de tool com realimentação (↻) ou resposta final; budgets podem forçar a saída a qualquer momento.

Parte 2 · Chamada ao LLM

técniconegócio

Cada iteração do loop começa aqui: o harness pega o contexto acumulado — system prompt, histórico e todos os resultados de tools até agora — e envia ao modelo junto com as definições de tools.

Detalhes que importam

  • O histórico inteiro vai de novo, sempre. Como o modelo é stateless, a iteração 8 reenvia tudo o que aconteceu nas iterações 1–7. Consequência direta: o custo por iteração cresce ao longo da execução, porque o contexto só acumula. Prompt caching mitiga (o prefixo estável não é reprocessado a preço cheio), mas não elimina.
  • Resiliência da chamada. A API do provedor falha: rate limits (HTTP 429), erros de servidor (5xx), timeouts. O harness aplica retry com backoff exponencial + jitter, respeita cabeçalhos de rate limit e, em setups maduros, tem fallback para um segundo provedor ou modelo.
  • Parâmetros por propósito. Temperatura baixa (0–0.3) para decisões de roteamento e tool calls (queremos consistência); mais alta para geração criativa. O harness escolhe por etapa, não usa um valor global.
  • Streaming. Para UX, o harness pode consumir a resposta token a token e repassar ao usuário em tempo real — o que complica o parsing (tool calls chegam fragmentadas) e é mais um trabalho da camada de harness.
Leitura de negócio

Esta etapa é o taxímetro do sistema. Custo = (tokens de entrada × preço) + (tokens de saída × preço), por iteração, e a entrada cresce a cada volta. Uma tarefa que converge em 3 iterações vs. 10 iterações não é 3× mais barata — é mais, porque as iterações finais carregam contexto maior. Otimizar convergência (boas descrições de tools, contexto limpo) é otimizar margem.

Parte 2 · Parsing da resposta

técnico

A resposta do modelo chega e o harness precisa responder uma pergunta: isso é uma ação ou uma resposta final? Esta é a bifurcação central da Fig. 2.

Como funciona

A decisão é determinística — não envolve outra chamada ao LLM. A API do provedor devolve a resposta em campos estruturados: um bloco de texto e/ou uma lista de tool_calls, cada uma com id, name e arguments (JSON). O parsing é inspecionar esse payload:

PYTHON
# A conditional edge do LangGraph faz exatamente isto:
def route(state: AgentState) -> str:
    last = state["messages"][-1]
    if last.tool_calls:          # o modelo pediu ações
        return "tools"
    return "end"               # texto puro = resposta final

Onde mora a complexidade real

  • Tool calls paralelas. O modelo pode pedir várias tools numa única resposta ("busque X e consulte Y"). O harness decide se executa em paralelo (mais rápido) ou em série (mais seguro quando há dependências ou efeitos colaterais).
  • Respostas mistas. Texto + tool calls na mesma resposta: o texto costuma ser raciocínio ou explicação intermediária. O harness decide se exibe ao usuário, guarda no histórico ou descarta.
  • Malformações. Argumentos que não são JSON válido, nomes de tools inexistentes, JSON truncado por limite de tokens de saída. A política correta quase sempre é devolver o erro ao modelo como observação — ele se corrige na iteração seguinte — em vez de derrubar a execução.
  • Saída estruturada. Quando a resposta final precisa seguir um schema (um JSON de decisão, por exemplo), o parsing também valida isso — com Pydantic — e reprompta em caso de violação.
Por que isso é "harness" e não "modelo"

O modelo só emitiu tokens. Interpretar esses tokens como intenção de agir, decidir a ordem de execução e tratar malformações é pura engenharia de software — e cada uma dessas políticas muda o comportamento do agente sem mudar uma linha do modelo.

Parte 2 · Executar tool

técniconegócio

Com a tool call parseada, entra o pipeline de execução protegida — os três subtítulos do box no diagrama: validação, timeout, retry.

O pipeline, passo a passo

  1. Autorização. O usuário desta sessão pode executar esta tool? (checagem de escopo/ACL — cacheável para não custar uma ida ao banco por call).
  2. Validação de schema. model_validate() do Pydantic sobre os argumentos. Falhou → o ValidationError vira observação para o modelo, não exceção para o usuário.
  3. Execução com timeout. Toda tool roda dentro de um limite de tempo. Em cadeias de serviços, os timeouts formam um orçamento em cascata: se a requisição toda tem 30s, e já se passaram 12s, a próxima chamada downstream recebe no máximo o que resta (menos uma margem) — nunca um timeout fixo maior que o orçamento restante, o que causaria estouros silenciosos na ponta.
  4. Retry seletivo. Backoff exponencial com jitter, apenas para falhas transitórias (rede, 429, 5xx). Erros determinísticos (validação, 404, permissão negada) não se repetem — retry neles só queima tempo e dinheiro.
  5. Circuit breaker. Se uma dependência falha repetidamente, o breaker "abre" e as próximas chamadas falham imediatamente por um período, dando tempo de recuperação ao serviço e devolvendo um erro claro ao modelo ("serviço indisponível, tente outra abordagem").
  6. Captura do resultado. Sucesso ou erro, o desfecho é serializado em texto — porque o destino dele é a janela de contexto.
Detalhe traiçoeiro

Resultados gigantes explodem o contexto. Uma query que retorna 50 mil linhas, ou uma página HTML inteira, não pode ir crua para o histórico. O harness trunca, pagina ou resume resultados de tools — e informa o modelo de que o resultado foi cortado, para ele poder pedir mais se precisar.

Leitura de negócio

Esta etapa concentra a confiabilidade percebida do produto. O usuário não vê "o circuit breaker abriu" — vê "o agente resolveu mesmo com instabilidade" ou "o agente travou". SLAs de produtos agênticos são, na prática, SLAs desta camada: é aqui que se decide se uma dependência instável derruba a experiência ou vira um desvio elegante.

Parte 2 · Realimentar histórico

técnico

O resultado da tool (ou o erro) vira uma mensagem do tipo tool_result, vinculada ao id da tool call original, e é anexado ao histórico. O loop volta ao topo — e na próxima chamada, o modelo "vê" o que aconteceu e decide o próximo passo. O símbolo ↻ do diagrama é este retorno.

Três propriedades fundamentais

  • O histórico É o estado. Não existe máquina de estados paralela escondida: a lista de mensagens é a única fonte de verdade sobre onde a execução está. Persistir o histórico é persistir o loop inteiro; retomar é continuar de onde as mensagens pararam. É exatamente assim que o checkpointing do LangGraph funciona — o checkpointer (MemorySaver, RedisSaver, etc.) serializa o estado do grafo por thread_id a cada superstep, habilitando resiliência a crashes, pausas para aprovação humana e time travel de debugging.
  • Erros são observações, não exceções. Quando a tool falha, o texto do erro volta ao modelo — que tem uma capacidade notável de se autocorrigir: ajusta argumentos, tenta outra tool, muda a abordagem. Essa é a fonte da resiliência dos agentes… e também do risco de laços infinitos, tratado nas saídas forçadas.
  • Todo tool_call exige um tool_result. As APIs rejeitam históricos com tool calls "órfãs" (pedidas e nunca respondidas). Se o harness decidiu não executar uma call (permissão negada, por exemplo), ainda precisa anexar um resultado dizendo isso.
Conexão com avaliação

A sequência acumulada de mensagens — pensamentos, ações, observações — é a trajetória da execução. Gravá-la integralmente é o que permite trajectory evals depois: analisar não só se o agente acertou, mas como chegou lá (quantas iterações, quais desvios, quais erros se autocorrigiram).

Parte 2 · Resposta final

técnico

A condição de parada natural: o modelo responde sem tool calls. A interpretação semântica é "não preciso de mais informações nem ações — eis a resposta". O harness então:

  1. Pós-processa a saída — valida formato/schema se a resposta é estruturada, aplica os guardrails de saída (PII, conteúdo, vazamento de dados), anexa citações de fontes quando houve RAG.
  2. Persiste o estado final — o checkpoint da conversa fica pronto para o próximo turno do usuário, que reabre o loop com o histórico completo.
  3. Emite a telemetria de fechamento — total de iterações, tokens, custo, latência ponta a ponta, desfecho (sucesso natural vs. saída forçada). São as métricas agregadas que alimentam dashboards e evals.
Nuance

"Sem tool calls" significa que o modelo acha que terminou — não que a tarefa foi cumprida. Modelos terminam cedo demais ("declaro sucesso!") ou desistem sem avisar. Harnesses maduros adicionam uma verificação: um passo de crítica (o próprio modelo ou um segundo modelo revisa a resposta contra o objetivo) ou validação programática (os testes passam? o arquivo existe?) antes de aceitar o desfecho. É a diferença entre confiar na autoavaliação do modelo e verificar.

Parte 2 · Saídas forçadas (budgets)

técniconegócio

A faixa âmbar do diagrama: as condições em que o harness encerra o loop contra a vontade do modelo. Sem elas, um agente pode ciclar para sempre — tentando a mesma tool quebrada, alternando entre duas abordagens, ou simplesmente queimando tokens sem convergir.

BudgetMecanismoProtege contra
Máximo de iteraçõesContador de voltas do loop (no LangGraph, recursion_limit).Laços infinitos e não-convergência.
Timeout globalDeadline da execução inteira, propagado em cascata para cada chamada interna.Tarefas que "penduram" e seguram recursos/conexões.
Teto de custoAcumulador de tokens/dinheiro por execução, por usuário e por tenant.Explosão de custo — acidental (bug) ou maliciosa (abuso).
Guardrail disparadoInterrupção imediata quando um trilho de segurança detecta violação.Dano em andamento: injection bem-sucedida, vazamento, ação fora de escopo.

O que fazer ao estourar o budget

Encerrar bem importa tanto quanto encerrar. As políticas comuns, da mais simples à mais sofisticada:

  • Falha explícita — devolver erro claro ao usuário ("não consegui concluir em N passos"), preservando a trajetória para análise.
  • Resumo forçado — uma última chamada ao modelo, sem tools, pedindo o melhor resultado parcial possível com o que foi apurado.
  • Escalação humana — rotear a tarefa, com todo o contexto, para uma fila de atendimento humano (padrão obrigatório em produtos de suporte).
Leitura de negócio

Budgets são a apólice de seguro do produto — e um instrumento de pricing. O teto de custo por execução define o pior caso da sua margem unitária; sem ele, um único usuário (ou um único bug) pode gerar uma fatura de API de cinco dígitos numa madrugada. Casos assim são recorrentes o suficiente na indústria para que "cost ceiling por tenant" seja checklist de lançamento, não otimização futura. A taxa de saídas forçadas também é um KPI de qualidade: se 20% das execuções morrem por limite de iterações, o problema não é o limite — é o agente que não converge.

O loop completo em LangGraph

técnico

O LangGraph materializa tudo que vimos como um grafo de estados: cada etapa vira um nó, a bifurcação do parsing vira uma aresta condicional, e a realimentação vira a aresta que fecha o ciclo. O mapeamento direto entre os diagramas e o código:

Etapa do diagramaConstruto no LangGraph
Chamada ao LLMNó agent — invoca llm.bind_tools(tools)
Parsing da respostaAresta condicional (tools_condition ou função própria)
Executar tool + realimentarNó tools (ToolNode: dispatch, validação e append do ToolMessage)
↻ nova iteraçãoAresta tools → agent, fechando o ciclo
Resposta finalAresta condicional → END
Histórico como estadoAgentState com add_messages (reducer de append)
Persistência do estadoCheckpointer (MemorySaver / RedisSaver) por thread_id
Máximo de iteraçõesrecursion_limit na invocação
agent_harness.py PYTHON
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.tools import tool
from langchain_anthropic import ChatAnthropic


# ---- 1. Estado: o histórico É o estado --------------------------------
class AgentState(TypedDict):
    # add_messages é um "reducer": novos itens são anexados, não substituem
    messages: Annotated[list, add_messages]


# ---- 2. Tools: schema inferido da assinatura + docstring --------------
# A docstring é o que o modelo lê para decidir QUANDO usar a tool.
@tool
def consultar_pedido(pedido_id: str) -> str:
    """Consulta o status de um pedido pelo seu ID."""
    return buscar_no_banco(pedido_id)   # timeout/retry ficam aqui dentro

tools = [consultar_pedido]


# ---- 3. Nó agent: a "chamada ao LLM" do diagrama ----------------------
llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0)
llm_com_tools = llm.bind_tools(tools)   # anexa os JSON Schemas à chamada

def agent(state: AgentState):
    # montagem de contexto: aqui entrariam compactação, RAG, memória...
    resposta = llm_com_tools.invoke(state["messages"])
    return {"messages": [resposta]}    # reducer anexa ao histórico


# ---- 4. Grafo: as setas do diagrama viram edges -----------------------
builder = StateGraph(AgentState)
builder.add_node("agent", agent)
builder.add_node("tools", ToolNode(tools))   # dispatch + ToolMessage

builder.add_edge(START, "agent")
# parsing da resposta: tool_calls? -> "tools" ; senão -> END
builder.add_conditional_edges("agent", tools_condition)
builder.add_edge("tools", "agent")          # ↻ fecha o ciclo

# ---- 5. Checkpointer: persistir o histórico = persistir o loop --------
grafo = builder.compile(checkpointer=MemorySaver())


# ---- 6. Invocação com budgets -----------------------------------------
config = {
    "configurable": {"thread_id": "sessao-42"},  # isola a conversa
    "recursion_limit": 25,                        # máx. de supersteps
}
resultado = grafo.invoke(
    {"messages": [("user", "Qual o status do pedido A-981?")]},
    config,
)

O que este código não mostra — e que separa um tutorial de um harness de produção — são as camadas transversais: autenticação na borda, ACL por tool, timeouts em cascata dentro de cada tool, circuit breakers nas dependências, instrumentação OpenTelemetry por nó, teto de custo por tenant e o checkpointer trocado por um backend durável (Redis/Postgres) em vez de memória. Cada uma delas se encaixa exatamente nos pontos descritos nas Partes 1 e 2.

Padrão supervisor

Em arquiteturas multiagente, o mesmo loop se aninha: um agente supervisor roda seu próprio ciclo tratando cada agente especialista como uma "tool" — o resultado do especialista realimenta o roteador do supervisor, que decide o próximo passo. É o mesmo padrão do diagrama, recursivo: harnesses dentro de harnesses, com identidade e budgets propagados entre os níveis.

Custo, risco e decisão de negócio

negócio

Consolidando as leituras de negócio espalhadas pelo guia em um único quadro de decisão:

1. O harness é o produto

O modelo é acessível por API a qualquer concorrente pelo mesmo preço. O que diferencia produtos é o que está em volta: a curadoria de contexto, as tools e suas descrições, os guardrails, a experiência de recuperação de erro. Investimento em harness é investimento em moat; investimento em "prompt mágico" é vantagem que evapora no próximo lançamento de modelo.

2. Unit economics vivem no loop

  • Custo por tarefa = tokens acumulados ao longo das iterações. As alavancas, em ordem de impacto típico: reduzir iterações até a convergência, compactar contexto, prompt caching, model routing (modelo barato onde ele basta).
  • Sem teto de custo por execução/tenant, não há pricing seguro. O pior caso da margem é definido pelos budgets — ou pela sorte.
  • Latência é composta: N iterações × (latência do modelo + latência das tools). Produtos interativos precisam de streaming e de convergência rápida; produtos batch podem trocar latência por custo.

3. Risco operacional tem endereço

RiscoCamada do harness que o controla
Vazamento de dados / violação de privacidadeFronteira de entrada, guardrails de saída, ACLs
Ação destrutiva indevidaAutorização por tool + human-in-the-loop
Prompt injectionDefesa em camadas (entrada → contexto → tools → saída)
Explosão de custoBudgets (iterações, custo, timeout)
Impossibilidade de auditar decisõesTelemetria e persistência de trajetórias
Lock-in de fornecedorAbstração de provedor no componente LLM

4. Build vs. buy

A pergunta certa não é "framework ou código próprio?", e sim onde está o seu diferencial. Frameworks como LangGraph resolvem bem a mecânica genérica (grafo, checkpointing, ToolNode) e custam pouco a adotar; o que nenhum framework entrega são as suas tools, os seus guardrails, o seu modelo de permissões e a sua observabilidade — que é onde o esforço deveria se concentrar de qualquer forma. O anti-padrão é gastar meses reescrevendo o loop genérico e chegar sem fôlego na parte que diferencia.

5. Qualidade se mede, não se sente

Como o sistema é não-determinístico, "parece bom nas demos" não é evidência. O ciclo de melhoria maduro: gravar trajetórias em produção → construir datasets de avaliação a partir de casos reais → rodar evals (outcome + trajectory) no mesmo harness a cada mudança de prompt, tool ou modelo → tratar regressão de eval como quebra de build. Sem isso, cada ajuste de prompt é um lançamento às cegas.

Glossário

TermoDefinição
HarnessInfraestrutura de software que envolve o LLM: contexto, loop, tools, guardrails e telemetria.
TokenUnidade mínima de texto processada pelo modelo; base de cobrança e do limite de contexto.
Janela de contextoMáximo de tokens que o modelo processa por chamada.
Function calling / tool useCapacidade do modelo de responder com pedidos estruturados de execução de funções.
Tool call / tool resultO pedido de ação emitido pelo modelo / a observação devolvida pelo harness após executar.
Loop agêntico (ReAct)Ciclo raciocinar → agir → observar que estrutura a execução do agente.
IteraçãoUma volta completa do loop (uma chamada ao modelo + eventuais execuções de tools).
TrajetóriaSequência completa de decisões, ações e observações de uma execução; insumo de debugging e evals.
CheckpointingPersistência do estado do loop (o histórico) por sessão, habilitando retomada e auditoria.
thread_idIdentificador que isola o estado de uma conversa/sessão no checkpointer e na telemetria.
GuardrailControle automático que restringe entradas, comportamentos ou saídas do agente.
Prompt injectionAtaque em que instruções maliciosas embutidas em dados tentam sequestrar o agente.
Human-in-the-loopPonto de aprovação humana obrigatória antes de ações sensíveis ou irreversíveis.
Circuit breakerPadrão que interrompe chamadas a uma dependência degradada, falhando rápido até ela se recuperar.
Backoff exponencial + jitterEstratégia de retry com esperas crescentes e aleatorizadas para falhas transitórias.
BudgetLimite imposto pelo harness (iterações, tempo, custo) que força o encerramento do loop.
RAGRecuperação de trechos relevantes de uma base de conhecimento para enriquecer o contexto.
Prompt cachingReuso do prefixo estável do contexto entre chamadas, reduzindo custo e latência.
Model routingDirecionar cada tarefa ao modelo mais custo-efetivo capaz de resolvê-la.
MCPModel Context Protocol — padrão aberto para expor tools e contexto de servidores externos a agentes.
Supervisor / especialistaPadrão multiagente em que um agente roteador orquestra agentes especializados como se fossem tools.
Eval (outcome / trajectory)Avaliação sistemática do agente: pelo resultado final e/ou pelo caminho percorrido.