Где бы вы ни работали и каким идеальным продуктом или сервисом вы бы ни занимались, вас всегда будут сопровождать жалобы и рекламации от клиентов.
Рекламации — это вежливо-агрессивная форма общения между заказчиком и поставщиком, где каждая сторона добивается максимально приемлемого для себя результата. Потребитель, в идеале, хочет замену товара без дополнительных затрат, а производитель — соблюсти баланс между полным отзывом по гарантийному случаю , или вежливым ответом: «ваше обращение очень важно для нас, но помочь ничем не можем — вот вам промокод в размере 2% на последующие покупки».
С одной стороны, жалобы от клиентов являются неотъемлемой частью развития компании, ведь их отсутствие может коррелировать с отрицательным, как у нас любят говорить, ростом продаж. Но с другой стороны, каждая компания стремится минимизировать их количество, стараясь выработать какой-то базовый чеклист для работы с ними.
Об этом я и хочу написать — опыт одной компании и как она внедрила систему отслеживания рекламаций на производстве с использованием искусственного интеллекта.
В конце, ссылка на весь проект. Копируйте и применяйте на свой бизнес!
Предыстория
Группа компаний Эпотос — один из крупнейших производителей систем пожаротушения в России. Организация уже более 30 лет защищает различные объекты, которые встречаются нам на ежедневной основе — от метрополитена до БЕЛАЗов.

Можно только представить, какой спрос к компаниям, которые занимаются непосредственно нашей с вами безопасностью.
Как устроена работа с рекламациями в компании сегодня?
Есть разные направления, с которыми работает компания: Спецтехника, ВЭД, Наземный транспорт и так далее. Все они имеют свои чеклисты по работе с клиентами в случае получения рекламаций.
В прошлом году количество обращений выросло кратно и порой составляло несколько десятков в неделю. Связано это с тем, что краткосрочных задач много, а эксперты по рекламациям не успевали анализировать более глубоко первопричины возникшей проблемы.
Все рекламации поступают на почту компании info@company.ru, куда скидываются и коммерческие приглашения от других дилеров, и приглашения на выставки. По старинке, секретариат пересылал вручную письма и регистрировал входящие рекламации. У сотрудников, спустя время, начинает формироваться полотно из бесконечной переписки с клиентом по их жалобе.
Все рекламации фиксировались в Excel-таблице с довольно удобной разметкой, но она заполнялась вручную, в облаке, со всеми статусами и обновлениями по прогрессу.
Схема до автоматизации выглядела так:

Что решили сделать?
Чтобы не ломать существующий процесс (а это один из самых важных составляющих, когда речь заходит о цифровизации и автоматизации), было принято решение составить следующую схему работы:
Подключаемся по IMAP к почте компании, куда скидываются рекламации. Тем самым, торговые дома и другие потребители имеют тот же привычный канал коммуникации в случае дефекта.
Разворачиваем локальный ИИ для работы с данными клиентов, чтобы все результаты обработки хранились на сервере. Приходится работать с данными, которые не сильно хочется посылать в облако. Тем более, когда речь идет о чтении всего почтового ящика компании.
Создаём фильтр, какие письма мы считаем рекламациями и по какому направлению эта рекламация должна вестись. ИИ интерпретирует слово «рекламация» по-разному, и вы это увидите в статье. Нужно чётко обозначить рамки того, что именно является жалобой на продукцию.
Интегрируем почту с CRM-системой, а именно Bitrix24:
Создаём Битрикс-списки и туда фиксируем результат обработки по 25–30 полям. От номера рекламационного акта до текста письма вместе с файлами, которые приложили к жалобе.
Создаём бизнес-процесс, который автоматически будет создавать задачу в Битрикс со своим чеклистом, формирующимся в зависимости от категории рекламации. Исполнители и наблюдатели добавляются в задачу в зависимости от направления, кому пришла жалоба.
Фиксируем последовательность принятия работы с рекламацией — когда можно закрыть задачу и кем.
Даже предварительно, на бумаге скажем так, начинает звучать не так легко, как кажется :)
Схема работы:

Первые приключения
В ходе работы мы читаем не только сырой текст из имейла, но ещё и вложенные файлы внутри письма. Всё это следует прочитать и обработать, а затем послать в ИИ для определения, является ли входящее обращение рекламацией или нет.
Для оцифровки текста из PDF, фото и других форматов, где простым способом не считываются слова и буквы, была выбрана библиотека Tesseract с адаптацией для русского языка. Это самый адекватный способ OCR — другие библиотеки и подходы выдавали не такой качественный результат.
Можете поставить любую OCR-модель по типу Deepseek, но для нашего кейса это ни к чему — не понятно, зачем тратить ресурсы CPU/GPU на это.

