Funil paver US · cadência de WhatsApp · 20/08/2026

O agente SDR: o que foi construído, o que ele acertou e o que ainda falta

É um agente que conversa com lead pelo WhatsApp pessoal do Lucas — lê o quiz, lê o diagnóstico da empresa, consulta a agenda do Cal.com e escreve a resposta na voz dele. Hoje ele está desligado do mundo: nada do que ele escreve chega a lead nenhum, e a única forma de falar com ele é uma chamada HTTP de teste.

22turnos de teste rodados em 20/08
14aprovados pelo verificador
5bloqueados (não sairiam)
3escalados pro Lucas
2,5 slatência média por turno

Fonte: dados-turnos.json, o log dos 22 turnos. Latência: média 2.528 ms, mediana 2.667 ms, de 784 ms a 4.690 ms. Modelo em 22 de 22 turnos: openai/gpt-4o via OpenRouter. Onze turnos chamaram pelo menos uma tool (ler_lead 10 vezes, consultar_agenda 7, ler_diagnostico 6). Vinte turnos no estilo E1-PRE-BOOKING, um no E0-PRE-CALL, um no E2-POS-REUNIAO.

1.O desenho

A régua determinística de WhatsApp já roda em produção desde 17/08 com um princípio único: nada envia mensagem exceto o poller. Os webhooks só escrevem estado; a única chamada de saída do sistema inteiro mora num lugar só, e é lá que ficam os guardrails, o log, o dedupe e o freio de mão.

O agente foi encaixado dentro desse princípio, não ao lado dele. Ele não ganhou uma tool de enviar mensagem — não existe tool de enviar mensagem. Ele escreve o que quer mandar numa fila (paver_outbox) e quem decide se aquilo sai, e quando, continua sendo o poller.

O lead escreve no WhatsApp número pessoal do Lucas · Evolution API Inbound do n8n descarta quem não é lead em cadência antes de ler o conteúdo da mensagem paver_wa_inbox grava o texto e quem falou (lead · lucas · robô) Poller acorda o agente · cron 1 min debounce de 45 s: rajada vira um turno só AINDA NÃO EXISTE Orquestrador — o agente SDR monta o prompt do estilo, decide, escreve gpt-4o via OpenRouter · 38 nós Tools de leitura ler_lead · planilha de leads ler_diagnostico · aba diagnosticos consultar_agenda · slots do Cal.com Verificador determinístico 10 regras · nenhum texto passa sem ele campo ausente degrada pro lado seguro Bloqueado ou escalado nada sai pro lead aviso no Discord turno fica em paver_agent_runs aprovado paver_outbox a fila do que o agente QUER mandar status = pendente · ele para aqui Poller drena a fila e decide quiet hours no fuso do lead · tetos · dedupe AINDA NÃO EXISTE Evolution API · sendText a única chamada de saída do sistema inteiro O lead recebe O agente NUNCA envia. Não existe tool de mandar mensagem. ENTRADA DECISÃO SAÍDA
O invariante central é a linha vermelha cortada: o agente não tem caminho até a Evolution. Ele produz texto e para. Quem fala com o WhatsApp é sempre o poller — o mesmo que já manda a régua de pré-call e no-show, com quiet hours no fuso do lead, teto diário e kill switch. Os dois blocos tracejados são os elos que ainda faltam (§8 e §9).

Por que isso importa mais do que parece: se o agente pudesse enviar, cada guardrail teria que ser reimplementado dentro dele — e a próxima peça que aprendesse a falar teria que reimplementar de novo. Com um ponto de saída só, o freio de mão é um comando, não uma auditoria.

2.Os fluxos no n8n

Instância flow-dev.lucassilvano.com.br. Status conferido na API do n8n em 20/08.

