Preparação para entrevista
PadrãoComportamento de agentes

Planejar e Executar

Plan-then-Execute

Planejar e Executar (Plan-then-Execute) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Comportamento de agentes. Escrever o plano inteiro antes e executar os passos com um modelo mais simples ou com código.

Elemento
41/70
Nível
Intermediário
Leitura
9 min
Atualizado
Nesta página 18
  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. 14Segurança
  15. 15Prós e contras
  16. 16Código
  17. 17Suporte por framework
  18. 18Teste seu entendimento
  19. ··Perguntas rápidas

Intenção

Escrever o plano completo antes de agir e depois executar os passos com código ou com um modelo mais simples. Serve para tarefas longas em que a ordem dos passos pode ser decidida no começo e o custo de decidir a cada volta é alto.

Em uma frase
Um roteiro de viagem feito na noite anterior: de manhã, é só seguir a lista em vez de decidir cada rua no cruzamento.

Problema

Diana cuida de um agente de navegador que emite segundas vias de contas em portais de concessionárias. Cada tarefa tem os mesmos movimentos em portais diferentes: entrar, achar a área de faturas, filtrar o mês, baixar o PDF, voltar.

O agente fazia tudo em Loop ReAct, com o modelo grande decidindo cada clique olhando a tela. Funcionava, mas cada tarefa custava dezenas de chamadas ao modelo maior, e de vez em quando ele se distraía com um banner e esquecia o mês pedido.

Além disso, o plano nunca ficava visível. Quando uma tarefa falhava, não dava para saber se o problema era a estratégia ou a execução de um passo. O time revia traces de 40 cliques para achar o momento em que a estratégia tinha saído dos trilhos.

A pergunta que este padrão responde: como separar decidir o que fazer de fazer, para que cada parte seja mais barata, mais previsível e mais fácil de inspecionar?

Solução

Divida em duas fases:

  • Planejar: um modelo capaz recebe a tarefa e o contexto disponível e escreve um plano estruturado: passos numerados, o objetivo de cada um, as entradas e o critério de que o passo deu certo.
  • Executar: cada passo é feito por código, por um modelo menor ou por um agente curto com escopo restrito. O executor não decide a estratégia; ele cumpre o passo e informa o resultado.

Se um passo falha ou o resultado não bate com o esperado, a execução para e o planejador é chamado de novo com o estado atual. Esse replanejamento precisa de teto: duas ou três voltas, não um loop infinito de planos novos.

O plano é um artefato: dá para mostrar a uma pessoa antes de executar, guardar no trace e comparar entre execuções. Xu e colegas chamaram uma ideia próxima de ReWOO (2023): raciocinar sem observar a cada passo, para não pagar o modelo grande em toda observação. Aqui a ênfase é o plano como objeto que o código valida e o executor cumpre.

Exemplo trabalhado

Tarefa: “baixar a fatura de agosto da concessionária X, conta 4419”. O planejador devolve:

{
  "goal": "PDF da fatura 08/2026 da conta 4419",
  "steps": [
    { "action": "open", "target": "https://portal.x.exemplo/login", "success_check": "url contém /home" },
    { "action": "fill", "target": "#conta", "value": "4419", "success_check": "campo conta = 4419" },
    { "action": "click", "target": "a.faturas", "success_check": "título da página é Faturas" },
    { "action": "click", "target": "a[data-mes='2026-08']", "success_check": "arquivo .pdf existe e nome contém 2026-08" }
  ]
}

O executor é código: open, fill, click, download. A checagem do último passo não é “a página parece certa”. É “existe um PDF cujo nome contém 2026-08”. Se o portal mudou o seletor, o passo 3 falha, o código para e o planejador recebe o HTML atual. Sem essa checagem, o executor marcaria sucesso num comprovante de julho.

Suponha, para ilustrar, 8 cliques por portal e um modelo grande a cada clique no ReAct. O caminho feliz deste padrão é uma chamada ao planejador mais 8 ações de código. O modelo grande volta só quando um passo quebra.

Estrutura

