Conteúdo da trilha
9 minNarração em inglês, legenda em portuguêsPodcast (RSS)
Capítulos
  1. 0:00Abertura
  2. 0:59A ideia
  3. 2:02Como funciona
  4. 2:38Um caso real
  5. 4:00Um desafio
  6. 4:42Escolhendo os limites
  7. 5:24Erros comuns
  8. 6:18Em produção
  9. 7:34O código
  10. 8:17Fechamento
Transcrição

Abertura

A Larissa colocou no ar um agente de pesquisa. Ele recebe uma pergunta de mercado, busca na web, lê fontes e escreve um relatório. As primeiras semanas foram bem. Aí começaram três tipos de reclamação.

Algumas tarefas nunca terminavam. O agente continuava procurando mais uma fonte, até o orçamento acabar.

Outras terminavam cedo demais. Depois de duas buscas, o agente escrevia um relatório confiante, com informação rasa.

E algumas terminavam com: não consegui terminar. Sem dizer o que tinha encontrado, nem por que parou.

As três têm uma raiz só. Terminar estava definido numa frase do prompt. Quando tiver informação suficiente, escreva o relatório. O modelo decidia sozinho o que era suficiente, e não havia outro jeito de terminar. Nesta aula, você vai aprender a desenhar o fim de um agente, para toda execução terminar, e terminar no estado certo.

A ideia

Trate o fim como parte do desenho. Com quatro saídas explícitas, cada uma conferida por código sempre que possível.

Sucesso. O objetivo foi cumprido, e existe um critério verificável para isso. O teste passa. O relatório tem o número exigido de fontes válidas.

Limite. O orçamento de passos, tempo ou custo acabou. O agente entrega o que tem e diz que parou no limite.

Desistência. O agente concluiu que não consegue fazer a tarefa, por falta de acesso ou de informação. E explica por quê.

E pedido de ajuda. O agente precisa de uma decisão, ou de um fato, que só uma pessoa tem.

O modelo pode sinalizar sucesso, desistência ou pedido de ajuda, por uma ferramenta própria, ou por saída estruturada. O seu código confirma o sucesso quando consegue. E o limite pertence sempre ao seu código. O juiz encerra a partida no apito, não quando um time cansa.

Como funciona

Cada turno do loop termina com uma decisão do modelo.

Mas o código confere o orçamento antes de tudo. Sem orçamento, a execução para com o estado atual, e o motivo.

O modelo sinaliza uma de quatro intenções, de forma estruturada.

E terminei não basta. O código confere o critério de sucesso, quando ele é verificável.

Sucesso confirmado encerra a tarefa.

Desistir é uma saída válida, com a explicação do que foi tentado.

E pedir ajuda pausa a execução, até uma pessoa responder.

Um caso real

De volta ao agente da Larissa. O critério de sucesso virou uma regra no código. O relatório precisa citar pelo menos três fontes independentes, e todo link precisa abrir. O orçamento é de vinte e cinco passos.

No passo seis, o modelo diz: relatório pronto. A checagem acha duas fontes válidas de três. Recusado. E o modelo recebe o motivo. Falta uma fonte independente.

Então ele continua. No passo catorze, diz que terminou de novo. Quatro fontes válidas, todos os links abrem. Aceito. Motivo da parada: sucesso.

Aquele primeiro terminei era exatamente o relatório raso das reclamações. A checagem devolveu o motivo, e o modelo continuou trabalhando. Sem ela, a tarefa teria parado no passo seis, com uma fonte a menos.

Agora uma pergunta sem resposta pública, como a margem de lucro de uma empresa fechada. Existem duas saídas boas.

No passo nove, o agente desiste. Nenhuma fonte pública dá a margem. Só achei estimativas de terceiros, listadas abaixo.

Ou, se ele continuar tentando, o código para no passo vinte e cinco e entrega um resumo do que achou.

A saída ruim, a que esse padrão evita, é um relatório confiante com um número inventado.

Um desafio

Uma pergunta para você. O seu agente de código chama finish, com terminei, e a nota: corrigi o bug. Os testes continuam vermelhos. O que o loop deve fazer?

Recusar o sucesso, dizer ao modelo quais testes falham e continuar o loop. A declaração do modelo é um pedido para terminar, não uma prova. Quando existe critério verificável, quem decide é o código.

