Skip to main content
ai voice11 de março de 202620 min de leitura

Como Construir uma Recepcionista IA por Voz com Vapi.ai

Guia passo a passo para construir uma recepcionista IA por voz em produção que atende chamadas 24/7. Estudo de caso real de uma empresa de encanamento que nunca mais perde um lead.

Loic Bachellerie

Senior Product Engineer

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çãoDisponibilidadeCusto/MêsCaptura de LeadsPersonalização
Recepcionista humanaHorário comercial$2.500-$4.000ManualAlta
Correio de voz24/7~$0Frequentemente abandonadoNenhuma
Call center24/7$800-$2.000InconsistenteBaixa
Agente de voz IA24/7$50-$200AutomatizadaConfigurá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

1

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.

2

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.

3

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.

4

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.

TwilioVapi.aiDeepgram STTOpenAI GPT-4ElevenLabs TTSAirtable CRM

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/web

Crie 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

Faça mais de 20 chamadas de teste cobrindo fluxos normais, casos extremos e emergências
Verifique se o endpoint do webhook é acessível via HTTPS com certificado SSL válido
Confirme que o SMS de emergência chega a todos os números de plantão em até 30 segundos
Verifique se os registros do CRM são criados corretamente com todos os campos obrigatórios
Teste o fallback do Twilio apontando temporariamente a serverUrl para um endpoint inexistente
Configure alertas de monitoramento para erros de webhook e function calls com falha
Revise gravações de chamadas de teste com o dono da empresa
Adicione rate limiting ao seu endpoint de webhook (o Vapi envia retries em timeout)
Defina limites de gastos nos dashboards do Vapi e da OpenAI para evitar custos descontrolados

Detalhamento de Custos em Escala

Entender o modelo de custos é importante antes de apresentar isso a um cliente ou se comprometer com um deployment.

ComponenteCusto Unitário200 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:

  1. Um system prompt claro e restrito que define o persona e os guardrails
  2. Function calling que conecta o agente aos seus sistemas reais
  3. Um webhook handler que retorna instruções acionáveis, não apenas dados
  4. Tratamento adequado de casos extremos para silêncio, escalações e chamadas caídas
  5. 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.

Share:

Receba insights práticos de engenharia

Agentes de voz com IA, fluxos de automação e entregas rápidas. Sem spam, cancele quando quiser.