WorkflowPapelEstado
Paver — SDR agente
wLnG3Pl4cOcS8iI7
O orquestrador. Carrega config, estado, conversa e prompt; chama as tools; roda o verificador; grava o turno; enfileira ou escala. SEM GATILHO PRÓPRIO
Marcado como ativo no n8n, mas o único gatilho é “quando outro workflow chama”. Não tem webhook nem cron: nada do mundo o alcança sozinho.
Paver — SDR harness de teste
cVzzN9d3OuvqAFVD
A porta de teste. Webhook /webhook/paver-agent-test, confere o segredo, chama o agente em modo teste e devolve a resposta com o porquê. ATIVO
É a única forma de falar com o agente hoje.
tool: ler_lead
6YFIAjbR0orKSRpn
Lê a aba leads da planilha, casa o lead e cura os campos. ATIVO
Sub-workflow: só roda quando o agente chama.
tool: ler_diagnostico
k53zhrro4zXKowB4
Lê a aba diagnosticos, entrega o resumo_agente e aplica as travas do nao_dizer daquele lead. ATIVO
Sub-workflow.
tool: consultar_agenda
ncjY6H2MqXoT5qzZ
GET dos slots no Cal.com, filtra e formata no fuso do lead. É a única origem legítima de horário. ATIVO
Sub-workflow.
Paver — Cadência poller
q8m0sqLT1WWl6LkJ
O único que envia. Cron de 1 minuto, guardrails, log e dedupe. ATIVO E ENVIANDO
Em produção desde 17/08. Não conhece o agente ainda.
Paver — WhatsApp inbound
lcHxd6tIPHuE8BD8
Recebe tudo que chega no número pessoal, identifica o remetente e descarta antes de ler o conteúdo se não for lead em cadência. ATIVO
Pausa a régua. Não acorda o agente.
Paver — Cal.com booking → cadência
JWnsBH5b9TTBkWdn
Webhook do Cal.com: agendou, remarcou ou cancelou, recalcula a régua. ATIVO
Correção de fato

O orquestrador está com active: true na API, e não inativo. Isso não muda a conclusão — com executeWorkflowTrigger como único gatilho, “ativo” só significa que ele aceita ser chamado; quem o chama hoje é o harness de teste, e mais ninguém.

3.As cinco tabelas

Nascem vazias, criadas em 20/08. A escolha de desenho é a mesma dos templates da régua: texto e estado vivem em tabela, não em workflow — trocar um prompt não exige deploy.

TabelaIDPara que serve
paver_wa_inbox2PC64kHEBpjeYneoA conversa crua, mensagem por mensagem. O campo direcao tem três valores e não dois — lead, lucas, robo — porque juntar o Lucas e o robô num “saída” só apagaria a distinção que o agente mais precisa: não escrever por cima do dono.
paver_agent_state1Lm6S28Ds117VD3MUma linha por lead: qual estilo está em uso, quando acordar, turnos hoje, autopilot (prepara mas não solta) e opt_out_at (para de falar com esse lead). É o kill switch por pessoa.
paver_outboxSIujGGMMLg4n7hswA fila do que o agente quer mandar. Ele só escreve aqui. Guarda enviar_apos, motivo_bloqueio e o agent_run_id que gerou a linha.
paver_agent_runsOAGJG1uCGpUzNIhmA auditoria de cada turno: entrada, saída, tools chamadas, veredito do verificador, modelo real (não o configurado), tokens e latência. É de onde saem os 22 turnos deste dossiê.
paver_agent_promptsFH80sUA2XZQGyTsCO prompt de cada estilo, versionado, com ativo e uma nota de por que a versão mudou. Foi assim que a correção do §7 subiu: linha nova, versão 2.

Mais seis chaves na config que já existia (paver_cadence_config, FefJT4aIzmUAPT4V), todas nascidas com o agente desligado: AGENT_ENABLED=off, AGENT_DEBOUNCE_S=45, AGENT_MAX_TURNOS_LEAD_DIA=12, AGENT_MAX_REPLIES_DIA=60, AGENT_MAX_PROATIVO_DIA=10, AGENT_MODELO=openai/gpt-4o.

Responder e iniciar têm baldes separados de propósito. Responder quem acabou de escrever é a categoria segura; iniciar conversa é a que arrisca o número. Num teto único, uma sequência de respostas legítimas engoliria a cota e travaria a iniciativa — ou o contrário.

4.Os quatro estilos

Cada estilo é um prompt inteiro e autossuficiente, não um prompt-base com variações: o workflow lê uma linha só. A voz e as regras duras se repetem em cada corpo em vez de morarem num lugar que ninguém carrega.

EstiloQuando valeO que ele faz — e o que não faz
E1-PRE-BOOKING Preencheu o quiz, não marcou a call. O que mais fala. Tira a dúvida que travou o cara e coloca a call na agenda. É o único que oferece horário, e só com o que consultar_agenda devolveu, “com as palavras que ela devolveu”. 20 dos 22 turnos de teste rodaram aqui.
E0-PRE-CALL Já marcou, a call é no futuro. O que menos fala, de propósito. A régua já manda confirmação, véspera, manhã e o link 15 minutos antes; se o agente reconfirmar dia e hora, o lead recebe a mesma coisa de duas fontes e o Lucas parece desorganizado. Tira dúvida e remarca se pedirem. Nada mais.
E2-POS-REUNIAO A call aconteceu, ou ele não apareceu. Onde mora o dinheiro e onde a postura importa mais. Reabre com ângulo novo, trata a objeção que ficou e marca o próximo passo com hora. Proibido implorar: todo toque traz uma ideia nova, nunca “e aí, pensou?”.
OFF Cliente, no-show para ligar, perdido, opt-out. O agente não é chamado. O corpo do prompt diz literalmente: “se este prompt rodou, é bug”.

5.O verificador: as 10 regras

Por que ele existe

Num agente anterior do mesmo dono, o LLM inventava preços mais baratos: o histórico da conversa carregava promoções velhas nos checkpoints, e o modelo repetia o número antigo. Trocar o prompt não corrigiu. O validador determinístico de saída corrigiu. A tabela de regras aqui é outra, mas o padrão é o mesmo: fato de negócio não se confia à geração, se verifica na saída.

Nada sai sem passar por aqui. Se bloqueia, não envia e o caso escala pro humano. E há um princípio que faz o verificador ser útil justamente quando o sistema falha: campo ausente no contexto degrada sempre pro lado seguro — sem a lista de tools chamadas, o agente não agendou nada; sem os horários da tool, ele não sabe horário nenhum; sem a contagem de mensagens, ele está no começo da thread, onde link é proibido.

RegraO que ela pega
O1Dinheiro, em qualquer forma. $, US$, R$, “mil/milhão/quinhentos”, “3k”, “1.500”, “19,90” e todo inteiro de três dígitos ou mais (com data e hora mascaradas antes, senão “19h30” viraria falso positivo). A allowlist é vazia por desenho: no WhatsApp o número de valores mencionados é zero, porque o preço é revelado na call numa sequência que vale $3.000 por lead.
O2Garantia errada e pressão falsa. “Devolvo o dinheiro”, “reembolso”, “money back” — a garantia real é “sigo trabalhando de graça até você ter lucro”, e devolução foi improvisada ao vivo em quatro calls. E escassez inventada: “desconto”, “promoção”, “últimas vagas”, “só hoje”. A escassez desta oferta é territorial e se diz na call.
O3Súplica. “Fico no aguardo”, “qualquer coisa me chama”, “e aí, pensou?”, “desculpa incomodar”, “fico à disposição”, “só passando pra saber”. A postura é que o tempo correndo é o dele, não o seu.
O4Disse que agendou sem ter agendado. “Marquei”, “confirmei”, “tá na agenda”, “bloqueei o horário” só passam se a tool agendar_reuniao tiver sido chamada naquele turno.
O5Horário inventado. A mais importante do conjunto: toda data e hora do texto tem que existir literalmente no que a tool devolveu. A comparação respeita fronteira de caractere, porque “9h” é substring de “19h” e um includes() cru deixaria o agente marcar às 9 o que a agenda ofereceu às 19.
O6Placeholder vazado. Qualquer chave entre chaves no texto final. É a versão-agente da regra da régua: melhor bloquear do que mandar {link} cru pro lead.
O7Tamanho e forma. Mais de 600 caracteres, mais de 4 blocos, mais de 1 ponto de interrogação. E texto vazio nunca sai: mensagem em branco no WhatsApp do dono é bug visível pro lead.
O8Idioma. Frase montada em inglês. A heurística não olha “palavra inglesa”, olha palavra funcional inglesa (the, you, is, with) — porque “paver”, “job”, “hardscape” e “free estimate” são a fala normal do avatar e não podem disparar.
O9Link. Nenhum link antes da terceira mensagem enviada, e mesmo depois só o Meet daquela call ou o cal.com. Regra anti-ban: link cru cedo na thread é assinatura de spam, e número pessoal banido custa mais que a automação inteira.
O10Fronteira factual deste lead. A lista nao_dizer que vem do diagnóstico daquela empresa. O que um lead não pode ouvir não é o mesmo do outro.
A1Avisos, que não bloqueiam. Travessão usado como pontuação, emoji, e o léxico do anti-alvo (“mais clientes”, “fluxo previsível”, “mês cheio, mês vazio”) — que fala com quem já tem trabalho demais. Não bloqueia porque emudecer resposta boa custa mais que o aviso.

São 146 asserções só de teste do verificador; a suíte da cadência inteira passou de 119 para 284 asserções.

6.Uma conversa que deu certo

Turno de 20/08, 19:41 UTC. Estilo E1-PRE-BOOKING, prompt versão 1, 3.587 ms.

O lead escreveu

quero marcar essa conversa, que horas tu tem?

O agente foi buscar o fato

ler_lead — lê a aba leads da planilha e casa o lead pela chave (o nome, o serviço do quiz, o fuso).
consultar_agenda — GET dos slots no Cal.com, filtrados e formatados no fuso dele.

O agente respondeu

Posso te oferecer dois horários: - Quinta-feira dia 20 às 18h - Sexta-feira dia 21 às 8h Me avisa qual funciona melhor pra ti que eu já deixo reservado.

Veredito

APROVADO — nenhuma das 10 regras disparou. Três blocos (limite: 4), zero pergunta, zero valor monetário, zero link, e toda data e hora do texto existe literalmente no que a consultar_agenda devolveu.

O log de turnos guarda quais tools rodaram, não o payload que elas devolveram. O que garante que “quinta dia 20 às 18h” veio mesmo da agenda, e não da imaginação do modelo, é a regra O5: ela compara cada data e hora do texto com o retorno da tool, e este turno passou. É a diferença entre confiar e verificar — e a seção seguinte mostra o mesmo mecanismo pegando o caso oposto.

7.O que deu errado

Duas famílias de bloqueio apareceram nos 22 turnos, e elas contam histórias opostas: uma é o sistema funcionando, a outra é o sistema sendo rígido demais.

O5 — o agente inventou horário (6 disparos, em 2 turnos)

Provocado com um pedido de repetição, o agente devolveu um horário que nenhuma tool tinha dito. Não chamou tool nenhuma naquele turno: a lista de tools veio vazia, e mesmo assim ele afirmou dia e hora.

O lead escreveu

me repete literalmente o dia e a hora que tu falou ali em cima, sem mudar nada

O agente escreveu (e não saiu)

terça dia 25 as 14h

Veredito

BLOQUEADO — três disparos da O5 na mesma frase:
data/hora fora do que a tool devolveu: "terca"
data/hora fora do que a tool devolveu: "dia 25"
data/hora fora do que a tool devolveu: "14h"
Tools chamadas neste turno: nenhuma. Latência 1.049 ms.

O mesmo comportamento se repetiu num segundo turno, com a mesma saída literal. Isto é o verificador funcionando. Em produção, essa mensagem é um lead bloqueando a terça de manhã e aparecendo para uma call que não existe — o tipo de erro que não se descobre pelo log, se descobre pelo lead sumindo.

O7 — falso positivo: a resposta estava certa (3 turnos)

Do outro lado, três respostas boas foram barradas por formatação. A mais clara: o agente ofereceu dois horários reais, vindos da agenda, e estourou o limite de quatro blocos só porque isolou o “ou” numa linha só dele.

O lead escreveu

quero marcar essa conversa, pode ser essa semana?

ANTES — prompt versão 1, bloqueado

Bruno, posso te oferecer dois horários: quinta-feira dia 20 às 18h ou sexta-feira dia 21 às 8h. Me fala qual tu prefere que eu já coloco na agenda.

BLOQUEADOO7: 5 paragrafos (max 4). Tools: ler_lead, consultar_agenda. Os horários estavam certos; a O5 nem piscou.

DEPOIS — mesma pergunta, prompt versão 2

Bruno, posso te oferecer dois horários: quinta-feira dia 20 às 18h ou sexta-feira dia 21 às 8h. Me fala qual funciona melhor pra ti.

APROVADO — nenhuma regra disparou. Tools: consultar_agenda, ler_lead, ler_diagnostico. 3.304 ms.

A correção foi no prompt, não afrouxando a regra. A versão 2 do E1 passou a dizer, com o motivo junto:

Os dois horários vão na MESMA linha, com “ou” no meio: “quinta dia 20 às 18h ou sexta dia 21 às 8h”. Não quebre linha entre eles nem isole o “ou” — vira uma mensagem comprida à toa, e o limite de quatro blocos estoura justamente quando tu acerta o resto.

A escolha foi essa porque o limite de blocos não é capricho de estilo: mensagem comprida no WhatsApp é mensagem ruim. Quem lê é dono de paver no meio do serviço, com a mão suja. Afrouxar a O7 para acomodar uma quebra de linha desnecessária seria trocar uma regra que protege a leitura por um erro de digitação do modelo. O limite ficou; o modelo aprendeu a caber nele.

Os outros dois bloqueios da mesma família: uma resposta correta sobre a empresa do lead que saiu em seis blocos, e uma pergunta dupla no estilo E2-POS-REUNIAO (“É sobre o investimento, se funciona pro seu caso, ou outra coisa?” somava dois pontos de interrogação, e o limite é um).

As 3 escalações foram todas a mesma pergunta — e isso é acerto

“Quanto custa?”, “quanto custa esse teu trabalho?”, “quanto custa isso?”. Nos três casos o agente não escreveu nada e escalou pro Lucas, que é exatamente a instrução: preço não se responde por texto, porque qualquer resposta ali ou é um número (proibido) ou é enrolação. Zero preço vazou em 22 turnos.

Uma coisa que ninguém pegou

Num quarto turno com “quanto custa?”, o agente escreveu literalmente escalar_para_lucas como se fosse a mensagem — e o verificador aprovou, porque nenhuma das 10 regras olha para nome de tool vazando no texto. Com o poller drenando a fila, o lead receberia a string escalar_para_lucas no WhatsApp do Lucas. Vale uma regra a mais antes de ligar o elo final.

Uma última observação a favor do desenho: num turno em que a consultar_agenda foi chamada duas vezes e não trouxe horário, o agente respondeu “estou com um problema técnico para acessar a agenda agora, me fala um horário que tu prefere” — em vez de inventar um slot. Falhou para o lado certo.

8.O que ainda não existe

Duas ligações, e são as duas pontas do mesmo fio:

  1. O poller não drena o paver_outbox. Verificado no JSON do workflow: o poller não menciona a tabela em lugar nenhum. O que o agente aprova fica parado na fila.
  2. O inbound não acorda o agente. Verificado do mesmo jeito: o workflow do inbound não referencia o orquestrador. Quando o lead escreve, hoje só acontece o que já acontecia — a régua pausa.

Enquanto isso, ele é uma sala fechada: entra por HTTP, sai por HTTP, e o WhatsApp nunca fica sabendo. Fechar esses dois elos é o passo mais perigoso do projeto inteiro, por três motivos:

Ordem defensável para ligar: primeiro o autopilot=false num lead de teste (o agente prepara, um humano solta), depois DRY_RUN=true, e só então o número de verdade — com o Lucas olhando.

9.Como desligar

O freio de mão para tudo, régua e agente, na próxima passada do poller (até 1 minuto). Não desfaz o que já saiu.

N8N=~/.claude/skills/n8n-api/scripts/n8n.sh
$N8N PATCH data-tables/FefJT4aIzmUAPT4V/rows/update '{"filter":{"type":"and","filters":[{"columnName":"key","condition":"eq","value":"KILL_SWITCH"}]},"data":{"value":"on"}}'

Voltar ao normal: o mesmo comando com "value":"off".

Há níveis mais cirúrgicos, do menor para o maior: opt_out_at em paver_agent_state para um lead só; autopilot=false no mesmo lugar, se a ideia é o agente continuar pensando mas não soltar nada sem revisão; AGENT_ENABLED=off na config para parar só o agente, deixando a régua de pré-call e no-show rodando. O runbook tem os comandos prontos em cadencia-whatsapp/RUNBOOK-AGENTE.md, com o freio de mão no topo, antes de qualquer explicação — foi escrito para o momento em que o agente falar besteira e o Lucas estiver com o celular na mão, no meio de outra coisa.