中文翻译暂不可用,显示俄语原文。

Агент с внешними API (Slack, Gmail, Calendar)

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Агент с внешними API (Slack, Gmail, Calendar)

1. Цель задачи

Создать программного агента, который интегрируется с тремя внешними сервисами — Slack, Gmail и Google Calendar — для автоматизации типового рабочего сценария: получение запроса на встречу из Slack, создание события в календаре и отправка подтверждения по электронной почте. Задача направлена на отработку навыков работы с REST API, OAuth-аутентификацией, обработки данных в реальном времени и написания изолированных юнит-тестов для каждого интеграционного модуля.
Ключевой результат полностью рабочий агент, запускаемый командой python main.py --scenario "meeting_request", который успешно выполняет E2E-сценарий, а также набор юнит-тестов с покрытием каждого модуля не менее 80%.

2. Исходные данные

Что нужноОткуда взять
Учётная запись Slack и workspaceSlack API (https://api.[slack](/wiki/Slack).com/apps) — создать приложение с правами channels:history, chat:write, users:read
Учётная запись Google (Gmail, Calendar)Google Cloud Console (https://console.cloud.google.com) — включить Gmail API и Google Calendar API, создать OAuth 2.0 Client ID (desktop application)
Доступ к API через токены/кредыSlack: Bot Token (xoxb-...); Google: credentials.json для OAuth, получить token.json после первой авторизации
Среда разработкиPython 3.11+, любой текстовый редактор / IDE, Git
Дополнительные данныеТестовые каналы Slack (#test-bot), тестовые email (you+test@gmail.com), тестовый календарь Google (можно основной)

Если нет реального инструмента — симулируем:

  1. Вместо реального Slack можно написать фейковый HTTP-сервер (Flask) на порту 5050, который эмулирует ответы Slack API (list conversations, post message, get user info).
  2. Для Gmail/Calendar — использовать библиотеку unittest.mock для подмены всех HTTP-запросов внутри google-api-python-client.
  3. Для локального тестирования OAuth — создать mock_credentials.json и заглушку google.oauth2.credentials.Credentials, которая возвращает статичный token.json.
  4. Все моки описаны в файле tests/mocks.py и переиспользуются в тестах.

3. Технологический стек

КомпонентИнструментыНазначение
Язык программированияPython 3.11+Основной язык разработки
Управление зависимостямиPoetry (или pip + virtualenv)Изолированное окружение
Slack APIslack_sdk (>=3.27)Взаимодействие с Slack (чтение каналов, отправка сообщений)
Google APIsgoogle-api-python-client, google-auth-httplib2, google-auth-oauthlibРабота с Gmail и Calendar
HTTP-клиентrequests (>=2.31)Альтернативный прямой вызов REST (если нужно)
Тестированиеpytest (>=7.4), pytest-mock (>=3.12), pytest-covЮнит-тесты и измерение покрытия
Форматирование и линтингblack, ruffЕдиный стиль кода
CI (опционально)GitHub ActionsЗапуск тестов при пуше
Хранение секретов.env (python-dotenv)Токены и credentials

4. Этапы выполнения

Этап 1: Подготовка окружения и получение доступов (оценка времени: 1.5 часа)

Действия

  1. Создать репозиторий и структуру папок:
    project-root/
    ├── src/
    │   ├── __init__.py
    │   ├── slack_client.py
    │   ├── gmail_client.py
    │   ├── calendar_client.py
    │   ├── agent.py
    │   └── utils.py
    ├── tests/
    │   ├── __init__.py
    │   ├── mocks.py
    │   ├── test_slack_client.py
    │   ├── test_gmail_client.py
    │   ├── test_calendar_client.py
    │   └── test_agent.py
    ├── .env.example
    ├── pyproject.toml
    └── README.md
    
  2. Инициализировать Poetry: poetry new agent_integrations или poetry init.
  3. Установить зависимости:
    poetry add slack_sdk google-api-python-client google-auth-httplib2 google-auth-oauthlib requests python-dotenv
    poetry add --dev pytest pytest-mock pytest-cov black ruff
    
  4. Зарегистрировать Slack App на api.slack.com:
    • Создать приложение, бот-токен, добавить бота в тестовый канал.
    • Сохранить токен в .env как SLACK_BOT_TOKEN.
  5. В Google Cloud Console:
    • Включить Gmail API и Google Calendar API.
    • Создать OAuth 2.0 Client ID (тип Desktop), скачать credentials.json, поместить в корень.
    • Выполнить первый запуск (ручная авторизация) для получения token.json (заглушка на этапе тестов).
  6. Создать файл .env с переменными:
    SLACK_BOT_TOKEN=xoxb-...
    GOOGLE_CREDENTIALS_PATH=credentials.json
    GOOGLE_TOKEN_PATH=token.json
    SLACK_TEST_CHANNEL=C123456
    
  7. Настроить линтеры (pyproject.toml):
    [tool.black]
    line-length = 100
    [tool.ruff]
    target-version = "py311"
    

Ожидаемый результат этапа

  • Рабочее Python-окружение, .env с токенами, credentials.json и token.json (если реальный доступ).
  • Структура папок с пустыми модулями.

Этап 2: Реализация модуля Slack (оценка времени: 2 часа)

Действия

  1. В src/slack_client.py создать класс SlackClient:
    • init принимает токен и имя тестового канала.
    • Метод get_latest_message(channel_id: str, limit: int = 1) — возвращает последнее сообщение из канала.
    • Метод post_message(channel_id: str, text: str) — отправляет сообщение.
    • Метод get_user_email(user_id: str) — получает email пользователя Slack (для интеграции с Gmail).
  2. Использовать slack_sdk.WebClient, обернуть все вызовы в try-except, логировать ошибки.
  3. Добавить простую обработку rate limit (retry с экспоненциальной задержкой).
  4. Написать тестовую заглушку в tests/mocks.py:
    class MockWebClient:
        def __init__(self):
            self.conversations_history = lambda channel, limit: {"ok": True, "messages": [{"text": "meeting tomorrow 3pm", "user": "U123"}]}
            self.chat_postMessage = lambda channel, text: {"ok": True, "ts": "12345"}
            self.users_info = lambda user: {"ok": True, "user": {"profile": {"email": "test@example.com"}}}
    
  5. Проверить импорт: from src.slack_client import SlackClient без ошибок.

Ожидаемый результат этапа

  • Рабочий класс SlackClient с тремя методами.
  • Моки для тестов.

Этап 3: Реализация модулей Gmail и Calendar (оценка времени: 2.5 часа)

Действия

  1. В src/gmail_client.py — класс GmailClient:
    • init принимает путь к credentials.json и token.json.
    • send_email(to: str, subject: str, body: str) — отправляет письмо через Gmail API.
    • Использовать googleapiclient.discovery.build для сервиса gmail v1.
    • Обработка ошибок (квоты, неверный адрес).
  2. В src/calendar_client.py — класс CalendarClient:
    • create_event(summary: str, start_time: datetime, duration_minutes: int, attendees: list[str]) — создаёт событие в Google Calendar.
    • Использовать сервис calendar v3.
    • Формат события (RFC 3339), напоминания (email, popup).
  3. Для локального тестирования в tests/mocks.py создать:
    • MockGmailService — заглушка для users.messages.send.
    • MockCalendarService — заглушка для events.insert.
  4. В src/utils.py написать функцию parse_meeting_request(text: str) -> dict:
    • Из текста выделить тему встречи, время (использовать библиотеку dateparser или регулярки).
    • Пример: "meeting tomorrow 3pm"{"summary": "meeting", "start": datetime.now()+timedelta(days=1, hours=15), "duration": 60}.
    • (Опционально) Упрощённый парсер без NLP.

Ожидаемый результат этапа

  • Классы GmailClient и CalendarClient с методами.
  • Функция парсинга запросов.
  • Моки для Gmail и Calendar.

Этап 4: Сборка агента и E2E сценарий (оценка времени: 1.5 часа)

Действия

  1. В src/agent.py создать класс MeetingAgent:
    • init принимает экземпляры трёх клиентов.
    • Метод run(channel_id: str):
      1. Получить последнее сообщение из Slack (slack.get_latest_message).
      2. Извлечь email пользователя из Slack (slack.get_user_email).
      3. Распарсить запрос (parse_meeting_request).
      4. Создать событие в календаре (calendar.create_event).
      5. Отправить подтверждение по email (gmail.send_email).
      6. Ответить в Slack, что всё завершено (slack.post_message).
    • Обработка исключений: логирование, ретраи, fallback (например, если нет email — не создавать событие).
  2. Написать точку входа main.py:
    import argparse, os
    from dotenv import load_dotenv
    from src.slack_client import SlackClient
    from src.gmail_client import GmailClient
    from src.calendar_client import CalendarClient
    from src.agent import MeetingAgent
    
    load_dotenv()
    parser = argparse.ArgumentParser()
    parser.add_argument("--scenario", default="meeting_request")
    args = parser.parse_args()
    if args.scenario == "meeting_request":
        slack = SlackClient(os.getenv("SLACK_BOT_TOKEN"), os.getenv("SLACK_TEST_CHANNEL"))
        gmail = GmailClient(os.getenv("GOOGLE_CREDENTIALS_PATH"), os.getenv("GOOGLE_TOKEN_PATH"))
        calendar = CalendarClient(os.getenv("GOOGLE_CREDENTIALS_PATH"), os.getenv("GOOGLE_TOKEN_PATH"))
        agent = MeetingAgent(slack, gmail, calendar)
        agent.run(os.getenv("SLACK_TEST_CHANNEL"))
    
  3. Проверить локальный запуск (с реальными API или заглушками):
    • Переключение между реальным и mock-режимом через переменную окружения USE_MOCK=1.
  4. Опционально: добавить возможность запускать другие сценарии (аргумент --scenario).

Ожидаемый результат этапа

  • Центральный класс MeetingAgent, файл main.py.
  • Агент корректно выполняет сценарий при реальном доступе или при подмене моков.

Этап 5: Юнит-тесты (оценка времени: 2 часа)

Действия

  1. В tests/test_slack_client.py — тесты для SlackClient:
    • test_get_latest_message_returns_text — подменяем WebClient через mock, проверяем, что возвращается текст.
    • test_post_message_calls_api — проверяем, что вызывается chat.postMessage с нужными параметрами.
    • test_get_user_email_returns_email — проверяем извлечение email.
    • test_handles_rate_limit — имитируем HTTP-ошибку 429, проверяем ретрай.
  2. В tests/test_gmail_client.py:
    • test_send_email_calls_api — mock сервиса Gmail, проверка вызова.
    • test_send_email_invalid_recipient — обработка ошибки 400.
  3. В tests/test_calendar_client.py:
    • test_create_event_returns_event_id — mock сервиса Calendar.
    • test_create_event_requires_valid_time — проверка валидации.
  4. В tests/test_agent.py — интеграционные тесты с моками всех трёх клиентов:
    • test_meeting_request_happy_path — полный сценарий.
    • test_meeting_request_missing_email — сценарий, когда email не найден (агент не создаёт событие, но отправляет сообщение об ошибке в Slack).
  5. В tests/test_utils.py:
    • test_parse_meeting_request_basic — парсинг строки "meeting tomorrow 3pm".
    • test_parse_meeting_request_no_time — возврат дефолтного времени.
  6. Все тесты запускать с опцией --cov=src, требовать покрытие >= 80%.
  7. Создать pytest.ini:
    [pytest]
    testpaths = tests
    addopts = --cov=src --cov-report=term-missing -v
    

Ожидаемый результат этапа

  • Набор из минимум 10 юнит-тестов.
  • Покрытие исходного кода тестами > 80%.
  • Возможность прогона одной командой pytest.

5. Критерии приемки (Definition of Done)

  • Репозиторий содержит полный код модулей slack_client, gmail_client, calendar_client, agent, utils.
  • Реализован сценарий "meeting_request": получение сообщения из Slack → создание события в Calendar → отправка email → ответ в Slack.
  • Все три интеграции имеют юнит-тесты, покрывающие как успешный путь, так и основные ошибки (rate limit, отсутствие прав, неверные данные).
  • Общее покрытие кода тестами (измеренное pytest-cov) составляет не менее 80 %.
  • main.py запускается с аргументом --scenario meeting_request (или без аргумента) без ошибок (режим mock или real).
  • В проекте есть README.md с описанием архитектуры, инструкцией по настройке (регистрация приложений, .env) и запуску.
  • Код отформатирован Black, линтинг Ruff без ошибок.
  • CI (GitHub Actions) успешно прогоняет тесты (опционально: настроен .github/workflows/tests.yml).

6. Ожидаемый результат

На выходе — директория agent-integrations (или название на усмотрение студента) со следующим содержимым:

  • src/ — исходный код (модули Slack, Gmail, Calendar, Agent, утилиты).
  • tests/ — юнит-тесты для каждого модуля.
  • main.py — точка входа для запуска агента.
  • .env.example — шаблон с переменными (без реальных токенов).
  • pyproject.toml (или requirements.txt) — список зависимостей.
  • README.md — описание задачи, настройка, запуск, результаты тестов.

Опциональные дополнительные результаты:

  • docs/ARCHITECTURE.md — диаграмма взаимодействия компонентов.
  • scripts/demo.sh — скрипт демонстрации сценария (создание тестового сообщения в Slack, прогон, проверка календаря).

7. Возможные сложности и их решение

СложностьРешение
OAuth 2.0 для Google требует браузерного подтверждения при первом запускеДля тестов использовать заранее сохранённый token.json (один раз получить вручную). В CI использовать service account (GSuite) или мок-credentials.
Slack API не возвращает email пользователя без прав users:read.emailУбедиться, что при регистрации приложения включён скоуп users:read.email. В тестах возвращать email из mock.
Rate limit у Google Calendar (100 событий в час)Ввести очередь с задержкой (time.sleep(0.1) перед вызовом). В тестах не проверять лимиты.
Синтаксис парсинга времени из сообщения может быть ненадёжнымИспользовать dateparser с поддержкой русского и английского. Для простоты задать фиксированный формат: "встреча завтра в 15:00".
Разные форматы времени в Gmail/Calendar (RFC 3339)Конвертировать через datetime.isoformat() и передавать строку.

8. Бюджет времени (оценка)

ЭтапВремя (часы)
1. Подготовка окружения и получение доступов1.5
2. Реализация модуля Slack2.0
3. Реализация модулей Gmail и Calendar2.5
4. Сборка агента и E2E сценарий1.5
5. Юнит-тесты2.0
Итого9.5

Примечание: при первом выполнении рекомендуется заложить +1.5 часа на регистрацию приложений и решение неожиданных ошибок OAuth.

9. Связанные вопросы из базы знаний

ВопросТема
42Работа с REST API и HTTP-сетевые запросы
115Библиотека для работы со Slack API (slack_sdk)
203Модульное тестирование с pytest и mock
314OAuth 2.0 аутентификация в Google APIs
456Обработка ошибок и исключений при вызове внешних сервисов
523Создание и управление событиями Google Calendar
678Автоматизация сценариев с помощью event-driven архитектуры
789CI/CD с GitHub Actions: настройка прогона тестов
821Форматирование кода и линтинг в Python-проектах
900Работа с переменными окружения и конфиденциальными данными

10. Чек-лист самопроверки

  • Я зарегистрировал(а) Slack App и указал(а) правильный бот-токен.
  • Я создал(а) OAuth 2.0 Client ID в Google Cloud Console и скачал(а) credentials.json.
  • Я выполнил(а) первый ручной запуск для получения token.json (или настроил(а) мок для тестов).
  • Все три интеграции работают изолированно: я запускал(а) python src/slack_client.py (тестовый прогон) и получил(а) данные.
  • Я написал(а) тесты для каждого клиента и агента, и покрытие кода не меньше 80% по отчёту pytest --cov.
  • Я проверил(а) сценарий "meeting_request" в mock-режиме: агент создаёт событие, отправляет email и сообщение в Slack.
  • Я удалил(а) реальные токены из репозитория и убедился(ась), что .env добавлен в .gitignore.
  • README содержит понятную инструкцию по настройке и запуску проекта.