Alternar tema

Design de agentes human-in-the-loop: quais etapas precisam de aprovação humana

Easton editorial illustration: agent rollout and rollback rail

"A documentação human-in-the-loop do OpenAI Agents SDK descreve tools que exigem approval, interruptions do run e retomada com RunState após approve ou reject."

O rascunho de uma mensagem no Feishu já está pronto: título, corpo e link do anexo. Falta só enviar. Mas o agente para antes de send_message e espera sua confirmação.

Um e-mail pode ser gerado automaticamente, mas antes de ir para um cliente a interface precisa mostrar destinatário, assunto, resumo do corpo e anexos. Um formulário CMS pode ser preenchido sozinho, mas submit_form é bloqueado por policy e espera owner approve. Parece um botão de confirmação, mas a fronteira real é o estado de execução. O RunState do agente é salvo e a execução só continua depois da decisão humana. Onde pausar, quem aprova e o que fazer em reject ou timeout não é detalhe de frontend: é segurança do sistema de tools.

Este guia traz uma matriz de risco, uma checklist de pontos de aprovação, um mecanismo de pausa/retomada e um modelo de auditoria. A ideia é sair de “colocar um botão” para um estado serializável, retomável e auditável.

Matriz de risco: decidir quais ações precisam de aprovação

Nem tudo precisa de aprovação. Leituras podem rodar sozinhas; apagar e pagar devem esperar uma pessoa. Comece com cinco dimensões:

DimensãoCritérioExemplos de ações
Impacto externoToca sistemas ou usuários externosEnviar e-mail, enviar formulário, chamar API externa, escrever em Feishu/Slack
ReversibilidadeA ação pode ser desfeita?Apagar registro (irreversível), salvar rascunho (reversível), pagamento (compensação parcial), enviar mensagem (irreversível)
Sensibilidade dos dadosNível dos dados envolvidosConsultar dados públicos, modificar registros internos, exportar privacidade de usuários, ler config de produção
Limite financeiro/permissõesEnvolve dinheiro ou permissõesPagamento, transferência, reembolso, mudança de permissões, operação em massa, apagar dados de usuários
Nível de autonomiaAutomação permitidaConsulta read-only (automática), rascunho (automático), mensagem externa (confirmação), apagar/pagar (approval)

Essa matriz segue a lógica da OWASP LLM06: funcionalidade excessiva, permissões excessivas e autonomia excessiva. Use como base e ajuste os limites ao negócio.

Impacto externo: qualquer ação que alcance outra pessoa ou sistema merece cuidado. Um e-mail enviado não volta. Um formulário pode disparar um pedido. Uma API externa pode alterar dados de outro sistema.

Reversibilidade: apagar registro é irreversível; rascunho se edita; pagamento pode exigir reembolso ou compensação.

Sensibilidade dos dados: dados públicos podem ser lidos com mais liberdade; dados internos precisam de controle de escrita; privacidade e configuração de produção pedem aprovação.

Limite financeiro/permissões: dinheiro e permissões devem pausar. Pagamentos, transferências, reembolsos, mudanças de permissão e operações em massa são pontos de alto risco.

Nível de autonomia: leituras podem ser automáticas. Rascunhos também. Saídas externas exigem confirmação; apagar e pagar exigem approval.

A matriz evolui. Uma mensagem interna no Feishu pode ficar em confirmação; um pagamento real continua com approval e revisão de duas pessoas.

Três cenários reais de classificação

Caso 1: rascunho de mensagem no Feishu

Escrever um rascunho no Feishu é automático (L0). Ele fica em rascunhos, não é enviado, é reversível e não tem impacto externo. Chamar send_message para um cliente exige approval (L2). Depois de enviada, a mensagem é irreversível, sai para fora e pode conter informação sensível. Confirmar antes de MCP tools/call é esse caso.

Caso 2: envio de e-mail

Gerar o conteúdo do e-mail é automático (L0). Ainda é texto. Enviar pela API exige approval com destinatário, assunto e anexos (L2). A UI deve mostrar evidência e resumo, não só “confirmar envio”.

Caso 3: envio de formulário CMS

Preencher o formulário é automático (L0). Nada foi enviado. Chamar a API do CMS para enviar deve bloquear e esperar owner approve (L2). O bloqueio pode vir de um guardrail, como “valor acima do limite”, ou de uma policy fixa.

Caso 4: apagar banco de dados de produção

Consultar produção é automático (L0). É leitura. Apagar em produção exige aprovação humana forte + auditoria de backup (L3). É irreversível, sensível e afeta usuários. Deve ser limitado por Policy, não só por Guardrail.