E também não é desistência. O modelo não desistiu. Ele achou que tinha terminado. Encerrar aqui desperdiçaria uma tarefa que pode estar a dois passos de passar. E o orçamento continua valendo, então o loop não fica preso nisso para sempre.

Escolhendo os limites

Não chute o teto. Olhe os traces das execuções que terminaram em sucesso e veja quantos passos, segundos e dólares elas usaram. Depois coloque o teto acima da grande maioria, com folga.

Digamos que 95% das execuções de sucesso usam até catorze passos, e a mais longa usa dezenove. Um teto de vinte e cinco dá espaço para o caso difícil e corta cedo um loop preso. Um teto de duzentos, por via das dúvidas, deixa uma execução presa gastar dez vezes mais antes de parar.

E revise os limites quando o tipo de tarefa mudar. Um teto certo para corrigir um teste é curto demais para migrar um módulo.

Erros comuns

Agora, os erros comuns. Um limite que lança exceção. A tarefa some, sem estado, sem registro e sem resposta. O limite é uma saída como as outras. Ele entrega o que foi feito e diz que parou ali.

Sucesso é o modelo ter dito que terminou. Se existe um teste, um schema ou uma contagem, use. A opinião do modelo só decide quando não há nada para conferir.

Um prompt que empurra para a desistência. Frases como na dúvida, desista, fazem a taxa de desistência disparar. Diga exatamente quando desistir. Falta de acesso, dado que não existe, permissão negada.

E pedir ajuda sem ninguém do outro lado. Se a pessoa demora horas, a execução fica pendurada. Defina um prazo de espera, e o que acontece quando ele vence.

Em produção

Em produção, registre um motivo de parada em todo trace, e olhe a distribuição por tipo de tarefa. Sucesso, limite, desistência e ajuda.

E vigie de perto as paradas por limite. Digamos que no agente de navegador, as paradas por limite vão de 3% para 25% das execuções, numa semana. Ninguém fez deploy. Dobrar o orçamento só deixa as execuções presas gastarem mais. Trocar de modelo é o palpite mais caro e menos provável. Abra os traces. Um salto desses, sem mudança de código, normalmente vem do mundo lá fora. Um portal mudou, ou uma ferramenta começou a falhar em silêncio. É exatamente por isso que o motivo da parada vai para o trace.

E monte pelo menos um caso de eval por saída. Uma tarefa fácil espera sucesso. Uma tarefa impossível de propósito, como um portal fora do ar, espera desistência. Uma tarefa que depende de decisão de negócio espera pedido de ajuda. E uma ferramenta que responde ok sem progresso espera parada por limite. Se o agente responde a tarefa impossível com sucesso, você achou o caso mais perigoso de todos. Um falso sucesso.

O código

Na página, todas as versões dão ao modelo uma ferramenta finish, com três tipos, terminei, desisto e preciso de ajuda, e rodam o loop dentro de um orçamento de passos e de tempo.

Quando o sinal é terminei, o código roda os testes. Se passam, sucesso. Se não, o modelo é avisado, e o loop segue.

E quando o orçamento acaba, devolve uma parada por limite, com um resumo.

Em Python e TypeScript, o sinal é lido dentro do loop. No LangGraph, o mesmo sinal é lido numa aresta. Limite, sucesso, desistência e humano viram destinos do grafo. Não frases num prompt.

Fechamento

Então, a ideia para levar. Um agente precisa de um fim desenhado, não de um fim torcido. Sucesso que o código confirma, limite que é do código, desistência que explica e um jeito de pedir ajuda a uma pessoa. Aí toda execução termina num estado conhecido, e o usuário sabe o que recebeu, e por quê.

Na próxima aula, a gente sai de como um agente termina para o que ele tem nas mãos: tool sprawl. Até lá.

PadrãoComportamento de agentes

Condições de Parada

Stop Conditions

Condições de Parada (Stop Conditions) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Comportamento de agentes. Definir com clareza quando o agente terminou, desistiu ou precisa de ajuda.

