Как мы внедрили ИИ для обработки рекламаций на производстве. bitrix24.. bitrix24. ocr.. bitrix24. ocr. ollama.. bitrix24. ocr. ollama. python.. bitrix24. ocr. ollama. python. qwen.. bitrix24. ocr. ollama. python. qwen. Tesseract.. bitrix24. ocr. ollama. python. qwen. Tesseract. автоматизация.. bitrix24. ocr. ollama. python. qwen. Tesseract. автоматизация. искусственный интеллект.. bitrix24. ocr. ollama. python. qwen. Tesseract. автоматизация. искусственный интеллект. локальные llm.. bitrix24. ocr. ollama. python. qwen. Tesseract. автоматизация. искусственный интеллект. локальные llm. рекламации.. bitrix24. ocr. ollama. python. qwen. Tesseract. автоматизация. искусственный интеллект. локальные llm. рекламации. сезон heavy digital.. bitrix24. ocr. ollama. python. qwen. Tesseract. автоматизация. искусственный интеллект. локальные llm. рекламации. сезон heavy digital. Управление продуктом.. bitrix24. ocr. ollama. python. qwen. Tesseract. автоматизация. искусственный интеллект. локальные llm. рекламации. сезон heavy digital. Управление продуктом. Управление проектами.

Где бы вы ни работали и каким идеальным продуктом или сервисом вы бы ни занимались, вас всегда будут сопровождать жалобы и рекламации от клиентов.

Рекламации — это вежливо-агрессивная форма общения между заказчиком и поставщиком, где каждая сторона добивается максимально приемлемого для себя результата. Потребитель, в идеале, хочет замену товара без дополнительных затрат, а производитель — соблюсти баланс между полным отзывом по гарантийному случаю , или вежливым ответом: «ваше обращение очень важно для нас, но помочь ничем не можем — вот вам промокод в размере 2% на последующие покупки».

С одной стороны, жалобы от клиентов являются неотъемлемой частью развития компании, ведь их отсутствие может коррелировать с отрицательным, как у нас любят говорить, ростом продаж. Но с другой стороны, каждая компания стремится минимизировать их количество, стараясь выработать какой-то базовый чеклист для работы с ними.

Об этом я и хочу написать — опыт одной компании и как она внедрила систему отслеживания рекламаций на производстве с использованием искусственного интеллекта.

В конце, ссылка на весь проект. Копируйте и применяйте на свой бизнес!

Предыстория

Группа компаний Эпотос — один из крупнейших производителей систем пожаротушения в России. Организация уже более 30 лет защищает различные объекты, которые встречаются нам на ежедневной основе — от метрополитена до БЕЛАЗов.

Фото сотрудников Эпотос на фоне завода

Фото сотрудников Эпотос на фоне завода

Можно только представить, какой спрос к компаниям, которые занимаются непосредственно нашей с вами безопасностью.

Как устроена работа с рекламациями в компании сегодня?

Есть разные направления, с которыми работает компания: Спецтехника, ВЭД, Наземный транспорт и так далее. Все они имеют свои чеклисты по работе с клиентами в случае получения рекламаций.

В прошлом году количество обращений выросло кратно и порой составляло несколько десятков в неделю. Связано это с тем, что краткосрочных задач много, а эксперты по рекламациям не успевали анализировать более глубоко первопричины возникшей проблемы.

Все рекламации поступают на почту компании info@company.ru, куда скидываются и коммерческие приглашения от других дилеров, и приглашения на выставки. По старинке, секретариат пересылал вручную письма и регистрировал входящие рекламации. У сотрудников, спустя время, начинает формироваться полотно из бесконечной переписки с клиентом по их жалобе.

Все рекламации фиксировались в Excel-таблице с довольно удобной разметкой, но она заполнялась вручную, в облаке, со всеми статусами и обновлениями по прогрессу.

Схема до автоматизации выглядела так:

Изображение 1. Диаграмма работы с рекламациями до ИИ

Изображение 1. Диаграмма работы с рекламациями до ИИ

Что решили сделать?

Чтобы не ломать существующий процесс (а это один из самых важных составляющих, когда речь заходит о цифровизации и автоматизации), было принято решение составить следующую схему работы:

  1. Подключаемся по IMAP к почте компании, куда скидываются рекламации. Тем самым, торговые дома и другие потребители имеют тот же привычный канал коммуникации в случае дефекта.

  2. Разворачиваем локальный ИИ для работы с данными клиентов, чтобы все результаты обработки хранились на сервере. Приходится работать с данными, которые не сильно хочется посылать в облако. Тем более, когда речь идет о чтении всего почтового ящика компании.

  3. Создаём фильтр, какие письма мы считаем рекламациями и по какому направлению эта рекламация должна вестись. ИИ интерпретирует слово «рекламация» по-разному, и вы это увидите в статье. Нужно чётко обозначить рамки того, что именно является жалобой на продукцию.

  4. Интегрируем почту с CRM-системой, а именно Bitrix24:

    • Создаём Битрикс-списки и туда фиксируем результат обработки по 25–30 полям. От номера рекламационного акта до текста письма вместе с файлами, которые приложили к жалобе.

    • Создаём бизнес-процесс, который автоматически будет создавать задачу в Битрикс со своим чеклистом, формирующимся в зависимости от категории рекламации. Исполнители и наблюдатели добавляются в задачу в зависимости от направления, кому пришла жалоба.

    • Фиксируем последовательность принятия работы с рекламацией — когда можно закрыть задачу и кем.