A lição: na mesma tarefa, cada etapa tem risco diferente. Rascunho é automático, envio pede approval, apagar produção pede revisão dupla. Classifique ações concretas.

Checklist de pontos de aprovação: descer até o tipo de ação

Com a matriz, defina níveis:

L0 automático: consultas de banco, busca vetorial, leitura de configuração; salvar rascunho ou gerar preview. Sem impacto externo, reversível, sem dados sensíveis.

L1 confirmação: mensagens ou dados externos, como e-mail, formulário, API externa; leituras em massa como exportação. Há impacto externo, mas controlável.

L2 approval: apagar, mudar permissões, apagar em massa; escrever em Feishu, Slack ou CRM. São ações irreversíveis ou de alto impacto.

L3 strong approval + revisão dupla: pagamentos, reembolsos, exportar privacidade, mudar config de produção, apagar banco de produção. Dinheiro ou dados sensíveis.

A lista combina com needs_approval no OpenAI Agents SDK e com segurança MCP. MCP espera tool calls visíveis, recusáveis e confirmados para ações sensíveis.

Você pode ajustar:

Se Feishu é só notificação interna, L1 pode bastar.
Se apagar toca dados de usuários, mantenha L2.
Se pagamento é crítico, suba para L3 com motivo obrigatório.

A lista muda com o negócio. Se a regra mudar, “enviar mensagem” pode sair de approval.

Máquina de estados do fluxo de aprovação: pausar, salvar, retomar

Aprovação não é pop-up. É um run pausado. Quando um tool call precisa de approval, RunState é salvo e a execução retoma depois da decisão.

Diagrama de transição de estado

O fluxo é:

request -> pending -> approved/rejected/timeout -> resume/abort/compensate

request: o tool call cria a solicitação; RunState contém tool, argumentos e contexto.
pending: o run espera decisão; o estado é salvo em checkpoint e ligado a thread_id.
approved: aprovado; o run retoma do checkpoint e chama o tool.
rejected: rejeitado; vai para abort ou convert to draft.
timeout: expira; escala ou auto-reject.
resume/abort/compensate: continuar, parar ou compensar.

Modos de retomada

approve: continuar, chamar o tool e seguir.
reject: parar ou converter em rascunho, sem chamar o tool.
edit: modificar parâmetros, como destinatário ou conteúdo, e aprovar de novo.

Checkpoint e thread state são a base técnica. O artigo publicado sobre LangGraph checkpoint/thread state explica como salvar o ponto de pausa e voltar a ele.

Exemplo de código OpenAI Agents SDK HITL

Este exemplo mostra o fluxo no OpenAI Agents SDK. A API pode mudar; confira a documentação oficial antes de produção:

from agents import Agent, Runner, function_tool


@function_tool(needs_approval=True)
def send_email(to: str, subject: str, body: str) -> str:
    return send_email_handler(to=to, subject=subject, body=body)


agent = Agent(
    name="EmailAgent",
    tools=[send_email],
    instructions="Rascunhar o e-mail e esperar aprovação antes do envio",
)

result = Runner.run_sync(agent, "Escreva um aviso de reembolso para o cliente")

if result.interruptions:
    state = result.to_state()

    for interruption in result.interruptions:
        print(f"Tool pendente de aprovação: {interruption.tool_name}")
        print(f"Argumentos: {interruption.arguments}")

        decision = show_approval_ui(interruption)

        if decision == "approve":
            state.approve(interruption)
        elif decision == "reject":
            state.reject(interruption)

    result = Runner.run_sync(agent, state)

Pontos principais:

needs_approval=True marca o tool.
interruptions contém tool calls pendentes.
result.to_state() cria um RunState serializável.
state.approve() ou state.reject() registra a decisão.
Runner.run_sync(agent, state) retoma da pausa.

Os nomes podem mudar depois de 2026-07; confira a documentação oficial.

Exemplo de código LangGraph interrupt/resume

Este exemplo usa interrupt e Command(resume=...):

from langgraph.graph import StateGraph, MessagesState
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import Command, interrupt


def send_email_node(state: MessagesState):
    approved = interrupt({
        "action": "send_email",
        "summary": state["email_summary"],
    })

    if approved != "approved":
        return {"messages": ["O envio do e-mail foi rejeitado; a mensagem foi salva como rascunho"]}

    email_result = send_email(state["email_params"])
    return {"messages": [email_result]}