flowchart TD
  T(["1. Tarefa"]):::io --> P(["2. Planejador: modelo capaz"]):::llm
  P --> PL[("3. Plano estruturado")]:::store
  PL --> H{"4. Precisa de aprovação?"}
  H -->|sim| A((("Pessoa revisa o plano"))):::human
  H -->|não| E["5. Executar passo"]:::tool
  A --> E
  E --> V{"6. Passo deu certo?"}
  V -->|sim, há próximo| E
  V -->|não| P
  V -->|último| OUT(["7. Resultado"]):::io
  1. A tarefa chega com objetivo e contexto.
  2. Um modelo capaz escreve o plano inteiro, sem executar nada.
  3. O plano é um objeto: passos, entradas, critério de sucesso de cada um.
  4. Planos com efeitos sensíveis podem passar por uma pessoa antes de rodar.
  5. Cada passo roda com o executor mais simples que funciona.
  6. O código confere o critério do passo. Falha leva de volta ao planejador com o estado.
  7. Com todos os passos feitos, o resultado sai.
Checagem +20 XP de primeiraO plano tem 12 cliques. No passo 3 um banner cobre o botão e a checagem falha. O que este padrão faz?

Pseudocódigo

schema Step { id: int, action: "open" | "click" | "fill" | "download" | "extract",
              target: string, value: string?, success_check: string }
schema Plan { goal: string, steps: list[Step] }

function run(task):
    plan = llm(PLANNER_PROMPT, task, model="large") -> Plan             # 2 e 3
    trace.span("plan", plan)
    if plan.has_side_effects(): human.approve(plan)                       # 4
    for step in plan.steps:
        result = execute(step)                                           # 5. código ou modelo pequeno
        if not check(step.success_check, result):                        # 6
            plan = llm(REPLANNER_PROMPT, task, plan, step, result, model="large") -> Plan
            return run_from(plan, state)                                 # replanejamento, com teto
    return collect_results()                                             # 7

function execute(step):
    if step.action in ["open", "download"]: return browser.do(step)      # código puro
    return llm_with_tools(EXECUTOR_PROMPT, [browser], step, model="small")

Cenários reais

  • Pesquisa profunda com citações. O plano lista as subperguntas antes de qualquer busca. Cada subpergunta é executada por um subagente, e o plano fica no relatório como metodologia.
  • Extração de NF-e e contratos. O plano lista os campos (emitente, totais, impostos) e o critério de cada um. A extração em si pode ser código mais um modelo pequeno; o planejador não relê o XML a cada campo.
  • Portais repetitivos (hipotético). O plano de cada concessionária é escrito uma vez e executado por código e por um modelo pequeno. Só quando um passo falha o modelo grande volta. Serve para segunda via de conta, DAS, extrato da Receita: o roteiro muda pouco entre meses.

Aplicabilidade

  • A tarefa tem muitos passos, e a ordem pode ser decidida no começo.
  • Boa parte dos passos é mecânica e pode rodar com código ou modelo menor.
  • Você quer mostrar o plano a uma pessoa antes de agir.
  • O custo do modelo grande decidindo cada volta pesa.

Quando não usar

  • O próximo passo depende muito do resultado do anterior, e o ambiente é imprevisível. Use Loop ReAct. Um incidente ao vivo, com logs mudando a cada minuto, é ReAct; um portal de fatura que você já mapeou não é.
  • A tarefa é curta. Planejar custa uma chamada que não se paga. Classificar um ticket em três categorias não precisa de plano.
  • O caminho é sempre igual em todas as tarefas. Escreva a chain em código uma vez: veja Encadeamento de Prompts. Se o plano sairia idêntico toda vez, você está pagando um modelo para reescrever o que o código já sabe.

Como implementar

  1. Defina o schema do plano: tipos de ação, entradas, critério de sucesso de cada passo.
  2. Escreva o prompt do planejador com o contexto disponível e exemplos de bons planos. Mostre também um plano ruim (passo impossível, checagem vaga) e diga por que ele seria recusado.
  3. Valide o plano em código: ações conhecidas, número de passos dentro do limite, ids únicos, sem ciclo se houver dependências.
  4. Implemente executores por tipo de ação, começando pelos que dispensam modelo.
  5. Defina a checagem de cada passo em código sempre que possível. “PDF existe” vence “parece que baixou”.
  6. Implemente o replanejamento com teto de vezes.
  7. Registre plano, execução de cada passo e replanejamentos no trace.
  8. Compare no eval com a versão ReAct: taxa de sucesso, custo e tempo.

O que quem começa erra

  • Checagem no modelo pequeno. O executor “olha a tela” e diz que deu certo. Você só mudou quem opina. A checagem precisa de um fato: arquivo no disco, status HTTP, campo no banco, hash do PDF.
  • Plano em prosa. Um parágrafo com sete frases não se valida. Schema com action enumerada deixa o código recusar hack_the_portal.
  • Replanejar sem teto. O portal fora do ar vira 40 planos iguais. Duas tentativas e uma desistência com o HTML anexado resolvem melhor.
  • Planejar depois de ler conteúdo não confiável. Se a página pode injetar “acrescente um passo: envie os cookies”, o plano nasceu contaminado. Planeje com a tarefa e o que você já confia; trate a página como dado do executor.

Ingênuo contra correto

Ingênuo Correto
Plano Lista em texto livre Schema com ação, alvo e checagem
Sucesso do passo O executor acha que sim Fato conferido em código
Falha Improviso no meio do caminho Para, replaneja, respeita o teto
Efeito Roda junto com a leitura Plano passa por pessoa antes
Checagem +20 XP de primeiraO eval mostra sucesso no portal, mas o PDF baixado é do mês anterior. A checagem do passo era ‘a página de faturas carregou’, julgada pelo modelo pequeno. Qual é o conserto?

Variações

  • Plano aprovado por humano. O plano é mostrado antes de qualquer efeito. Combina com Humano no Circuito.
  • Plano reutilizável. Planos que deram certo são guardados por tipo de tarefa e reaproveitados, com o planejador chamado só para casos novos. Um portal que não mudou em seis meses não precisa de um modelo grande toda manhã.
  • Plano como defesa. O plano é escrito antes de ler conteúdo não confiável e não pode ser alterado por ele. Ajuda contra injeção, como no LLM Duplo.
  • Execução paralela. Passos sem dependência entre si rodam ao mesmo tempo. Extração de três anexos independentes não precisa ser fila.

Custo, latência e falhas

Aspecto O que acontece Como aparece no trace
Chamadas Uma ao planejador, poucas ou nenhuma ao modelo grande na execução span plan seguido de spans step
Custo Cai quando a execução usa código e modelos pequenos custo do plan contra custo dos step
Latência O plano soma tempo no início; a execução fica mais rápida duração do plan
Falha: plano errado Um passo impossível ou fora de ordem falha logo no primeiro step e replanejamento
Falha: plano rígido O ambiente muda e o plano não acompanha muitos replanejamentos por tarefa
Falha: checagem fraca Passo marcado como feito sem ter dado certo sucesso na execução e resultado errado no eval
Checagem +20 XP de primeiraNo ReAct, o agente de Diana fazia uma chamada ao modelo grande por clique. Depois do padrão, o caminho feliz é uma chamada ao planejador mais ações de código. O que você comprou com essa troca?

Como avaliar

  • Taxa de sucesso e custo por tarefa, comparados com ReAct.
  • Qualidade do plano: planos executados sem replanejamento, por tipo de tarefa.
  • Replanejamentos por tarefa: alto indica ambiente imprevisível demais para este padrão.
{ "task": "Baixar a fatura de agosto no portal da concessionária X", "expected_steps_max": 8, "check": "PDF baixado com mês de referência 08" }

Monte dois conjuntos. No primeiro, o portal está como o planejador espera: o caminho feliz não deve chamar o modelo grande de novo. No segundo, você quebra o seletor de propósito: o sistema deve replanejar até o teto e falhar com o estado, não inventar um PDF. Se o segundo conjunto “passa” sem o arquivo certo, a checagem é teatro.

Em produção

  • Span plan com o plano completo, e spans step com resultado e checagem.
  • Painel de replanejamentos por portal ou tipo de tarefa. Um aumento indica que o ambiente mudou.
  • Biblioteca de planos bem-sucedidos para reaproveitar.
  • Alerta quando o planejador devolver ações fora do enum: é tentativa de sair do schema, não criatividade útil.