Даже предварительно, на бумаге скажем так, начинает звучать не так легко, как кажется :)

Схема работы:

Изображение 2. Логика работы с рекламациями после внедрения ИИ и Битрикс

Изображение 2. Логика работы с рекламациями после внедрения ИИ и Битрикс

Первые приключения

В ходе работы мы читаем не только сырой текст из имейла, но ещё и вложенные файлы внутри письма. Всё это следует прочитать и обработать, а затем послать в ИИ для определения, является ли входящее обращение рекламацией или нет.

Для оцифровки текста из PDF, фото и других форматов, где простым способом не считываются слова и буквы, была выбрана библиотека Tesseract с адаптацией для русского языка. Это самый адекватный способ OCR — другие библиотеки и подходы выдавали не такой качественный результат.

https://github.com/tesseract-ocr/tesseract

Можете поставить любую OCR-модель по типу Deepseek, но для нашего кейса это ни к чему — не понятно, зачем тратить ресурсы CPU/GPU на это.

Изображение 3. Скрин Tesseract OCR

Изображение 3. Скрин Tesseract OCR

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

https://huggingface.co/t-tech/T-lite-it-1.0

Изображение 4. Модель t-lite-1.0

Изображение 4. Модель t-lite-1.0

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

https://huggingface.co/Qwen/Qwen3-30B-A3B

Изображение 5. Модель Qwen3-30B-A3B

Изображение 5. Модель Qwen3-30B-A3B

Готовимся к запуску

1. Устанавливаем ИИ локально

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

Изображение 6. Интерфейс Ollama

Изображение 6. Интерфейс Ollama

Итак, скачиваем и запускаем:

# Установка 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:

  1. Узнаёте IMAP-адрес вашего почтового сервера (обычно imap.domain.ru, порт 993, SSL).

  2. Создаёте отдельную учётку или берёте пароль приложения — не пароль от личного кабинета.

  3. Подключаетесь через imaplib (встроена в Python, ничего ставить не нужно).

  4. Выбираете папку 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 += "nn--- ВЛОЖЕНИЯ ---n" + "nn".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. Соединяем с Битрикс

Соединяем всё с Битрикс-списком по входящему вебхуку. Его надо создать с правами администратора, иначе ничего не выйдет.

Извините, но скрин будет выглядеть как файл из дела об Эпштейне, потому что я не могу с вами делиться этими данными.

Изображение 7. Пример заполненного Битрикс-списка

Изображение 7. Пример заполненного Битрикс-списка

Делаем затем бизнес-процесс, чтобы автоматически создавать задачу в CRM с нужными нам ответственными и уникальными чеклистами на закрытие задачи.

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

Изображение 8. Пример задачи в Битрикс по рекламации

Изображение 8. Пример задачи в Битрикс по рекламации

7. Админка

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

(Все данные выдуманные, а то мне прилетит по бошке)

Изображение 9. Главный экран — список рекламаций

Изображение 9. Главный экран — список рекламаций

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

Изображение 10. Все письма — полный лог обработки

Изображение 10. Все письма — полный лог обработки
Изображение 11. Live логи — что происходит в реальном времени

Изображение 11. Live логи — что происходит в реальном времени
Изображение 12. История запусков — статистика по каждому прогону

Изображение 12. История запусков — статистика по каждому прогону
Изображение 13. Настройки — промты, системный промт, блеклист

Изображение 13. Настройки — промты, системный промт, блеклист

Тут можно и за конкретную дату запустить анализ по рекламациям (как вы поняли, сервис на какое-то количество суток падал, и секретариат вручную по старинке отправлял письма, но задачи в Битрикс уже надо фиксировать).

Чего мы добились?

Автоматизация жалоб от потребителей дала возможность хранить цепочку информации по жизненному циклу продукции. Это уже не просто «завести дату маркировки серийного номера и забыть, зачем мы это сделали», а целая сеть с причинно-следственной связью — что за чем идёт и почему так произошло.

Конечно, теперь дашборды на совещаниях по рекламациям рисовать удобнее, но самое важное — получилось ещё и стандартизировать чеклист по работе с обращениями от клиентов. Теперь новые сотрудники не будут стесняться спрашивать, что делать в той или иной ситуации, а просто посмотрят на список подзадач, который сформировался за годы работы их коллег.

Ну и банально — теперь есть контроль за работой над жалобой в режиме реального времени, что тоже неплохо.

Отдельное спасибо ГК Эпотос, что позволили рассказать про интересный кейс, как можно оцифровывать такой важный процесс, как рекламации с помощью ИИ!


Буду рад за лайк и подписку на канал :) https://t.me/notes_from_cto


Наш сайт: https://bvmax.ru/ai
Сайт ГК Эпотос: https://epotos.ru

Ссылка на открытый проект: https://github.com/Chashchin-Dmitry/reclamation-monitor/tree/main


Автор: Dmitrii-Chashchin

Источник