O Problema Que Começou Tudo
Um dono de empresa de encanamento me ligou frustrado. Sua empresa estava perdendo 40% das chamadas recebidas. Emergências iam para o correio de voz às 2 da manhã. Clientes em potencial estavam ligando para concorrentes. Ele tinha uma recepcionista em tempo integral durante o horário comercial, mas nada fora dessa janela. Ele estava perdendo dinheiro real, todas as noites.
Três horas depois que conversamos, a Captain Plumber tinha uma recepcionista IA por voz totalmente implantada, construída no Vapi.ai. Ela agora atende mais de 200 chamadas por mês, encaminha emergências para um encanador de plantão em 90 segundos, captura dados de leads no CRM automaticamente e reduziu as chamadas perdidas de 40% para menos de 2%.
Este guia é o walkthrough completo. Vou mostrar a arquitetura, a configuração do Vapi.ai, o backend TypeScript, o tratamento de casos extremos e a configuração de deployment em produção - tudo que eu realmente usei. Ao final, você terá uma recepcionista IA por voz funcional que pode implantar para qualquer empresa de serviços.
Por Que IA de Voz É a Automação de Maior ROI para Empresas de Serviços
Antes de escrever uma linha de código, vale entender por que voz especificamente importa. A maioria dos projetos de automação em que trabalho vive na categoria "seria bom ter". Agentes de voz são diferentes - eles operam no ponto exato onde uma empresa ganha ou perde um cliente.
Quando alguém liga para um encanador às 23h com um cano estourado, não vai tentar um segundo número se ninguém atender. Vai pesquisar novamente imediatamente. A empresa que atende fica com o trabalho.
Veja como a IA de voz se compara às alternativas que a maioria das pequenas empresas usa por padrão:
| Opção | Disponibilidade | Custo/Mês | Captura de Leads | Personalização |
|---|---|---|---|---|
| Recepcionista humana | Horário comercial | $2.500-$4.000 | Manual | Alta |
| Correio de voz | 24/7 | ~$0 | Frequentemente abandonado | Nenhuma |
| Call center | 24/7 | $800-$2.000 | Inconsistente | Baixa |
| Agente de voz IA | 24/7 | $50-$200 | Automatizada | Configurável |
O agente de voz IA não é apenas mais barato. Ele é estruturalmente melhor na tarefa: nunca tem um dia ruim, sempre segue o script, captura os mesmos campos toda vez e encaminha corretamente em qualquer horário.
Para a Captain Plumber, o cálculo de ROI foi simples. Um trabalho de emergência capturado a $400-$800 cobre o custo mensal inteiro do sistema de IA.
Visão Geral da Arquitetura
Uma recepcionista IA por voz construída no Vapi.ai tem quatro camadas. Entender todas as quatro antes de começar a construir vai economizar horas de debugging.
Recepcionista IA por Voz - Arquitetura do Sistema
Como uma chamada flui do cliente até o seu CRM
Camada Telefônica - Twilio
O cliente liga para seu número. O Twilio encaminha a chamada para o Vapi via webhook. Você é dono do número e controla as regras de roteamento.
Processamento de Voz - Vapi.ai
O Vapi lida com Speech-to-Text (Deepgram), orquestração de LLM (GPT-4 ou Claude) e Text-to-Speech (ElevenLabs). Latência inferior a 1 segundo.
Lógica de Negócio - Seu Backend
Uma API Next.js ou Express recebe function calls do Vapi. Lida com salvamento de leads, roteamento de emergências, agendamento de compromissos e confirmações por SMS.
Camada de Dados - CRM / Banco de Dados
Cada chamada produz dados estruturados: nome do cliente, endereço, tipo de problema, urgência, horário preferido de retorno. Sincronizado com Airtable, HubSpot ou Supabase.
O insight principal: O Vapi lida com a complexidade que levaria meses para construir por conta própria - detecção de turnos, tratamento de interrupções, otimização de latência e failover de provedores. Seu trabalho é configurar o comportamento do agente e escrever os endpoints de lógica de negócio.
Pré-requisitos e Stack
Aqui está tudo que você precisa antes de começar:
- Conta Vapi.ai - o plano gratuito dá $10 em créditos, suficiente para testar bem
- Conta Twilio - para um número de telefone dedicado (~$1/mês)
- Chave de API OpenAI - GPT-4o é o melhor equilíbrio latência/qualidade em 2026
- Node.js 18+ com TypeScript
- Um backend implantado - Vercel, Railway ou Render funcionam bem
Tempo estimado de configuração: 3-4 horas para seu primeiro deployment em produção.
Passo 1 - Configuração da Conta e Primeiro Assistente
Instale o SDK
npm install @vapi-ai/server-sdk @vapi-ai/webCrie Seu Primeiro Assistente via API
Prefiro criar assistentes programaticamente em vez de pelo dashboard. Isso torna sua configuração versionada e repetível entre ambientes.
// lib/vapi/create-assistant.ts
import { VapiClient } from "@vapi-ai/server-sdk";
const client = new VapiClient({ token: process.env.VAPI_API_KEY! });
export async function createPlumbingReceptionist() {
const assistant = await client.assistants.create({
name: "Captain Plumber - AI Receptionist",
model: {
provider: "openai",
model: "gpt-4o",
temperature: 0.4,
systemPrompt: buildSystemPrompt(),
},
voice: {
provider: "elevenlabs",
voiceId: "21m00Tcm4TlvDq8ikWAM", // Rachel - warm, professional
stability: 0.5,
similarityBoost: 0.75,
},
firstMessage:
"Thanks for calling Captain Plumber. I'm here to help. Can I get your name and tell me what's going on with your plumbing?",
endCallMessage:
"You're all set. A member of our team will be in touch shortly. Have a good one.",
recordingEnabled: true,
transcriptPlan: {
enabled: true,
},
silenceTimeoutSeconds: 30,
maxDurationSeconds: 600,
serverUrl: process.env.VAPI_WEBHOOK_URL!, // your backend endpoint
});
console.log("Assistant created:", assistant.id);
return assistant;
}
function buildSystemPrompt(): string {
return `You are the AI receptionist for Captain Plumber, a licensed plumbing company serving the Greater Boston area.
## Your Role
You handle inbound calls to collect information, assess urgency, and route the caller appropriately. You are professional, warm, and efficient. You do not diagnose problems - you gather information and connect people to the right help.
## Information to Collect on Every Call
1. Caller full name
2. Service address (street, city)
3. Brief description of the issue
4. Best callback number (confirm if different from caller ID)
5. Whether the situation is urgent or can wait
## Emergency Classification
A call is an EMERGENCY if the caller mentions:
- Active water leak or flooding
- No hot water (in winter months)
- Sewage backup
- Gas smell (redirect to 911 first, then collect info)
- Pipe burst
## Conversation Rules
- Keep responses under 25 words when possible - this is a phone call
- Repeat back key details to confirm accuracy
- Never say "I don't know" - say "Let me make sure someone calls you about that"
- If a caller is distressed, acknowledge it before asking questions
- Confirm the callback number by reading it back digit by digit
## Routing Logic
- EMERGENCY: Tell them a plumber will call within 15 minutes. Trigger the emergency function immediately.
- NON-EMERGENCY: Offer available appointment slots for the next business day. Trigger the schedule function.
- INFORMATION ONLY: Answer basic questions from the FAQ, then offer to book an estimate.
## What You Never Do
- Quote prices over the phone
- Guarantee specific arrival times beyond the 15-minute emergency callback
- Collect payment information
- Make promises outside your defined scope`;
}Por Que Temperature 0.4?
Para uma recepcionista, você quer consistência acima de criatividade. Temperature mais baixa (0.3-0.5) significa que o modelo segue de perto suas instruções. Já vi agentes com temperature 0.8+ começarem a improvisar respostas que conflitam com as regras de negócio. Para contextos profissionais, mantenha baixa.
Passo 2 - Configurando Function Calling
Function calling é o que separa uma demo de brinquedo de um sistema em produção. Sem isso, seu agente só consegue conversar - não consegue realmente fazer nada. Functions permitem que o LLM dispare ações reais em seus sistemas durante a chamada.
Defina Suas Functions no Assistente
// lib/vapi/assistant-functions.ts
export const vapiTools = [
{
type: "function" as const,
function: {
name: "capture_lead",
description:
"Save the caller's information as a new lead in the CRM. Call this once you have collected name, address, issue, and callback number.",
parameters: {
type: "object",
properties: {
callerName: {
type: "string",
description: "Full name of the caller",
},
callbackPhone: {
type: "string",
description: "Phone number to call back, digits only",
},
serviceAddress: {
type: "string",
description: "Full service address including city",
},
issueDescription: {
type: "string",
description: "Brief description of the plumbing issue in the caller's own words",
},
isEmergency: {
type: "boolean",
description: "True if the caller described an active leak, flooding, or similar emergency",
},
preferredTime: {
type: "string",
description: "Preferred appointment window if not an emergency, e.g. 'tomorrow morning'",
},
},
required: ["callerName", "callbackPhone", "serviceAddress", "issueDescription", "isEmergency"],
},
},
},
{
type: "function" as const,
function: {
name: "route_emergency",
description:
"Immediately notify the on-call plumber via SMS and paging. Only call this when isEmergency is true.",
parameters: {
type: "object",
properties: {
callerName: { type: "string" },
callbackPhone: { type: "string" },
serviceAddress: { type: "string" },
issueDescription: { type: "string" },
},
required: ["callerName", "callbackPhone", "serviceAddress", "issueDescription"],
},
},
},
{
type: "function" as const,
function: {
name: "check_availability",
description:
"Check available appointment slots for the next 3 business days. Call this for non-emergency scheduling.",
parameters: {
type: "object",
properties: {
preferredDate: {
type: "string",
description: "Caller's preferred date if mentioned, e.g. 'tomorrow' or 'Thursday'",
},
timePreference: {
type: "string",
enum: ["morning", "afternoon", "any"],
description: "Caller's time preference",
},
},
required: [],
},
},
},
];Atualize o Assistente para Incluir as Functions
// Add tools to the assistant create call
const assistant = await client.assistants.create({
name: "Captain Plumber - AI Receptionist",
model: {
provider: "openai",
model: "gpt-4o",
temperature: 0.4,
systemPrompt: buildSystemPrompt(),
tools: vapiTools, // attach the functions here
},
// ... rest of config
});Passo 3 - Construindo o Webhook Handler
Quando o LLM decide chamar uma function, o Vapi envia uma requisição POST para sua serverUrl. É aqui que vive sua lógica de negócio.
// app/api/vapi/webhook/route.ts (Next.js App Router)
import { NextRequest, NextResponse } from "next/server";
import { captureLeadInCRM } from "@/lib/crm/capture-lead";
import { routeEmergency } from "@/lib/notifications/route-emergency";
import { getAvailableSlots } from "@/lib/calendar/availability";
interface VapiFunctionCallPayload {
message: {
type: "function-call";
functionCall: {
name: string;
parameters: Record<string, unknown>;
};
call: {
id: string;
phoneNumber?: { number: string };
};
};
}
export async function POST(req: NextRequest): Promise<NextResponse> {
const body = (await req.json()) as VapiFunctionCallPayload;
const { message } = body;
if (message.type !== "function-call") {
return NextResponse.json({ result: "ok" });
}
const { name, parameters } = message.functionCall;
const callId = message.call.id;
try {
switch (name) {
case "capture_lead":
return await handleCaptureLead(parameters, callId);
case "route_emergency":
return await handleRouteEmergency(parameters, callId);
case "check_availability":
return await handleCheckAvailability(parameters);
default:
console.warn(`Unknown function called: ${name}`);
return NextResponse.json({
result: "Function not recognized. Apologize to the caller and offer a callback.",
});
}
} catch (error) {
console.error(`Webhook handler error for ${name}:`, error);
// Return a graceful message - the agent will speak this to the caller
return NextResponse.json({
result: "There was a technical issue on our end. Please tell the caller a team member will call them back within the hour.",
});
}
}
async function handleCaptureLead(
params: Record<string, unknown>,
callId: string
): Promise<NextResponse> {
const lead = {
callerName: params.callerName as string,
callbackPhone: params.callbackPhone as string,
serviceAddress: params.serviceAddress as string,
issueDescription: params.issueDescription as string,
isEmergency: params.isEmergency as boolean,
preferredTime: (params.preferredTime as string) ?? null,
callId,
source: "ai-receptionist",
createdAt: new Date().toISOString(),
};
const savedLead = await captureLeadInCRM(lead);
return NextResponse.json({
result: `Lead saved successfully. Reference number ${savedLead.id}. Tell the caller their information has been recorded and confirm the callback number you collected.`,
});
}
async function handleRouteEmergency(
params: Record<string, unknown>,
callId: string
): Promise<NextResponse> {
await routeEmergency({
callerName: params.callerName as string,
callbackPhone: params.callbackPhone as string,
serviceAddress: params.serviceAddress as string,
issueDescription: params.issueDescription as string,
callId,
});
return NextResponse.json({
result: "Emergency alert sent. Tell the caller: an on-call plumber has been notified and will call them back within 15 minutes. Give them a confirmation.",
});
}
async function handleCheckAvailability(
params: Record<string, unknown>
): Promise<NextResponse> {
const slots = await getAvailableSlots({
preferredDate: params.preferredDate as string | undefined,
timePreference: (params.timePreference as "morning" | "afternoon" | "any") ?? "any",
});
if (slots.length === 0) {
return NextResponse.json({
result: "No slots available this week. Offer the caller the first available slot next week or take their info for a callback when slots open.",
});
}
const slotDescriptions = slots
.slice(0, 3)
.map((s) => s.label)
.join(", ");
return NextResponse.json({
result: `Available slots: ${slotDescriptions}. Read these options to the caller and ask which works best.`,
});
}O Padrão Chave: Retorne Instruções, Não Apenas Dados
Note que toda function retorna uma string result formulada como uma instrução para o agente. O LLM lê esse resultado e usa para formular sua resposta falada. Isso dá a você controle preciso sobre o que o agente diz após cada ação sem precisar atualizar o system prompt constantemente.
Passo 4 - Lógica de Roteamento de Emergências
Esta é a parte que teve o maior impacto de negócio para a Captain Plumber. Quando alguém liga às 2 da manhã com um cano estourado, o sistema precisa alertar um humano imediatamente.
// lib/notifications/route-emergency.ts
import twilio from "twilio";
const twilioClient = twilio(
process.env.TWILIO_ACCOUNT_SID!,
process.env.TWILIO_AUTH_TOKEN!
);
interface EmergencyAlert {
callerName: string;
callbackPhone: string;
serviceAddress: string;
issueDescription: string;
callId: string;
}
export async function routeEmergency(alert: EmergencyAlert): Promise<void> {
const oncallNumbers = getOnCallNumbers(); // rotate through your on-call schedule
const messageBody = buildEmergencyMessage(alert);
// SMS all on-call staff simultaneously
const smsPromises = oncallNumbers.map((number) =>
twilioClient.messages.create({
body: messageBody,
from: process.env.TWILIO_FROM_NUMBER!,
to: number,
})
);
// Also call the primary on-call number - SMS can be missed
const callPromise = twilioClient.calls.create({
twiml: buildEmergencyTwiML(alert),
from: process.env.TWILIO_FROM_NUMBER!,
to: oncallNumbers[0],
});
await Promise.all([...smsPromises, callPromise]);
// Log for audit trail
console.log(`Emergency routed for call ${alert.callId}`, {
caller: alert.callerName,
address: alert.serviceAddress,
notifiedNumbers: oncallNumbers.length,
});
}
function buildEmergencyMessage(alert: EmergencyAlert): string {
return [
"EMERGENCY CALL - Captain Plumber",
`Caller: ${alert.callerName}`,
`Phone: ${alert.callbackPhone}`,
`Address: ${alert.serviceAddress}`,
`Issue: ${alert.issueDescription}`,
"Please call back within 15 min.",
].join("\n");
}
function buildEmergencyTwiML(alert: EmergencyAlert): string {
return `<Response>
<Say voice="Polly.Joanna">
Emergency plumbing call. Caller ${alert.callerName} at ${alert.serviceAddress}
reports ${alert.issueDescription}.
Call back ${alert.callbackPhone} within 15 minutes.
Press 1 to acknowledge.
</Say>
<Gather numDigits="1" />
</Response>`;
}
function getOnCallNumbers(): string[] {
// In production, pull this from your scheduling system or a simple env var
const raw = process.env.ONCALL_NUMBERS ?? "";
return raw.split(",").filter(Boolean);
}Passo 5 - Integração com CRM
Toda chamada deve produzir um registro limpo. Usei Airtable para a Captain Plumber porque o dono já usava para rastreamento de trabalhos, mas o padrão funciona com qualquer CRM.
// lib/crm/capture-lead.ts
import Airtable from "airtable";
const base = new Airtable({ apiKey: process.env.AIRTABLE_API_KEY }).base(
process.env.AIRTABLE_BASE_ID!
);
interface LeadRecord {
callerName: string;
callbackPhone: string;
serviceAddress: string;
issueDescription: string;
isEmergency: boolean;
preferredTime: string | null;
callId: string;
source: string;
createdAt: string;
}
interface SavedLead {
id: string;
}
export async function captureLeadInCRM(lead: LeadRecord): Promise<SavedLead> {
const record = await base("Leads").create({
Name: lead.callerName,
Phone: lead.callbackPhone,
Address: lead.serviceAddress,
Issue: lead.issueDescription,
Emergency: lead.isEmergency,
"Preferred Time": lead.preferredTime ?? "Flexible",
"Call ID": lead.callId,
Source: lead.source,
Status: lead.isEmergency ? "Emergency - Needs Immediate Follow-up" : "New Lead",
"Created At": lead.createdAt,
});
return { id: record.id };
}Confirmação por SMS para o Cliente
Após salvar o lead, envie uma confirmação ao cliente. Esse único passo aumentou significativamente a satisfação pós-chamada da Captain Plumber - os clientes sentem que lidaram com uma empresa real, não com um bot.
// lib/notifications/send-caller-confirmation.ts
import twilio from "twilio";
const client = twilio(
process.env.TWILIO_ACCOUNT_SID!,
process.env.TWILIO_AUTH_TOKEN!
);
export async function sendCallerConfirmation(
phone: string,
callerName: string,
isEmergency: boolean
): Promise<void> {
const message = isEmergency
? `Hi ${callerName}, this is Captain Plumber. An on-call plumber has been notified and will call you within 15 minutes. Save this number: ${process.env.BUSINESS_PHONE}.`
: `Hi ${callerName}, thanks for calling Captain Plumber. We've got your info and will be in touch to confirm your appointment. Questions? Call or text ${process.env.BUSINESS_PHONE}.`;
await client.messages.create({
body: message,
from: process.env.TWILIO_FROM_NUMBER!,
to: phone,
});
}Passo 6 - Tratamento de Casos Extremos
Agentes de voz em produção falham de formas previsíveis. Aqui estão os casos extremos que tratei para a Captain Plumber, e como.
O Cliente Não Fala
Se alguém liga e fica em silêncio, o agente deve lidar graciosamente em vez de desligar abruptamente.
// Add to assistant config
silenceTimeoutSeconds: 10,
// Vapi will end the call after 10s of silence
// Set firstMessage to prompt immediately so callers know someone is there
firstMessage: "Thanks for calling Captain Plumber. Can you hear me okay? Take your time and let me know what you need.",O Cliente Diz Que Quer Falar com um Humano
Adicione isso ao seu system prompt:
If the caller explicitly asks to speak with a person, say: "Absolutely, I understand. Let me make sure I get your details first so our team can call you back with everything they need. What's the best number to reach you?"
Do not argue. Collect the info, then tell them: "Perfect, someone from our team will call you back shortly. We appreciate your patience."
Isso mantém a captura de lead mesmo quando o cliente opta por não interagir com a IA - que é o objetivo real.
Áudio Confuso ou Distorcido
If you cannot understand what the caller said, ask once for clarification: "Sorry, I missed that - could you repeat that for me?"
If you still cannot understand after a second attempt, say: "Let me make sure our team reaches out directly. Can I get your callback number?"
Falhe graciosamente para a captura de lead. Sempre.
Chamada Cai ou Desconecta
Configure o Vapi para tratar eventos de fim de chamada e salvar quaisquer dados parciais coletados:
// app/api/vapi/call-end/route.ts
import { NextRequest, NextResponse } from "next/server";
interface CallEndPayload {
message: {
type: "end-of-call-report";
call: { id: string };
transcript: string;
summary: string;
};
}
export async function POST(req: NextRequest): Promise<NextResponse> {
const body = (await req.json()) as CallEndPayload;
if (body.message.type !== "end-of-call-report") {
return NextResponse.json({ ok: true });
}
const { call, transcript, summary } = body.message;
// Save the full transcript and summary to your database
// Even if the lead capture function was never triggered, you have the transcript
await saveCallRecord({
callId: call.id,
transcript,
summary,
endedAt: new Date().toISOString(),
});
return NextResponse.json({ ok: true });
}O Vapi envia um relatório de fim de chamada com a transcrição completa e um resumo gerado. Para a Captain Plumber, isso tem sido inestimável - quando uma chamada cai no meio, o dono pode revisar a transcrição e fazer o acompanhamento manualmente.
Passo 7 - Conectando Seu Número Twilio
Uma vez que seu assistente está criado e seu webhook está ativo, conecte um número de telefone:
// lib/vapi/connect-phone-number.ts
import { VapiClient } from "@vapi-ai/server-sdk";
const client = new VapiClient({ token: process.env.VAPI_API_KEY! });
export async function connectPhoneNumber(
twilioPhoneNumber: string,
assistantId: string
): Promise<void> {
await client.phoneNumbers.create({
number: twilioPhoneNumber,
twilioAccountSid: process.env.TWILIO_ACCOUNT_SID!,
twilioAuthToken: process.env.TWILIO_AUTH_TOKEN!,
assistantId,
name: "Captain Plumber Main Line",
});
console.log(`Phone number ${twilioPhoneNumber} connected to assistant ${assistantId}`);
}Chame isso uma vez durante a configuração. Depois disso, toda chamada para aquele número Twilio vai direto para sua recepcionista IA.
Roteamento de Fallback
Para a Captain Plumber, configurei um fallback para que se o Vapi estiver inacessível (raro mas possível), o Twilio cai para uma mensagem gravada com o número de celular do dono. Sempre tenha um fallback.
// Twilio Webhook fallback TwiML (paste in your Twilio console)
<Response>
<Say>
Thanks for calling Captain Plumber. We're experiencing a brief technical issue.
Please call back at 617-555-0100 or leave a message and we'll return your call shortly.
</Say>
<Record maxLength="60" />
</Response>Captain Plumber - Resultados Reais
Aqui está o que 90 dias de dados de produção mostraram para este deployment específico.
Captain Plumber - Resultados de 90 Dias
Antes e depois da implantação da recepcionista IA por voz
98%
Chamadas atendidas
Era 60%
217
Chamadas atendidas/mês
Média 7,2 por dia
34
Encaminhamentos de emergência/mês
Tempo médio de resposta: 11 min
$127
Custo mensal da IA
vs $3.200 recepcionista
Qualidade dos leads
100% dos leads capturados com nome, endereço, descrição do problema e número de retorno - comparado a aproximadamente 60% de completude das mensagens de correio de voz.
Receita fora do horário comercial
Trabalhos de emergência agendados entre 21h e 7h aumentaram 3x no primeiro mês. O dono estima que isso sozinho cobre mais de 15x o custo mensal da IA.
Um número que me surpreendeu: 34% de todas as chamadas vieram fora do horário comercial. Antes da recepcionista IA, essas chamadas iam para o correio de voz. A maioria nunca era retornada porque as pessoas já tinham encontrado outro encanador.
Checklist de Deployment em Produção
Antes de ir ao ar, passe por cada item desta lista:
Checklist Pré-Lançamento
Detalhamento de Custos em Escala
Entender o modelo de custos é importante antes de apresentar isso a um cliente ou se comprometer com um deployment.
| Componente | Custo Unitário | 200 chamadas/mês (3 min média) | 1.000 chamadas/mês |
|---|---|---|---|
| Plataforma Vapi | ~$0,05/min | $30 | $150 |
| OpenAI GPT-4o | ~$0,01/min equiv | $6 | $30 |
| ElevenLabs TTS | ~$0,003/min | $1,80 | $9 |
| Twilio (chamadas) | $0,013/min | $7,80 | $39 |
| Twilio (SMS) | $0,0079/msg | $1,58 (200 SMS) | $7,90 |
| Total | ~$47/mês | ~$236/mês |
Com 200 chamadas por mês, o custo total é aproximadamente $50. Um único trabalho de emergência convertido a $500 dá um retorno de 10x só no primeiro mês. Conforme o volume aumenta, você pode otimizar para GPT-4o-mini para interações mais simples e cortar custos significativamente sem perda perceptível de qualidade.
Dicas de Engenharia de System Prompt
O system prompt é onde a maioria dos problemas de produção se origina. Aqui estão os padrões que fizeram a maior diferença na Captain Plumber:
1. Defina o persona nas duas primeiras frases. Não enterre a descrição do papel. A prioridade principal do LLM deve ser clara imediatamente.
2. Use listas numeradas para regras, não parágrafos. Quando o modelo precisa seguir 10 regras, listas numeradas são interpretadas de forma mais confiável do que prosa corrida.
3. Inclua anti-padrões explicitamente. A instrução "não cite preços" é mais confiável do que esperar que o modelo deduza isso. Diga exatamente o que você não quer.
4. Mantenha o prompt abaixo de 1.500 tokens. Prompts mais longos diluem o foco. Se seu prompt está ficando longo, geralmente significa que seu agente está tentando fazer coisas demais. Divida as responsabilidades.
5. Versione seus prompts. Armazene-os no seu codebase como constantes, não no dashboard do Vapi. Quando algo quebra às 3 da manhã, você quer um rastro de git blame, não um mistério.
O Que Este Sistema Não Faz
Quero ser direto sobre as limitações para que você defina expectativas realistas com os clientes.
A recepcionista IA se destaca em coleta estruturada de informações e roteamento. Ela não negocia preços, não lida com agendamento complexo entre múltiplos técnicos, nem trata de clientes hostis que escalam para linguagem abusiva (você deve adicionar caminhos explícitos de escalação para isso).
Para a Captain Plumber, definimos um guardrail claro: qualquer cliente que pedisse um técnico específico pelo nome, ou elevasse a voz, era encaminhado imediatamente para um voicemail-to-text com callback humano garantido em 2 horas. A IA não é a ferramenta certa para toda situação - saber quando fazer o handoff é parte de um bom design de sistema.
Próximos Passos
Se você quiser ir além com essa configuração:
- Adicione análise de sentimento na transcrição da chamada para sinalizar clientes insatisfeitos para acompanhamento prioritário
- Conecte o Google Calendar em vez de gerenciamento customizado de horários para disponibilidade em tempo real
- Construa um dashboard usando a API de analytics do Vapi para que o dono da empresa possa revisar resumos de chamadas sem ouvir gravações
- Faça testes A/B de vozes - o ElevenLabs tem dezenas de opções de voz, e a certa para um consultório médico é diferente da certa para um encanador
- Integre com o Google Meu Negócio para puxar contexto do cliente antes da conversa começar
A arquitetura neste guia é a mesma base que uso para todas essas extensões. Uma vez que o webhook handler e o padrão de function calling estão no lugar, adicionar capacidades é direto.
Resumo
Construir uma recepcionista IA por voz com Vapi.ai se resume a cinco coisas bem feitas:
- Um system prompt claro e restrito que define o persona e os guardrails
- Function calling que conecta o agente aos seus sistemas reais
- Um webhook handler que retorna instruções acionáveis, não apenas dados
- Tratamento adequado de casos extremos para silêncio, escalações e chamadas caídas
- Um caminho de roteamento de emergência que funciona às 2 da manhã sem falha
Para a Captain Plumber, isso foi do conceito à produção em uma sessão de trabalho. A empresa agora atende 98% das chamadas, captura cada lead com informações completas e encaminha emergências em menos de 90 segundos - 24 horas por dia, por menos de $130 por mês.
IA de voz não é o futuro do atendimento ao cliente para pequenas empresas. Está disponível agora, e as empresas que a implantam hoje estão construindo uma vantagem estrutural sobre concorrentes que ainda mandam chamadas para o correio de voz.
Precisa de ajuda para construir uma recepcionista IA por voz para sua empresa? Eu construo esses sistemas de ponta a ponta - da configuração do Vapi à integração com CRM ao deployment em produção. Entre em contato e podemos conversar sobre qual é a configuração certa para o seu caso de uso.