Elemento
51/70
Nível
Intermediário
Leitura
9 min
Atualizado
Nesta página 17
  1. 01Intenção
  2. 02Problema
  3. 03Solução
  4. 04Estrutura
  5. 05Pseudocódigo
  6. 06Cenários reais
  7. 07Aplicabilidade
  8. 08Quando não usar
  9. 09Como implementar
  10. 10Variações
  11. 11Custo, latência e falhas
  12. 12Como avaliar
  13. 13Em produção
  14. 14Prós e contras
  15. 15Código
  16. 16Suporte por framework
  17. 17Teste seu entendimento
  18. ··Perguntas rápidas

Intenção

Definir, fora do modelo, quando um agente terminou, quando desistiu e quando precisa de ajuda. Serve para todo agente que roda em loop e precisa acabar sempre num estado conhecido.

Em uma frase
O juiz de uma partida: o jogo acaba no apito, não quando um dos times cansa.

Problema

Larissa colocou em produção um agente de pesquisa. Ele recebe uma pergunta de mercado, busca na web, lê fontes e escreve um relatório. Nas primeiras semanas deu certo. Depois começaram três tipos de reclamação.

Algumas tarefas nunca terminavam: o agente seguia buscando “mais uma fonte” até o orçamento acabar. Outras terminavam cedo demais: depois de duas buscas, o agente escrevia um relatório confiante com informação rasa. E algumas terminavam com “não consegui concluir”, sem dizer o que tinha encontrado e por que tinha parado.

Os três problemas têm a mesma raiz. “Terminar” estava definido em uma frase do prompt: “quando tiver informação suficiente, escreva o relatório”. O modelo decidia sozinho o que era suficiente, e não havia nenhum outro jeito de acabar.

A pergunta que este padrão responde: como garantir que todo loop de agente termine, e termine no estado certo?

Solução

Trate o fim como parte do design, com quatro saídas explícitas, cada uma checada por código sempre que possível:

  • Sucesso: o objetivo foi cumprido, e há um critério verificável para isso (o teste passa, o relatório tem N fontes válidas).
  • Limite: o orçamento de passos, tempo ou custo acabou. O agente entrega o que tem e diz que parou no limite.
  • Desistência: o agente concluiu que não consegue, por falta de acesso ou de informação. Ele explica o motivo.
  • Pedido de ajuda: o agente precisa de uma decisão ou de um dado que só uma pessoa tem.

O modelo pode sinalizar sucesso, desistência ou pedido de ajuda com uma ferramenta própria ou com saída estruturada. O código confirma o sucesso quando dá, e o limite é sempre do código.

Não é uma ideia exótica. No texto “Building effective agents” (dezembro de 2024), a Anthropic recomenda que agentes tenham condições de parada, como um número máximo de iterações, para manter o controle. Este padrão leva a recomendação adiante: além do teto, cada jeito de terminar ganha um nome e um tratamento.

Exemplo trabalhado

Volte ao agente de Larissa. O critério de sucesso virou regra em código: o relatório precisa citar pelo menos três fontes independentes, e todo link precisa abrir. O orçamento é de 25 passos. Um trace com as quatro saídas funcionando:

step 6   finish(kind="done", note="Relatório pronto")
         verify: 2 fontes válidas de 3 exigidas         -> rejeitado
         tool_result: "Critério não cumprido: falta 1 fonte independente."
step 7   web_search("market share pagamentos instantâneos 2026")
...
step 14  finish(kind="done", note="Relatório pronto, 4 fontes")
         verify: 4 fontes válidas, todos os links abrem -> aceito
stop.reason = success

O primeiro “concluí” do modelo era o mesmo relatório raso das reclamações. A verificação devolveu o motivo, e o modelo continuou. Sem ela, a tarefa teria parado no passo 6 com uma fonte a menos.

Agora uma pergunta sem resposta pública, como a margem de lucro de uma empresa de capital fechado. Duas saídas são boas: no passo 9 o agente chama finish(kind="give_up", note="Nenhuma fonte pública traz a margem; achei só estimativas de terceiros, listadas abaixo"). Ou, se continuar tentando, o código para no passo 25 e entrega o resumo do que achou, com stop.reason = limit. A saída ruim, que o padrão impede, é um relatório confiante com um número inventado.

Estrutura

