Objetivo da Aula
Ao concluir esta aula, você será capaz de:
Implementar o padrão ReAct (Reasoning + Acting) com tool calls intercaladas
Implementar Plan-Execute: planejamento JSON seguido de execução determinística
Implementar Reflection: geração → crítica → melhoria cíclica
Selecionar o padrão adequado para cada tipo de tarefa
Comparar latência, custo e qualidade entre os 3 padrões
Por que isso importa
O pattern de raciocínio define como o agente pensa, não só o que faz. A mesma tarefa com ReAct, Plan-Execute e Reflection vai produzir resultados diferentes, com custos e latências diferentes.
Em 2024, “agente de IA” se tornou sinônimo de “loop com tool calls”. Mas profissionais que entendem os padrões de raciocínio tomam decisões muito melhores de design:
RAG pipeline para documento novo: ReAct (cada busca informa a próxima)
Pipeline de CI/CD assistido: Plan-Execute (plano validado antes de executar)
Geração de relatório executivo: Reflection (qualidade > velocidade)
Sistema de atendimento ao cliente: ReAct com guardrails
Geração de código com testes: Plan-Execute + Reflection no output
Entender os 3 padrões é a diferença entre um dev que “usa LLM” e um que arquiteta sistemas de IA.
Conceitos Fundamentais
Decisão de Arquitetura: ReAct vs Plan-Execute vs Reflection
São três estratégias de raciocínio com perfis distintos de custo, latência, qualidade e adaptabilidade:
• ReAct (raciocinar → agir → observar → repetir): adapta-se a resultados inesperados, boa para exploração e diagnóstico. Imprevisível em número de iterações — definir max_iterations como válvula de custo.
• Plan-Execute (planejar tudo → executar deterministicamente): auditável, estável, boa para fluxos irreversíveis onde o plano pode ser revisado por um humano antes da execução. Não se adapta a falhas durante a execução.
• Reflection (gerar → criticar → refinar): maximiza qualidade ao custo de 2N+1 chamadas ao LLM. Use quando qualidade da saída é crítica e latência é tolerável.
Regra geral: prefira Plan-Execute para ações de alto impacto; ReAct para diagnóstico e exploração; Reflection para geração de conteúdo de alta qualidade.
Padrão ReAct (Reasoning + Acting)
O nome vem do paper “ReAct: Synergizing Reasoning and Acting in Language Models” (Yao et al., 2022). A ideia: intercalar raciocínio e ação, onde cada ação informa o próximo raciocínio.
Thought: Preciso calcular juros compostos. Fórmula: A=P(1+r)^n
Action: calcular(10000 * (1+0.01)**24)
Observation: 12697.35
Thought: O resultado é R$12.697. Agora verifico a fórmula de juros simples para comparar.
Action: calcular(10000 * (1 + 0.01 * 24))
Observation: 12400
Thought: Tenho ambos os valores. Posso responder.O Claude implementa ReAct naturalmente quando você: 1. Passa tools=TOOLS_SCHEMA 2. Adiciona response.content inteiro ao histórico (inclui os TextBlocks de raciocínio)
def react_agent(tarefa: str) -> str:
mensagens = [{'role': 'user', 'content': f'Execute a tarefa passo a passo:\n{tarefa}'}]
for _ in range(8):
response = client.messages.create(
model='claude-haiku-4-5-20251001',
max_tokens=600,
tools=TOOLS,
messages=mensagens,
)
mensagens.append({'role': 'assistant', 'content': response.content})
if response.stop_reason == 'end_turn':
return next((b.text for b in response.content if hasattr(b, 'text')), '')
tool_results = []
for block in response.content:
if block.type != 'tool_use':
continue
resultado = TOOL_FUNCTIONS[block.name](**block.input)
tool_results.append({'type': 'tool_result', 'tool_use_id': block.id, 'content': resultado})
mensagens.append({'role': 'user', 'content': tool_results})
return 'Iterações esgotadas.'Quando ReAct vence: tarefas exploratórias onde cada resultado muda o plano. Diagnóstico de bugs, pesquisa iterativa, tarefas onde o caminho não é conhecido antecipadamente.
Padrão Plan-Execute
Dois estágios separados: primeiro cria um plano completo, depois executa deterministicamente cada passo.
[Estágio 1 — PLAN]
Input: "Calcule juros compostos de R$10.000 a 1% por 24 meses"
Output (JSON): [
{"passo": 1, "acao": "buscar_info", "parametro": "juros compostos fórmula"},
{"passo": 2, "acao": "calcular", "parametro": "10000 * (1+0.01)**24"},
]
[Estágio 2 — EXECUTE]
Passo 1: buscar_info("juros compostos fórmula") → "A=P(1+r)^n"
Passo 2: calcular("10000 * (1+0.01)**24") → "12697.35"
[Estágio 3 — SYNTHESIZE]
Input: tarefa + resultados
Output: resposta final
def plan_execute_agent(tarefa: str) -> str:
# Etapa 1: gerar plano JSON
plan_response = client.messages.create(
model='claude-haiku-4-5-20251001',
max_tokens=400,
messages=[{
'role': 'user',
'content': f'Crie um plano JSON: [{{"passo":1,"acao":"calcular|buscar_info","parametro":"..."}}]\nTAREFA: {tarefa}'
}],
)
plano = json.loads(plan_response.content[0].text)
# Etapa 2: executar deterministicamente
resultados = []
for passo in plano:
func = TOOL_FUNCTIONS.get(passo['acao'])
if func:
chave = 'expressao' if passo['acao'] == 'calcular' else 'topico'
resultado = func(**{chave: passo['parametro']})
resultados.append(f"Passo {passo['passo']}: {resultado}")
# Etapa 3: síntese
sintese = client.messages.create(
model='claude-haiku-4-5-20251001',
max_tokens=300,
messages=[{'role': 'user', 'content': f'Tarefa: {tarefa}\nResultados: {resultados}\nSintetize.'}],
)
return sintese.content[0].textVantagem: o plano pode ser validado por um humano antes de executar — útil para ações irreversíveis. Desvantagem: se o passo 2 falhar, o plano não se adapta (ao contrário do ReAct).
Quando Plan-Execute vence: tarefas bem definidas onde o plano pode ser auditado. Pipelines de dados, geração de relatórios com passos fixos, automação de workflows.
Padrão Reflection
Gera uma resposta inicial, depois a critica e melhora iterativamente.
[Turno 1] Gerar resposta inicial
[Turno 2] Criticar a resposta: "Faltou dados quantitativos no ponto 2"
[Turno 3] Melhorar baseado na crítica
[Turno 4] Criticar novamente... (até N ciclos)
def reflection_agent(tarefa: str, ciclos: int = 2) -> str:
# Gerar resposta inicial
resposta = client.messages.create(
model='claude-haiku-4-5-20251001',
max_tokens=400,
messages=[{'role': 'user', 'content': tarefa}],
).content[0].text
for ciclo in range(ciclos):
# Criticar
critica = client.messages.create(
model='claude-haiku-4-5-20251001',
max_tokens=300,
messages=[{
'role': 'user',
'content': f'Critique esta resposta e liste 2-3 melhorias:\n\nRESPOSTA: {resposta}'
}],
).content[0].text
# Melhorar
resposta = client.messages.create(
model='claude-haiku-4-5-20251001',
max_tokens=400,
messages=[{
'role': 'user',
'content': f'Melhore a resposta com base nas críticas:\n\nResposta: {resposta}\nCríticas: {critica}'
}],
).content[0].text
return respostaCusto: 2N+1 chamadas de API para N ciclos de reflexão. Para ciclos=2: 5 chamadas. Use com parcimônia.
Quando Reflection vence: geração de texto de alta qualidade (relatórios executivos, documentação, propostas), código complexo, qualquer saída onde “bom o suficiente na primeira tentativa” não serve.
Aprofundamento Técnico
Comparativo Empírico dos 3 Padrões
Para a tarefa “Calcule R$10.000 a 1% a.m. por 24 meses com juros compostos”:
Métrica | ReAct | Plan-Execute | Reflection |
Chamadas de API | 2-4 | 3 (fixo) | 5 (2 ciclos) |
Latência | Variável | Previsível | Alta |
Qualidade | Boa | Boa | Excelente |
Adaptabilidade | Alta | Baixa | N/A |
Custo | Médio | Baixo-médio | Alto |
ReAct com Tool Calling Paralelo
O Claude pode chamar múltiplas tools em paralelo em uma iteração (parallelism in tool use):
Iteração 1:
→ buscar_info('juros compostos') ← chamadas paralelas
→ calcular('10000 * (1+0.01)**24')Isso acontece quando o Claude vê que as ações são independentes. Para habilitar, o SDK do Python não precisa de configuração adicional — o Claude decide automaticamente. Os tool_use blocks vão aparecer múltiplos no mesmo response.content.
Seu loop já suporta isso:
for block in response.content:
if block.type != 'tool_use':
continue
# Processa todos os tool_use blocks da iteraçãoPlan-Execute com Validação de Plano
Em produção, você pode validar o plano antes de executar:
def plan_execute_com_validacao(tarefa: str) -> str:
plano = gerar_plano(tarefa)
# Validar: máximo de passos, ações permitidas
if len(plano) > 10:
raise ValueError(f'Plano muito longo: {len(plano)} passos')
acoes_permitidas = {'calcular', 'buscar_info'}
for passo in plano:
if passo['acao'] not in acoes_permitidas:
raise ValueError(f'Ação não permitida: {passo["acao"]}')
# Human-in-the-loop: mostrar plano e confirmar
print(f'Plano gerado:\n{json.dumps(plano, indent=2, ensure_ascii=False)}')
confirmacao = input('Confirmar execução? (s/n): ')
if confirmacao.lower() != 's':
return 'Execução cancelada pelo usuário.'
return executar_plano(plano, tarefa)Reflection com Critério de Parada
def reflection_com_criterio(tarefa: str, max_ciclos: int = 3) -> str:
resposta = gerar_resposta_inicial(tarefa)
for ciclo in range(max_ciclos):
avaliacao = client.messages.create(
model='claude-haiku-4-5-20251001',
max_tokens=100,
messages=[{
'role': 'user',
'content': f'Esta resposta está boa o suficiente? Responda apenas: APROVADO ou MELHORAR.\n\nResposta: {resposta}'
}],
).content[0].text.strip()
if 'APROVADO' in avaliacao.upper():
print(f'Aprovado no ciclo {ciclo+1}')
break
resposta = melhorar_resposta(resposta, tarefa)
return respostaExemplos Anotados
Exemplo 1: ReAct para Cálculo Financeiro
tarefa = 'Calcule quanto vale R$10.000 em juros compostos com 1% ao mês por 24 meses. Mostre o cálculo passo a passo.'
# O Claude vai (tipicamente) fazer:
# Iter 1: buscar_info('juros compostos') para confirmar fórmula
# Iter 2: calcular('10000 * (1+0.01)**24')
# Iter 3 (end_turn): explicar resultado com contexto
# Output esperado:
# "R$10.000 a 1% ao mês por 24 meses = R$12.697,35 (juros compostos)
# Isso é R$2.697,35 de juros, 26,97% de crescimento.
# Para comparação, juros simples dariam apenas R$12.400."Exemplo 2: Diferença de Qualidade — Reflection
# Sem reflection:
resultado_simples = 'Juros compostos têm crescimento exponencial. A fórmula é A=P(1+r)^n.'
# Com 2 ciclos de reflection:
resultado_refletido = """
Juros compostos são fundamentais em finanças. A fórmula A=P(1+r)^n mostra como
o capital cresce exponencialmente:
- P = R$10.000 (principal)
- r = 0,01 (1% ao mês)
- n = 24 (24 meses = 2 anos)
- A = R$10.000 × (1,01)^24 = R$12.697,35
Comparado com juros simples (R$12.400), a diferença de R$297,35 parece pequena,
mas em 120 meses (10 anos) seria: compostos R$33.004 vs simples R$22.000 —
uma diferença de R$11.004 (50% a mais).
Regra prática: taxa 1% ao mês ≈ 12,68% ao ano (não 12%, por causa do efeito composto).
"""Padrões e Armadilhas
Padrões
Padrão 1: Instrução explícita de raciocínio no user message para ReAct
# Bom: pede raciocínio explícito
'Pense passo a passo antes de agir. Para cada passo, explique o raciocínio.'
# Menor qualidade: só pede resultado
'Resolva esta tarefa.'Padrão 2: Plan-Execute com JSON Schema estrito
'Formato: [{"passo": int, "acao": "calcular|buscar_info", "parametro": "string"}]'Schema explícito produz JSON válido em 95%+ dos casos. Sem schema, o modelo pode retornar JSON com campos extras ou nomes diferentes.
Padrão 3: Número de ciclos de Reflection como parâmetro configurável Para produção, 1-2 ciclos é suficiente. Para geração crítica (contratos, relatórios médicos), 3. Nunca hardcode.
Armadilhas
⚠️ Armadilha 1: Usar Reflection para tudo Reflection usa 2N+1 chamadas. Para uma query simples (“qual o capital do Brasil?”), é desperdício. Reserve para outputs de alta qualidade.
⚠️ Armadilha 2: Plan-Execute sem tratamento de erro no plan_response
# PERIGOSO: json.loads() vai explodir se o Claude não retornar JSON puro
plano = json.loads(plan_response.content[0].text)
# SEGURO: parse com fallback
try:
texto = plan_response.content[0].text
# Extrair JSON mesmo se tiver texto ao redor
inicio = texto.find('[')
fim = texto.rfind(']') + 1
plano = json.loads(texto[inicio:fim])
except Exception:
plano = [{'passo': 1, 'acao': 'buscar_info', 'parametro': tarefa}]⚠️ Armadilha 3: ReAct sem limite de iterações em tarefas abertas Tarefas vagas como “pesquise tudo sobre IA” podem gerar 20+ iterações. Limite de 8 é seguro para a maioria dos casos.
Se não for realizar o laboratório, pule para o próximo capítulo.
Ponte para o Lab
O starter desta unidade implementa os 3 padrões completos — sem TODOs de código.
Rodando:
python starter.pyO que observar: 1. ReAct: quantas iterações o Claude usa? O raciocínio (TextBlocks) aparece no output? 2. Plan-Execute: o plano JSON gerado faz sentido? Quantos passos? 3. Reflection: a resposta após 2 ciclos é visivelmente melhor que a inicial?
Experimentos para aprofundar: - Mude ciclos=2 para ciclos=1 e ciclos=3 em Reflection — observe a diferença - Adicione print(resposta) após cada ciclo para ver a evolução - Implemente o critério de parada antecipada do Exemplo “Reflection com Critério de Parada” - Para Plan-Execute: adicione validação que rejeita planos com mais de 5 passos
Agora você está pronto para o lab.