graph = StateGraph(MessagesState)
graph.add_node("send_email", send_email_node)
graph.add_edge("draft_email", "send_email")

checkpointer = MemorySaver()
app = graph.compile(checkpointer=checkpointer)

thread_id = "thread_123"
config = {"configurable": {"thread_id": thread_id}}

result = app.invoke(
    {"messages": ["Escreva uma notificação de reembolso para o cliente"]},
    config=config,
)

# Após a pausa, o payload de interrupt volta ao chamador.
# Mostre a UI de aprovação e espere a decisão humana.
decision = show_approval_ui(result["__interrupt__"])

if decision == "approve":
    app.invoke(Command(resume="approved"), config=config)
elif decision == "reject":
    app.invoke(Command(resume="rejected"), config=config)
elif decision == "edit":
    app.update_state(config, {"email_params": {"to": "new_customer@example.com"}})
    app.invoke(Command(resume="approved"), config=config)

Pontos principais:

interrupt() pausa o graph.
Command(resume=...) retoma.
checkpoint + thread_id mantêm consistência.
approve/reject/edit são rotas de retomada.

Confira também a API do LangGraph.

Campos de evidência de aprovação: o que guardar e como rastrear

Aprovação também é registro. O audit log mínimo:

CampoDescriçãoExemplo
tool_nameTool + tipo de operaçãosend_email / delete_record
tool_argumentsJSON completo de argumentos{“to”: “customer@example.com”, “subject”: “Aviso de reembolso”}
invoker_idIdentidade chamadorauser@example.com / agent_run_abc123
request_timeHora da solicitação2026-06-23T09:26:10Z
approver_idIdentidade do aprovadoron-call-engineer@example.com
decision_timeHora da decisão2026-06-23T09:35:12Z
decisionResultadoapproved / rejected / timeout_auto_reject
evidenceEvidência: captura ou resumo”Destinatário correto, sem dados sensíveis”
audit_trail_idLink para logs do runrun_abc123_step_5_tool_3

Esses campos vêm de recomendações MCP Tools e de items OpenAI API como mcp_approval_request. Logs de aprovação são observabilidade; o artigo Agent monitoring/recovery cobre logging de forma mais ampla.

Como serializar RunState? No OpenAI Agents SDK, result.to_state() converte o resultado pausado. LangGraph usa checkpoint + thread_id. Salve o estado em banco ou logs e vincule a audit_trail_id.

Usos principais:

Rastreamento de incidentes: saber quem aprovou o quê e quando.
Evidência de compliance: demonstrar aprovação humana em ações críticas.
Melhoria de policy: medir ações aprovadas, rejeitadas ou expiradas.

Você pode adicionar duração, canal Slack/Feishu/e-mail ou revisão dupla. Mas o mínimo precisa existir.

Rejeição e timeout: o que acontece quando a aprovação falha

A aprovação nem sempre passa. Reject e timeout precisam de rotas claras para o run não travar.

Três rotas após rejeição

Rota 1: continue with fallback. Use uma ação menos arriscada. Se o envio de e-mail for rejeitado, salve como rascunho e continue.

Rota 2: convert to draft. Transforme a ação em rascunho. Se o envio CMS for rejeitado, deixe para edição humana.

Rota 3: abort task. Pare toda a tarefa. Se apagar produção for rejeitado, o run deve parar.

Escolha por tipo:

Reversível: fallback ou convert to draft.
Irreversível e de alto risco: abort task.
Precisa de intervenção humana: escalate.

Duas rotas após timeout

Rota 1: escalate to backup approver. Se o aprovador principal não responder em 30 minutos, envie ao on-call engineer.

Rota 2: auto-reject. Após uma hora, rejeite automaticamente e pare. Útil para baixo risco com limite de tempo.

Escolha por contexto:

Alto risco: escalar, não executar automaticamente.
Tempo crítico: auto-reject para não bloquear.
Caso geral: escalar.

Como desfazer etapas já executadas

Depois de uma rejeição, algo pode ter sido feito. Se o agente criou um pedido antes da rejeição do pagamento, cancele o pedido.

Estratégias:

Checkpoint rollback: voltar ao checkpoint antes de approval.
Transação compensatória: chamar uma API, como cancelar pedido.
Intervenção humana: avisar alguém quando não dá para automatizar.

Nem sempre dá para desfazer. E-mail enviado não volta. Nesse caso ficam audit log e tratamento posterior.

Quatro fronteiras de segurança: combinar Policy, Guardrail, Approval e Audit

Approval não é proteção isolada. Policy, Guardrail, Approval e Audit se combinam e não se substituem.

