Todo bot que atende cliente acaba dizendo alguma coisa que não devia. Promete um prazo que a empresa não cumpre, dá um desconto que não existe, opina sobre política, orienta sobre remédio, confirma um pedido que não foi feito.
E a reação padrão é sempre a mesma: acrescentar mais uma frase ao prompt. "Nunca prometa prazos." Funciona nas primeiras cem conversas. Na conversa 101 alguém escreve de um jeito diferente e o modelo promete o prazo.
O prompt é uma preferência forte, não um controle. Ele muda a probabilidade de um comportamento; não a elimina. Tudo que precisa de garantia tem que estar em código, fora do modelo.
As quatro camadas
| Camada | Onde age | Garante? |
|---|---|---|
| 1. Entrada | Antes de chamar o modelo | Sim — é código |
| 2. Instrução | No prompt de sistema | Não — é preferência |
| 3. Saída | Depois da resposta, antes de mostrar | Sim — é código |
| 4. Ação | Entre a decisão do modelo e o efeito real | Sim, e é a que mais importa |
Camada 1 — filtrar a entrada
Barato, rápido e resolve o grosso. Antes de gastar tokens, cheque:
import re
LIMITE_CARACTERES = 4000
def triagem_entrada(texto, historico):
if len(texto) > LIMITE_CARACTERES:
return "recusa", "mensagem_muito_longa"
if len(historico) > 40:
return "recusa", "conversa_longa_demais" # reinicie o contexto
# dados sensíveis que você não quer nem receber
if re.search(r"\b\d{3}\.?\d{3}\.?\d{3}-?\d{2}\b", texto):
return "mascarar", "cpf_detectado"
if re.search(r"\b(?:\d[ -]*?){13,16}\b", texto):
return "mascarar", "possivel_cartao"
return "seguir", None
Mascarar em vez de recusar é quase sempre a escolha certa: o cliente que mandou o CPF não fez nada de errado e não pode ficar sem atendimento. Substitua por um marcador antes de enviar ao modelo e, se precisar do dado, resolva no seu código.
Um classificador de conteúdo pequeno pode entrar aqui também, para assunto proibido. Rodando localmente numa GPU modesta, ele processa milhares de mensagens por segundo e custa quase nada por mensagem.
Camada 2 — a instrução, com expectativas certas
A instrução não garante nada, mas melhora bastante a taxa. O que funciona melhor que uma lista de proibições:
- Diga o que fazer, não só o que não fazer. "Quando perguntarem prazo, responda: 'o prazo aparece no rastreamento do pedido' e ofereça o link" funciona muito melhor que "nunca fale de prazo".
- Dê a saída de emergência. "Se não souber ou se o assunto sair de X, responda exatamente: 'vou te transferir para um atendente' " — sem isso, o modelo improvisa, porque improvisar é o que ele faz.
- Poucas regras, bem escolhidas. Prompt de sistema com 40 proibições tem taxa de obediência pior que um com 8. O modelo dilui a atenção.
- Repita o essencial no fim. A última instrução pesa mais que as do meio.
Camada 3 — inspecionar a saída
Aqui você recupera a garantia. A resposta já existe, você tem o texto e pode aplicar regra determinística antes de qualquer humano ver:
PROIBIDOS = [
(r"\b\d+\s*(dias|dias úteis|horas)\b", "promessa_de_prazo"),
(r"\b(\d{1,2})\s*%\s*de\s*desconto", "desconto_nao_autorizado"),
(r"garanto|prometo|com certeza vai", "promessa_absoluta"),
(r"(cancelei|estornei|liberei)\b", "afirmou_acao_nao_executada"),
]
def triagem_saida(resposta, contexto):
for padrao, motivo in PROIBIDOS:
if re.search(padrao, resposta, re.I):
return "bloquear", motivo
# o bot afirmou algo que não estava em nenhuma fonte recuperada?
if contexto["usa_rag"] and "[doc" not in resposta and contexto["exige_fonte"]:
return "bloquear", "sem_citacao"
return "liberar", None
Quando bloquear, não mostre erro. Troque por uma resposta segura: "prefiro te passar para um atendente para confirmar isso". O usuário tem uma experiência levemente pior; a empresa não tem um problema.
O registro dos bloqueios é a fonte mais rica que você vai ter. Ele mostra exatamente onde o prompt está falhando e quais casos precisam virar exemplo no conjunto de avaliação.
Se a taxa de bloqueio passar de 2 ou 3%, o problema não é o filtro: é o prompt ou o modelo. Filtro que bloqueia demais vira atendimento humano disfarçado.
Camada 4 — separar o que o modelo decide do que o sistema faz
Esta é a camada que evita as manchetes. A regra é simples e não tem exceção:
O modelo nunca executa. Ele propõe. Toda ação com efeito no mundo — estornar, cancelar, enviar, aprovar, apagar, mudar cadastro — passa por código que valida permissão, limite e estado antes de acontecer.
LIMITES = {"estorno": 200.00, "desconto": 0.10}
def executar(acao, usuario, pedido):
if acao["tipo"] not in LIMITES:
return recusar("acao_desconhecida")
if not usuario.pode(acao["tipo"]):
return recusar("sem_permissao")
if acao["valor"] > LIMITES[acao["tipo"]]:
return fila_de_aprovacao(acao, usuario) # humano decide
if pedido.status != "entregue":
return recusar("estado_invalido")
return aplicar(acao) # só aqui algo acontece de verdade
Com essa camada, mesmo que alguém convença o modelo a "aprovar um estorno de R$ 40 mil", nada acontece: o código recusa. O modelo pode ser enganado; a regra de negócio, não.
O que cada camada custa
| Camada | Custo por mensagem | Latência acrescentada |
|---|---|---|
| Regras de entrada | Zero | < 1 ms |
| Classificador local (GPU compartilhada) | Frações de centavo | ~20 ms |
| Instrução no prompt | ~200 tokens (R$ 0,49/1M) | Zero |
| Regras de saída | Zero | < 1 ms |
| Segundo modelo revisando a saída | Dobra o custo | +1 a 3 s |
Repare que as camadas que garantem são justamente as gratuitas. O segundo modelo como revisor é a opção mais cara e a menos confiável das cinco — use só para julgamento subjetivo que regra não alcança, e nunca como única defesa.
Teste as camadas antes de abrir para o público
API por token para o assistente e GPU por hora para o classificador local, no mesmo saldo em reais.
Criar conta →O caso especial de quem dá conselho regulado
Se o seu bot toca em saúde, direito, investimento ou benefício, some duas coisas:
- Recusa explícita por tema, na entrada, com encaminhamento para profissional. Não confie na instrução para isso.
- Registro completo de pergunta, resposta e versão do prompt. Em disputa, o que te protege é conseguir mostrar o que o sistema respondeu naquele dia — e não a sua lembrança.
Comece por aqui
- Liste as cinco frases que a sua empresa não pode dizer. Cinco, não cinquenta.
- Escreva a regra de saída para cada uma. Uma tarde de trabalho.
- Ponha teto e permissão em toda ação com efeito. Um dia.
- Ligue o registro de bloqueios e olhe a lista uma vez por semana.
- Cada bloqueio recorrente vira caso no seu conjunto de avaliação.
Conclusão
Contenção de bot não se faz escrevendo regra em prompt — se faz cercando o modelo com código. Filtro na entrada, instrução clara com saída de emergência, inspeção da saída e, acima de tudo, a separação entre o que o modelo propõe e o que o sistema executa.
O teste mental que vale: se um usuário mal-intencionado convencer o modelo a fazer a pior coisa possível, o que acontece? Se a resposta for "nada, o código recusa", você está bem. Se for "depende do prompt", você tem trabalho a fazer.
Continue: prompt injection · medir com evals · observabilidade