English translation is not available yet. Showing Russian content.

Настроить 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/
Два агента (скрипты-заглушки)Написать самостоятельно
Тестовые сообщения разных версийСгенерировать скриптами

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

  1. Установите Python и создайте venv.
  2. Создайте минимальный проект с файлами:
    • agent_v1.py — агент, работающий со схемой v1.
    • agent_v2.py — агент, работающий со схемой v2 (расширенной).
    • test_compat.py — тесты совместимости.
  3. Используйте 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 час)

Действия

  1. Создайте каталог proto/.
  2. Напишите файл message_v1.proto:
syntax = "proto3";

package agent.communication;

message AgentMessage {
    int32 id = 1;
    string payload = 2;
    int64 timestamp = 3;
}
  1. Сгенерируйте Python код:

    protoc --python_out=. --proto_path=proto proto/message_v1.proto
    

    (Предварительно может понадобиться pip install grpcio-tools для protoc)

  2. Напишите агента v1 (agent_v1.py):

    • Функция serialize_message_v1(id, payload, timestamp) -> bytes
    • Функция deserialize_message_v1(data: bytes) -> AgentMessage
    • При запуске агент принимает аргумент режима: --send, --receive.
  3. Напишите скрипт для тестового сообщения v1:

    • Сериализуйте образец и сохраните в messages/v1_sample.bin.

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

  • Схема v1 сгенерирована в Python-классы.
  • Агент v1 может сериализовать/десериализовать сообщения v1.
  • Бинарный файл v1_sample.bin готов.

Этап 2: Эволюция схемы — создание версии v2 с backward совместимостью (1 час)

Действия

  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;            // теги
}
  1. Сгенерируйте Python код для v2 (аналогично этапу 1).

  2. Напишите агента v2 (agent_v2.py):

    • Функция serialize_message_v2(id, payload, timestamp, metadata, tags) -> bytes
    • Функция deserialize_message_v2(data: bytes) -> AgentMessage
    • При десериализации v1-сообщения: новые поля будут иметь значения по умолчанию (None для optional, [] для repeated).
  3. Задокументируйте изменения в CHANGELOG_SCHEMA.md:

    • v1 → v2: добавлены поля metadata (field 4, optional string), tags (field 5, repeated int32).
    • Причина: необходимость передачи дополнительного контекста между агентами.

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

  • Схема v2 сгенерирована, агент v2 умеет читать и писать сообщения v2.
  • Документация изменений.

Этап 3: Проверка backward compatibility — старый агент читает новое сообщение (1 час)

Действия

  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")
    
  2. Запустите тест: python test_backward.py.

  3. Проверьте, что старый агент действительно может работать с новым сообщением:

    • Запустите agent_v1.py --receive и подайте на stdin бинарные данные от v2.
    • Или используйте файл: agent_v1.py --file messages/v2_sample.bin.

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

  • Тест backward compatibility проходит без ошибок.
  • Старый агент (v1) корректно читает сообщение, созданное по схеме v2.

Этап 4: Проверка forward compatibility — новый агент читает старое сообщение (30 минут)

Действия

  1. Создайте тестовый скрипт 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
    
  2. Запустите тест: python test_forward.py.

  3. Подтвердите, что новый агент может обрабатывать старые сообщения без ошибок.

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

  • Forward compatibility обеспечена.
  • Новый агент корректно заполняет значения по умолчанию для отсутствующих полей.

Этап 5: Интеграция, автоматизация и документирование (1.5 часа)

Действия

  1. Объедините тесты в один файл test_schema_evolution.py с использованием pytest:

    • test_backward_compat
    • test_forward_compat
    • test_v1_serialize_deserialize
    • test_v2_serialize_deserialize
  2. Добавьте проверку на строгую backward совместимость — убедитесь, что поля v1 не были удалены или изменены (например, проверить через google.protobuf.descriptor). Это может быть отдельный тест, который анализирует дескрипторы.

  3. Напишите стратегию эволюции в файле SCHEMA_EVOLUTION.md:

    • Какие изменения разрешены (добавление полей, добавление enum values).
    • Какие запрещены (удаление полей, изменение типов, изменение номера поля).
    • Процесс обновления схемы: создать новый .proto, сгенерировать код, протестировать совместимость, задеплоить агентов по очереди (сначала новые, потом старые или наоборот).
  4. Зафиксируйте версии в 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 и агент v11 час
Этап 2: Эволюция — создание v21 час
Этап 3: Проверка backward compatibility1 час
Этап 4: Проверка forward compatibility30 мин
Этап 5: Интеграция, автоматизация, документация1.5 часа
Итого~5 часов

Примечание для первого раза Если вы ранее не работали с Protobuf, добавьте 1 час на изучение синтаксиса и установку инструментов.


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

ВопросТема
42Protobuf vs JSON: производительность и типы
87Schema registry в Kafka
113Backward compatibility vs forward compatibility
145Проектирование контрактов для микросервисов
203Стратегии версионирования API
267Serialization/deserialization в распределённых системах
342Rule of thumb для номеров полей в Protobuf
410gRPC и schema evolution
556Avro schema resolution (reader/writer)
678Breaking 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 не ломает старых клиентов.