Настроить schema evolution для меж-агентной коммуникации
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить schema evolution для меж-агентной коммуникации
1. Цель задачи
Научиться проектировать и реализовывать эволюцию схем (schema evolution) при обмене сообщениями между агентами в распределённой AI-системе. Используя Protobuf (или Avro), необходимо обеспечить backward compatibility — старый агент должен корректно десериализовать сообщение, созданное по новой версии схемы, и forward compatibility — новый агент должен читать старые сообщения.
Ключевой результат Два агента (v1 и v2) успешно обмениваются сообщениями с разными версиями схем, проходят автоматизированные тесты на совместимость, и вы документируете стратегию эволюции.
2. Исходные данные
Перед началом необходимо подготовить:
| Что нужно | Откуда взять |
|---|---|
| Python 3.10+ и виртуальное окружение | Установить |
Библиотека protobuf (или avro) | pip install protobuf grpcio-tools (или avro / fastavro) |
| Директория для прото-файлов | Создать в корне проекта proto/ |
| Два агента (скрипты-заглушки) | Написать самостоятельно |
| Тестовые сообщения разных версий | Сгенерировать скриптами |
Если нет реального окружения — симулируем:
- Установите Python и создайте venv.
- Создайте минимальный проект с файлами:
agent_v1.py— агент, работающий со схемой v1.agent_v2.py— агент, работающий со схемой v2 (расширенной).test_compat.py— тесты совместимости.
- Используйте Protobuf (или Avro) — выберите один инструмент; в данном ТЗ мы используем Protobuf.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык реализации | Python 3.10+ | Агенты и тесты |
| Сериализация/схема | Protobuf (protobuf 3.x) | Определение структуры сообщений |
| Генерация кода | protoc + grpcio-tools | Создание Python классов по .proto |
| Тестирование совместимости | pytest, unittest | Проверка backward/forward |
| Управление версиями | Git + локальный репозиторий | Фиксация версий схем |
| Документация | Markdown | Стратегия эволюции |
4. Этапы выполнения
Этап 1: Проектирование схемы v1 и создание агента v1 (1 час)
Действия
- Создайте каталог
proto/. - Напишите файл
message_v1.proto:
syntax = "proto3";
package agent.communication;
message AgentMessage {
int32 id = 1;
string payload = 2;
int64 timestamp = 3;
}
-
Сгенерируйте Python код:
protoc --python_out=. --proto_path=proto proto/message_v1.proto(Предварительно может понадобиться
pip install grpcio-toolsдляprotoc) -
Напишите агента v1 (
agent_v1.py):- Функция
serialize_message_v1(id, payload, timestamp) -> bytes - Функция
deserialize_message_v1(data: bytes) -> AgentMessage - При запуске агент принимает аргумент режима:
--send,--receive.
- Функция
-
Напишите скрипт для тестового сообщения v1:
- Сериализуйте образец и сохраните в
messages/v1_sample.bin.
- Сериализуйте образец и сохраните в
Ожидаемый результат этапа
- Схема v1 сгенерирована в Python-классы.
- Агент v1 может сериализовать/десериализовать сообщения v1.
- Бинарный файл
v1_sample.binготов.
Этап 2: Эволюция схемы — создание версии v2 с backward совместимостью (1 час)
Действия
- Создайте
message_v2.protoна основе v1, добавив новые поля согласно правилам backward compatibility Protobuf (новые поля — новые номера, не удалять и не менять старые номера, не менять типы):
syntax = "proto3";
package agent.communication;
message AgentMessage {
int32 id = 1;
string payload = 2;
int64 timestamp = 3;
// Новые поля:
optional string metadata = 4; // опциональная мета-информация
repeated int32 tags = 5; // теги
}
-
Сгенерируйте Python код для v2 (аналогично этапу 1).
-
Напишите агента v2 (
agent_v2.py):- Функция
serialize_message_v2(id, payload, timestamp, metadata, tags) -> bytes - Функция
deserialize_message_v2(data: bytes) -> AgentMessage - При десериализации v1-сообщения: новые поля будут иметь значения по умолчанию (None для optional, [] для repeated).
- Функция
-
Задокументируйте изменения в
CHANGELOG_SCHEMA.md:- v1 → v2: добавлены поля
metadata(field 4, optional string),tags(field 5, repeated int32). - Причина: необходимость передачи дополнительного контекста между агентами.
- v1 → v2: добавлены поля
Ожидаемый результат этапа
- Схема v2 сгенерирована, агент v2 умеет читать и писать сообщения v2.
- Документация изменений.
Этап 3: Проверка backward compatibility — старый агент читает новое сообщение (1 час)
Действия
-
Создайте тестовый скрипт
test_backward.py:- Сгенерируйте сообщение v2 (с заполненными новыми полями).
- Сериализуйте его с помощью
serialize_message_v2. - Попытайтесь десериализовать с помощью
deserialize_message_v1(используя классы v1). - Обработка ошибок: Protobuf игнорирует неизвестные поля при десериализации, если они не помечены как
reserved. Убедитесь, что при десериализации v1 не выбрасывается исключение.
Код теста
from agent_v1 import deserialize_message_v1 from agent_v2 import serialize_message_v2 data = serialize_message_v2( id=1, payload="hello", timestamp=1710000000, metadata="test metadata", tags=[10, 20] ) msg_v1 = deserialize_message_v1(data) assert msg_v1.id == 1 assert msg_v1.payload == "hello" assert msg_v1.timestamp == 1710000000 # Поля metadata и tags будут проигнорированы — это ок. print("Backward compatibility OK") -
Запустите тест:
python test_backward.py. -
Проверьте, что старый агент действительно может работать с новым сообщением:
- Запустите
agent_v1.py --receiveи подайте на stdin бинарные данные от v2. - Или используйте файл:
agent_v1.py --file messages/v2_sample.bin.
- Запустите
Ожидаемый результат этапа
- Тест backward compatibility проходит без ошибок.
- Старый агент (v1) корректно читает сообщение, созданное по схеме v2.
Этап 4: Проверка forward compatibility — новый агент читает старое сообщение (30 минут)
Действия
-
Создайте тестовый скрипт
test_forward.py:- Возьмите бинарное сообщение v1 (из
messages/v1_sample.bin). - Десериализуйте его с помощью
deserialize_message_v2. - Проверьте, что поля, отсутствующие в v1, получают значения по умолчанию.
from agent_v2 import deserialize_message_v2 with open("messages/v1_sample.bin", "rb") as f: data = f.read() msg_v2 = deserialize_message_v2(data) assert msg_v2.id == ... # из v1_sample assert msg_v2.metadata == "" # default для optional string assert msg_v2.tags == [] # default для repeated - Возьмите бинарное сообщение v1 (из
-
Запустите тест:
python test_forward.py. -
Подтвердите, что новый агент может обрабатывать старые сообщения без ошибок.
Ожидаемый результат этапа
- Forward compatibility обеспечена.
- Новый агент корректно заполняет значения по умолчанию для отсутствующих полей.
Этап 5: Интеграция, автоматизация и документирование (1.5 часа)
Действия
-
Объедините тесты в один файл
test_schema_evolution.pyс использованиемpytest:test_backward_compattest_forward_compattest_v1_serialize_deserializetest_v2_serialize_deserialize
-
Добавьте проверку на строгую backward совместимость — убедитесь, что поля v1 не были удалены или изменены (например, проверить через
google.protobuf.descriptor). Это может быть отдельный тест, который анализирует дескрипторы. -
Напишите стратегию эволюции в файле
SCHEMA_EVOLUTION.md:- Какие изменения разрешены (добавление полей, добавление enum values).
- Какие запрещены (удаление полей, изменение типов, изменение номера поля).
- Процесс обновления схемы: создать новый .proto, сгенерировать код, протестировать совместимость, задеплоить агентов по очереди (сначала новые, потом старые или наоборот).
-
Зафиксируйте версии в git:
git initи коммит с v1.- Коммит с v2 и тестами.
- (Опционально) добавьте теги:
v1.0.0,v2.0.0.
Ожидаемый результат этапа
- Единый тестовый набор, который можно запускать
pytest. - Стратегия эволюции документально оформлена.
- Git-репозиторий с историей схем.
5. Критерии приемки (Definition of Done)
- Создана схема v1 (proto-файл + сгенерированный код).
- Создана схема v2 с добавлением полей (optional string, repeated int32).
- Агент v1 (скрипт) умеет сериализовать и десериализовать v1-сообщения.
- Агент v2 умеет сериализовать и десериализовать v2-сообщения, а также v1-сообщения.
- Тест backward compatibility проходит успешно: старый агент (v1) читает новое сообщение (v2).
- Тест forward compatibility проходит успешно: новый агент (v2) читает старое сообщение (v1).
- Документ
SCHEMA_EVOLUTION.mdс правилами эволюции схем написан. - Все тесты запускаются одной командой (
pytest test_schema_evolution.py) без ошибок.
6. Ожидаемый результат
Основной артефакт Директория проекта, содержащая:
.
├── proto/
│ ├── message_v1.proto
│ └── message_v2.proto
├── agent_v1.py
├── agent_v2.py
├── messages/
│ ├── v1_sample.bin
│ └── v2_sample.bin
├── test_schema_evolution.py
├── SCHEMA_EVOLUTION.md
├── CHANGELOG_SCHEMA.md
├── requirements.txt (protobuf)
└── README.md (короткое описание)
Содержание Полная реализация эволюции схемы с подтверждённой совместимостью.
Опциональные дополнительные результаты
- Настройка CI (например, GitHub Actions), который запускает тесты при изменении proto-файлов.
- Пример использования с gRPC (замена простой сериализации на вызовы RPC).
- Сравнение с Avro (если захотите попробовать альтернативу).
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
protoc не установлен или версия не подходит | Используйте grpcio-tools — он включает protoc для Python. Установите pip install grpcio-tools, и используйте python -m grpc_tools.protoc. |
| Разные версии Protobuf (proto2 vs proto3) | Убедитесь, что во всех .proto файлах указан syntax = "proto3". Избегайте proto2. |
| Нарушение backward compatibility (удалили поле, изменили номер) | Используйте google.protobuf.descriptor для автоматической проверки: сравните дескрипторы обеих версий, проверьте, что все поля v1 присутствуют в v2 с теми же номерами и типами. |
Проблемы с repeated и optional в разных версиях protobuf | В proto3 для optional строк используйте optional string (доступно с protobuf 3.15). Если ваша версия старше, используйте отдельный oneof или поле-заменитель в виде string metadata = 4; без optional — но тогда backward compatibility сохраняется, но теряется явное отсутствие. |
| Агенты написаны на разных языках (Java, Go) | В рамках задачи ограничьтесь Python, но в реальности концепция та же. |
| Тесты не учитывают изменения дефолтных значений | Проверяйте, что значения по умолчанию для новых полей не ломают логику старого агента (если старый не ожидает их). |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Проектирование v1 и агент v1 | 1 час |
| Этап 2: Эволюция — создание v2 | 1 час |
| Этап 3: Проверка backward compatibility | 1 час |
| Этап 4: Проверка forward compatibility | 30 мин |
| Этап 5: Интеграция, автоматизация, документация | 1.5 часа |
| Итого | ~5 часов |
Примечание для первого раза Если вы ранее не работали с Protobuf, добавьте 1 час на изучение синтаксиса и установку инструментов.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 42 | Protobuf vs JSON: производительность и типы |
| 87 | Schema registry в Kafka |
| 113 | Backward compatibility vs forward compatibility |
| 145 | Проектирование контрактов для микросервисов |
| 203 | Стратегии версионирования API |
| 267 | Serialization/deserialization в распределённых системах |
| 342 | Rule of thumb для номеров полей в Protobuf |
| 410 | gRPC и schema evolution |
| 556 | Avro schema resolution (reader/writer) |
| 678 | Breaking changes в enum и oneof |
| 721 | Примеры реальных сценариев schema evolution в AI-агентах |
| 889 | Тестирование совместимости в CI/CD |
10. Чек-лист самопроверки
- Я создал два .proto-файла с разными версиями схемы (v1, v2).
- Я сгенерировал Python-классы и написал агентов, которые могут сериализовать/десериализовать.
- Я создал тест backward compatibility: старый агент (v1) читает сообщение v2.
- Я создал тест forward compatibility: новый агент (v2) читает сообщение v1.
- Я документально оформил правила эволюции (SCHEMA_EVOLUTION.md).
- Я добавил чейнджлог изменений схемы (CHANGELOG_SCHEMA.md).
- Я запустил
pytestи все тесты прошли. - Я закоммитил всё в git и пометил версии.
- Я могу объяснить, почему добавление нового поля с номером 4 не ломает старых клиентов.