Затем выберем модель для работы с рекламациями. В самом начале мы скачали T-lite 7B 1.0. Она является дообученной (fine-tune) моделью Qwen-2.5 с русским датасетом и чем-то ещё внутри (мы не знаем чем).

Но в ходе тестирования мы и сами прокачали свои знания по ИИ, и в целом узнали, что у нас тянет не с самой лучшей скоростью, но прекрасная модель Qwen3-30B-A3B. Сложно найти что-то лучше за такой объём обученной модели, так ещё и с reasoning, и с системой Mixture-of-Experts. Что это значит? ИИ сам применяет на себя образы, какая роль лучше всего подойдёт для ответа на тот или иной запрос, и пользователь получает более точный ответ. Более того, вставка «A3» у модели свидетельствует о том, что при 30 миллиардах обученных параметров ИИ тратит только 3 миллиарда в активной фазе для ответа на вопрос. Звучит слишком хорошо, чтобы быть правдой, но модель и вправду очень хороша.

Готовимся к запуску
1. Устанавливаем ИИ локально
Покажу, как установить ИИ. Сейчас это делается довольно просто. Фреймворк Ollama довёл этот процесс до совершенства и даже добавил возможность запустить модель в локальном ChatGPT-подобн��м интерфейсе, чтобы мучать её на своих мощностях.

