Суть урока
Представь умный ресепшн в большой больнице. Все пациенты сначала идут к нему. Большую часть вопросов он решает сам: объясняет как записаться, где парковка, какие документы нужны. Часть — готовит краткую сводку и передаёт нужному врачу. Критичные случаи — сразу сигнализирует дежурному. Врачи занимаются только тем, для чего они нужны.
AI Customer Support работает так же. Не вместо команды поддержки — рядом с ней. Берёт на себя типовое, освобождает людей для сложного.
В этом уроке строим полноценную систему: тикет-систему с классификацией, RAG по базе знаний компании, автоответы и умную эскалацию. Результат — основа, из которой можно собрать услугу для клиентов.
Ключевые концепции
- RAG (Retrieval-Augmented Generation) — Claude отвечает опираясь на конкретные документы, не выдумывает
- Классификация тикетов — типовой / нетипичный / критичный
- Эскалация — автоматическая передача сложных случаев человеку
- Deflection Rate — процент вопросов которые AI решил без человека
- CSAT — оценка клиента (Customer Satisfaction Score)
Теория
Зачем AI Customer Support
Без AI первый ответ в поддержке часто приходит через несколько часов. Это нормально для сложного вопроса. Но большинство вопросов типовые: "как сменить тариф", "где моя посылка", "как подключить интеграцию". На них клиент ждёт часами и получает ответ из двух строк который агент скопировал из FAQ.
С AI:
- Типовой вопрос → автоответ за 30 секунд (клиент доволен)
- Нетипичный → AI-драфт для агента, агент редактирует → 5 минут вместо 20
- Критичный → немедленная эскалация + уведомление агента
Агент теперь занимается только нетипичным и критичным. Объём его работы заметно падает, а качество ответов на сложное растёт, потому что есть фокус.
Архитектура системы
Клиент пишет вопрос
↓
Классификация (Claude): типовой / нетипичный / критичный
↓
Типовой → RAG по базе знаний → автоответ (большая часть, ориентир 80%)
Нетипичный → AI-драфт для агента → агент редактирует → отправляет (ориентир 15%)
Критичный → немедленная эскалация + уведомление → агент сам отвечает (ориентир 5%)Три слоя:
- Классификатор — решает куда маршрутизировать
- RAG по базе знаний — находит точный ответ из документов
- Интеграция с каналом (Intercom / Telegram / email) — принимает вопрос и отправляет ответ
RAG — шпаргалка которую AI всегда помнит
RAG расшифровывается как Retrieval-Augmented Generation. Звучит сложно, суть простая.
Обычный Claude отвечает из своих общих знаний. Это хорошо для общих вопросов. Плохо — если нужен ответ о конкретной компании: её тарифах, политике возвратов, особенностях продукта.
RAG — это когда ты даёшь Claude шпаргалку прямо в промпте. Документы FAQ, инструкции, политики — всё это вставляется в системный промпт или контекст. Claude видит базу знаний и отвечает строго из неё. Если ответа в базе нет — говорит об этом честно, не выдумывает.
Образ: не студент который пытается вспомнить — а студент со шпаргалкой. Отвечает точно. Ссылается на источник. Если в шпаргалке нет — так и говорит.
Практика
Шаг 1. Структура базы знаний
Создай папку knowledge-base/ с MD файлами. По одному файлу на каждую тему:
knowledge-base/
billing.md — вопросы об оплате
shipping.md — доставка и возвраты
integrations.md — подключение интеграций
plans.md — тарифные планы
account.md — управление аккаунтомПример billing.md:
## Как изменить тарифный план
Зайдите в личный кабинет → Настройки → Тарифный план → выберите новый план.
Изменение вступает в силу немедленно. Доплата рассчитывается пропорционально.
## Возврат средств
Возврат возможен в течение 14 дней после оплаты при первом заказе.
Для возврата напишите на [email protected] с темой "Возврат" и номером заказа.
Срок обработки — 5 рабочих дней.
## Чем отличаются планы Basic и Pro
Basic: до 5 пользователей, 10 GB хранилища, email-поддержка.
Pro: неограниченно пользователей, 100 GB хранилища, приоритетная поддержка, API доступ.Шаг 2. Загрузка базы знаний и ответ на тикет
import anthropic
import json
import time
from pathlib import Path
from datetime import datetime, timezone
client = anthropic.Anthropic()
def load_knowledge_base(kb_path: str) -> str:
"""Загружает базу знаний из MD файлов"""
kb_content = []
for md_file in Path(kb_path).glob("**/*.md"):
kb_content.append(f"\n## {md_file.stem}\n{md_file.read_text()}")
return "\n".join(kb_content)
KB = load_knowledge_base("knowledge-base/")
def answer_support_ticket(question: str) -> dict:
"""Классифицирует тикет и отвечает или эскалирует"""
start_time = time.time()
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=800,
system=f"""Ты AI-агент поддержки клиентов.
БАЗА ЗНАНИЙ КОМПАНИИ:
{KB}
Правила:
1. Если ответ есть в базе знаний — ответь точно используя её, не добавляй ничего от себя
2. В конце ответа укажи: Источник: [название раздела базы знаний]
3. Если ответа в базе знаний нет — напиши в первой строке ESCALATE и объясни почему нужен человек
4. Если вопрос касается возврата денег — ВСЕГДА пиши ESCALATE (даже если ответ в базе есть)
5. Если вопрос о технической неполадке у клиента — ESCALATE
6. Тон: дружелюбный, конкретный, без воды""",
messages=[{"role": "user", "content": question}]
)
answer = "".join(b.text for b in response.content if b.type == "text")
response_time = time.time() - start_time
return {
"answer": answer,
"needs_human": answer.strip().startswith("ESCALATE"),
"response_time_sec": round(response_time, 2),
"confidence": "low" if answer.strip().startswith("ESCALATE") else "high"
}
def track_metrics(result: dict, question: str):
"""Записывает метрики в лог"""
log_entry = {
"ts": datetime.now(timezone.utc).isoformat(),
"escalated": result["needs_human"],
"response_time_sec": result["response_time_sec"],
"confidence": result["confidence"],
"question_length": len(question)
}
with open("support-metrics.jsonl", "a") as f:
f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
# Пример использования
if __name__ == "__main__":
questions = [
"Как изменить тарифный план?",
"Хочу вернуть деньги за подписку",
"У меня не работает интеграция с Slack, всё сломалось"
]
for question in questions:
print(f"\nВопрос: {question}")
result = answer_support_ticket(question)
track_metrics(result, question)
if result["needs_human"]:
print(f"ЭСКАЛАЦИЯ -> передаём живому агенту")
print(f"Причина: {result['answer']}")
else:
print(f"Автоответ ({result['response_time_sec']} сек):")
print(result["answer"])Шаг 3. Интеграция с Intercom
from flask import Flask, request
import requests
import os
app = Flask(__name__)
INTERCOM_TOKEN = os.environ["INTERCOM_TOKEN"]
AI_BOT_ID = os.environ["INTERCOM_BOT_ID"]
def assign_to_human_agent(conversation_id: str, priority: str = "normal"):
"""Назначает тикет живому агенту и ставит метку"""
requests.post(
f"https://api.intercom.io/conversations/{conversation_id}/parts",
headers={
"Authorization": f"Bearer {INTERCOM_TOKEN}",
"Content-Type": "application/json"
},
json={
"type": "admin",
"admin_id": AI_BOT_ID,
"message_type": "assignment",
"assignee_id": None # назначит команде, не конкретному агенту
}
)
# Ставим метку приоритета
if priority == "high":
requests.post(
f"https://api.intercom.io/conversations/{conversation_id}/tags",
headers={"Authorization": f"Bearer {INTERCOM_TOKEN}"},
json={"id": os.environ["INTERCOM_HIGH_PRIORITY_TAG_ID"]}
)
@app.post("/intercom-webhook")
def handle_message():
data = request.json
if data.get("type") != "conversation.user.created":
return {"status": "ignored"}
item = data["data"]["item"]
conversation_id = item["id"]
parts = item["conversation_parts"]["conversation_parts"]
if not parts:
return {"status": "no_message"}
message = parts[0]["body"]
# AI отвечает
result = answer_support_ticket(message)
track_metrics(result, message)
if not result["needs_human"]:
# Отправляем автоответ от имени бота
requests.post(
f"https://api.intercom.io/conversations/{conversation_id}/reply",
headers={
"Authorization": f"Bearer {INTERCOM_TOKEN}",
"Content-Type": "application/json"
},
json={
"type": "admin",
"admin_id": AI_BOT_ID,
"message_type": "comment",
"body": result["answer"]
}
)
else:
# Сообщаем клиенту что подключаем человека
requests.post(
f"https://api.intercom.io/conversations/{conversation_id}/reply",
headers={
"Authorization": f"Bearer {INTERCOM_TOKEN}",
"Content-Type": "application/json"
},
json={
"type": "admin",
"admin_id": AI_BOT_ID,
"message_type": "comment",
"body": "Ваш вопрос передан специалисту. Ответим в течение 2 часов."
}
)
assign_to_human_agent(conversation_id, priority="high")
return {"status": "ok"}
if __name__ == "__main__":
app.run(port=5000)Шаг 4. Telegram-бот для поддержки
Если у клиента нет Intercom — Telegram бот закрывает задачу за пару часов.
import asyncio
import os
from telegram import Update, Bot
from telegram.ext import Application, MessageHandler, filters
TELEGRAM_BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
SUPPORT_TEAM_CHAT = os.environ["SUPPORT_TEAM_CHAT_ID"]
bot = Bot(token=TELEGRAM_BOT_TOKEN)
async def handle_support_message(update: Update, context):
user_message = update.message.text
user_id = update.effective_user.id
username = update.effective_user.username or str(user_id)
# Индикатор что бот работает
await update.message.reply_text("Проверяю...")
result = answer_support_ticket(user_message)
track_metrics(result, user_message)
if not result["needs_human"]:
await update.message.reply_text(result["answer"])
else:
# Клиенту — сообщение что подключаем человека
await update.message.reply_text(
"Ваш вопрос требует внимания специалиста. "
"Ответим в течение 2 часов рабочего времени."
)
# Команде — уведомление с полным контекстом
escalation_text = (
f"Эскалация от @{username} (id: {user_id})\n\n"
f"Вопрос: {user_message}\n\n"
f"Причина эскалации: {result['answer']}"
)
await bot.send_message(
chat_id=SUPPORT_TEAM_CHAT,
text=escalation_text
)
def run_bot():
application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
application.add_handler(
MessageHandler(filters.TEXT & ~filters.COMMAND, handle_support_message)
)
application.run_polling()
if __name__ == "__main__":
run_bot()Шаг 5. Метрики
Без метрик не знаешь работает ли система. Четыре числа которые нужно отслеживать:
| Метрика | Что измеряет | Цель |
|---|---|---|
| First Response Time | Время до первого ответа | < 1 мин (AI), < 4ч (человек) |
| Deflection Rate | % тикетов решённых без человека | > 75% |
| Resolution Rate | % тикетов закрытых с первого ответа | > 60% |
| CSAT Score | Оценка клиента 1-5 | > 4.2 |
def generate_support_report(metrics_file: str = "support-metrics.jsonl") -> dict:
"""Считает основные метрики за период"""
entries = []
with open(metrics_file) as f:
for line in f:
entries.append(json.loads(line))
if not entries:
return {"error": "нет данных"}
total = len(entries)
escalated = sum(1 for e in entries if e["escalated"])
deflection_rate = round((total - escalated) / total * 100, 1)
avg_response_time = round(
sum(e["response_time_sec"] for e in entries) / total, 2
)
return {
"total_tickets": total,
"escalated": escalated,
"auto_resolved": total - escalated,
"deflection_rate_pct": deflection_rate,
"avg_response_time_sec": avg_response_time
}
# Пример вывода:
# {
# "total_tickets": 150,
# "escalated": 28,
# "auto_resolved": 122,
# "deflection_rate_pct": 81.3,
# "avg_response_time_sec": 2.4
# }Шаг 6. Обновление базы знаний
База знаний устаревает. Новые тарифы, новые фичи, изменились правила возвратов. Простой процесс обновления:
- Редактируешь MD файл в
knowledge-base/ - Перезапускаешь сервер (или добавляешь hot-reload)
- Claude мгновенно отвечает по новым данным
Это главное преимущество MD-файлов перед векторными базами: обновление за 30 секунд, не нужна переиндексация. Подход работает, пока база знаний помещается в контекст модели. Если база большая, а вопросов много, включай prompt caching (урок Prompt Caching и Batch API): повторяющаяся часть промпта обходится дешевле.
Инструменты и ресурсы
| Инструмент | Для чего | Цена |
|---|---|---|
| Intercom | Основная тикет-система (enterprise) | Платно, тарифы на сайте |
| Crisp | Альтернатива Intercom (проще и дешевле) | Тарифы на сайте |
| Telegram Bot API | Бесплатный канал поддержки | Бесплатно |
| Flask | Webhook сервер для интеграций | Бесплатно |
| Python-telegram-bot | Telegram бот библиотека | Бесплатно |
| Claude Sonnet | Основная модель (баланс цена/качество) | Зависит от размера базы знаний и ответа; Актуальное сейчас |
Стек минимального MVP:
- Python 3.11+
anthropic— SDK Claudepython-telegram-bot— если Telegramflask— если webhook интеграция- MD файлы — база знаний
Build-to-Sell
Это можно оформить как услугу для небольших компаний, у которых поддержкой занимаются 1-3 человека. Найдутся ли клиенты и сколько они заплатят, зависит от ниши, рынка и твоей работы. Гарантий нет.
Экономика для клиента:
Считай вместе с клиентом по его данным. Часы в день на типовые вопросы × ставка сотрудника = стоимость этих вопросов за день. AI освобождает только часть этих часов: сложные тикеты и проверка ответов остаются людям. Срок окупаемости = цена услуги ÷ реальная экономия в день. Пример с вымышленными числами: 4 часа × $15 = $60 в день до внедрения, а если AI закрывает половину вопросов, экономия около $30 в день.
Структура предложения:
Цены назначай по своим затратам и ценности для клиента, подробнее в уроке Как назначить цену.
| Вариант | Что включает |
|---|---|
| Setup | Разработка + настройка + первая база знаний |
| Ежемесячное сопровождение | Хостинг + мониторинг + обновления базы знаний |
| Enterprise | Custom интеграция + обучение команды |
Время разработки MVP: 4-6 часов (Telegram бот + Claude + MD FAQ).
Что продаёшь клиенту:
- Telegram-бот или интеграция с Intercom/Crisp
- Настроенная база знаний из их FAQ
- Дашборд метрик (Deflection Rate, время ответа)
- Документация по обновлению базы знаний
Ключевые выводы
- AI Customer Support — не замена команды. Усиление: типовое берёт на себя AI, сложное остаётся людям
- 80/15/5 — ориентир, а не закон: большая часть автоответ, меньшая AI-драфт для агента, остальное эскалация; пропорции у каждой компании свои
- RAG с MD файлами — простейший путь к точным ответам. Обновление базы знаний за 30 секунд
- Три метрики которые важны клиенту: Deflection Rate (> 75%), First Response Time (< 1 мин), CSAT (> 4.2)
- Минимальный MVP собирается за несколько часов. Как услугу его можно оформить из трёх частей: setup, ежемесячное сопровождение и интеграции под заказ
Следующий урок
→ Call Support AI — Vapi + Bland.ai, голосовые звонки в поддержке
Разбираем следующий уровень: не текстовые тикеты, а голосовые звонки. Как AI принимает звонок, отвечает по базе знаний и передаёт сложные случаи человеку.
Отметка хранится только в этом браузере и никуда не отправляется. Мой прогресс