flowchart TD
  L(["1. Volta do loop"]):::llm --> B{"2. Orçamento ok?"}
  B -->|não| LIM(["3. Parada por limite"]):::io
  B -->|sim| S{"4. Sinal do modelo"}
  S -->|continuar| L
  S -->|concluí| V["5. Verificar critério"]:::tool
  V -->|passou| OK(["6. Sucesso"]):::io
  V -->|falhou| L
  S -->|não consigo| G(["7. Desistência explicada"]):::io
  S -->|preciso de ajuda| H((("8. Humano"))):::human
  1. Cada volta do loop termina com uma decisão do modelo.
  2. O código checa o orçamento antes de qualquer outra coisa.
  3. Sem orçamento, a execução para com o estado atual e o motivo.
  4. O modelo sinaliza uma de quatro intenções, de forma estruturada.
  5. “Concluí” não basta: o código checa o critério de sucesso quando ele é verificável.
  6. Sucesso confirmado encerra a tarefa.
  7. Desistência é uma saída válida, com explicação do que foi tentado.
  8. Pedido de ajuda pausa a execução até uma pessoa responder.
Checagem +20 XP de primeiraSeu agente de código chama finish com kind=done e a nota ‘corrigi o bug’. Os testes ainda estão vermelhos. O que o loop deve fazer?

Pseudocódigo

schema Signal { kind: "continue" | "done" | "give_up" | "need_help", note: string }

function run(task):
    while true:
        if not budget.check(steps=25, seconds=600, dollars=2.00):      # 2
            return finish("limit", summarize(state))                    # 3

        reply = llm_with_tools(SYSTEM, TOOLS + [signal_tool], state)    # 1 e 4
        signal = reply.signal or Signal("continue")
        trace.span("signal", signal)

        if signal.kind == "done":
            if verify(task.success_criteria, state):                    # 5
                return finish("success", reply.text)                    # 6
            state.add("O critério ainda não foi cumprido: " + failed_checks())
        elif signal.kind == "give_up":
            return finish("gave_up", signal.note)                       # 7
        elif signal.kind == "need_help":
            answer = human.ask(signal.note)                              # 8
            state.add(answer)
        else:
            state.add(execute(reply.tool_calls))

Cenários reais

  • Agente de código. Sucesso tem critério verificável: os testes passam e o lint não reclama. O agente não pode declarar “pronto” com teste vermelho.
  • Agente de navegador para tarefas repetitivas. O portal pode estar fora do ar ou pedir um captcha. Desistência e pedido de ajuda evitam horas de cliques inúteis.
  • Pesquisa profunda com citações. “Suficiente” vira regra: pelo menos N fontes independentes por afirmação, ou o relatório sai marcado como incompleto.

Aplicabilidade

  • Qualquer agente que roda em loop sem uma pessoa olhando cada passo.
  • Tarefas com critério de sucesso verificável por código: testes, schema, contagem, consulta.
  • Tarefas em que pode faltar acesso, dado ou permissão, e desistir cedo é melhor que tentar para sempre.
  • Fluxos em que uma decisão de negócio pode aparecer no meio.

Quando não usar

  • Uma chamada única ao modelo, sem loop. Use max_tokens e trate o motivo de parada da API.
  • Uma chain fixa, em que o código já define cada passo e o fim. Ali o fim é o último passo.
  • Um chat em que o usuário controla cada turno. O usuário é a condição de parada.

Como implementar

  1. Escreva o critério de sucesso de cada tipo de tarefa em uma frase verificável.
  2. Transforme o que der em código: comando de teste, validação de schema, contagem de fontes.
  3. Dê ao modelo uma ferramenta finish (ou saída estruturada) com os quatro tipos de sinal.
  4. Implemente o orçamento como em Adicionar Orçamento ao Loop.
  5. Escreva o fechamento de cada saída: o que o usuário recebe em caso de limite, desistência ou espera.
  6. Registre o tipo de parada no trace de toda execução.
  7. Monte casos de eval para cada saída, inclusive tarefas impossíveis de propósito.

Como escolher os limites

Não chute o teto. Olhe os traces das execuções que terminaram com sucesso e veja quantos passos, segundos e reais elas usaram. O teto fica acima da grande maioria delas, com folga, para que o limite pegue execuções presas e não tarefas difíceis.

Suponha que 95% das execuções bem-sucedidas usem até 14 passos e a mais longa use 19. Um teto de 25 deixa espaço para o caso difícil e corta o loop preso cedo. Um teto de 200 “para garantir” deixa uma execução presa gastar dez vezes mais antes de parar.