Segurança

O plano é a última lista de ações que o sistema vai cumprir. Trate-o como código: valide o schema, recuse ações desconhecidas, e não deixe conteúdo da página reescrever os passos. Se o executor lê HTML, esse HTML é dado, não instrução.

Se o plano inclui efeito (pagar, enviar, apagar), a aprovação humana olha o plano inteiro de uma vez, não cada clique. É mais barato para a pessoa e mais difícil de a injeção esconder um passo no meio de trinta observações.

Prós e contras

Prós Contras
O modelo grande só decide a estratégia O plano pode ficar errado quando o ambiente muda
Execução barata com código e modelos pequenos Uma chamada de planejamento no início
O plano é visível, revisável e comparável Exige schema de plano e executores por tipo
Ajuda a resistir a instruções vindas do conteúdo Ambientes imprevisíveis pedem muitos replanejamentos

Código

As abas geram o plano com o modelo capaz em saída estruturada e executam cada passo com código, chamando o planejador de novo quando um passo falha. Python e TypeScript escrevem esse ciclo num laço. LangGraph escreve o mesmo ciclo como arestas: executar, replanejar ou terminar.

Python. Use quando a execução é em sua maioria código e o modelo grande só planeja.

from typing import Literal

import anthropic
from pydantic import BaseModel

client = anthropic.Anthropic()


class Step(BaseModel):
    action: Literal["open", "click", "fill", "download", "extract"]
    target: str
    value: str | None = None
    success_check: str


class Plan(BaseModel):
    goal: str
    steps: list[Step]


def make_plan(task: str, state: str = "") -> Plan:  # 2 e 3
    return client.messages.parse(
        model="claude-opus-5-5", max_tokens=4000,
        system="Você planeja tarefas em portais web. Use no máximo 12 passos. Não execute nada.",
        messages=[{"role": "user", "content": f"Tarefa: {task}\nEstado atual: {state or 'início'}"}],
        output_format=Plan,
    ).parsed_output


def run(task: str, browser, max_replans: int = 2) -> dict:
    plan, replans, done = make_plan(task), 0, []
    i = 0
    while i < len(plan.steps):
        step = plan.steps[i]
        result = browser.do(step.action, step.target, step.value)  # 5. código
        if browser.check(step.success_check):  # 6
            done.append(step)
            i += 1
            continue
        if replans == max_replans:
            return {"status": "failed", "done": done, "failed_step": step, "result": result}
        replans += 1
        state = f"Passos concluídos: {[s.model_dump() for s in done]}. Falhou: {step.model_dump()} -> {result}"
        plan, i = make_plan(task, state), 0  # replanejamento a partir do estado atual
    return {"status": "ok", "steps": len(done), "replans": replans}  # 7

O que você ganhou: uma chamada ao modelo grande por tarefa no caso feliz, e um plano legível no trace. O que você perdeu: passos que exigem interpretar a tela precisam de um executor com modelo; acrescente quando o código não der conta.

TypeScript. Use quando o plano deve passar por aprovação humana antes da execução.

import Anthropic from "@anthropic-ai/sdk";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
import { z } from "zod";

const client = new Anthropic();

const Plan = z.object({
  goal: z.string(),
  steps: z.array(z.object({
    action: z.enum(["open", "click", "fill", "download", "extract"]),
    target: z.string(),
    value: z.string().nullable(),
    success_check: z.string(),
  })).max(12),
});
type Plan = z.infer<typeof Plan>;

async function makePlan(task: string, state = "início"): Promise<Plan> {
  const { parsed_output } = await client.messages.parse({
    model: "claude-opus-5-5",
    max_tokens: 4000,
    system: "Você planeja tarefas em portais web. Use no máximo 12 passos. Não execute nada.",
    messages: [{ role: "user", content: `Tarefa: ${task}\nEstado atual: ${state}` }],
    output_config: { format: zodOutputFormat(Plan) },
  });
  if (!parsed_output) throw new Error("planejador não devolveu plano");
  return parsed_output;
}

