Domínio 1 — Agentic Architecture & Orchestration · Lição 1 de 7

O agentic loop e o controle por stop_reason

Objetivos de aprendizagem

O que é um agente, afinal

Um agente é um modelo que executa um ciclo repetido: reúne contexto, decide uma ação (geralmente chamando uma ferramenta), observa o resultado e decide de novo — até concluir a tarefa. A definição prática usada pela Anthropic: modelos usando ferramentas em um loop, dirigindo dinamicamente seus próprios processos. O ciclo conceitual tem três fases:

O ponto central para a prova: quem decide a próxima ação é o modelo, raciocinando sobre o contexto acumulado — não uma árvore de decisão if/else pré-configurada no seu código. Seu código apenas orquestra: envia requisições, executa as ferramentas pedidas e devolve os resultados.

O sinal de controle: stop_reason

Cada resposta da Messages API traz um campo stop_reason dizendo por que o modelo parou de gerar. É esse campo — e somente ele — que dirige o controle de fluxo do loop:

stop_reasonSignificadoO que o seu código faz
end_turnO modelo concluiu a resposta naturalmente.Encerra o loop e apresenta a resposta final ao usuário.
tool_useO modelo quer executar uma ou mais ferramentas.Executa as ferramentas pedidas, devolve os tool_result e continua o loop.
max_tokensA geração foi truncada pelo limite max_tokens.Trata como resposta incompleta: aumenta max_tokens e refaz a chamada (ou usa streaming). Nunca executa um tool_use possivelmente truncado.
pause_turnUm turno longo (ex.: ferramentas server-side) foi pausado pelo servidor.Reenvia a conversa com o response.content anexado como turno assistant; o servidor retoma de onde parou. Não adicione uma mensagem "continue".
refusalO modelo recusou por razões de segurança.Encerra o fluxo normal, registra e trata (ex.: mensagem ao usuário, fallback); inspecione stop_details para a categoria. Não executa ferramentas desse turno.
stop_sequenceUma sequência de parada customizada foi atingida.Trata como terminação intencional definida pela aplicação; response.stop_sequence informa qual sequência disparou.

Regra de ouro da prova: o loop continua enquanto stop_reason == "tool_use" e termina quando stop_reason == "end_turn". Qualquer alternativa que decida a parada olhando o texto da resposta ("verifique se o assistente escreveu 'tarefa concluída'") está errada.

flowchart TD
    A[Mensagem do usuário] --> B[messages.create com tools]
    B --> C{stop_reason?}
    C -- tool_use --> D[Executar ferramentas pedidas]
    D --> E[Anexar response.content ao histórico
+ tool_result em mensagem user] E --> B C -- pause_turn --> F[Reenviar conversa com o turno pausado] F --> B C -- max_tokens --> G[Aumentar max_tokens e repetir] G --> B C -- refusal --> H[Tratar recusa / fallback] C -- stop_sequence --> I[Terminação definida pela aplicação] C -- end_turn --> J[Resposta final ao usuário]

Esqueleto do loop em Python

O exemplo abaixo mostra o controle de fluxo completo, tratando todos os stop_reason. Note que a condição de parada é sempre o stop_reason — nunca o conteúdo do texto:

import anthropic

client = anthropic.Anthropic()

tools = [{
    "name": "lookup_order",
    "description": "Busca um pedido pelo numero. Use quando o cliente citar um pedido.",
    "input_schema": {
        "type": "object",
        "properties": {"order_id": {"type": "string"}},
        "required": ["order_id"],
    },
}]

def execute_tool(name, tool_input):
    # Implementacao real chamaria o backend; aqui, um stub.
    return '{"order_id": "%s", "status": "shipped"}' % tool_input.get("order_id", "?")

user_input = "Qual o status do pedido 4412?"
messages = [{"role": "user", "content": user_input}]
max_tokens = 4096

while True:
    response = client.messages.create(
        model="claude-opus-5",
        max_tokens=max_tokens,
        tools=tools,
        messages=messages,
    )

    if response.stop_reason == "end_turn":
        break  # tarefa concluida: sai do loop

    if response.stop_reason == "max_tokens":
        max_tokens = max_tokens * 2  # resposta truncada: nao execute tools deste turno
        continue

    if response.stop_reason == "pause_turn":
        # turno pausado pelo servidor: reenvie e ele retoma de onde parou
        messages.append({"role": "assistant", "content": response.content})
        continue

    if response.stop_reason == "refusal":
        print("Recusa de seguranca; encerrando o fluxo.")
        break

    if response.stop_reason == "stop_sequence":
        print("Parada customizada:", response.stop_sequence)
        break

    # stop_reason == "tool_use": executar e devolver resultados
    messages.append({"role": "assistant", "content": response.content})
    tool_results = []
    for block in response.content:
        if block.type == "tool_use":
            result = execute_tool(block.name, block.input)
            tool_results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": result,
            })
    messages.append({"role": "user", "content": tool_results})

final_text = next((b.text for b in response.content if b.type == "text"), "")
print(final_text)

Atenção com max_tokens + tool_use: se a resposta contém um bloco tool_use mas stop_reason é max_tokens, o input da ferramenta pode ter sido cortado no meio e ainda assim "parsear" como JSON válido parcial. Nunca execute a ferramenta nesse caso — repita a chamada com um limite maior.

Anti-padrões de terminação

Os task statements 1.1 listam explicitamente três anti-padrões — e as questões da prova os usam como distratores plausíveis:

Pegadinhas da prova

Resumo em 5 linhas

  1. Agente = modelo usando ferramentas em um loop: gather context → take action → verify results, com o modelo decidindo a próxima ação.
  2. O controle de fluxo do loop é dirigido exclusivamente por stop_reason: continua em tool_use, encerra em end_turn.
  3. max_tokens = truncado (repita com limite maior, sem executar tools); pause_turn = reenvie a conversa e o servidor retoma; refusal = trate a recusa; stop_sequence = parada definida pela aplicação.
  4. Anti-padrões: parsear texto para decidir a parada, cap de iterações como mecanismo primário e presença de texto como sinal de conclusão.
  5. Cap de iterações é aceitável apenas como rede de segurança secundária contra loops descontrolados.

Documentação oficial

Questões de fixação

1. Em um agentic loop com a Messages API, qual condição deve encerrar o loop e apresentar a resposta final ao usuário?

2. A resposta veio com um bloco tool_use e stop_reason == "max_tokens". O input da ferramenta parseia como JSON válido. O que fazer?

3. Durante um turno com ferramentas server-side, a resposta retorna stop_reason == "pause_turn". Qual é o tratamento correto?

4. (escolha duas) Quais das práticas abaixo são anti-padrões de terminação de agentic loop, segundo o exam guide?

5. O que diferencia um agente de decisão dirigida pelo modelo de um pipeline pré-configurado?