Итак, скачиваем и запускаем:
# Установка Ollama (macOS / Linux) curl -fsSL https://ollama.com/install.sh | sh # Запускаем сервер (если не стартанул автоматом) ollama serve # Скачиваем модель ollama pull qwen3:30b-a3b # Проверяем, что модель на месте ollama list # Запускаем в интерактивном режиме для теста ollama run qwen3:30b-a3b
Делаем первые тестовые запросы:
# Через API curl http://localhost:11434/api/generate -d '{ "model": "qwen3:30b-a3b", "prompt": "Что такое рекламация в контексте производства?", "stream": false }'
# Или через Python import requests response = requests.post("http://localhost:11434/api/generate", json={ "model": "qwen3:30b-a3b", "prompt": "Что такое рекламация в контексте производства?", "stream": False }) print(response.json()["response"])
Запуском ИИ у себя на ноутбуке уже никого не удивишь, поэтому мы должны идти дальше.
2. Подключаемся к почте по IMAP
Чтобы читать, что пишут нам разного рода люди и не только. Базовая инструкция, как подключиться к IMAP:
Узнаёте IMAP-адрес вашего почтового сервера (обычно
imap.domain.ru, порт993, SSL).Создаёте отдельную учётку или берёте пароль приложения — не пароль от личного кабинета.
Подключаетесь через
imaplib(встроена в Python, ничего ставить не нужно).Выбираете папку
INBOXи фильтруете по дате — чтобы не перечитывать всю историю к��ждый раз.
import imaplib import email from email.header import decode_header # Подключение mail = imaplib.IMAP4_SSL("imap.your-server.ru", 993) mail.login("info@your-domain.ru", "your-app-password") mail.select("INBOX") # Ищем письма за конкретную дату status, messages = mail.search(None, '(SINCE "10-Mar-2026")') mail_ids = messages[0].split() print(f"Найдено писем: {len(mail_ids)}") # Читаем каждое письмо for mail_id in mail_ids: status, msg_data = mail.fetch(mail_id, "(RFC822)") raw_email = msg_data[0][1] msg = email.message_from_bytes(raw_email) # Тема subject, encoding = decode_header(msg["Subject"])[0] if isinstance(subject, bytes): subject = subject.decode(encoding or "utf-8") # Отправитель sender = msg.get("From") print(f"От: {sender} | Тема: {subject}") mail.logout()
3. Женим ИИ и почту
Вычленяем текст и файлы из письма, прогоняем через OCR при необходимости, и отправляем в локальную модель:
import os import pytesseract from PIL import Image from PyPDF2 import PdfReader import requests import tempfile OLLAMA_URL = "http://localhost:11434/api/generate" MODEL = "qwen3:30b-a3b" def extract_text_from_attachment(part): """Извлекаем текст из вложения письма.""" filename = part.get_filename() content_type = part.get_content_type() payload = part.get_payload(decode=True) if not payload or not filename: return "" with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(filename)[1]) as tmp: tmp.write(payload) tmp_path = tmp.name text = "" try: if content_type == "application/pdf": reader = PdfReader(tmp_path) text = "\n".join(page.extract_text() or "" for page in reader.pages) elif content_type.startswith("image/"): img = Image.open(tmp_path) text = pytesseract.image_to_string(img, lang="rus") elif "text" in content_type or filename.endswith((".txt", ".csv")): text = payload.decode("utf-8", errors="ignore") finally: os.unlink(tmp_path) return text def get_email_full_text(msg): """Собираем весь текст из письма: тело + вложения.""" body = "" attachments_text = [] for part in msg.walk(): content_type = part.get_content_type() if content_type == "text/plain" and not part.get_filename(): charset = part.get_content_charset() or "utf-8" body = part.get_payload(decode=True).decode(charset, errors="ignore") elif part.get_filename(): att_text = extract_text_from_attachment(part) if att_text.strip(): attachments_text.append(f"[Файл: {part.get_filename()}]\n{att_text}") full_text = body if attachments_text: full_text += "\n\n--- ВЛОЖЕНИЯ ---\n" + "\n\n".join(attachments_text) return full_text def ask_ollama(prompt, system_prompt=""): """Отправляем запрос в локальную модель.""" payload = { "model": MODEL, "prompt": prompt, "stream": False, } if system_prompt: payload["system"] = system_prompt response = requests.post(OLLAMA_URL, json=payload) return response.json().get("response", "")
4. Система промтов
Составим промты для анализа. Вот система — какой за чем идёт и когда применяется:
Промт 1 — Классификация письма. Определяем, является ли письмо рекламацией вообще:
SYSTEM_PROMPT = """Ты — специалист по анализу документов. Правила: 1. Один физический продукт = одна запись в products 2. Если продукт упоминается несколько раз — объедини в одну запись 3. Категория: определи по контексту (Наземка/Метро/Спецтехника/ЖДТ)""" CLASSIFICATION_PROMPT = """Ты — специалист по анализу технической документации ЭПОТОС. ДОКУМЕНТ: {filename} ТИП ФАЙЛА: {content_type} ПОЛНЫЙ ТЕКСТ ДОКУМЕНТА: {full_text} КОНТЕКСТ ПИСЬМА: - Тема: {email_subject} - Отправитель: {email_sender} - Дата: {email_date} ЗАДАЧА: Определи, является ли это письмо рекламацией на продукцию ЭПОТОС. Рекламация — это жалоба на дефект, неисправность, несоответствие продукции. НЕ рекламация — коммерческое предложение, приглашение, счёт, спам, внутренняя переписка. Ответь строго в формате JSON: { "is_reclamation": true/false, "confidence": 0.0-1.0, "reason": "краткое обоснование" }"""
Промт 2 — Детальный разбор. Если письмо признано рекламацией, вытаскиваем структурированные данные:
EXTRACTION_PROMPT = """Это письмо является рекламацией. Извлеки данные: ТЕКСТ ПИСЬМА: {full_text} Ответь строго в формате JSON: { "products": [ { "name": "название изделия", "serial_number": "серийный номер если есть", "quantity": 1, "defect_description": "описание дефекта" } ], "category": "Наземка/Метро/Спецтехника/ЖДТ", "sender_organization": "название организации отправителя", "contact_person": "контактное лицо", "reclamation_act_number": "номер акта если указан", "severity": "критическая/средняя/низкая", "summary": "краткое описание проблемы в 1-2 предложениях" }"""
4 (бонус). Фильтр внутренних писем
Важный момент: уже в ходе тестирования мы узнали, что рабочие внутри Эпотос отправляют ответы на рекламации на ту же почту info@. Поэтому делаем хардкод-фильтр: любые имейлы, пришедшие от доменного имени @epotos, мы игнорируем. За исключением одного сотрудника, потому что ему периодически пересылают жалобы на личный имейл — он работает в компании более 20 лет, и не всегда легко объяснить клиенту, что мы тут, понимаете ли, ИИ-автоматизацией занимаемся, а не делаем непойми что.
INTERNAL_DOMAIN = "@epotos.ru" EXCEPTION_EMAILS = ["ivanov@epotos.ru"] # Тот самый сотрудник с 20-летним стажем def is_internal_email(sender: str) -> bool: """Фильтруем внутренние письма, кроме исключений.""" sender_lower = sender.lower() # Проверяем исключения for exception in EXCEPTION_EMAILS: if exception in sender_lower: return False # Всё остальное от @epotos — игнорируем if INTERNAL_DOMAIN in sender_lower: return True return False
5. Запускаем процессор
import json import time from datetime import datetime PROCESSED_FILE = "processing_results.json" CHECK_INTERVAL = 120 # секунд def process_mailbox(target_date: str): """Основной цикл обработки почты.""" mail = connect_imap() mail.select("INBOX") status, messages = mail.search(None, f'(SINCE "{target_date}")') mail_ids = messages[0].split() results = {"processed": 0, "reclamations": 0, "skipped": 0, "errors": 0} for mail_id in mail_ids: try: status, msg_data = mail.fetch(mail_id, "(RFC822)") msg = email.message_from_bytes(msg_data[0][1]) sender = msg.get("From", "") subject = decode_header(msg["Subject"])[0][0] if isinstance(subject, bytes): subject = subject.decode("utf-8", errors="ignore") # Фильтр внутренних if is_internal_email(sender): results["skipped"] += 1 continue # Собираем текст full_text = get_email_full_text(msg) # Спрашиваем ИИ classification = ask_ollama( CLASSIFICATION_PROMPT.format( filename="email", content_type="text/plain", full_text=full_text, email_subject=subject, email_sender=sender, email_date=msg.get("Date", ""), ), system_prompt=SYSTEM_PROMPT, ) result = json.loads(classification) if result.get("is_reclamation"): # Детальный разбор details = ask_ollama(EXTRACTION_PROMPT.format(full_text=full_text)) reclamation_data = json.loads(details) # Отправляем в Битрикс create_bitrix_list_entry(reclamation_data, msg) results["reclamations"] += 1 results["processed"] += 1 except Exception as e: results["errors"] += 1 print(f"Ошибка обработки письма {mail_id}: {e}") mail.logout() return results # Бесконечный цикл проверки while True: today = datetime.now().strftime("%d-%b-%Y") print(f"[{datetime.now()}] Проверяем почту за {today}...") results = process_mailbox(today) print(f"Результаты: {results}") with open(PROCESSED_FILE, "w") as f: json.dump(results, f, ensure_ascii=False, indent=2) print(f"Следующая проверка через {CHECK_INTERVAL} секунд") time.sleep(CHECK_INTERVAL)
6. Соединяем с Битрикс
Соединяем всё с Битрикс-списком по входящему вебхуку. Его надо создать с правами администратора, иначе ничего не выйдет.
Извините, но скрин будет выглядеть как файл из дела об Эпштейне, потому что я не могу с вами делиться этими данными.

