Как вы делаете schema evolution для метаданных документов в RAG?
Краткий тезис
Schema evolution (эволюция схемы) в RAG — это процесс изменения структуры метаданных документов без остановки системы и потери данных. Ключевой подход — использование сериализационных форматов с поддержкой backward compatibility (обратной совместимости), таких как Avro или Protobuf. При изменении схемы создаётся новая версия, а старые документы читаются с default значениями для новых полей. Все поля должны быть optional (необязательными) для обеспечения гибкости.
1. Термин: Schema evolution (эволюция схемы)
Что это: Способность системы изменять структуру данных (добавлять, удалять, переименовывать поля) без необходимости переписывать все существующие данные и останавливать сервис.
Почему это важно в RAG
- Метаданные — это не только текст документа, но и дата, автор, категория, версия, теги, source URL, permissions и т.д.
- Со временем требования меняются: нужно добавить поле
language,confidence_score,embedding_model_version. - Если нет эволюции схемы, каждое изменение требует полной переиндексации всех документов — дорого и долго.
Термин «Backward compatibility» (обратная совместимость): Новый код может читать данные, записанные старой схемой. Старый код может читать данные, записанные новой схемой (forward compatibility — прямая совместимость).
2. Основные подходы к сериализации метаданных
| Формат | Поддержка эволюции | Производительность | Размер данных | Типизация |
|---|---|---|---|---|
| JSON | Нет (ломкая) | Низкая | Большой | Слабая |
| Avro | Отличная (backward + forward) | Высокая | Компактный (бинарный) | Строгая |
| Protobuf | Хорошая (backward) | Высокая | Компактный | Строгая |
| Parquet | Хорошая (columnar) | Средняя | Сжатый | Строгая |
Рекомендация Для метаданных в RAG чаще используют Avro или Protobuf, так как они нативно поддерживают эволюцию схемы и компактны для хранения в векторной БД.
3. Практика: Как реализовать schema evolution с Avro
3.1 Определение схемы (пример на Python)
# Версия 1 схемы (avro schema)
schema_v1 = {
"type": "record",
"name": "DocumentMetadata",
"fields": [
{"name": "doc_id", "type": "string"},
{"name": "title", "type": "string"},
{"name": "created_at", "type": "long", "logicalType": "timestamp-millis"},
{"name": "category", "type": ["null", "string"], "default": None}
]
}
3.2 Добавление нового поля (версия 2)
schema_v2 = {
"type": "record",
"name": "DocumentMetadata",
"fields": [
{"name": "doc_id", "type": "string"},
{"name": "title", "type": "string"},
{"name": "created_at", "type": "long", "logicalType": "timestamp-millis"},
{"name": "category", "type": ["null", "string"], "default": None},
{"name": "language", "type": ["null", "string"], "default": "en"} # новое поле с default
]
}
Ключевые правила
- Новое поле должно иметь default value (значение по умолчанию]]).
- Тип поля — union с null (например,
["null", "string"]), чтобы старые записи могли быть прочитаны. - Не удалять старые поля, только добавлять (или помечать deprecated).
3.3 Чтение старых документов новой схемой
import fastavro
# Старый документ (версия 1) без поля language
old_record = {"doc_id": "123", "title": "Old Doc", "created_at": 1700000000000, "category": "tech"}
# Сериализуем старой схемой
with open("old_doc.avro", "wb") as f:
fastavro.writer(f, schema_v1, [old_record])
# Читаем новой схемой — поле language получит default "en"
with open("old_doc.avro", "rb") as f:
reader = fastavro.reader(f, reader_schema=schema_v2)
for record in reader:
print(record["language"]) # Выведет "en"
4. Стратегии эволюции схемы в RAG-системе
4.1 Backward compatibility (обратная совместимость)
- Что даёт Новый код может читать старые данные.
- Как реализовать Новые поля с default, старые поля не удалять.
- Пример: Добавили поле
embedding_modelс default "text-embedding-ada-002". Все старые документы получат это значение при чтении.
4.2 Forward compatibility (прямая совместимость)
- Что даёт Старый код может читать новые данные (если не знает о новых полях — игнорирует их).
- Как реализовать В Avro — автоматически (новые поля игнорируются). В Protobuf — использовать
unknown fields. - Пример: Старый сервис, который не знает о поле
language, просто пропустит его.
4.3 Full compatibility (полная совместимость)
- Что даёт Оба направления работают.
- Как реализовать Комбинация backward + forward. Все поля optional, default везде.
5. Интеграция с векторной БД
5.1 Хранение метаданных
Векторные БД (Pinecone, Weaviate, Qdrant, Milvus) хранят метаданные как JSON-подобные структуры. Но при сериализации в Avro/Protobuf вы можете:
- Хранить метаданные в бинарном виде (Avro) как одно поле
metadata_blob. - При чтении десериализовать с актуальной схемой.
# Пример записи в Qdrant
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct
import fastavro
client = QdrantClient(host="localhost")
# Сериализуем метаданные
metadata = {"doc_id": "123", "title": "Doc", "created_at": 1700000000000}
with io.BytesIO() as buf:
fastavro.writer(buf, schema_v2, [metadata])
metadata_bytes = buf.getvalue()
# Сохраняем как бинарное поле
client.upsert(
collection_name="my_collection",
points=[PointStruct(id=1, vector=[0.1, 0.2], payload={"metadata": metadata_bytes})]
)
5.2 Фильтрация по метаданным
Если нужно фильтровать по новым полям (например, language), нужно либо:
- Переиндексировать старые документы с новыми полями (если фильтрация критична).
- Использовать default значения для фильтрации (например, все старые документы считаются
language="en").
6. Версионирование схемы
6.1 Хранение версий
Храните все версии схемы в реестре (например, Schema Registry от Confluent для Avro).
# Пример регистрации схемы
from confluent_kafka.schema_registry import SchemaRegistryClient
from confluent_kafka.schema_registry.avro import AvroSchema
schema_registry = SchemaRegistryClient({"url": "http://localhost:8081"})
schema = AvroSchema(schema_v2)
schema_id = schema_registry.register("document-metadata-value", schema)
6.2 Идентификация версии
Добавьте в метаданные поле schema_version:
schema_v3 = {
"type": "record",
"name": "DocumentMetadata",
"fields": [
{"name": "doc_id", "type": "string"},
{"name": "title", "type": "string"},
{"name": "created_at", "type": "long", "logicalType": "timestamp-millis"},
{"name": "category", "type": ["null", "string"], "default": None},
{"name": "language", "type": ["null", "string"], "default": "en"},
{"name": "schema_version", "type": "int", "default": 3}
]
}
7. Обработка breaking changes (критических изменений)
Иногда нужно удалить поле или изменить его тип. Это breaking change (ломающее изменение). Стратегии:
| Ситуация | Решение |
|---|---|
| Удаление поля | Пометить deprecated, не удалять физически. Через N версий — удалить, но с миграцией данных. |
| Изменение типа | Создать новое поле с новым именем, старое оставить. Например, category_id вместо category. |
| Переименование | Добавить новое поле, старое оставить с default. |
Пример миграции
# Старое поле category (string) -> новое category_id (int)
schema_v4 = {
"type": "record",
"name": "DocumentMetadata",
"fields": [
# ... старые поля
{"name": "category", "type": ["null", "string"], "default": None}, # deprecated
{"name": "category_id", "type": ["null", "int"], "default": None} # новое поле
]
}
8. Инструменты для schema evolution
| Инструмент | Назначение |
|---|---|
| Apache Avro | Сериализация с поддержкой эволюции |
| Protocol Buffers (Protobuf) | Альтернатива Avro, популярна в микросервисах |
| Confluent Schema Registry | Реестр схем для Kafka/Avro |
| Great Expectations | Валидация данных при миграции |
| Apache Parquet | Хранение больших объёмов метаданных с эволюцией |
Пет-проект для закрепления
Задача Создать RAG-систему для хранения статей, где метаданные эволюционируют со временем.
Инструменты Python, FastAPI, Qdrant (векторная БД), fastavro, Schema Registry (можно эмулировать через файл).
Шаги:
- Определите схему метаданных v1 (doc_id, title, created_at).
- Реализуйте endpoint для добавления документа с сериализацией в Avro.
- Через неделю добавьте поле
languageс default"en"(v2). - Реализуйте чтение старых документов — они должны корректно читаться.
- Добавьте поле
category_id(v3) с миграцией. - Напишите тест, проверяющий backward compatibility.
Ожидаемый результат Система, которая может читать документы, записанные любой версией схемы, без ошибок.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 5 | Оценка качества retrieval |
| 9 | Обновление документов в RAG |
| 10 | Self-RAG |
| 15 | Версионирование эмбеддингов |
| 20 | Обработка дубликатов документов |
| 25 | Стратегии индексации |
Навигация
- Предыдущий: 270
- Следующий: 272
- Индекс: 00. Индекс разборов