Revise os limites quando o tipo de tarefa mudar. Um teto certo para “corrigir um teste” é curto demais para “migrar um módulo”.

O fechamento de cada saída

Cada saída precisa de uma resposta pensada para quem recebe. Escreva as quatro antes de ir para produção:

Saída O que o usuário recebe O que o sistema faz
Sucesso O resultado e a evidência da verificação (“4 fontes, todos os links conferidos”) Registra stop.reason=success
Limite O que foi feito até ali, o que faltou e onde parou Guarda o estado para retomar ou revisar
Desistência O motivo concreto e o que foi tentado Manda para a fila de revisão
Pedido de ajuda Uma pergunta objetiva, com as opções quando houver Pausa a execução com prazo de espera

Pense no agente de navegador que baixa boletos. “Não consegui” não ajuda ninguém. “O portal pediu um captcha na tela de login às 3h12; baixei 14 de 20 boletos, os 6 restantes estão listados abaixo” deixa a pessoa terminar o trabalho em dois minutos.

Erros comuns

  • Limite que lança exceção. A tarefa some sem estado, sem registro e sem resposta. O limite é uma saída como as outras: entrega o que foi feito e diz que parou ali.
  • Sucesso igual a “o modelo disse que acabou”. Se existe um teste, um schema ou uma contagem, use. A opinião do modelo só decide quando não há nada para checar.
  • Prompt que empurra para a desistência. Frases como “na dúvida, desista” fazem a taxa de gave_up disparar. Diga quando desistir: falta de acesso, dado inexistente, permissão negada.
  • Pedido de ajuda sem ninguém do outro lado. Se a pessoa demora horas, a execução fica pendurada. Defina um prazo de espera e o que acontece quando ele vence.

Variações

  • Critério por checagem externa. Em vez de o modelo dizer “concluí”, o código checa o estado a cada volta (o arquivo existe, o teste passou) e encerra sozinho.
  • Detecção de estagnação. Sem progresso mensurável em N voltas (mesmos arquivos, mesma ferramenta, mesmo resultado), o código encerra com desistência.
  • Parada por avaliador. Um segundo modelo julga se o resultado cumpre o critério. Útil quando o critério é qualitativo; é o laço do Avaliador e Otimizador.

Custo, latência e falhas

Aspecto O que acontece Como aparece no trace
Chamadas Nenhuma extra no caminho normal; uma no fechamento por limite span wrap_up só quando há limite
Latência Verificações de sucesso somam o tempo delas span verify após cada done
Falha: sucesso falso O modelo declara “concluí” e a verificação é fraca tarefa com stop=success falhando no eval
Falha: desistência demais Prompt ou ferramentas tornam desistir fácil demais taxa de gave_up subindo
Falha: ajuda demais O agente pergunta o que poderia descobrir sozinho taxa de need_help acima da meta
Checagem +20 XP de primeiraNo agente de navegador, as paradas por limite passam de 3% para 25% das execuções em uma semana. Ninguém fez deploy. Qual é o próximo passo?

Como avaliar

  • Distribuição de paradas: fração de sucesso, limite, desistência e ajuda por tipo de tarefa.
  • Sucesso verdadeiro: das tarefas marcadas como sucesso, quantas passam na checagem independente do eval.
  • Desistência correta: em tarefas impossíveis de propósito, o agente desiste em vez de inventar.
{ "task": "Baixe a fatura de setembro no portal X", "fixture": "portal_offline", "expected_stop": "gave_up" }

Monte pelo menos um caso por saída. Uma tarefa fácil espera success. Uma tarefa impossível de propósito (portal fora do ar, arquivo que não existe) espera gave_up. Uma tarefa que depende de uma decisão de negócio espera need_help. E uma fixture que faz a ferramenta responder “ok” sem progresso espera limit. Se o agente responde a tarefa impossível com success, você achou o caso mais perigoso: sucesso falso.

Em produção

  • Atributo stop.reason em todo trace, com painel por tipo de tarefa.
  • Alerta quando a fração de paradas por limite sobe. Normalmente é uma ferramenta quebrada ou uma mudança no mundo lá fora.
  • Fila de revisão para desistências, que viram casos de eval ou melhorias nas ferramentas.
  • Tempo de espera por ajuda humana como métrica própria.

