Реализовать handshake при соединении агентов

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать handshake при соединении агентов

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

Спроектировать и реализовать протокол handshake для соединения двух агентов в распределённой системе. Обеспечить обмен версиями протокола и capabilities (возможностями) с поддержкой обратной совместимости. Разработать серверную и клиентскую части, а также тесты, проверяющие корректную работу при разных версиях.

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


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

Перед началом необходимо иметь:

Что нужноОткуда взять
Python 3.9+ (с asyncio)Установить с python.org или через пакетный менеджер
Среда для разработки (IDE, терминал)Любая (VS Code, PyCharm, etc.)
Утилиты для отладки сети (опционально)netcat, Wireshark (не обязательно)
Существующая агентная система (если есть)
Документация по протоколу handshakeРазрабатывается в рамках задачи

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

  1. Создаём два скрипта: agent_server.py и agent_client.py, которые общаются через TCP (localhost).
  2. Сервер слушает порт (например, 8888) и ожидает handshake.
  3. Клиент подключается к этому порту, отправляет handshake-сообщение.
  4. Вся логика взаимодействия после handshake (например, обмен произвольными сообщениями) может быть минимальной (echo).

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

КомпонентИнструментыНазначение
Язык программированияPython 3.9+Реализация агентов и тестов
Асинхронное сетевое взаимодействиеasyncio (start_server, open_connection)TCP-соединения
СериализацияJSONЛёгкий, читаемый формат сообщений
Тестированиеpytest, pytest-asyncioЮнит и интеграционные тесты
Логированиеlogging (из стандартной библиотеки)Отладка handshake
Версионирование протоколаСемантическое версионирование (major.minor)Управление совместимостью

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

Этап 1: Проектирование протокола handshake (30–45 мин)

Действия

  1. Определить структуру handshake-сообщений
    Используем JSON.
    Запрос (client → server):

    {
      "type": "handshake_request",
      "protocol_version": {
        "major": 1,
        "minor": 0
      },
      "agent_id": "agent_A",
      "capabilities": ["text", "image", "video"]
    }
    

    Ответ (server → client):

    {
      "type": "handshake_response",
      "status": "accepted",
      "negotiated_version": {"major": 1, "minor": 0},
      "server_capabilities": ["text", "image"],
      "message": "Handshake successful"
    }
    
  2. Правила согласования версий

    • Если client.major == server.major → accepted, negotiated = min(minor).
    • Если client.major < server.major и сервер поддерживает обратную совместимость (major_diff <= 1) → accepted с negotiated версией клиента.
    • Если client.major > server.major → rejected (возможен ответ с предложением переподключиться с меньшей версией).
    • Если client.major вообще не поддерживается → rejected.
  3. Спецификация capabilities
    Список строк – идентификаторы функций, которые агент может обрабатывать. Пример: ["text", "image", "audio", "video", "streaming"].
    После handshake сервер сообщает свой набор, клиент может принять решение о дальнейшем взаимодействии.

Ожидаемый результат этапа
Документированный протокол handshake (файл PROTOCOL.md) с примерами сообщений и правилами согласования.


Этап 2: Реализация серверной части (1–1.5 часа)

Действия

  1. Создать модуль server.py

    • Асинхронная функция handle_client(reader, writer).
    • Читать данные до символа \n (TCP line-based протокол) или использовать фиксированную длину (например, префикс длины в 4 байта). Выбираем line-based для простоты.
    • Декодировать JSON. Проверить type == "handshake_request". Иначе закрыть соединение.
    • Извлечь версию и capabilities клиента.
    • Вызвать функцию согласования версии (negotiate_version), возвращающую статус и negotiated_version.
    • Сформировать ответ (с типом handshake_response), сериализовать в JSON, дописать \n и отправить.
    • Если статус rejected – закрыть соединение после отправки ответа. Если accepted – перейти в основной цикл взаимодействия (например, эхо-сервер).
  2. Реализовать вспомогательную логику

    • Функция negotiate_version(client_version, supported_versions).
    • Хранить поддерживаемые версии в конфиге (например, (1,0), (1,1), (2,0)).
    • Для каждой версии установить флаг backward_compatible.
  3. Добавить обработку ошибок и логирование

    • logging.info на этапе подключения, отправки/приёма handshake.
    • Обработка исключений при разрыве соединения, таймауты asyncio.

Ожидаемый результат этапа
Работающий сервер, который корректно принимает и отвечает на handshake, и после успешного согласования работает как echo-сервер.


Этап 3: Реализация клиентской части (1 час)

Действия

  1. Создать модуль client.py

    • Асинхронная функция connect_to_agent(host, port, agent_id, own_version, own_capabilities).
    • Установить TCP-соединение через asyncio.open_connection.
    • Сформировать handshake_request и отправить (с переводом строки).
    • Прочитать ответ (до \n), распарсить JSON.
    • Проверить status: если rejected – залогировать причину, вернуть None. Если accepted – сохранить negotiated_version и server_capabilities.
    • Вернуть кортеж (reader, writer, negotiated_version, server_capabilities).
  2. Реализовать логику повторных попыток (опционально)

    • При rejected можно попробовать переподключиться с другой версией, если сервер прислал рекомендуемую.
  3. Добавить unit-тесты для клиента

    • Мок сервера для проверки корректной отправки и обработки ответов.

Ожидаемый результат этапа
Рабочий клиент, который успешно проходит handshake с сервером, или корректно завершает работу при несовместимости.


Этап 4: Тестирование совместимости версий (1.5–2 часа)