Делаем затем бизнес-процесс, чтобы автоматически создавать задачу в CRM с нужными нам ответственными и уникальными чеклистами на закрытие задачи.
Как я писал выше, чеклисты всегда существовали где-то в уме или устно передавались из уст в уста новоприбывшим сотрудникам. Но нигде порядок действий не был зафиксирован в цифровом или другом виде. Было очень сложно вытягивать эту информацию у разных отделов, но у нас получилось!

7. Админка
Я периодически отсутствую на рабочем месте из-за командировок. Или же я просто хочу отдохнуть в выходные не в офисе, а дома (удивительно, правда?). Секретариат всё ещё внимательно отслеживает каждое письмо, пришедшее на почтовый ящик, и так произошло, что на первых порах сервис по анализу с ИИ останавливался сам собой. Для этого я создал свою админку с блекджеком и Клодом, и вот что из этого вышло:
(Все данные выдуманные, а то мне прилетит по бошке)

Главный экран — список рекламаций




Тут можно и за конкретную дату запустить анализ по рекламациям (как вы поняли, сервис на какое-то количество суток падал, и секретариат вручную по старинке отправлял письма, но задачи в Битрикс уже надо фиксировать).
Чего мы добились?
Автоматизация жалоб от потребителей дала возможность хранить цепочку информации по жизненному циклу продукции. Это уже не просто «завести дату маркировки серийного номера и забыть, зачем мы это сделали», а целая сеть с причинно-следственной связью — что за чем идёт и почему так произошло.
Конечно, теперь дашборды на совещаниях по рекламациям рисовать удобнее, но самое важное — получилось ещё и стандартизировать чеклист по работе с обращениями от клиентов. Теперь новые сотрудники не будут стесняться спрашивать, что делать в той или иной ситуации, а просто посмотрят на список подзадач, который сформировался за годы работы их коллег.
Ну и банально — теперь есть контроль за работой над жалобой в режиме реального времени, что тоже неплохо.
Отдельное спасибо ГК Эпотос, что позволили рассказать про интересный кейс, как можно оцифровывать такой важный процесс, как рекламации с помощью ИИ!
Буду рад за лайк и подписку на канал :) https://t.me/notes_from_cto
Наш сайт: https://bvmax.ru/ai
Сайт ГК Эпотос: https://epotos.ru
Ссылка на открытый проект: https://github.com/Chashchin-Dmitry/reclamation-monitor/tree/main
