В 2013 году розничный клиент попросил нас построить автоматизированную систему поддержки. У них было 3 агента, обрабатывающих 200-300 одинаковых вопросов в день: «Где мой заказ?», «Как вернуть товар?», «Какой у вас режим работы?»
Им не нужен был ИИ. Им нужна была автоматизация. Мы построили. В процессе углубились в обработку естественного языка.
Слой rule-based обработки
Сначала простейший подход: сопоставление с образцом.
import re
class PatternMatcher:
def __init__(self):
self.rules = [
(
re.compile(r'\b(где|статус|отслеживание|трек)\b.*\bзаказ\b', re.IGNORECASE),
"Для отслеживания заказа посетите страницу /orders или укажите номер заказа.",
0.95
),
(
re.compile(r'\b(возврат|обмен|вернуть|отправить обратно)\b', re.IGNORECASE),
"Возврат возможен в течение 30 дней. Начните процесс возврата на /returns.",
0.90
),
(
re.compile(r'\b(часы|работает|открыт|закрыт|расписание)\b', re.IGNORECASE),
"Работаем пн-пт 9:00-18:00, сб 10:00-16:00. Вс - выходной.",
0.95
),
]
def match(self, text):
matches = []
for pattern, response, confidence in self.rules:
if pattern.search(text):
matches.append({'response': response, 'confidence': confidence})
return max(matches, key=lambda x: x['confidence']) if matches else None
Это обрабатывало ~60% входящих сообщений с высокой точностью. Оставшиеся 40% требовали понимания намерения из более разнообразных формулировок.
Классификация намерений через Naive Bayes
import math
from collections import defaultdict
class NaiveBayesClassifier:
def __init__(self):
self.word_counts = defaultdict(lambda: defaultdict(int))
self.class_counts = defaultdict(int)
self.vocabulary = set()
self.total_docs = 0
def tokenize(self, text):
stop_words = {'я', 'мне', 'мой', 'в', 'на', 'с', 'по', 'и', 'или', 'но', 'а'}
return [w for w in re.findall(r'[а-яё]+', text.lower())
if w not in stop_words and len(w) > 2]
def train(self, text, label):
for token in self.tokenize(text):
self.word_counts[label][token] += 1
self.vocabulary.add(token)
self.class_counts[label] += 1
self.total_docs += 1
def predict(self, text):
tokens = self.tokenize(text)
V = len(self.vocabulary)
scores = {}
for label, count in self.class_counts.items():
score = math.log(count / self.total_docs)
total_words = sum(self.word_counts[label].values())
for token in tokens:
word_count = self.word_counts[label].get(token, 0) + 1
score += math.log(word_count / (total_words + V))
scores[label] = score
best = max(scores, key=scores.get)
return best, scores
classifier = NaiveBayesClassifier()
training_data = [
("не получил посылку", "missing_package"),
("заказ не пришёл", "missing_package"),
("где мой товар", "order_status"),
("когда доставят", "order_status"),
("прислали не то", "wrong_item"),
("неправильный товар", "wrong_item"),
("товар сломан", "damaged_item"),
("пришло повреждённым", "damaged_item"),
("хочу отменить заказ", "cancel_order"),
]
for text, intent in training_data:
classifier.train(text, intent)
Пайплайн ответов
class ChatbotEngine:
def process(self, message):
# Шаг 1: Сначала сопоставление с паттерном (высокая точность)
pattern_match = self.pattern_matcher.match(message)
if pattern_match and pattern_match['confidence'] > 0.90:
return {'response': pattern_match['response'], 'escalate': False}
# Шаг 2: Классификация ML
intent, scores = self.classifier.predict(message)
score_values = sorted(scores.values())
confidence = 1 - (score_values[-2] / score_values[-1]) if len(score_values) > 1 else 0
# Шаг 3: Ответить или передать оператору
if confidence < 0.6:
return {
'response': "Давайте я соединю вас с оператором.",
'escalate': True
}
return {
'response': self.intent_responses.get(intent),
'escalate': False
}
Метрики через 3 месяца
| Метрика | До бота | После бота |
|---|---|---|
| Сообщений требующих ответа оператора | 280/день | 68/день |
| Среднее время ответа | 4.2 часа | 8 сек (бот) / 2.8 ч (передано) |
| Удовлетворённость клиентов (1-5) | 3.1 | 3.8 |
| Сообщений корректно обработано ботом | - | 71% |
| Некорректных ответов бота | - | 6% |
Показатель 6% некорректных ответов был чувствительным. Неправильные ответы подрывали доверие сильнее, чем медленные. Мы настроили пороги уверенности для более агрессивной передачи оператору - снизили некорректные ответы до 2%, подняв передачи до 18%.
Что изменили LLM
В 2023 году чат-бот на основе GPT-4 делает то, что делала наша система 2013 года - но без обучающих данных, без библиотеки паттернов, без настройки классификатора. Он понимает контекст через несколько реплик без явного управления сессией.
Система 2013 года: 6 недель разработки, 3 месяца настройки, обработка 71% случаев. Система на LLM 2023 года: 2 дня на деплой, обработка 90%+ случаев из коробки.
Что дала работа 2013 года: задача всегда была декомпозируемой - классификация намерений, извлечение сущностей, генерация ответа, передача оператору. LLM решают все четыре одновременно. Но понимание декомпозиции делает вас лучшим пользователем LLM: вы знаете, что тестировать, что настраивать, что измерять.