Действия

  1. Написать интеграционные тесты (pytest-asyncio)

    • Запускать сервер на случайном свободном порту (через socket или pytest-asyncio event loop).
    • Тест 1: Основной сценарий – клиент и сервер версии 1.0 → accepted.
    • Тест 2: Совместимость 1.0 → 1.1 – клиент 1.0, сервер 1.1 → accepted, negotiated 1.0.
    • Тест 3: Несовместимость 2.0 → 1.0 – клиент 2.0, сервер 1.0 → rejected.
    • Тест 4: Capabilities – сервер с capabilities ["text"], клиент запрашивает ["video"] должен быть принят (capabilities не влияют на handshake).
    • Тест 5: Невалидный JSON – сервер должен ответить rejected с сообщением об ошибке.
    • Тест 6: Таймаут – клиент не отвечает, сервер закрывает соединение по таймауту.
  2. Реализовать тест для обратной совместимости (backward)

    • Создать конфигурацию сервера, поддерживающую версии 1.x и 2.x, и проверить, что клиент 1.5 подключается к серверу 2.0 с согласованием версии 1.5 (если backward_compatible=True).

Ожидаемый результат этапа
Все тесты проходят, покрыты основные случаи совместимости. Отчёт о покрытии (например, через pytest-cov).


Этап 5: Интеграция в агентную архитектуру (1 час, опционально)

Действия

  1. Адаптировать код для использования в существующем фреймворке

    • Если есть агентная система (например, на основе asyncio), добавить вызов handshake в метод connect.
    • Сделать handshake неблокирующим и обрабатывать его как первый этап установления сессии.
  2. Подготовить документацию

    • Как добавить новую версию протокола.
    • Как определить собственные capabilities.

Ожидаемый результат этапа
Код, который может быть легко внедрён в реальную систему агентов (документация и пример использования).


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

  • Сервер и клиент реализуют full-duplex handshake по TCP (JSON, разделённый новыми строками).
  • Версии согласуются согласно описанным правилам (включая обратную совместимость).
  • При несовместимости возвращается корректное сообщение rejected с указанием причины.
  • Capabilities передаются и доступны обеим сторонам после handshake.
  • Написаны интеграционные тесты (минимум 5), покрывающие все сценарии совместимости.
  • Все тесты проходят при запуске pytest -v.
  • Код соответствует PEP 8, используется типизация (typing).
  • Добавлена документация протокола (PROTOCOL.md) и краткое README с инструкцией по запуску.
  • Обрабатываются таймауты и некорректные входные данные (защита от «плохих» сообщений).

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

После выполнения задачи должен быть создан репозиторий/папка со следующей структурой:

handshake_protocol/
├── server.py          # Серверная часть
├── client.py          # Клиентская часть
├── protocol.md        # Спецификация протокола
├── tests/
│   ├── test_protocol.py   # Интеграционные тесты
│   └── conftest.py        # Фикстуры сервера
├── requirements.txt   # pytest, pytest-asyncio (если не встроены)
└── README.md          # Запуск, пример использования

Основной артефакт Рабочая реализация handshake, которую можно запустить и протестировать.

Дополнительные результаты

  • Возможность лёгкого добавления новых версий протокола.
  • Механизм расширения capabilities без изменения ядра handshake.
  • Тестовое покрытие, документирующее корректное поведение при edge-case'ах.

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

СложностьРешение
Синхронизация чтения/записиИспользовать asyncio.StreamReader и StreamWriter с разделителем строк (readline).
Несовместимость версий при обратной совместимостиЧётко определить политику backward_compatible; хранить маппинг major версий в конфиге сервера.
Таймауты во время handshakeУстановить asyncio.wait_for на чтение/запись (по умолчанию 5 секунд).
Недоступность портаВ тестах использовать bind на порт 0, чтобы избежать конфликтов.
Сложность отладкиВключить подробное логирование на уровне handshake (DEBUG) и возможность сохранять сырые сообщения.

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

ЭтапВремя
Этап 1: Проектирование протокола0.5 ч
Этап 2: Реализация сервера1.5 ч
Этап 3: Реализация клиента1 ч
Этап 4: Тестирование совместимости2 ч
Этап 5: Интеграция (опционально)1 ч
Итого6 ч (без опционального этапа – 5 ч)

Примечание: Для первого раза ожидайте на 30–50% больше времени (до 9 часов) из-за изучения asyncio и отладки.


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

ВопросТема
45Принципы протокола TCP/IP
78Разница между семантическим версионированием и календарным
123Обработка ошибок в асинхронном коде Python
201Проектирование сетевого протокола для микросервисов
305Что такое capabilities в контексте distributed agents
412Интеграционное тестирование асинхронных систем
567Использование JSON-схем для контрактов (contract testing)
634Асинхронные сокеты: asyncio.start_server
789Rollback-стратегии при несовместимости протоколов
890Best practices для логирования в Python

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

  • Я спроектировал протокол handshake до начала кодирования и зафиксировал в protocol.md.
  • Я разделил логику сервера и клиента на отдельные модули (можно использовать как библиотеку).
  • Я написал тесты, покрывающие как минимум основной сценарий, несовместимость версий и неверный ввод.
  • Я проверил, что все тесты проходят при запуске pytest --asyncio-mode=auto.
  • Я добавил документацию по запуску и примеры в README.
  • Я убедился, что код обрабатывает таймауты и разрывы соединения без зависания.
  • Я использовал типизацию для всех публичных функций (через typing).