English translation is not available yet. Showing Russian content.
Как обеспечивать backward compatibility при изменении протокола?
Краткий тезис
Backward compatibility (обратная совместимость) позволяет старым агентам корректно обрабатывать сообщения от новых версий протокола и наоборот. Достигается через эволюцию схемы сообщений (message schema evolution) с использованием форматов вроде Protobuf или Avro, где новые поля добавляются как optional, удаляемые помечаются deprecated, а на уровне взаимодействия вводится version negotiation (согласование версий]]) и adapter layer (слой адаптации) для преобразования между версиями.
1. Термин: Протокол взаимодействия агентов
Протокол — это набор правил и форматов сообщений, по которому обмениваются AI-агенты. В Agentic RAG агенты могут общаться для передачи запросов, документов, метаданных и управляющих команд. Изменение протокола (добавление/удаление полей, изменение типов) ломает совместимость, если не предусмотрена backward compatibility. Без неё при обновлении части агентов система перестаёт работать — старые агенты не поймут новые сообщения или упадут с ошибкой десериализации.
Ключевые принципы:
- Forward compatibility: старые потребители могут читать сообщения, сгенерированные новыми отправителями (часть backward compatibility).
- Backward compatibility: новые потребители могут читать сообщения старых отправителей.
2. Message schema evolution (эволюция схемы сообщения)
Чтобы протокол можно было менять без поломок, используется система сериализации с поддержкой эволюции. Самые популярные — Apache Avro и Protocol Buffers (Protobuf). Они предоставляют явное описание схемы (schema) и правила обратной совместимости. JSON Schema тоже подходит, но менее эффективен по размеру и скорости.
Сравнение форматов:
| Формат | Эволюция | Поддержка optional | Типизация | Скорость | Размер |
|---|---|---|---|---|---|
| Protobuf | Через optional, deprecated, reserved | Явно optional | Строгая | Высокая | Малый |
| Avro | Через default values, aliases, union | union {null, type} | Строгая | Высокая | Малый |
| JSON Schema | Через required / additionalProperties | nullable (не всегда) | Слабая | Низкая | Большой |
| MessagePack | Нет встроенной эволюции | Нет | Слабая | Средняя | Средний |
Для агентных систем чаще выбирают Protobuf или Avro из-за компактности и явного управления версиями.
3. Добавление полей: optional и default values
Ключевое правило: при добавлении нового поля оно должно быть optional (необязательным) — ни в одной старой версии не должно требоваться его присутствие. В Protobuf это поле помечается модификатором optional или используется proto3 (все поля по умолчанию optional). Старый агент при десериализации просто проигнорирует новое поле (оно будет отсутствовать). Новый агент, читая старую версию, получит default value (нулевое для чисел, пустая строка и т.д.).
Пример в Protobuf:
syntax = "proto3";
message AgentMessage {
int32 id = 1;
string payload = 2;
// old version without this field
optional string trace_id = 3; // added later, optional
}
Правило: не менять tag number поля (= 3) и не переиспользовать удалённые номера (использовать reserved).
4. Удаление полей: пометка deprecated
Нельзя физически удалять поле из схемы — старые сообщения могут его содержать. Вместо этого:
- Пометить поле как deprecated в комментарии или через атрибут deprecated (в Protobuf нет стандартного ключевого слова, используют комментарии; в Avro есть
logicalType). - Зарезервировать номер поля через reserved в Protobuf, чтобы случайно не использовать его снова.
- В коде нового агента при чтении можно просто игнорировать deprecated-поля.
Пример с reserved:
message AgentMessage {
reserved 3; // was trace_id, now deprecated
int32 id = 1;
string payload = 2;
}
5. Version negotiation (согласование версий)
При установлении соединения между агентами (handshake) они обмениваются поддерживаемыми версиями протокола. Каждый агент посылает список версий, которые он может принимать. Выбирается максимальная общая версия.
Протокол handshake (пример на Python):
class Handshake:
SUPPORTED_VERSIONS = [1, 2, 3]
def __init__(self):
self.version = None
def negotiate(self, peer_versions: List[int]) -> int:
common = set(self.SUPPORTED_VERSIONS) & set(peer_versions)
if not common:
raise IncompatibleProtocol("No common version")
self.version = max(common)
return self.version
Важно: версия протокола должна быть указана в заголовке каждого сообщения, а не только в handshake (для long-lived соединений). Это позволяет агенту динамически выбирать обработчик.
6. Adapter layer (слой адаптации)
Когда handshake не дал общей версии, или нужно поддерживать legacy-агентов, используется adapter layer — промежуточный модуль, который преобразует сообщения одной версии в другую.
Реализация:
- Хранятся правила трансляции (например,
v1 -> v3: добавить missing fields со значениями по умолчанию, удалить deprecated поля). - Включается в API-шлюзе или как middleware.
Пример простого адаптера:
class ProtocolAdapter:
def convert(self, msg: bytes, from_ver: int, to_ver: int) -> bytes:
if from_ver == 1 and to_ver == 2:
# добавить поле trace_id = ""
parsed = parse_v1(msg)
v2_msg = V2Message(id=parsed.id, payload=parsed.payload, trace_id="")
return serialize_v2(v2_msg)
raise UnsupportedConversion()
7. Практический пример эволюции протокола на Protobuf
Предположим, изначально протокол состоял из полей id и payload. Затем понадобилось добавить priority (int, optional) и убрать payload (заменив на data).
Шаг 1 (v1):
message AgentMessage {
uint32 id = 1;
string payload = 2;
}
Шаг 2 (v2):
message AgentMessage {
uint32 id = 1;
reserved 2; // payload удалено
string data = 3; // новое поле
optional uint32 priority = 4; // добавили
}
Но такое изменение нарушит совместимость: старый клиент ожидает payload на tag 2, а новый использует tag 3. Лучше не удалять сразу, а ввести новое поле как optional, а старое пометить deprecated. Схема v2 должна быть:
message AgentMessage {
uint32 id = 1;
string payload = 2 [deprecated = true]; // ещё присутствует
string data = 3 [deprecated = false];
optional uint32 priority = 4;
}
Затем через несколько версий можно зарезервировать tag 2.
8. Продвинутые техники
- Semantic versioning протокола: версия major.minor.patch. Major — ломающие изменения (требуют адаптера), minor — обратно совместимые добавления, patch — исправления.
- Test compatibility матрица: CI-тесты для всех комбинаций старых и новых версий.
- Feature detection: вместо версий агент может явно объявлять поддерживаемые возможности (capabilities), что гибче.
- Graceful degradation: если новый агент не может передать отсутствующее в старом поле, он генерирует fallback-значение.
9. Пет-проект для закрепления
Задача: Реализовать мини-протокол обмена сообщениями между двумя агентами с поддержкой backward compatibility.
Инструменты: Python, protobuf (или flatbuffers), asyncio для асинхронного взаимодействия.
Шаги:
- Определить начальную схему Protobuf с полями
msg_id, text. - Создать v1 агентов (клиент и сервер) на отдельных процессах/потоках, обменивающихся сообщениями через TCP.
- Выпустить v2 протокола: добавить optional поле
source_agentи изменить обязательность text (сделать его optional с default). - Реализовать handshake: при соединении агенты посылают поддерживаемые версии, выбирают максимальную общую.
- Для неподдерживаемой версии (тест) — adapter: v1 → v3 (например, заполнить source_agent строкой "unknown").
- Написать тесты: старый клиент против нового сервера, новый клиент против старого сервера.
- Промоделировать ситуацию с удалённым полем: задепрекейтить text, ввести payload. Проверить, что старый клиент может отправить сообщение, а новый сервер его корректно прочитает.
Ожидаемый результат:
- Работающий чат между агентами разных версий.
- Код, использующий reserved, optional, handshake, adapter.
- Тесты демонстрируют 100% backward и forward compatibility.
10. Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 812 | Версионирование схем данных в RAG-системах |
| 813 | Выбор формата сериализации для агентных сообщений |
| 815 | Handshake и установка соединения между агентами |
| 817 | Rolling update протокола без downtime |
| 818 | Мониторинг и логирование версий протокола |
| 810 | Проектирование протокола взаимодействия агентов |
11. Навигация
- Предыдущий: 815
- Следующий: 817
- Индекс: 00. Индекс разборов
Навигация
- Предыдущий: 815
- Следующий: 817
- Индекс: 00. Индекс разборов