English translation is not available yet. Showing Russian content.
Агент для email
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Агент для email
1. Цель задачи
Разработать автоматизированного email-агента, который подключается к почтовому ящику, читает входящие письма, классифицирует их на 5 заданных типов и генерирует ответные сообщения через LLM API. Проект позволяет освоить интеграцию с почтовыми протоколами (IMAP/SMTP), построение конвейера обработки текста и вызов внешних AI-моделей.
Ключевой результат Работающий CLI‑ или веб‑сервис, который корректно обрабатывает не менее 5 писем разных типов (по одному на категорию) и отправляет осмысленные ответы.
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| Аккаунт электронной почты (реальный или тестовый) | Gmail / Яндекс / тестовый IMAP-сервер (см. ниже) |
| Тестовые письма 5 типов (например: запрос, жалоба, спам, уведомление, прочее) | Создать вручную или использовать публичные датасеты (Enron subset) |
| API‑ключ LLM (OpenAI / Anthropic / локальная модель) | OpenAI API / Ollama + Mistral |
| Набор меток (5 категорий) | Определить самостоятельно, зафиксировать в коде |
Если нет реального инструмента — симулируем:
- Установить [Вики/Mailpit|Mailpit(Вики/TLS|https://github.com/axllent/mailpit) (локальный Вики/SMTP|SMTP+Вики/IMAP|IMAP сервер) или использовать Вики/pytest|pytest‑фикстуру
Вики/aiosmtpd|aiosmtpd. - Написать скрипт, который генерирует 5 тестовых писем (MIME‑формат) и отправляет их через SMTP на локальный сервер.
- Для классификации без LLM — использовать простой регулярный классификатор или предобученную модель из Hugging Face (например, distilbert-base-uncased-finetuned-sst-2-english), дообучив на 5 категориях (синтетические данные).
- Если нет доступа к SMTP-отправке — сохранять сгенерированные ответы в JSON.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык программирования | Python 3.10+ | Основной язык разработки |
| Почтовые протоколы | imaplib, smtplib / aiosmtpd | Чтение и отправка писем |
| Разбор MIME | email (stdlib) | Извлечение текста/вложений |
| Классификация | LLM API (OpenAI) / Hugging Face transformers | Определение типа письма |
| Генерация ответа | LLM API (OpenAI) / Anthropic | Создание текста ответа |
| Оркестрация | FastAPI / CLI на Click | Точка входа и управление |
| Логирование | structlog / loguru | Отслеживание работы агента |
| Тестирование | pytest, responses (для API) | Юнит‑тесты и интеграционные тесты |
| Хранение результатов | SQLite / JSON‑файлы | Сохранение обработанных писем |
4. Этапы выполнения
Этап 1: Настройка окружения и подключение к почте (2 часа)
Действия
- Создать виртуальное окружение (python -m venv .venv) и установить зависимости (см. стек).
- Развернуть локальный почтовый сервер (Mailpit) для тестов:
docker run --name mailpit -d -p 1025:1025 -p 8025:8025 axllent/mailpit - Реализовать модуль
mail_client.pyс функциями:connect_imap(host, port, user, password)– устанавливает IMAP‑соединение.fetch_unseen()– получает список непрочитанных писем (UID).fetch_message(uid)– возвращает объектemail.message.Message.
- Написать скрипт генерации тестовых писем (
generate_test_emails.py), который через SMTP отправляет 5 писем с разными темами и телами на адресtest@localhost. Пример категорий:categories = ["Запрос", "Жалоба", "Спам", "Уведомление", "Прочее"] - Протестировать: запустить генератор, затем запустить
fetch_unseen()и убедиться, что письма читаются.
Ожидаемый результат этапа Рабочее IMAP-соединение, прочитано не менее 3 тестовых писем, их заголовки и тело выводятся в консоль.
Этап 2: Разбор писем и извлечение содержимого (1 час)
Действия
- Реализовать функцию
parse_email(msg):- Извлечь
From, Subject,Date,Body(plain text или извлечение из HTML с помощью BeautifulSoup). - Обработать вложения (извлечь текст из pdf/docx, если нужно – опционально).
- Вернуть словарь {uid, sender, subject, body, attachments, date}.
- Извлечь
- Учесть кодировки (UTF‑8, base64, quoted-printable) – использовать email.policy.default.
- Написать юнит‑тест на парсинг одного MIME‑письма со вложением.
Ожидаемый результат этапа Модуль парсинга корректно извлекает данные из 5 тестовых писем (проверено pytest).
Этап 3: Классификация писем (3 часа)
Действия
- Определить 5 категорий и составить примеры для каждой (записать в YAML/JSON).
- Выбрать метод классификации:
- Вариант A (LLM): Вызов OpenAI API с промптом, который просит отнести письмо к одной из 5 категорий.
- Вариант B (ML): Дообучить distilbert на синтетическом датасете (500 примеров, сгенерированных через GPT).
- Реализовать класс Classifier с методом
classify(email_text) -> category.- Для варианта A: использовать openai.ChatCompletion.create() с temperature=0, парсить ответ.
- Для варианта B: загрузить модель, токенизировать, применить softmax, взять топ‑1.
- Интегрировать классификатор в конвейер: после парсинга вызывать
classify()для каждого нового письма. - Протестировать на 5 подготовленных письмах – каждое должно попасть в свою категорию (точность 100% на тестовом наборе).
Ожидаемый результат этапа Консольный вывод: Письмо от <sender>: категория = <category> для всех 5 писем.
Этап 4: Генерация ответа и отправка (2 часа)
Действия
- Реализовать функцию
generate_reply(email_data, category):- Промпт: "Напиши вежливый ответ на письмо [текст] в категории [категория]".
- Вызвать LLM API, получить сгенерированный текст.
- (Опционально) добавить проверку длины и фильтр грубых слов.
- Реализовать send_reply(sender, subject, body, original_msg_id):
- Для отладки сначала сохранять ответы в JSON, не отправляя реально.
- Написать тест, который проверяет, что ответ действительно отправляется (mock SMTP).
Ожидаемый результат этапа Агент генерирует и отправляет (или сохраняет) корректный ответ для каждого из 5 типов писем.
Этап 5: Оркестрация, обработка ошибок и логирование (2 часа)
Действия
- Организовать главный цикл в
main.py(или FastAPI‑эндпоинт/process): - Добавить логирование:
structlog/loguru– пишем время, UID, категорию, успех отправки.- Отдельный лог ошибок.
- Настроить управление сессиями: не повторять одну и ту же операцию (запоминать
processed_uidsв SQLite). - Запустить полный тестовый прогон:
- Сгенерировать 5 новых писем.
- Запустить агента.
- Проверить, что все ответы отправлены и категории верны.
Ожидаемый результат этапа Стабильная работа агента на локальном окружении, все 5 типов обработаны, логи записаны.
5. Критерии приемки (Definition of Done)
- IMAP-соединение устанавливается и письма читаются корректно (не менее 3 писем).
- Парсер корректно извлекает текст из plain text и HTML (через BeautifulSoup).
- Классификатор относит каждое из 5 тестовых писем к ожидаемой категории (100% accuracy на тесте).
- Генератор ответа создаёт осмысленный текст (не пустой, не содержит грубых слов, длина от 50 символов).
- Ответ отправляется (или сохраняется) без ошибок, заголовки
In-Reply-ToиReferencesприсутствуют. - Обработаны граничные случаи: письмо без темы, пустое тело, вложение (проигнорировано, но не упало).
- Проект запускается одной командой (например,
python -m email_agent) или через Docker Compose. - Все зависимости зафиксированы в
requirements.txt/pyproject.toml. - Покрытие тестами – не менее 60% ключевых модулей (классификатор, парсер, клиент).
6. Ожидаемый результат
Основной артефакт Репозиторий с кодом email-агента, включающий:
mail_client.py– IMAP/SMTP клиентparser.py– разбор писемclassifier.py– классификация (и модель, если используется локальная)responder.py– генерация и отправка ответовmain.py– точка входа (CLI или FastAPI)tests/– юнит‑тестыgenerate_test_emails.py– скрипт для тестовых писемREADME.md– инструкция по запуску, описание 5 категорий
Дополнительные результаты
- Логи обработанных писем в
logs/email_agent.log - JSON-файл с историей (опционально)
- Dockerfile и
docker-compose.yml(опционально)
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| IMAP-сервер не отвечает / требует SSL | Использовать локальный Mailpit. Для удалённого – настроить OAuth2 (Gmail). Для тестов – mock IMAP через aiosmtpd. |
| LLM API нестабилен / превышение лимита | Внедрить tenacity retry + fallback (заранее заготовленные шаблоны ответов). |
| MIME-письмо с вложением не парсится | Использовать email.policy.default и email.iterators.typed_subpart_iterator. |
| Классификатор путает категории | Уточнить промпт (few-shot examples) или дообучить модель на 50–100 примерах на категорию. |
| Отправка письма не проходит | Проверить SMTP-настройки. Для Gmail – включить «ненадёжные приложения» или App Password. |
| Проект не переиспользуем (захардкожены пароли) | Вынести настройки в .env (python-dotenv) и config.yaml. |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Настройка окружения и подключение к почте | 2 часа |
| Этап 2: Разбор писем и извлечение содержимого | 1 час |
| Этап 3: Классификация писем | 3 часа |
| Этап 4: Генерация ответа и отправка | 2 часа |
| Этап 5: Оркестрация, обработка ошибок и логирование | 2 часа |
| Итого | 10 часов |
Примечание: Время может увеличиться в 1,5–2 раза при первом выполнении (из‑за изучения документации).
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 101 | Работа с IMAP через imaplib |
| 205 | Разбор MIME-писем в Python |
| 312 | Использование OpenAI API для классификации текста |
| 415 | Обработка ошибок и retry в сетевых запросах |
| 523 | Тестирование кода, работающего с внешними API (mocking) |
| 601 | Логирование в production-grade приложениях |
| 708 | FastAPI – создание эндпоинта для фоновой обработки |
| 815 | Docker Compose для локального тестового окружения |
| 890 | Оценка качества классификации (precision/recall) |
10. Чек-лист самопроверки
- Я проверил, что IMAP-клиент читает письма с локального или тестового сервера, а код не падает при отсутствии соединения.
- Я убедился, что классификатор корректно обрабатывает все 5 тестовых писем (по одному на категорию).
- Я проверил, что ответ генерируется, не содержит ничего лишнего и отправляется (или сохраняется).
- Я написал минимум 3 теста (на парсер, классификатор, отправку) и они проходят.
- Я задокументировал в README, как запустить проект, какие переменные окружения нужны, и какие категории используются.