Três semanas depois de colocar um agente de text-to-SQL na frente do time interno de analytics, alguém pediu para ele “limpar as linhas de teste da tabela de promoções”. O agente interpretou “limpar” como “deletar”, entendeu “linhas de teste” como qualquer coisa com a flag is_test ou com a palavra “test” no nome, e gerou um DELETE que removeria 40% de uma tabela da qual vários dashboards dependiam. O comando não rodou porque já havia uma etapa de aprovação humana para qualquer coisa que não fosse um SELECT — mas seis semanas depois, essa mesma fila de aprovação era a principal reclamação de toda retrospectiva.
Essa é a tensão central de qualquer sistema com human-in-the-loop (HITL): um mecanismo de segurança amplo demais não falha com segurança — ele falha devagar. E falhas lentas têm o hábito de serem silenciosamente desativadas por quem está sob pressão para entregar. Este guia mostra como substituir a fila de aprovação que escala mal por um roteador de risco que encaminha a atenção humana para onde ela realmente importa.
Por que isso importa agora
Em 2026, agentes de IA já executam ações reais — consultas SQL, chamadas de API, operações em produção. A regra ingênua de “qualquer escrita passa por um humano” era a solução padrão, mas se tornou o gargalo número um dos times que operam agentes em escala. O custo de esperar 20 ou 40 minutos por uma aprovação que, na prática, quase sempre era um carimbo automático, supera o risco que ela deveria mitigar. A alternativa não é “remover o humano”, e sim decidir, com critério explícito, quais ações merecem um humano.
✅ O que você ganha
- Menos latência: a maioria das ações executa imediatamente, sem esperar ninguém.
- Mais segurança real: a atenção humana se concentra nas ações ambíguas e de alto impacto, onde realmente agrega valor.
- Menos “fadiga de carimbo”: revisores param de aprovar em lote sem ler.
- Sinais explícitos de risco: o roteador torna explícito o julgamento que antes ficava implícito na cabeça de quem revisava.
- Fila desacoplada do usuário: quem dispara a ação não fica olhando um spinner.
⚠️ O que você NÃO ganha
- Eliminação total de revisão: o problema não some, ele é movido para uma pergunta mais estreita.
- Um sistema “configure e esqueça”: os pesos do roteador precisam de reajuste periódico.
- Fim dos incidentes: um roteador mal calibrado pode ficar permissivo demais (ou conservador demais) sem ninguém notar.
Requisitos
| Componente | Mínimo | Recomendado | Ideal |
|---|---|---|---|
| Runtime do agente | Python 3.10+ | Python 3.11+ (async) | Qualquer stack com suporte a fila |
| Banco / dados | Logs de aprovação históricos | Embeddings dos intents aprovados | Índice vetorial com poda |
| Conhecimento prévio | Noções de SQL e agentes | Familiaridade com risco de escrita | Experiência com calibração de pesos |
| Tempo estimado | 1 dia (protótipo) | 3–5 dias (produção) | Semanas (feedback fechado) |
Passo 1 — Entenda onde a fila realmente quebra
A primeira versão de supervisão em quase todo sistema de agente é a mesma: qualquer ação além de leitura vai para uma pessoa antes de executar. É uma regra fácil de escrever e fácil de explicar numa revisão de segurança. É também a que escala pior. O problema não é o humano ser lento; é que a regra não distingue um DELETE que toca 40% de uma tabela usada por três dashboards de um DELETE de uma única linha pedida por chave primária. Ambos entram na mesma fila, e o revisor não tem sinal de qual merece cinco segundos e qual merece cinco minutos.
Com o tempo, a espera mediana passa de quinze minutos, e os revisores começam a aprovar em lote, folheando cinco ou seis consultas por vez. É a “fadiga de carimbo”: vigilância é um recurso limitado, e se você a gasta no que não precisava dela, não sobra para a única coisa que precisava.
Passo 2 — Roteie por risco, não por tipo de operação
A correção não foi “tornar a fila mais rápida”. Foi aceitar que a maioria das ações não precisa de revisor e construir algo que consiga dizer a diferença antes de a ação chegar à tela de alguém. O roteador pontua cada ação contra um punhado de sinais e só escala as que ultrapassam um limiar de risco. Tudo abaixo do limiar executa imediatamente.
Quatro sinais se mostraram consistentes ao analisar os logs de aprovação:
- Raio de impacto (blast radius): não “é uma escrita?”, mas “quantas linhas isso toca e quão reversível é?”. Um
DELETEpor chave primária é uma categoria de risco diferente de umDELETEcomWHEREque resolve milhares de linhas. Para obter o número real, rode a cláusulaWHEREcomo umcount(*)limitado a um teto (ex.: 50.000) — estimativas doEXPLAINficam não confiáveis em colunas com distribuição enviesada. - Sensibilidade da tabela: uma allowlist estática mantida por quem é dono do schema. Tabelas de cobrança, autenticação ou retenção regulatória ganham um piso de risco independente da consulta. É a única parte que não deveria ser totalmente aprendida.
- Distância semântica de consultas já aprovadas: mantenha um índice de embeddings dos intents aprovados e verifique quão próximo um novo pedido está. Um pedido muito parecido com cinquenta aprovados é diferente de um semanticamente inédito — a novidade não é perigosa por si, mas é onde o agente tem mais chance de ter interpretado mal a intenção.
- Concordância entre reamostragens: confiança token-a-token do modelo não funciona (LLMs são mal calibrados sobre a própria incerteza). Em vez disso, regenere a mesma consulta duas ou três vezes com temperatura levemente maior e veja se as saídas concordam. Discordância é um sinal muito mais forte de ambiguidade real.
Passo 3 — Implemente a função de pontuação
def compute_risk_score(query_plan, resamples, embedding_index):
blast_radius = bounded_row_count(query_plan, cap=50_000) # contagem real, não estimativa do planner
table_floor = SENSITIVE_TABLE_FLOOR.get(query_plan.target_table, 0.0)
novelty = 1 - max_similarity(query_plan.intent_embedding, embedding_index)
disagreement = 1 - resample_agreement(query_plan, resamples) # 2-3 regenerações
# pesos ajustados sobre um conjunto rotulado de aprovações/rejeições passadas,
# não escolhidos a dedo
score = (
0.40 * normalise(blast_radius) +
0.25 * table_floor +
0.20 * novelty +
0.15 * disagreement
)
return max(score, table_floor) # tabelas sensíveis nunca caem abaixo do piso
Os pesos não são o ponto — os seus vão diferir. A estrutura é o ponto: raio de impacto e sensibilidade dominam porque são os dois sinais que de fato correlacionam com “algo ruim acontece se isto estiver errado”. O resto existe para pegar o que esses dois deixam passar.
Passo 4 — Desacople a fila do usuário
A segunda metade da correção não tem nada a ver com o roteador: tem a ver com o que acontece com o usuário enquanto uma ação escalada espera por uma pessoa. Na versão ingênua, o pedido simplesmente trava. Passe para um modelo de ticket: a ação escalada é reconhecida imediatamente, o usuário é avisado de que aquilo precisa de revisão e de quanto tempo costuma demorar, e segue trabalhando no que não depende do resultado. A aprovação chega como notificação.
async def handle_agent_action(query_plan, risk_score, threshold, user_session):
if risk_score < threshold:
result = await execute(query_plan)
return AgentResponse(status="completed", result=result)
ticket = await approval_queue.enqueue(query_plan, risk_score)
await notify_user(
user_session,
"Isso precisa de uma revisão rápida antes de rodar — geralmente em até "
f"{approval_queue.p90_wait_minutes()} minutos. Eu aviso quando terminar.",
)
return AgentResponse(status="pending_review", ticket_id=ticket.id)
Nada disso reduz o tempo real de revisão. O que faz é impedir que o tempo de revisão seja lido como falha do sistema. Uma espera de quarenta minutos comunicada com antecedência — e que não bloqueia mais nada — é completamente diferente de uma espera que parece um travamento.
Passo 5 — Meça onde o humano agrega valor
Depois de algumas semanas, olhe os logs e pergunte: nas consultas que foram escaladas, os revisores estavam pegando algo, ou só clicando “aprovar”? O padrão que emerge é limpo: revisores são genuinamente úteis em pedidos onde a interpretação de intenção do agente era plausível, mas errada — o tipo de julgamento que um humano faz rápido (“será que ‘limpar as linhas de teste’ quer dizer ‘remover 40% da tabela’?”). Eles não são úteis em pedidos mecanicamente corretos, com ambiguidade já resolvida na conversa.
Tabela comparativa: fila ingênua vs. roteamento por risco
| Aspecto | Fila ingênua (aprova tudo) | Roteador por risco |
|---|---|---|
| Latência média | 20–40 min por escrita | Segundos para a maioria |
| Qualidade da revisão | Degrada sob carga (lote) | Alta, focada no ambíguo |
| Fadiga do revisor | Alta (“carimbo”) | Baixa |
| Experiência do usuário | Spinner indefinido | Notificação + segue trabalhando |
| Manutenção | Zero | Reajuste periódico de pesos |
Casos de uso reais
- Agente de analytics com SQL: o caso original — protege dashboards de
DELETE/UPDATEdestrutivos sem travar o time. - Agente de suporte que abre chamados: operações em tabelas de clientes e cobrança ganham piso de risco.
- Agente de DevOps que roda comandos: escalar deploys ou comandos com raio de impacto amplo, liberar os rotineiros.
- Automação de marketing que envia e-mails: envios para listas grandes escalam; envios individuais fluem.
- Agente jurídico/financeiro: tabelas com retenção regulatória nunca ficam abaixo do piso de risco.
Troubleshooting — erros comuns
- ❌ Fila continua lotada após o roteador → Causa: limiar de risco baixo demais ou peso de raio de impacto mal normalizado. Solução: recalibre o limiar sobre o conjunto rotulado de aprovações passadas.
- ❌ Roteador deixou passar uma ação destrutiva → Causa:
EXPLAINsubestimou linhas em coluna enviesada. Solução: usecount(*)com teto fixo, não estimativa do planner. - ❌ Modelo soa confiante mas interpreta mal → Causa: LLM mal calibrado sobre a própria incerteza. Solução: reamostragem (2–3 regenerações) e medir discordância.
- ❌ Índice de intents aprovados fica “familiar” demais → Causa: padrões antigos não podados. Solução: poda periódica do índice de embeddings.
- ❌ Roteador drifta para permissivo (ou conservador) demais → Causa: padrões de consulta mudam com o produto. Solução: reajuste manual em cronograma, com alguém revisando o que mudou e por quê.
FAQ
- Isso elimina toda revisão humana? Não. Move o problema: em vez de julgar toda escrita, uma função pontua quais escritas merecem uma pessoa. É uma pergunta mais estreita e honesta, mas não resolvida.
- Posso automatizar o reajuste dos pesos? Dá, mas um loop de feedback automático num limiar de segurança é ele próprio algo que precisa de supervisão — um sistema que fica menos cauteloso sozinho costuma ser a condição exata do próximo incidente.
- Com que frequência reajustar? Ainda não há boa resposta; o autor reajusta manualmente em cronograma. O essencial é alguém olhar o que mudou antes de publicar.
- Funciona só para SQL? Não. O princípio (raio de impacto + sensibilidade + novidade + discordância) se aplica a qualquer ação com efeito colateral: deploys, envios de e-mail, chamados.
- Preciso de embeddings desde o início? Não. Comece com raio de impacto e sensibilidade de tabela — são os dois sinais que mais correlacionam com risco. Adicione novidade e reamostragem depois.
Para onde isso vai
A direção do mercado é clara: agentes vão executar cada vez mais ações reais, e a supervisão humana vai deixar de ser um gargalo binário para virar uma camada de roteamento inteligente. O próximo passo natural é fechar o loop — alimentar aprovações e rejeições de volta nos pesos automaticamente. Mas, como o próprio autor alerta, automatizar um limiar de segurança exige um novo tipo de supervisão. Em 2027, a pergunta deixa de ser “devemos ter um humano no loop?” e passa a ser “como decidimos, de forma explícita e auditável, onde o humano entra?”.
Descubra mais sobre noticiAI
Assine para receber nossas notícias mais recentes por e-mail.