Tabela de responsabilidades em quatro camadas

CamadaResponsabilidadeExemplo
PolicyRegras estáticas que limitam tools”Não apagar produção”, “tool de pagamento só em sandbox”
GuardrailChecagens automáticas de entrada/saídaValidação, sanitização, filtro de dados sensíveis, limite de valor
ApprovalDecisão humana para alto riscoMostrar destinatário e conteúdo antes do e-mail, confirmar exclusão, aprovar pagamento
AuditRastreabilidade posteriorLogs de aprovação, tool calls, mudanças de estado

Policy não substitui Guardrail: é estática.
Guardrail não substitui Approval: não toma decisão de negócio.
Approval não substitui Audit: decisão e rastreabilidade são diferentes.
Audit não substitui as três primeiras: chega depois do risco.

Exemplos:

Pagamento: Policy limita valor, Guardrail valida argumentos, Approval exige revisão dupla, Audit registra.
E-mail: Policy limita domínios, Guardrail revisa conteúdo sensível, Approval mostra resumo, Audit registra envio.

Fronteira de segurança de tools MCP

MCP (Model Context Protocol) tem sua própria fronteira. Para a spec 2025-06-18, confira a versão antes de publicar:

tools/list mostra tools disponíveis.
tools/call antes de ações sensíveis corresponde a Approval.
inputSchema corresponde a Guardrail.
timeout evita tool calls travados.
audit logging corresponde a Audit.

Lembrete: MCP approval não substitui OAuth scope nem server-side authorization. MCP approval confirma um tool call; OAuth scope dá permissão de API; server-side authorization verifica permissões de negócio. Os três são necessários.

Um servidor Feishu MCP pode ter OAuth e scope send_message. Isso não torna toda mensagem segura. MCP approval revisa conteúdo; server-side authorization revisa destinatário permitido.

Casos de mensagens externas, escritas colaborativas e alterações em massa de tabelas ficarão no artigo planejado sobre Feishu MCP.

Design da UI de aprovação

A UI não é só approve/reject. Ela precisa mostrar informação suficiente.

Princípios:

Mostrar tool e argumentos.
Mostrar impacto esperado, como “enviar e-mail para customer@example.com com assunto Aviso de reembolso”.
Mostrar reversibilidade: “irreversível após envio” ou “restaurável após exclusão”.
Mostrar sensibilidade dos dados.
Separar cancel e reject. Cancel abandona a interação; reject nega o tool call e audita.

Elementos:

Tool + tipo de operação
Argumentos completos, dobráveis
Resumo do impacto
Aviso de reversibilidade
Rótulo de sensibilidade
Campo de motivo
Botões approve / reject / cancel

Importante: UI não substitui server-side authorization. Mesmo depois de confirmar, o backend verifica chamador, objeto e permissão.

Se a UI diz “apagar record ID=123”, o backend ainda verifica dono e permissão.

HITL não é pop-up isolado. Ele fica no tool gateway, logs e permissões. O artigo de arquitetura MCP vai detalhar.

Mapeamento de riscos OWASP LLM01/LLM06

OWASP LLM Top 10 define riscos de LLMs e agentes. Números e versões podem mudar; confira antes de publicar. Dois importam para approval:

RiscoDescriçãoResposta com approval
LLM01 Prompt InjectionEntrada externa induz chamadas não autorizadas, vazamento de dados ou comandos externosExigir aprovação para alto risco; não depender só de prompt; mostrar argumentos e impacto
LLM06 Excessive AgencyFunções, permissões e autonomia excessivas geram riscoLimitar tools com Policy, autonomia com Approval e restringir qualquer “always allow”

LLM01 mostra que prompt injection pode levar o modelo a chamar tools não autorizados. Approval pausa antes do alto risco e mostra argumentos + impacto.

LLM06 mostra que autonomia demais é perigosa. Approval precisa trabalhar com Policy e Guardrail. “Permitir sempre nesta sessão” deve ser bem restrito.

Mapeamento com NIST AI RMF Core

NIST AI RMF Core organiza risco de IA em quatro fases. Um time pequeno pode usar uma versão leve:

FaseResponsabilidade de aprovaçãoExemplo
GovernDefinir papéis e regrasPapéis owner/on-call engineer, níveis L0-L3, estratégias reject/timeout
MapIdentificar cenários de riscoExclusão, pagamento, mudança de permissões e prompt injection com a matriz
MeasureMedir cobertura e rejeiçãoAcompanhar coverage, rejection rate, timeout rate e ajustar policy
ManageResposta e recuperaçãoUsar logs, rollback e compensação

