Lo esencial
Los chatbots escriben. Los agentes de voz llaman.
Son dos tipos de herramientas distintos. Un chatbot espera a que el cliente escriba. Un agente de voz puede marcar por su cuenta poco después de que el cliente dejó una solicitud y aceptó que lo llamen, y calificarlo, hacerle las preguntas necesarias y agendarle una cita. Sin intervención humana.
En esta lección construimos un agente de voz de verdad con Vapi: llama, conversa, y después de la llamada Claude analiza la transcripción y guarda los datos en el CRM.
Conceptos clave
- Agente de voz con IA: un sistema que llama a personas reales y conversa con ellas por voz
- Latency: el retraso de la respuesta. Mientras más corto, más natural la conversación; las pausas largas suenan a robot
- STT (Speech-to-Text): convierte la voz del cliente en texto
- TTS (Text-to-Speech): convierte la respuesta del LLM en voz
- Vapi: una plataforma para desarrolladores, API-first; Claude se conecta como proveedor del modelo y se adapta a distintos escenarios
- Webhook: la URL a la que Vapi envía eventos (el estado de la llamada y, al terminar, un reporte con la transcripción)
- Primer mensaje (firstMessage): lo que el agente dice en cuanto el cliente contesta
Teoría
Por qué los agentes de voz son otra historia
Los chatbots y los agentes de voz resuelven problemas distintos.
Chatbot: el cliente tiene que entrar, escribir y esperar la respuesta. La iniciativa es del cliente.
Agente de voz: el sistema inicia el contacto, dirige la conversación y decide qué preguntar después. La iniciativa es del negocio.
Por eso los agentes de voz se usan donde la velocidad de reacción es crítica:
- Calificación de leads entrantes: llega la solicitud y el agente llama casi de inmediato (si el cliente aceptó la llamada). Responder rápido suele ayudar, pero el efecto depende del nicho: compruébalo con tus datos
- Recordatorios de citas: una llamada un día antes ayuda a reducir las inasistencias
- Seguimiento después de la compra: recoge opiniones y calificaciones sin un call center
- Campañas salientes: el costo se calcula por minuto (ver la sección de costos) y solo puedes llamar a quien dio su consentimiento
La principal limitación técnica: la latencia
Una conversación por voz exige respuestas en tiempo real. Para una persona, una pausa corta en la conversación, de alrededor de medio segundo, es cómoda. Todo lo que pase de ahí se siente como "el robot está pensando".
La cadena de latencia de un agente de voz:
El cliente habla
→ STT (reconocimiento de voz): cientos de milisegundos
→ LLM (generación de la respuesta): cientos de milisegundos
→ TTS (síntesis de voz): cientos de milisegundos
→ El cliente escucha la respuesta
Total: hay que caber en más o menos un segundo; mientras más rápido, mejorLos retrasos exactos dependen de los proveedores y del modelo que elijas, así que mídelos en tu propio stack con llamadas de prueba.
Por eso plataformas como Vapi y Retell concentran su esfuerzo en optimizar esta cadena. No solo envuelven Whisper + GPT + ElevenLabs: optimizan cada paso a nivel de infraestructura.
Comparación de plataformas
| Plataforma | Enfoque | Cobro (a octubre de 2026) | Modelos |
|---|---|---|---|
| Vapi.ai | Desarrolladores, API-first | $0.05/min por la plataforma (a octubre de 2026); lo demás se paga aparte según los precios de cada proveedor | Claude, GPT, Gemini y otros (lista en la documentación) |
| Retell AI | Negocios, arranque rápido | Por minuto, según el modelo y la voz elegidos; precios en el sitio | Se eligen en la configuración de la plataforma |
| Bland.ai | Llamadas outbound/SDR | Por minuto con modelo, reconocimiento y voz incluidos; también hay planes con cuota mensual; precios en el sitio | Modelos de la plataforma |
| ElevenLabs (agentes de voz) | Calidad de voz | Revisa los planes en el sitio | Se eligen en la configuración |
| Twilio + Claude DIY | Control máximo | Telefonía de Twilio más los proveedores de voz y de modelo | Cualquiera |
⚠️ El precio de la plataforma no es todo el precio del minuto. En Vapi, los $0.05/min son solo la orquestación; el reconocimiento de voz, el modelo, la voz y la telefonía se pagan aparte. El desglose está en la sección "El costo real del minuto". Precios y versiones vigentes: Lo vigente.
Recomendación: Vapi para desarrolladores: una API clara, Claude se conecta como proveedor del modelo, hay documentación y comunidad. Retell si necesitas arrancar rápido sin código. Bland.ai si el objetivo son las ventas outbound. Los precios y las funciones de las plataformas cambian; revísalos en sus sitios.
Vapi: cómo está hecha la plataforma
Vapi tiene tres componentes:
- Assistant: la configuración del agente: prompt de sistema, voz, primer mensaje, ajustes del LLM
- Call: una llamada concreta: a quién llamar y qué assistant usar
- Webhook: qué hacer después de la llamada: transcripción, estado, grabación
Todo se maneja con la REST API o desde el Dashboard en vapi.ai. Para producción, la API. Para pruebas, el Dashboard.
Práctica
Paso 1: Registro y primera prueba
- Regístrate en vapi.ai: al registrarte te dan créditos iniciales (a octubre de 2026: $5), suficientes para llamadas de prueba
- En Dashboard → API Keys → copia la clave
- En Dashboard → Phone Numbers → compra un número o conecta tu número de Twilio. Copia el ID del número (
phoneNumberId): lo necesitas para las llamadas salientes
Guarda la clave en .env:
VAPI_KEY=your-vapi-key-here
VAPI_PHONE_NUMBER_ID=your-phone-number-id
ANTHROPIC_API_KEY=sk-ant-your-key-herePaso 2: Un agente de voz mínimo
Creamos el assistant y hacemos la primera llamada de prueba.
# voice_agent.py
import requests
import os
from dotenv import load_dotenv
load_dotenv()
VAPI_KEY = os.getenv("VAPI_KEY")
HEADERS = {"Authorization": f"Bearer {VAPI_KEY}"}
def create_lead_qualifier() -> str:
"""
Crea un agente de voz para calificar leads.
Devuelve el ID del assistant creado.
"""
assistant = requests.post(
"https://api.vapi.ai/assistant",
headers=HEADERS,
json={
"name": "Lead Qualifier ES",
"model": {
"provider": "anthropic",
"model": "claude-sonnet-5-5", # revisa que el modelo esté en la lista de Vapi; modelos vigentes: página «Lo vigente»
"messages": [{"role": "system", "content": """Eres un ejecutivo de ventas profesional.
Tu tarea es calificar un lead entrante en 3 a 5 minutos.
Haz estas cuatro preguntas a lo largo de la conversación:
1. ¿Qué presupuesto contempla el cliente?
2. ¿Qué plazos tiene: cuándo piensa tomar la decisión?
3. ¿Qué busca exactamente: requisitos concretos?
4. ¿Quién toma la decisión final?
Reglas de la conversación:
- Habla breve, amable, sin sonar a guion
- No hagas todas las preguntas seguidas: intégralas en el diálogo
- Si el cliente ya respondió una pregunta, no la repitas
- Al final, ofrece agendar una cita con un ejecutivo
- La conversación no debe durar más de 5 minutos
- Si te preguntan si eres una persona, di con honestidad que eres un asistente de IA"""}],
"temperature": 0.7,
"maxTokens": 150
},
"voice": {
"provider": "11labs",
"voiceId": "pNInz6obpgDQGcFmaJgB"
},
"firstMessage": "¡Buenas tardes! Le habla el asistente de IA de la empresa. Usted dejó una solicitud en nuestro sitio, ¿tiene un par de minutos para platicar?",
"endCallMessage": "Perfecto, ya anoté todo lo necesario. Un ejecutivo se comunicará con usted en menos de una hora. ¡Que tenga buen día!",
"maxDurationSeconds": 300
}
).json()
assistant_id = assistant["id"]
print(f"Assistant creado: {assistant_id}")
return assistant_id
def make_call(assistant_id: str, phone_number: str) -> dict:
"""
Inicia una llamada al cliente.
phone_number en formato E.164: +525512345678
Solo puedes llamar a quien dio su consentimiento (ver la sección de leyes más abajo).
"""
call = requests.post(
"https://api.vapi.ai/call",
headers=HEADERS,
json={
"assistantId": assistant_id,
"phoneNumberId": os.getenv("VAPI_PHONE_NUMBER_ID"), # el número desde el que llamamos
"customer": {
"number": phone_number,
"name": "Cliente" # opcional, para los logs
}
}
).json()
print(f"Llamada iniciada: {call['id']}")
print(f"Estado: {call['status']}")
return call
def get_call_transcript(call_id: str) -> str:
"""
Obtiene la transcripción de una llamada terminada.
"""
call = requests.get(
f"https://api.vapi.ai/call/{call_id}",
headers=HEADERS
).json()
if call.get("artifact", {}).get("transcript"):
return call["artifact"]["transcript"]
return ""
# Ejecución
if __name__ == "__main__":
assistant_id = create_lead_qualifier()
# Para probar, llámate a tu propio número
call = make_call(assistant_id, "+525512345678")
print(f"\nID de la llamada para obtener la transcripción: {call['id']}")Instalar dependencias:
pip install requests python-dotenvPaso 3: RAG: el agente conoce tu producto
El agente debe conocer tus servicios, precios y preguntas frecuentes. Esto se hace con un prompt de sistema con contexto.
# knowledge_loader.py
from pathlib import Path
def load_knowledge_base(filename: str) -> str:
"""Lee el archivo con la base de conocimiento"""
path = Path("knowledge") / filename
if path.exists():
return path.read_text(encoding="utf-8")
return ""
def build_system_prompt() -> str:
"""Arma el prompt de sistema con la base de conocimiento"""
services = load_knowledge_base("services.md")
faq = load_knowledge_base("faq.md")
objections = load_knowledge_base("objections.md")
return f"""Eres asesor de una agencia inmobiliaria en Ecuador.
NUESTROS SERVICIOS Y PRECIOS:
{services}
PREGUNTAS FRECUENTES:
{faq}
CÓMO RESPONDER A LAS OBJECIONES:
{objections}
REGLAS:
- No menciones precios por encima de los indicados: siempre "desde X"
- Si la pregunta no está en las preguntas frecuentes, di "voy a confirmar los detalles y le devolvemos la llamada"
- Ofrece siempre agendar una cita o una videollamada de asesoría
- Habla en español, salvo que el cliente cambie de idioma
- No leas listas: integra la información en la conversación"""
# Ejemplo: crear un assistant con base de conocimiento
def create_realty_agent() -> str:
import requests
import os
VAPI_KEY = os.getenv("VAPI_KEY")
assistant = requests.post(
"https://api.vapi.ai/assistant",
headers={"Authorization": f"Bearer {VAPI_KEY}"},
json={
"name": "Realty Consultant",
"model": {
"provider": "anthropic",
"model": "claude-sonnet-5-5",
"messages": [{"role": "system", "content": build_system_prompt()}]
},
"voice": {
"provider": "11labs",
"voiceId": "pNInz6obpgDQGcFmaJgB"
},
"firstMessage": "¡Buenas tardes! Le habla el asesor de IA de la agencia. ¿En qué le puedo ayudar?"
}
).json()
return assistant["id"]Crea una carpeta knowledge/ con tres archivos:
knowledge/
services.md — lista de servicios con precios
faq.md — 10 a 15 preguntas frecuentes con sus respuestas
objections.md — objeciones típicas y cómo responderlasPaso 4: Webhook: qué hacer después de la llamada
Después de cada llamada, Vapi envía los datos a tu webhook. Ahí Claude analiza la transcripción y guarda el resultado.
# webhook_handler.py
from flask import Flask, request, jsonify
import anthropic
import json
import os
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
claude = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
def analyze_call(transcript: str) -> dict:
"""
Claude analiza la transcripción y extrae datos estructurados.
"""
response = "".join(b.text for b in claude.messages.create(
model="claude-sonnet-5-5", # modelos vigentes: página «Lo vigente»
max_tokens=600,
messages=[{
"role": "user",
"content": f"""Analiza la conversación con el cliente y devuelve un JSON.
TRANSCRIPCIÓN:
{transcript}
Devuelve SOLO un JSON válido, sin explicaciones:
{{
"qualified": true o false,
"budget": "monto o rango, o 'no lo especificó'",
"timeline": "cuándo piensa tomar la decisión",
"requirements": "qué busca exactamente (2-3 oraciones)",
"decision_maker": true o false,
"next_action": "qué hacer después (por ejemplo: llamada de un ejecutivo, enviar presentación, sacar del embudo)",
"sentiment": "positivo, neutral o negativo",
"summary": "2-3 oraciones sobre la conversación"
}}"""
}]
).content if b.type == "text")
# Limpiamos el markdown si el modelo lo agregó
if "```json" in response:
response = response.split("```json")[1].split("```")[0]
elif "```" in response:
response = response.split("```")[1].split("```")[0]
try:
return json.loads(response.strip())
except json.JSONDecodeError:
return {
"qualified": False,
"summary": "No se pudo analizar la transcripción",
"raw_response": response
}
def save_to_crm(analysis: dict, customer: dict, call_id: str):
"""
Guarda el resultado en tu sistema.
Aquí cámbialo por tu CRM real: Notion, Airtable, Google Sheets, HubSpot.
"""
record = {
"call_id": call_id,
"customer_phone": customer.get("number", "unknown"),
"customer_name": customer.get("name", "unknown"),
**analysis
}
# Opción sencilla: guardar en un archivo JSON
import datetime
filename = f"calls/call-{call_id}-{datetime.date.today()}.json"
os.makedirs("calls", exist_ok=True)
with open(filename, "w", encoding="utf-8") as f:
json.dump(record, f, ensure_ascii=False, indent=2)
print(f"Guardado: {filename}")
# Si está calificado, avisa al ejecutivo
if analysis.get("qualified"):
notify_manager(record)
def notify_manager(record: dict):
"""
Envía un aviso al ejecutivo sobre un lead calificado.
Cámbialo por un canal real: WhatsApp Business, Slack, email.
"""
# Ejemplo: imprimir en consola (en producción, la API de tu canal de mensajería)
print(f"""
=== LEAD CALIFICADO ===
Teléfono: {record['customer_phone']}
Presupuesto: {record.get('budget', 'no lo especificó')}
Plazos: {record.get('timeline', 'no lo especificó')}
Siguiente paso: {record.get('next_action', '')}
Resumen: {record.get('summary', '')}
==============================
""")
@app.post("/vapi-webhook")
def handle_vapi_event():
"""Manejador principal de los eventos de Vapi"""
# Vapi pone el evento dentro de la clave "message"
message = (request.json or {}).get("message", {})
event_type = message.get("type")
if event_type == "end-of-call-report":
call = message.get("call", {})
call_id = call.get("id", "unknown")
customer = call.get("customer", {})
transcript = message.get("artifact", {}).get("transcript", "")
print(f"Llamada terminada: {call_id}")
print(f"Motivo de término: {message.get('endedReason', 'unknown')}")
if transcript:
print("Analizando la transcripción...")
analysis = analyze_call(transcript)
save_to_crm(analysis, customer, call_id)
else:
print("Transcripción vacía: la llamada no se concretó o el cliente no contestó")
elif event_type == "status-update":
call_id = message.get("call", {}).get("id", "unknown")
print(f"Estado de la llamada {call_id}: {message.get('status', '?')}")
return jsonify({"status": "ok"})
if __name__ == "__main__":
# En producción protege el webhook con un secreto en el encabezado y no actives debug
app.run(port=5000, debug=False)Instalación:
pip install flask anthropic requests python-dotenvEjecutarlo localmente (para probar):
# Inicia el servidor del webhook
python webhook_handler.py
# En otra terminal, expón el puerto con ngrok para probar
ngrok http 5000
# ngrok te dará una URL pública como https://abc123.ngrok.ioRegistra el webhook en Vapi:
# Actualiza el assistant para que envíe los eventos a tu webhook
import requests
VAPI_KEY = "your-key"
ASSISTANT_ID = "your-assistant-id"
WEBHOOK_URL = "https://abc123.ngrok.io/vapi-webhook"
requests.patch(
f"https://api.vapi.ai/assistant/{ASSISTANT_ID}",
headers={"Authorization": f"Bearer {VAPI_KEY}"},
json={"server": {"url": WEBHOOK_URL}} # en ejemplos viejos aparece el campo serverUrl
)Paso 5: Llamadas automáticas a los leads
Un escenario real: llega una solicitud nueva desde el sitio → el agente llama automáticamente. La llamada automática solo es válida si la persona dio su consentimiento en la solicitud (ver la sección de leyes más abajo).
# auto_caller.py
"""
Script para llamadas automáticas.
Conéctalo al webhook de tu CRM o de tu formulario de contacto.
"""
import requests
import os
from dotenv import load_dotenv
load_dotenv()
VAPI_KEY = os.getenv("VAPI_KEY")
ASSISTANT_ID = os.getenv("VAPI_ASSISTANT_ID")
PHONE_NUMBER_ID = os.getenv("VAPI_PHONE_NUMBER_ID")
def call_new_lead(phone: str, name: str = "", source: str = "") -> str:
"""
Llama a un lead nuevo justo después de que envía la solicitud.
Devuelve el call_id para darle seguimiento.
"""
call = requests.post(
"https://api.vapi.ai/call",
headers={"Authorization": f"Bearer {VAPI_KEY}"},
json={
"assistantId": ASSISTANT_ID,
"phoneNumberId": PHONE_NUMBER_ID,
"customer": {
"number": phone,
"name": name
},
# Pasamos contexto al agente con overrides
"assistantOverrides": {
"variableValues": {
"lead_source": source,
"lead_name": name
}
}
}
).json()
return call.get("id", "")
def batch_call(leads: list) -> list:
"""
Llama a una lista de leads.
leads = [{"phone": "+52...", "name": "...", "source": "..."}, ...]
"""
results = []
for lead in leads:
print(f"Llamando: {lead['name']} ({lead['phone']})")
call_id = call_new_lead(
phone=lead["phone"],
name=lead.get("name", ""),
source=lead.get("source", "")
)
results.append({
"lead": lead,
"call_id": call_id
})
# Pausa entre llamadas para no saturar
import time
time.sleep(2)
return results
# Ejemplo: llamadas después de exportar del CRM
if __name__ == "__main__":
today_leads = [
{"phone": "+525511111111", "name": "Juan Pérez", "source": "Sitio web"},
{"phone": "+525522222222", "name": "María González", "source": "Publicidad"},
]
results = batch_call(today_leads)
print(f"\nLlamadas iniciadas: {len(results)}")
for r in results:
print(f" {r['lead']['name']}: call_id = {r['call_id']}")El costo real del minuto: desglose del stack completo
Vapi anuncia "$0.05/min", pero eso es solo la capa de orquestación. El stack real se paga por partes. Los precios de cada parte dependen de los proveedores que elijas y cambian; tómalos de la página de precios de Vapi y de los sitios de cada proveedor:
| Componente | Costo | Qué es |
|---|---|---|
| Plataforma Vapi | $0.05/min | Orquestación de la cadena STT→LLM→TTS |
| STT (por ejemplo, Deepgram) | Por minuto, según el proveedor | Reconocimiento de voz |
| LLM | Según el modelo; para Claude, calcula con el precio por tokens del modelo elegido | El "cerebro" del agente |
| TTS (por ejemplo, ElevenLabs) | Por minuto o por suscripción, según el proveedor y la voz | Síntesis de voz |
| Telefonía (por ejemplo, Twilio) | Por minuto, según el país y el número | La línea telefónica |
| TOTAL | La suma de todo lo anterior | El precio real del minuto; depende mucho del modelo y de la voz |
Precios de los modelos (a octubre de 2026), por millón de tokens de entrada/salida:
- Claude Haiku 4.5: $1 / $5, para preguntas frecuentes e intenciones sencillas (revisa que siga disponible: Anthropic indica que no se retirará antes del 15.10.2026)
- Claude Sonnet 5.5: $2 / $10, equilibrio entre precio y calidad para producción
- Claude Opus 5.5: $4 / $20, para conversaciones complejas; más caro
Para la voz de ElevenLabs puedes elegir una suscripción en lugar de pagar por uso: revisa los planes Starter y Creator en elevenlabs.io/pricing. Precios y versiones vigentes: Lo vigente.
Escenarios de uso
Las cifras de impacto dependen del nicho, del guion y de la calidad de los leads: mídelas con tus datos, no las tomes de promesas publicitarias.
Calificación de leads: Llega la solicitud → Vapi llama poco después → en unos minutos el agente reúne presupuesto, plazos y requisitos → el ejecutivo recibe un perfil del lead listo. Resultado: el ejecutivo ve de inmediato con quién vale la pena hablar.
Appointment reminders: 24 horas antes de la cita, el agente llama, confirma la hora y pregunta si hay que preparar algo. Resultado: se evita una parte de las inasistencias; cuántas, lo dirá tu propia medición.
Post-sale follow-up: 7 días después de la compra, el agente llama, pregunta si todo está bien y recoge comentarios. Resultado: retroalimentación sin call center y detección temprana de problemas.
Campañas outbound: Calcula el costo de 1000 minutos de conversación con el stack completo (Vapi + STT + LLM + TTS + telefonía), no con el precio de la plataforma: el platform fee es solo una parte. El agente atiende muchas llamadas en paralelo (el límite depende del plan); una persona necesitaría días para el mismo número de llamadas.
Leyes y ética de las llamadas
Las llamadas automáticas están reguladas por ley, y las reglas dependen del país. En Estados Unidos, por ejemplo, está la TCPA; en Canadá, las reglas de la CRTC; en la UE, el GDPR y ePrivacy; en América Latina, las leyes de protección de datos personales y de protección al consumidor de cada país. Principios generales:
- llama solo a quien dio su consentimiento (por ejemplo, dejó una solicitud y aceptó que lo contacten);
- al inicio de la conversación, informa que es un asistente de IA y da la opción de no seguir hablando y de no recibir más llamadas;
- si la conversación se graba, avísalo: en varios países es obligatorio;
- trata las transcripciones y los teléfonos como datos personales: limita el acceso y no mandes al CRM más de lo necesario.
Esto es una orientación general, no asesoría legal: antes de lanzar, revisa con un abogado las reglas de tu país y del país de tus clientes.
Cómo monetizar los agentes de voz
Puedes ofrecerlo como servicio, pero los precios y la demanda dependen del mercado y del nicho, y los ingresos no están garantizados.
Modelo 1: configuración única: el assistant, la base de conocimiento, el webhook con integración al CRM y llamadas de prueba antes del lanzamiento.
Modelo 2: soporte y optimización: monitoreo de las transcripciones, mejora de los prompts, nuevos escenarios.
Cómo calcular el beneficio para el cliente: horas del ejecutivo dedicadas a llamar × su tarifa por hora, frente al gasto en minutos de conversación con el stack completo más tu tarifa. Haz las cuentas con honestidad y con los datos del cliente; no prometas ahorros que no puedas comprobar. Cómo armar el paquete y calcular el precio: lecciones Empaquetar tu oferta y Precios y monetización.
Herramientas y recursos
- Vapi.ai: la plataforma y la documentación de la API
- Retell AI: alternativa enfocada en arrancar fácil
- Bland.ai: especializada en llamadas outbound/SDR
- ElevenLabs: voces de alta calidad para TTS (se integra con Vapi)
- ngrok: túnel local para probar el webhook
- Flask: servidor web mínimo para el webhook
Conclusiones clave
Los agentes de voz no son chatbots mejorados. Son otro tipo de herramienta: llaman por su cuenta, llevan la conversación y pasan los datos al CRM. Un agente honesto le dice de entrada a la persona que es una IA y solo llama a quien dio su consentimiento.
Vapi + Claude es una combinación que funciona: Claude se conecta como proveedor del modelo; los escenarios en español compruébalos con llamadas de prueba usando tus propios guiones.
Después de la llamada, otra vez Claude: analiza la transcripción, califica al lead y define el siguiente paso. El agente de voz recoge los datos y Claude los procesa.
La latencia es crítica. Si tu stack DIY Twilio + Claude + ElevenLabs tiene un retraso de un segundo o más, la conversación suena poco natural. Usa plataformas listas (Vapi, Retell) que ya optimizaron esto a nivel de infraestructura.
Siguiente lección
→ Realtime AI: conversación en tiempo real por WebSocket sin servicios intermedios
La marca se guarda solo en este navegador y no se envía a ningún sitio. Mi progreso