Prós e contras

Prós Contras
Toda execução termina num estado conhecido Exige critério de sucesso escrito para cada tipo de tarefa
Sucesso deixa de ser opinião do modelo Verificações custam tempo
Desistências viram dado útil Mais caminhos de fim para testar
O usuário sabe o que recebeu e por quê Pedido de ajuda exige alguém para responder

Código

As abas usam uma ferramenta finish com os sinais e um critério verificado por código. Python e TypeScript leem o sinal dentro do while. LangGraph lê o mesmo sinal numa aresta: limite, sucesso, desistência ou humano são destinos do grafo, não frases no prompt.

Python. Use quando você quer as quatro saídas explícitas num loop escrito à mão.

import subprocess
import time

import anthropic

client = anthropic.Anthropic()

FINISH = {
    "name": "finish",
    "description": "Chame quando terminar, desistir ou precisar de ajuda. Nunca responda sem chamar esta ferramenta.",
    "strict": True,
    "input_schema": {
        "type": "object",
        "properties": {
            "kind": {"type": "string", "enum": ["done", "give_up", "need_help"]},
            "note": {"type": "string", "description": "Resultado, motivo da desistência ou pergunta para a pessoa"},
        },
        "required": ["kind", "note"],
        "additionalProperties": False,
    },
}


def tests_pass() -> bool:  # 5. critério verificável
    return subprocess.run(["pytest", "-q"], capture_output=True).returncode == 0


def run(task: str, tools: list, execute, ask_human, max_steps=25, max_seconds=600) -> dict:
    messages, start = [{"role": "user", "content": task}], time.monotonic()
    for _ in range(max_steps):
        if time.monotonic() - start > max_seconds:
            break
        reply = client.messages.create(
            model="claude-opus-5-5", max_tokens=8000, tools=tools + [FINISH], messages=messages,
        )
        messages.append({"role": "assistant", "content": reply.content})
        results = []
        for block in (b for b in reply.content if b.type == "tool_use"):
            if block.name != "finish":
                results.append({"type": "tool_result", "tool_use_id": block.id, "content": execute(block)})
                continue
            kind, note = block.input["kind"], block.input["note"]
            if kind == "done" and tests_pass():
                return {"stop": "success", "result": note}               # 6
            if kind == "give_up":
                return {"stop": "gave_up", "result": note}               # 7
            content = ask_human(note) if kind == "need_help" else "Os testes ainda falham. Continue."
            results.append({"type": "tool_result", "tool_use_id": block.id, "content": content})
        messages.append({"role": "user", "content": results or "Use uma ferramenta ou chame finish."})
    return {"stop": "limit", "result": "Parei no limite. Último estado no trace."}  # 3

O que você ganhou: cada saída é um caminho de código testável. O que você perdeu: mais código que um while simples, e o fechamento por limite ainda é uma mensagem fixa.

TypeScript. Use quando o agente roda em Node e você quer a ferramenta finish tipada.

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const FINISH: Anthropic.Tool = {
  name: "finish",
  description: "Chame quando terminar, desistir ou precisar de ajuda. Nunca responda sem chamar esta ferramenta.",
  strict: true,
  input_schema: {
    type: "object",
    properties: {
      kind: { type: "string", enum: ["done", "give_up", "need_help"] },
      note: { type: "string" },
    },
    required: ["kind", "note"],
    additionalProperties: false,
  },
};

type Stop = { stop: "success" | "gave_up" | "limit"; result: string };

export async function run(
  task: string,
  tools: Anthropic.Tool[],
  execute: (b: Anthropic.ToolUseBlock) => Promise<string>,
  verify: () => Promise<boolean>,
  askHuman: (q: string) => Promise<string>,
  maxSteps = 25,
): Promise<Stop> {
  const messages: Anthropic.MessageParam[] = [{ role: "user", content: task }];
  for (let step = 0; step < maxSteps; step++) {
    const reply = await client.messages.create({
      model: "claude-opus-5-5", max_tokens: 8000, tools: [...tools, FINISH], messages,
    });
    messages.push({ role: "assistant", content: reply.content });
    const results: Anthropic.ToolResultBlockParam[] = [];
    for (const b of reply.content) {
      if (b.type !== "tool_use") continue;
      if (b.name !== "finish") {
        results.push({ type: "tool_result", tool_use_id: b.id, content: await execute(b) });
        continue;
      }
      const { kind, note } = b.input as { kind: string; note: string };
      if (kind === "done" && (await verify())) return { stop: "success", result: note };
      if (kind === "give_up") return { stop: "gave_up", result: note };
      const content = kind === "need_help" ? await askHuman(note) : "O critério ainda falha. Continue.";
      results.push({ type: "tool_result", tool_use_id: b.id, content });
    }
    messages.push({ role: "user", content: results.length ? results : "Use uma ferramenta ou chame finish." });
  }
  return { stop: "limit", result: "Parei no limite. Último estado no trace." };
}

O que você ganhou: o tipo Stop obriga quem chama a tratar cada saída. O que você perdeu: o mesmo que em Python, mais a verificação injetada por parâmetro.

Python + LangGraph. Use quando cada saída do loop é um destino do grafo.

from typing import Literal

from langgraph.graph import END, START, StateGraph
from typing_extensions import TypedDict

class State(TypedDict):
    steps: int
    max_steps: int
    signal: str  # continue | done | blocked | help
    verified: bool
    output: str

def turn(state: State) -> dict:
    # 1. uma volta; o modelo devolve o sinal estruturado
    signal = ask_model(state)  # sua chamada, saída em schema
    return {"signal": signal, "steps": state["steps"] + 1}

def route(state: State) -> Literal["turn", "limit", "success", "give_up", "human"]:
    if state["steps"] >= state["max_steps"]:
        return "limit"  # 2 e 3
    if state["signal"] == "continue":
        return "turn"
    if state["signal"] == "done":
        return "success" if state["verified"] else "turn"  # 5
    if state["signal"] == "blocked":
        return "give_up"  # 7
    return "human"  # 8

graph = StateGraph(State)
graph.add_node("turn", turn)
graph.add_node("limit", lambda s: {"output": "parei no teto"})
graph.add_node("success", lambda s: {"output": "ok"})
graph.add_node("give_up", lambda s: {"output": "não consegui"})
graph.add_node("human", lambda s: {"output": "aguardando pessoa"})
graph.add_edge(START, "turn")
graph.add_conditional_edges("turn", route, ["turn", "limit", "success", "give_up", "human"])
for name in ("limit", "success", "give_up", "human"):
    graph.add_edge(name, END)
app = graph.compile()

recursion_limit do LangGraph corta o grafo inteiro. Ele não substitui estas arestas. O loop em outras stacks está em Loop ReAct.

O que você ganhou: cada parada é um nó com nome no trace. O que você perdeu: a verificação do critério (verified) continua uma função sua, chamada antes de aceitar done.

Suporte por framework

Stack Onde mora a parada
Python, TypeScript O if depois de finish
Python + LangGraph A aresta com cinco destinos
Python + LangChain, OpenAI Agents, Pydantic AI, AI SDK Só o teto de passos. As outras paradas você escreve

Teste seu entendimento

Checagem +20 XP de primeiraUm colega propõe dar ao modelo uma ferramenta out_of_budget, para ele mesmo avisar quando o orçamento acabou. O que você responde?

Checagem +20 XP de primeiraAntes de ir para produção, como você descobre se o agente inventa um resultado quando a tarefa é impossível?

Checagem +20 XP de primeiraO agente pede ajuda em 40% das tarefas, com perguntas como ‘qual é o formato da data?’, que estão escritas na própria página que ele lê. O que você ajusta?

Checagem +20 XP de primeiraSeu produto é um chat em que o usuário manda uma mensagem por vez. Em alguns turnos, o modelo chama ferramentas em loop antes de responder. Onde entram condições de parada?

Perguntas rápidas

O que é Condições de Parada?

Condições de Parada (Stop Conditions) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Comportamento de agentes. Definir com clareza quando o agente terminou, desistiu ou precisa de ajuda.

Quais smells o padrão Condições de Parada ajuda a curar?

Loop Sem Fim, Subagentes em Cascata e Custo Sem Teto.

Onde Condições de Parada aparece na prática?

Nos cenários reais Agente de código, Agente de navegador para tarefas repetitivas e Pesquisa profunda com citações.

Para ir além

Relações

Refatorações que chegam aqui 1
Aparece nas trilhas 1
Buscar