Реализовать handshake при соединении агентов
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать handshake при соединении агентов
1. Цель задачи
Спроектировать и реализовать протокол handshake для соединения двух агентов в распределённой системе. Обеспечить обмен версиями протокола и capabilities (возможностями) с поддержкой обратной совместимости. Разработать серверную и клиентскую части, а также тесты, проверяющие корректную работу при разных версиях.
Ключевой результат Набор модулей (сервер, клиент, утилиты) и интеграционных тестов, который гарантирует успешное установление соединения с согласованием версии, отказ при несовместимых версиях, и возможность расширения протокола без нарушения работы старых агентов.
2. Исходные данные
Перед началом необходимо иметь:
| Что нужно | Откуда взять |
|---|---|
| Python 3.9+ (с asyncio) | Установить с python.org или через пакетный менеджер |
| Среда для разработки (IDE, терминал) | Любая (VS Code, PyCharm, etc.) |
| Утилиты для отладки сети (опционально) | netcat, Wireshark (не обязательно) |
| Существующая агентная система (если есть) | — |
| Документация по протоколу handshake | Разрабатывается в рамках задачи |
Если нет реальной агентной системы — симулируем:
- Создаём два скрипта:
agent_server.pyиagent_client.py, которые общаются через TCP (localhost). - Сервер слушает порт (например, 8888) и ожидает handshake.
- Клиент подключается к этому порту, отправляет handshake-сообщение.
- Вся логика взаимодействия после 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 мин)
Действия
-
Определить структуру 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" } -
Правила согласования версий
- Если
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.
- Если
-
Спецификация capabilities
Список строк – идентификаторы функций, которые агент может обрабатывать. Пример: ["text", "image", "audio", "video", "streaming"].
После handshake сервер сообщает свой набор, клиент может принять решение о дальнейшем взаимодействии.
Ожидаемый результат этапа
Документированный протокол handshake (файл PROTOCOL.md) с примерами сообщений и правилами согласования.
Этап 2: Реализация серверной части (1–1.5 часа)
Действия
-
Создать модуль
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– перейти в основной цикл взаимодействия (например, эхо-сервер).
-
Реализовать вспомогательную логику
- Функция
negotiate_version(client_version, supported_versions). - Хранить поддерживаемые версии в конфиге (например,
(1,0),(1,1),(2,0)). - Для каждой версии установить флаг backward_compatible.
- Функция
-
Добавить обработку ошибок и логирование
Ожидаемый результат этапа
Работающий сервер, который корректно принимает и отвечает на handshake, и после успешного согласования работает как echo-сервер.
Этап 3: Реализация клиентской части (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).
- Асинхронная функция
-
Реализовать логику повторных попыток (опционально)
- При rejected можно попробовать переподключиться с другой версией, если сервер прислал рекомендуемую.
-
Добавить unit-тесты для клиента
- Мок сервера для проверки корректной отправки и обработки ответов.
Ожидаемый результат этапа
Рабочий клиент, который успешно проходит handshake с сервером, или корректно завершает работу при несовместимости.
Этап 4: Тестирование совместимости версий (1.5–2 часа)
Действия
-
Написать интеграционные тесты (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: Таймаут – клиент не отвечает, сервер закрывает соединение по таймауту.
- Запускать сервер на случайном свободном порту (через
-
Реализовать тест для обратной совместимости (backward)
- Создать конфигурацию сервера, поддерживающую версии 1.x и 2.x, и проверить, что клиент 1.5 подключается к серверу 2.0 с согласованием версии 1.5 (если backward_compatible=True).
Ожидаемый результат этапа
Все тесты проходят, покрыты основные случаи совместимости. Отчёт о покрытии (например, через pytest-cov).
Этап 5: Интеграция в агентную архитектуру (1 час, опционально)
Действия
-
Адаптировать код для использования в существующем фреймворке
-
Подготовить документацию
- Как добавить новую версию протокола.
- Как определить собственные 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 |
| 789 | Rollback-стратегии при несовместимости протоколов |
| 890 | Best practices для логирования в Python |
10. Чек-лист самопроверки
- Я спроектировал протокол handshake до начала кодирования и зафиксировал в
protocol.md. - Я разделил логику сервера и клиента на отдельные модули (можно использовать как библиотеку).
- Я написал тесты, покрывающие как минимум основной сценарий, несовместимость версий и неверный ввод.
- Я проверил, что все тесты проходят при запуске
pytest --asyncio-mode=auto. - Я добавил документацию по запуску и примеры в README.
- Я убедился, что код обрабатывает таймауты и разрывы соединения без зависания.
- Я использовал типизацию для всех публичных функций (через
typing).