Суть урока
Чат-боты пишут. Голосовые агенты звонят.
Это разные классы инструментов. Чат-бот ждёт когда клиент напишет. Голосовой агент может сам набрать номер вскоре после того, как клиент оставил заявку и разрешил позвонить, — и квалифицировать его, задать нужные вопросы, записать на встречу. Без участия человека.
В этом уроке строим настоящего голосового агента через Vapi: он звонит, разговаривает, а после звонка Claude анализирует транскрипт и сохраняет данные в CRM.
Ключевые концепции
- Голосовой AI-агент — система которая звонит реальным людям и разговаривает с ними голосом
- Latency — задержка ответа. Чем она короче, тем естественнее разговор; длинные паузы звучат как робот
- STT (Speech-to-Text) — распознаёт речь клиента в текст
- TTS (Text-to-Speech) — превращает ответ LLM в голос
- Vapi — платформа для разработчиков: API-first, Claude подключается как провайдер модели, настраивается под разные сценарии
- Webhook — url на который Vapi отправляет события (статус звонка, а после звонка — отчёт с транскриптом)
- Первое сообщение (firstMessage) — то что агент произнесёт в момент когда клиент снимет трубку
Теория
Почему голосовые агенты — отдельная история
Чат-боты и голосовые агенты решают разные проблемы.
Чат-бот: клиент должен сам зайти, написать, ждать ответа. Инициатива на стороне клиента.
Голосовой агент: система сама инициирует контакт, сама управляет разговором, сама решает что спросить следующим. Инициатива на стороне бизнеса.
Именно поэтому голосовые агенты используют там где скорость реакции критична:
- Квалификация входящих лидов — заявка пришла, агент звонит почти сразу (если клиент разрешил звонок). Быстрый отклик обычно помогает, но эффект зависит от ниши: проверяй на своих данных
- Напоминания о встречах — звонок за сутки помогает снизить число неявок
- Обзвон после покупки — собирает отзывы и оценку без колл-центра
- Исходящие кампании — стоимость считается поминутно (см. раздел о стоимости), звонить можно только тем, кто дал согласие
Главное техническое ограничение: latency
Голосовой разговор требует ответа в реальном времени. Для человека комфортна короткая пауза в разговоре, около полусекунды. Всё что дольше — ощущается как "робот думает".
Цепочка latency в голосовом агенте:
Клиент говорит
→ STT (распознавание речи): сотни миллисекунд
→ LLM (генерация ответа): сотни миллисекунд
→ TTS (синтез голоса): сотни миллисекунд
→ Клиент слышит ответ
Итого: нужно уложиться примерно в секунду; чем быстрее, тем лучшеТочные задержки зависят от выбранных провайдеров и модели, поэтому измеряй их на своём стеке тестовыми звонками.
Именно поэтому платформы вроде Vapi и Retell вкладывают основные усилия в оптимизацию этой цепочки. Они не просто оборачивают Whisper + GPT + ElevenLabs — они оптимизируют каждый шаг на инфраструктурном уровне.
Сравнение платформ
| Платформа | Фокус | Оплата (на октябрь 2026) | Модели |
|---|---|---|---|
| Vapi.ai | Разработчикам, API-first | $0.05/мин за платформу (на октябрь 2026), остальное оплачивается отдельно по ценам провайдеров | Claude, GPT, Gemini и другие (список в документации) |
| Retell AI | Бизнесу, быстрый старт | поминутно, в зависимости от выбранных модели и голоса (смотри сайт) | выбор в настройках платформы |
| Bland.ai | Outbound/SDR звонки | поминутные тарифы (модель, распознавание и голос включены) и тарифы с абонентской платой (смотри сайт) | модели платформы |
| ElevenLabs (голосовые агенты) | Качество голоса | смотри тарифы на сайте | выбор в настройках |
| Twilio + Claude DIY | Максимальный контроль | оплата телефонии Twilio плюс провайдеры речи и модели | Любой |
⚠️ Цена платформы — это не вся цена минуты. У Vapi $0.05/мин — только оркестрация; распознавание речи, модель, голос и телефония оплачиваются отдельно. Разбивку см. в разделе «Реальная стоимость минуты». Актуальные цены и версии: Актуальное сейчас.
Рекомендация: Vapi для разработчиков — понятное API, Claude подключается как провайдер модели, есть документация и сообщество. Retell если нужен быстрый старт без кода. Bland.ai если цель — outbound продажи. Цены и возможности платформ меняются, сверяй их на сайтах.
Vapi: как устроена платформа
Vapi — это три компонента:
- Assistant — конфигурация агента: системный промпт, голос, первое сообщение, настройки LLM
- Call — конкретный звонок: кому звонить, какой assistant использовать
- Webhook — что делать после звонка: транскрипт, статус, запись
Всё управляется через REST API или через Dashboard на vapi.ai. Для продакшена — API. Для тестирования — Dashboard.
Практика
Шаг 1: Регистрация и первый тест
- Зарегистрируйся на vapi.ai — при регистрации дают стартовые кредиты (на октябрь 2026: $5), этого хватает на тестовые звонки
- В Dashboard → API Keys → скопируй ключ
- В Dashboard → Phone Numbers → купи номер или подключи свой номер Twilio. Скопируй ID номера (
phoneNumberId): он нужен для исходящих звонков
Сохрани ключ в .env:
VAPI_KEY=your-vapi-key-here
VAPI_PHONE_NUMBER_ID=your-phone-number-id
ANTHROPIC_API_KEY=sk-ant-your-key-hereШаг 2: Минимальный голосовой агент
Создаём ассистента и делаем первый тестовый звонок.
# 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:
"""
Создаёт голосового агента для квалификации лидов.
Возвращает ID созданного ассистента.
"""
assistant = requests.post(
"https://api.vapi.ai/assistant",
headers=HEADERS,
json={
"name": "Lead Qualifier RU",
"model": {
"provider": "anthropic",
"model": "claude-sonnet-5-5", # проверь, что модель есть в списке Vapi; актуальные модели: страница «Актуальное сейчас»
"messages": [{"role": "system", "content": """Ты профессиональный менеджер по продажам.
Твоя задача — квалифицировать входящий лид за 3-5 минут.
Задай эти четыре вопроса в ходе разговора:
1. Какой бюджет рассматривает клиент?
2. Какие сроки — когда планирует принять решение?
3. Что именно ищет — конкретные требования?
4. Кто принимает финальное решение?
Правила разговора:
- Говори кратко, дружелюбно, без скриптового звучания
- Не задавай все вопросы подряд — вплетай в диалог
- Если клиент уже ответил на вопрос — не повторяй его
- В конце предложи записаться на встречу с менеджером
- Разговор не дольше 5 минут
- Если спросят, ты ли человек, честно скажи, что ты AI-ассистент"""}],
"temperature": 0.7,
"maxTokens": 150
},
"voice": {
"provider": "11labs",
"voiceId": "pNInz6obpgDQGcFmaJgB"
},
"firstMessage": "Добрый день! Это AI-ассистент компании. Вы оставили заявку на нашем сайте — удобно поговорить пару минут?",
"endCallMessage": "Отлично, я записал всё что нужно. Наш менеджер свяжется с вами в течение часа. Хорошего дня!",
"maxDurationSeconds": 300
}
).json()
assistant_id = assistant["id"]
print(f"Ассистент создан: {assistant_id}")
return assistant_id
def make_call(assistant_id: str, phone_number: str) -> dict:
"""
Инициирует звонок клиенту.
phone_number в формате E.164: +79161234567
Звонить можно только тем, кто дал согласие (см. раздел о законах ниже).
"""
call = requests.post(
"https://api.vapi.ai/call",
headers=HEADERS,
json={
"assistantId": assistant_id,
"phoneNumberId": os.getenv("VAPI_PHONE_NUMBER_ID"), # номер, с которого звоним
"customer": {
"number": phone_number,
"name": "Клиент" # опционально, для логов
}
}
).json()
print(f"Звонок инициирован: {call['id']}")
print(f"Статус: {call['status']}")
return call
def get_call_transcript(call_id: str) -> str:
"""
Получает транскрипт завершённого звонка.
"""
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 ""
# Запуск
if __name__ == "__main__":
assistant_id = create_lead_qualifier()
# Для теста — звоним на свой номер
call = make_call(assistant_id, "+79161234567")
print(f"\nID звонка для получения транскрипта: {call['id']}")Установка зависимостей:
pip install requests python-dotenvШаг 3: RAG — агент знает ваш продукт
Агент должен знать ваши услуги, цены, FAQ. Это делается через системный промпт с контекстом.
# knowledge_loader.py
from pathlib import Path
def load_knowledge_base(filename: str) -> str:
"""Читает файл с базой знаний"""
path = Path("knowledge") / filename
if path.exists():
return path.read_text(encoding="utf-8")
return ""
def build_system_prompt() -> str:
"""Собирает системный промпт с базой знаний"""
services = load_knowledge_base("services.md")
faq = load_knowledge_base("faq.md")
objections = load_knowledge_base("objections.md")
return f"""Ты консультант агентства недвижимости в Эквадоре.
НАШИ УСЛУГИ И ЦЕНЫ:
{services}
ЧАСТО ЗАДАВАЕМЫЕ ВОПРОСЫ:
{faq}
КАК ОТВЕЧАТЬ НА ВОЗРАЖЕНИЯ:
{objections}
ПРАВИЛА:
- Не называй цены выше указанных — всегда "от X"
- Если вопрос не в FAQ — говори "уточню детали и перезвоним"
- Всегда предлагай записаться на встречу или видеоконсультацию
- Говори на русском, если клиент не переходит на другой язык
- Не читай списком — вплетай информацию в разговор"""
# Пример: создать ассистента с базой знаний
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": "Добрый день! Это AI-консультант агентства. Чем могу помочь?"
}
).json()
return assistant["id"]Создай папку knowledge/ с тремя файлами:
knowledge/
services.md — список услуг с ценами
faq.md — 10-15 частых вопросов с ответами
objections.md — типичные возражения и как на них отвечатьШаг 4: Webhook — что делать после звонка
После каждого звонка Vapi отправляет данные на ваш webhook. Здесь Claude анализирует транскрипт и сохраняет результат.
# 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 анализирует транскрипт и извлекает структурированные данные.
"""
response = "".join(b.text for b in claude.messages.create(
model="claude-sonnet-5-5", # актуальные модели: страница «Актуальное сейчас»
max_tokens=600,
messages=[{
"role": "user",
"content": f"""Проанализируй разговор с клиентом и верни JSON.
ТРАНСКРИПТ:
{transcript}
Верни ТОЛЬКО валидный JSON без пояснений:
{{
"qualified": true или false,
"budget": "сумма или диапазон, или 'не уточнил'",
"timeline": "когда планирует принять решение",
"requirements": "что именно ищет (2-3 предложения)",
"decision_maker": true или false,
"next_action": "что делать дальше (например: позвонить менеджеру, отправить презентацию, убрать из воронки)",
"sentiment": "позитивный, нейтральный или негативный",
"summary": "2-3 предложения о разговоре"
}}"""
}]
).content if b.type == "text")
# Очищаем от markdown если модель добавила
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": "Не удалось проанализировать транскрипт",
"raw_response": response
}
def save_to_crm(analysis: dict, customer: dict, call_id: str):
"""
Сохраняет результат в вашу систему.
Здесь замени на реальный CRM: Notion, Airtable, Google Sheets, AmoCRM.
"""
record = {
"call_id": call_id,
"customer_phone": customer.get("number", "unknown"),
"customer_name": customer.get("name", "unknown"),
**analysis
}
# Простой вариант — сохранить в 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"Сохранено: {filename}")
# Если квалифицирован — отправь уведомление менеджеру
if analysis.get("qualified"):
notify_manager(record)
def notify_manager(record: dict):
"""
Отправляет уведомление менеджеру о квалифицированном лиде.
Замени на реальный канал: Telegram, Slack, email.
"""
# Пример: вывод в консоль (в продакшене — Telegram Bot API)
print(f"""
=== КВАЛИФИЦИРОВАННЫЙ ЛИД ===
Телефон: {record['customer_phone']}
Бюджет: {record.get('budget', 'не уточнил')}
Сроки: {record.get('timeline', 'не уточнил')}
Следующий шаг: {record.get('next_action', '')}
Резюме: {record.get('summary', '')}
==============================
""")
@app.post("/vapi-webhook")
def handle_vapi_event():
"""Основной обработчик событий от Vapi"""
# Vapi кладёт событие внутрь ключа "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"Звонок завершён: {call_id}")
print(f"Причина завершения: {message.get('endedReason', 'unknown')}")
if transcript:
print("Анализирую транскрипт...")
analysis = analyze_call(transcript)
save_to_crm(analysis, customer, call_id)
else:
print("Транскрипт пустой — звонок не состоялся или клиент не ответил")
elif event_type == "status-update":
call_id = message.get("call", {}).get("id", "unknown")
print(f"Статус звонка {call_id}: {message.get('status', '?')}")
return jsonify({"status": "ok"})
if __name__ == "__main__":
# В продакшне закрой webhook секретом в заголовке и не включай debug
app.run(port=5000, debug=False)Установка:
pip install flask anthropic requests python-dotenvЗапуск локально (для теста):
# Запусти webhook сервер
python webhook_handler.py
# В другом терминале — пробрось порт через ngrok для тестирования
ngrok http 5000
# ngrok даст публичный URL вроде https://abc123.ngrok.ioРегистрируй webhook в Vapi:
# Обнови ассистента чтобы он отправлял события на твой 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}} # в старых примерах встречается поле serverUrl
)Шаг 5: Автоматический обзвон лидов
Реальный сценарий: новая заявка с сайта → агент звонит автоматически. Автозвонок допустим, только если человек в заявке дал согласие на звонок (см. раздел о законах ниже).
# auto_caller.py
"""
Скрипт для автоматического обзвона.
Подключи к вебхуку вашей CRM или формы обратной связи.
"""
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:
"""
Звонит новому лиду сразу после подачи заявки.
Возвращает call_id для отслеживания.
"""
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
},
# Передаём контекст агенту через overrides
"assistantOverrides": {
"variableValues": {
"lead_source": source,
"lead_name": name
}
}
}
).json()
return call.get("id", "")
def batch_call(leads: list) -> list:
"""
Обзванивает список лидов.
leads = [{"phone": "+7...", "name": "...", "source": "..."}, ...]
"""
results = []
for lead in leads:
print(f"Звоним: {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
})
# Пауза между звонками чтобы не перегружать
import time
time.sleep(2)
return results
# Пример: обзвон после выгрузки из CRM
if __name__ == "__main__":
today_leads = [
{"phone": "+79161111111", "name": "Иван Иванов", "source": "Сайт"},
{"phone": "+79162222222", "name": "Мария Петрова", "source": "Реклама"},
]
results = batch_call(today_leads)
print(f"\nЗапущено звонков: {len(results)}")
for r in results:
print(f" {r['lead']['name']}: call_id = {r['call_id']}")Реальная стоимость минуты — разбивка полного стека
Vapi называет "$0.05/мин", но это только orchestration layer. Реальный стек оплачивается по частям. Цена каждой части зависит от выбранного провайдера и меняется, поэтому актуальные цифры бери на странице тарифов Vapi и на сайтах провайдеров:
| Компонент | Стоимость | Что это |
|---|---|---|
| Vapi platform | $0.05/мин | Orchestration STT→LLM→TTS pipeline |
| STT (Deepgram) | поминутно, смотри цены провайдера | Распознавание речи |
| LLM | зависит от модели | "Мозг" агента; для Claude считай по цене токенов выбранной модели |
| TTS (ElevenLabs) | поминутно или по подписке | Синтез голоса |
| Telephony (Twilio) | поминутно, зависит от страны и номера | Телефонная линия |
| ИТОГО | сумма всех строк выше | Реальная цена минуты, сильно зависит от модели и голоса |
Цены моделей (на октябрь 2026), за миллион токенов вход/выход:
- Claude Haiku 4.5: $1 / $5 — для FAQ и простых интентов (проверь, что модель ещё доступна: Anthropic указывает «не раньше 15.10.2026» для её вывода)
- Claude Sonnet 5.5: $2 / $10 — баланс цены и качества для продакшена
- Claude Opus 5.5: $4 / $20 — для сложных разговоров, дороже
Для голоса от ElevenLabs можно выбрать подписку вместо оплаты по факту: тарифы Starter и Creator смотри на странице elevenlabs.io/pricing. Актуальные цены и версии: Актуальное сейчас.
Сценарии использования
Цифры эффекта зависят от ниши, скрипта и качества лидов: измеряй их на своих данных, а не бери из рекламных обещаний.
Квалификация лидов: Заявка приходит → Vapi звонит вскоре после неё → агент за несколько минут собирает бюджет, сроки, требования → менеджер получает готовый профиль лида. Результат: менеджер сразу видит, с кем стоит говорить.
Appointment reminders: За 24 часа до встречи агент звонит, подтверждает время, уточняет нужно ли что-то подготовить. Результат: часть неявок удаётся предотвратить; сколько именно, покажет твой замер.
Post-sale follow-up: Через 7 дней после покупки агент звонит, узнаёт всё ли хорошо, собирает обратную связь. Результат: обратная связь без колл-центра, раннее выявление проблем.
Outbound кампании: Стоимость 1000 минут разговоров считай по полному стеку (Vapi + STT + LLM + TTS + телефония), а не по цене платформы: platform fee — только часть суммы. Агент проводит много звонков параллельно (предел зависит от тарифа), человеку на то же число звонков нужны дни.
Законы и этика звонков
Автоматические звонки регулируются законом, и правила зависят от страны. В США это, например, TCPA, в Канаде — правила CRTC, в ЕС — GDPR и ePrivacy. Общие принципы:
- звони только тем, кто дал согласие на звонок (например, оставил заявку и разрешил связаться);
- в начале разговора сообщай, что это AI-ассистент, и давай возможность отказаться от разговора и от повторных звонков;
- если разговор записывается, предупреждай об этом: в ряде стран это обязательно;
- храни транскрипты и телефоны как персональные данные: ограничь доступ, не отправляй лишнее в CRM.
Это общий ориентир, а не юридическая консультация: перед запуском проверь правила своей страны и страны клиентов у юриста.
Монетизация голосовых агентов
Это можно предлагать как услугу, но цены и спрос зависят от рынка и ниши, а заработок не гарантирован.
Модель 1: Единоразовая настройка — ассистент, база знаний, webhook с CRM-интеграцией, тестовые звонки перед запуском.
Модель 2: Поддержка и оптимизация — мониторинг транскриптов, улучшение промптов, добавление новых сценариев.
Как посчитать выгоду клиента: часы менеджера на звонки × его ставка за час против расхода на минуты разговора по полному стеку плюс твоя плата. Считай честно на данных клиента, не обещай экономию, которую не можешь подтвердить. Как собрать пакет и посчитать цену, смотри в уроках Упаковка предложения и Цены и монетизация.
Инструменты и ресурсы
- Vapi.ai — платформа и документация API
- Retell AI — альтернатива с упором на простоту старта
- Bland.ai — специализация на outbound/SDR звонках
- ElevenLabs — лучшие голоса для TTS (интегрируется с Vapi)
- ngrok — локальный туннель для тестирования webhook
- Flask — минимальный веб-сервер для webhook
Ключевые выводы
Голосовые агенты — не улучшенные чат-боты. Это другой класс инструментов: они звонят сами, ведут разговор, передают данные в CRM. Честный агент сразу говорит человеку, что он AI, и звонит только тем, кто дал согласие.
Vapi + Claude — рабочая связка: Claude подключается как провайдер модели, а русскоязычные сценарии проверь тестовыми звонками на своих скриптах.
После звонка — Claude снова: анализирует транскрипт, квалифицирует лида, определяет следующий шаг. Голосовой агент собирает данные, Claude их обрабатывает.
Latency критична. Если ваш DIY-стек Twilio + Claude + ElevenLabs даёт задержку в секунду и больше — разговор звучит неестественно. Используйте готовые платформы (Vapi, Retell) которые оптимизировали это на уровне инфраструктуры.
Следующий урок
→ Realtime AI — разговор в реальном времени через WebSocket без промежуточных сервисов
Отметка хранится только в этом браузере и никуда не отправляется. Мой прогресс