Govern define regras. Map encontra riscos. Measure mede se o controle funciona. Manage trata incidentes.

Para times pequenos basta: níveis de aprovação, ações de alto risco, taxa de rejeição e audit logs.

Conclusão

Classificar risco é o primeiro passo. Nem tudo precisa de aprovação: leituras e rascunhos podem ser automáticos; exclusões e pagamentos devem esperar. Use impacto externo, reversibilidade, sensibilidade, dinheiro/permissões e autonomia.

Approval não é pop-up. É estado serializável, retomável e auditável. RunState é salvo em checkpoint, o run volta ao ponto de pausa e o audit log guarda decisão e cadeia de execução.

As quatro fronteiras têm papéis diferentes. Policy limita tools, Guardrail checa automaticamente, Approval adiciona decisão humana, Audit dá rastreabilidade. Use juntas.

OWASP LLM01 e LLM06 colocam prompt injection e excessive agency como riscos centrais. Approval deve trabalhar com Policy e Guardrail.

NIST AI RMF Core dá o marco. Para um time pequeno: níveis de approval, ações de alto risco, taxa de rejeição e audit logs.

Leituras seguintes:

Artigo publicado de LangGraph checkpoint/thread state: base técnica para salvar estado de aprovação.
Artigo publicado de Agent monitoring/recovery: approval logs como observabilidade.
Artigo planejado de arquitetura MCP em produção: por que HITL vive no gateway e nas permissões.
Artigo planejado de Feishu MCP: cenários para mensagens externas e escritas colaborativas.

Projetar um fluxo de aprovação humana para um agente

Use classificação de risco, pausa de execução, evidências de aprovação e logs de auditoria para criar um fluxo retomável.

  1. 1

    Step 1: Listar tools e ações

    Liste os tools, sistemas externos e ações concretas que o agente pode chamar. Não classifique apenas pelo nome do tool.
  2. 2

    Step 2: Marcar dimensões de risco

    Para cada ação, marque impacto externo, reversibilidade, sensibilidade dos dados, limite financeiro ou de permissões e nível de autonomia.
  3. 3

    Step 3: Definir níveis de aprovação

    Associe cada tipo de ação a auto, draft, approval, strong approval ou deny.
  4. 4

    Step 4: Persistir o estado de execução

    Em runtime, salve approval request, RunState ou checkpoint e relacione com taskId, runId e traceId.
  5. 5

    Step 5: Mostrar evidências de aprovação

    Mostre tool, resumo dos argumentos, objeto afetado, reversibilidade, sensibilidade e impacto esperado.
  6. 6

    Step 6: Tratar approve, reject e timeout

    Conforme a decisão, retome, rebaixe para rascunho, compense etapas feitas, escale ou pare a tarefa.
  7. 7

    Step 7: Registrar auditoria e testes

    Salve logs de aprovação e resultados de retomada; teste caminhos de reject, timeout e compensação.

FAQ

Quais ações de um agente de IA precisam de aprovação humana?
Mensagens externas, apagar ou sobrescrever dados, pagamentos, mudanças de permissões, leitura ou escrita de dados sensíveis, escritas em massa, envios irreversíveis e compartilhamento de contexto com tools remotos geralmente precisam de approval ou strong approval.
Ainda preciso de aprovação se já tenho guardrails?
Sim. Guardrail é uma checagem automática; aprovação humana é um ponto de decisão antes de uma ação de negócio arriscada. Eles cobrem riscos diferentes.
Por que preciso de aprovação depois de conceder OAuth scope?
OAuth scope indica permissão técnica para chamar uma API. Ele não decide se aquela ação de negócio deve acontecer no contexto atual.
Um botão pode significar sempre permitir nesta sessão?
Pode, mas com duração curta, escopo de tool limitado, escopo de objeto e audit log. Uma aprovação não pode virar acesso ilimitado.
O que deve acontecer depois de uma rejeição?
O run deve seguir uma ramificação explícita: salvar rascunho, pedir mais informações, escolher um caminho menos arriscado, escalar, compensar ou parar. Não deve repetir em silêncio a mesma ação.
Quais campos um registro de aprovação deve guardar?
No mínimo taskId ou runId, tool, resumo dos argumentos, objeto afetado, nível de risco, aprovador, decisão, motivo, horário, traceId, ação de retomada e código de erro.

13 min de leitura · Publicado em: 11 set 2026 · Atualizado em: 11 set 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog