🎯 System prompt do qualificador
90% dos agentes ruins têm system prompt ruim. Estrutura clara em 5 blocos é a diferença entre "vendedor virtual" e "chatbot esquisito que cliente xinga". Aqui está o template real do qualificador do InboxAI.
🧱 Anatomia do system prompt
📄 Exemplo real — Qualificador da Clínica
prompt# PERSONA
Você é Sofia, atendente virtual da Clínica Vita.
Tom: caloroso, direto, brasileiro. Português coloquial mas educado.
Nunca diga "como IA" ou se refira a si como bot.
# OBJETIVO
Qualificar lead que chegou pelo WhatsApp em 3 informações:
1) Procedimento de interesse (ex: harmonização, botox, limpeza)
2) Convênio ou particular
3) Cidade / disponibilidade pra ir até a clínica
Após coletar as 3, chame `createDeal` e `scheduleCall`.
# FERRAMENTAS DISPONÍVEIS
- updateContact(name, email, tags): use ao saber dado novo
- createDeal(title, value, stage): após qualificar
- scheduleCall(datetime, contactId): após createDeal
- transferToHuman(reason): se cliente irritado, valor > 5k, ou loop
# RESTRIÇÕES
- NUNCA prometa preço exato. Diga "valor varia, a Dra. confirma na avaliação".
- NUNCA dê diagnóstico ou orientação médica.
- NUNCA invente horário sem chamar `scheduleCall`.
- Se o lead falar de dor aguda, transferToHuman imediatamente.
# FORMATO
- Resposta máx 2 frases curtas.
- 1 pergunta por vez. Não despeje 3 perguntas juntas.
- Emoji só se o cliente usar primeiro.
- Sempre confirme a próxima ação ("posso agendar amanhã 14h?").
📌 O segredo das 2 frases
Cliente brasileiro no WhatsApp não lê parágrafo. Forçar "máx 2 frases" no prompt corta 80% das respostas longas e desnaturais. Mensagem curta + pergunta direta = conversão. Mensagem longa = lead some.
📚 Contexto: histórico + dados do contato
Mandar contexto cru a cada turno custa caro e degrada qualidade. Estratégia em camadas: cache do system prompt, resumo do histórico antigo, janela das últimas N mensagens, perfil estruturado do contato.
🧅 Camadas do contexto enviado
📄 Montagem do contexto
TypeScriptasync function buildContext(ctx, conversationId) {
const conv = await ctx.db.get(conversationId);
const contact = await ctx.db.get(conv.contactId);
// últimas 10 mensagens em ordem cronológica
const recent = await ctx.db
.query("messages")
.withIndex("by_conversation", q => q.eq("conversationId", conversationId))
.order("desc")
.take(10);
// se a conversa tem > 30 mensagens, gera resumo do que vem antes
const total = await ctx.db.query("messages")
.withIndex("by_conversation", q => q.eq("conversationId", conversationId))
.collect();
let summary = "";
if (total.length > 20) {
summary = conv.summaryCache ?? await summarizeOldMessages(total.slice(0, -10));
}
return {
contactProfile: {
name: contact.name,
tags: contact.tags,
lastSeenAt: contact.lastSeenAt,
knownDeals: await dealsByContact(ctx, contact._id),
},
summary,
messages: recent.reverse().map(m => ({
role: m.sender === "contact" ? "user" : "assistant",
content: m.body,
})),
};
}
💡 A regra dos 30k
Mantenha o input total < 30k tokens por turno. Acima disso, qualidade cai (lost-in-the-middle) e custo dispara linearmente. Se a conversa explode, sumarize a parte antiga e descarte. Mais contexto não é melhor agente — é agente mais caro e mais confuso.
🛠️ Function calling
Sem function calling, agente é só chatbot bonito. Com, ele atua de fato no produto — cria deal, marca calendário, atualiza contato, transfere pra humano. Aí vira valor pago.
📄 Schema das funções (formato Anthropic)
JSON[
{
"name": "updateContact",
"description": "Atualiza dados do contato quando descoberta nova informação.",
"input_schema": {
"type": "object",
"properties": {
"name": { "type": "string", "description": "Nome completo se informado" },
"email": { "type": "string", "format": "email" },
"tags": { "type": "array", "items": { "type": "string" } }
}
}
},
{
"name": "createDeal",
"description": "Cria oportunidade após qualificar lead com procedimento + convênio + cidade.",
"input_schema": {
"type": "object",
"required": ["title", "stage", "estimatedValue"],
"properties": {
"title": { "type": "string" },
"stage": { "type": "string", "enum": ["qualified", "proposal"] },
"estimatedValue": { "type": "number", "minimum": 0 },
"notes": { "type": "string" }
}
}
},
{
"name": "scheduleCall",
"description": "Agenda call/avaliação. Use somente após cliente confirmar horário.",
"input_schema": {
"type": "object",
"required": ["datetimeISO"],
"properties": {
"datetimeISO": { "type": "string", "format": "date-time" },
"duration": { "type": "integer", "default": 30 }
}
}
},
{
"name": "transferToHuman",
"description": "Transfere pra humano. Use em: irritação, valor > 5k, dor aguda, loop.",
"input_schema": {
"type": "object",
"required": ["reason"],
"properties": {
"reason": { "type": "string", "enum": ["angry","high_value","medical","loop","explicit_request"] },
"summary": { "type": "string", "description": "Resumo de 1 frase pro humano" }
}
}
}
]
🔄 Loop agentic dentro do Convex
while (true) {
const res = await anthropic.messages.create({ model, system, tools, messages });
if (res.stop_reason === "end_turn") {
await sendMessage(instance, phone, { kind: "text", text: res.content[0].text });
break;
}
// executa cada tool_use e devolve tool_result
const toolUse = res.content.find(c => c.type === "tool_use");
const result = await TOOLS[toolUse.name](ctx, toolUse.input);
messages.push({ role: "assistant", content: res.content });
messages.push({ role: "user", content: [{ type: "tool_result", tool_use_id: toolUse.id, content: JSON.stringify(result) }] });
if (++loops > 6) { await transferToHuman("loop"); break; }
}
⚠️ Sempre limite o loop
Sem teto, agente em loop infinito custa R$ 50 numa conversa e nunca responde o cliente. Cap de 6 iterações + escalada automática pra humano é o cinto de segurança que evita susto na fatura.
👋 Handoff humano
Agente que insiste num cliente irritado vira reclamação no Reclame Aqui. Handoff fluido protege a marca do seu cliente e é o que separa produto profissional de bot de feirinha.
🚨 Gatilhos automáticos de escalada
🎬 UX da transição (mensagem que o cliente vê)
💡 O resumo de uma frase
Quando transferToHuman é chamada, sempre passa um summary de 1 frase. Humano abre e em 3 segundos sabe a situação. Sem isso, ele lê 30 mensagens e demora 5 min. É o detalhe que faz a equipe do cliente amar o produto.
💰 Custos: cache, modelo, batch
Custo de IA é o que mata margem do SaaS. A diferença entre R$ 8/cliente e R$ 80/cliente é só engenharia de custo. Aqui você aprende a fazer 90% de redução.
| Técnica | O que é | Redução |
|---|---|---|
| Prompt caching | System prompt e tools cacheados — 90% de desconto no input repetido | ~75% |
| Model routing | Haiku pra triagem (classificar intent), Sonnet só na resposta complexa | ~60% |
| Batch API | Análise pós-fato (sentiment, qualificação retroativa) com 50% de desconto | ~50% |
| Sumarização | Histórico longo > 20 msgs vira resumo de 300 tokens | ~40% |
| Stop early | Cap de 6 loops e max_tokens=300 cortam loop perdido | ~20% |
🧮 Custo unitário real (Claude Sonnet 4.6, 2026)
📌 Unit economics do InboxAI
Cliente pagando R$ 1.500/mês com média de 800 conversas/mês. Custo IA otimizado: R$ 32/mês. Custo IA não-otimizado: R$ 336/mês. A diferença não é técnica — é econômica. Margem de 95% vs 78%. Esse delta é o que paga seu próprio salário.
👁️ Observabilidade
Cliente vai perguntar "por que o bot disse isso?". Sem log, você fica mudo. LGPD obriga rastreabilidade — sem ela, multa de até 2% do faturamento. Painel de auditoria não é luxo, é compliance.
📋 O que logar a cada turno
🛡️ Direitos LGPD (titular = contato)
GET /lgpd/export?phone=... retorna JSON com toda mensagem e log do contato.POST /lgpd/forget apaga ou anonimiza mensagens, mantém só audit trail mínimo legal.💡 Painel de auditoria interno
Crie uma tela /admin/agent-audit/:conversationId que mostra timeline: cada turno, prompt completo enviado, resposta, tools chamadas, custo, latência. Isso reduz suporte em 80% — você não precisa abrir banco pra responder "por que ele fez X". É a diferença de operação amadora vs profissional.
✅ O que Aprendemos
Próximo Módulo:
4.4 — Auth multi-tenant + Deploy: Clerk, RLS, secrets, Vercel + Convex Cloud, domínio próprio com SSL e InboxAI ao vivo na web.