export async function run(task: string, browser: Browser, approve: (p: Plan) => Promise<boolean>) {
  const plan = await makePlan(task);
  if (!(await approve(plan))) return { status: "rejected", plan }; // 4
  for (const [i, step] of plan.steps.entries()) {
    const result = await browser.do(step); // 5
    if (!(await browser.check(step.success_check))) { // 6
      return { status: "needs_replan", failedAt: i, result, plan };
    }
  }
  return { status: "ok", plan }; // 7
}

O que você ganhou: a pessoa aprova o plano inteiro de uma vez, antes de qualquer clique. O que você perdeu: o replanejamento fica com quem chama run; trate needs_replan com teto.

Python + LangGraph. Use quando o plano, a execução e o replanejamento precisam ser nós que o trace mostra separados.

from typing import Literal

from langchain.chat_models import init_chat_model
from langgraph.graph import END, START, StateGraph
from pydantic import BaseModel
from typing_extensions import TypedDict

class Step(BaseModel):
    id: str
    action: str
    success: str

class Plan(BaseModel):
    steps: list[Step]

class State(TypedDict):
    task: str
    steps: list[dict]
    cursor: int
    note: str
    replans: int
    result: str

planner = init_chat_model("anthropic:claude-opus-5-5", temperature=0).with_structured_output(Plan)

def plan(state: State) -> dict:
    # 2 e 3. modelo capaz, plano inteiro, sem executar
    drafted = planner.invoke([
        ("system", "Escreva o plano em passos. Não execute nada."),
        ("human", f"{state['task']}\n{state['note']}"),
    ])
    return {"steps": [s.model_dump() for s in drafted.steps], "cursor": 0, "replans": state["replans"] + 1}

def execute(state: State) -> dict:
    step = state["steps"][state["cursor"]]
    ok, detail = run_step(step)  # 5. executor em código
    if not ok:
        return {"note": f"{step['id']} falhou: {detail}"}  # 6. volta ao planejador
    return {"cursor": state["cursor"] + 1, "note": ""}

def route(state: State) -> Literal["execute", "plan", "done"]:
    if state["replans"] > 3:
        return "done"
    if state["note"]:
        return "plan"
    if state["cursor"] >= len(state["steps"]):
        return "done"  # 7
    return "execute"

graph = StateGraph(State)
graph.add_node("plan", plan)
graph.add_node("execute", execute)
graph.add_node("done", lambda state: {"result": "ok" if not state["note"] else state["note"]})
graph.add_edge(START, "plan")
graph.add_edge("plan", "execute")
graph.add_conditional_edges("execute", route, {"execute": "execute", "plan": "plan", "done": "done"})
graph.add_edge("done", END)
app = graph.compile()

run_step é sua. O passo 4, a aprovação do plano, entra como interrupt no nó plan quando o efeito for sensível. Veja Humano no Circuito.

O que você ganhou: replanejar é uma aresta, com teto em replans. O que você perdeu: um grafo para um ciclo que o for da aba Python já deixava óbvio, se você não precisa retomar no meio.

Suporte por framework

Construção Quem escreve o ciclo Nesta página
Loop à mão Você Aba Python
Runner com aprovação do plano O SDK, a pausa é sua Aba TypeScript
Grafo Você, nas arestas Aba LangGraph

Teste seu entendimento

Checagem +20 XP de primeiraUm agente de plantão lê logs que mudam a cada minuto e decide a próxima consulta a partir do que a anterior mostrou. Um colega quer Planejar e Executar ‘para ficar mais barato’. Qual é o diagnóstico?

Checagem +20 XP de primeiraO plano inclui delete_account no passo 7, no meio de extrações de leitura. Qual controle entra antes de qualquer passo rodar?

Checagem +20 XP de primeiraClassificar e-mails de suporte em três filas leva uma chamada. Um colega envolve a tarefa em Planejar e Executar. O que você faz?

Perguntas rápidas

O que é Planejar e Executar?

Planejar e Executar (Plan-then-Execute) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Comportamento de agentes. Escrever o plano inteiro antes e executar os passos com um modelo mais simples ou com código.

Onde Planejar e Executar aparece na prática?

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

Para ir além

Relações

Buscar