Ewolucja schematów i zgodność wsteczna
Zarządzaj przełamującymi zmianami schematów w długo działających potokach ekstrakcji, wersjonując schematy, migrując historyczne ekstrakcje i uruchamiając równoległą walidację w okresach przejściowych.
Ewolucja schematów i zgodność wsteczna to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Engineering Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Dlaczego schematy zmieniają się z czasem
Schematy ekstrakcji nie są stałe. Wymagania biznesowe ewoluują, pojawiają się nowe typy dokumentów, a Państwo odkrywają pola, które powinny były być rejestrowane od początku. Zmiana schematu w działającym potoku tworzy problem zgodności wstecznej: istniejące wyodrębnione rekordy korzystają ze starego schematu, a nowe rekordy — z nowego. Bezpieczne zarządzanie tym przejściem jest właśnie istotą ewolucji schematów.
Wersjonowanie schematów
Należy przypisać numer wersji do każdego schematu i przechowywać go wraz z każdym wyodrębnionym rekordem. Po zmianie schematu należy zwiększyć numer wersji. Umożliwia to wyszukiwanie rekordów według wersji schematu, uruchamianie migracji starych rekordów oraz utrzymywanie osobnej logiki walidacji dla każdej wersji. Wystarczy proste pole tekstowe schema_version w każdym modelu wynikowym.
from pydantic import BaseModel
from typing import Literal
class InvoiceV1(BaseModel):
schema_version: Literal['1.0'] = '1.0'
vendor: str
total_amount: float
class InvoiceV2(BaseModel):
schema_version: Literal['2.0'] = '2.0'
vendor: str
vendor_tax_id: str | None = None # new field
total_amount: float
currency: str = 'USD' # new field with defaultZmiany addytywne a zmiany łamiące zgodność
Zmiany addytywne są bezpieczne: dodanie pola Optional lub pola z wartością domyślną nie psuje starego kodu ekstrakcji ani starych rekordów. Zmiany łamiące zgodność są ryzykowne: zmiana nazwy pola, zmiana typu z string na int lub usunięcie pola spowoduje problemy u odbiorców downstream. Zawsze należy preferować zmiany addytywne. Gdy zmiana łamiąca zgodność jest nieunikniona, należy utworzyć nową główną wersję schematu i przeprowadzić migrację w kontrolowany sposób.
# Safe: additive change - add optional field
class ProductV2(BaseModel):
name: str
price: float
sku: str | None = None # NEW optional field - backward safe
category: str = 'general' # NEW with default - backward safe
# Risky: breaking change - rename or retype
# class ProductV2(BaseModel):
# product_name: str # RENAMED from name - breaks consumers
# price_cents: int # RETYPED from float - breaks dataPrzechowywanie wersji schematu w bazie danych
Wyniki ekstrakcji powinny zawierać wersję schematu, aby zawsze było wiadomo, która wersja utworzyła dany rekord. Kolumna jsonb przechowująca pełne wyodrębnione dane wraz z tekstową kolumną schema_version to często stosowany wzorzec. Umożliwia on tworzenie zapytań uwzględniających wersję oraz selektywną migrację starszych rekordów w okresach mniejszego ruchu.
-- PostgreSQL table design
CREATE TABLE extractions (
doc_id TEXT PRIMARY KEY,
schema_version TEXT NOT NULL,
extracted_data JSONB NOT NULL,
extracted_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_schema_version ON extractions(schema_version);
-- Query old records needing migration
SELECT doc_id, extracted_data
FROM extractions
WHERE schema_version = '1.0'
LIMIT 1000;Pisanie skryptów migracyjnych
Dla każdego przejścia między wersjami schematu należy napisać skrypt migracyjny, który odczytuje stare rekordy, przekształca je do nowego formatu i zapisuje ponownie z nową wersją. Migracje należy uruchamiać w małych partiach i w ramach transakcji, aby awaria nie pozostawiła bazy danych w częściowo zmigrowanym stanie. Stary schemat należy zawsze zachować do czasu potwierdzenia ukończenia migracji.
import asyncpg
import json
async def migrate_v1_to_v2(pool, batch_size=100):
async with pool.acquire() as conn:
rows = await conn.fetch(
'SELECT doc_id, extracted_data FROM extractions WHERE schema_version=$1 LIMIT $2',
'1.0', batch_size
)
for row in rows:
old = row['extracted_data']
new_data = {
'schema_version': '2.0',
'vendor': old['vendor'],
'vendor_tax_id': None, # unknown for old records
'total_amount': old['total_amount'],
'currency': 'USD' # assume USD for old records
}
await conn.execute(
'UPDATE extractions SET extracted_data=$1, schema_version=$2 WHERE doc_id=$3',
json.dumps(new_data), '2.0', row['doc_id']
)Równoległa walidacja podczas przejść
Podczas migracji schematu należy przeprowadzić równoległą walidację: dla próbki przychodzących dokumentów wykonać ekstrakcję jednocześnie przy użyciu starego i nowego schematu. Należy porównać wyniki, aby sprawdzić, czy nowy schemat rejestruje wszystko, co rejestrował stary, a także nowe pola. Stary schemat można wycofać dopiero wtedy, gdy równoległa walidacja wykaże stabilną zgodność na statystycznie istotnej próbce.
async def parallel_validate(text: str) -> dict:
v1_result, v2_result = await asyncio.gather(
extract_with_schema(text, InvoiceV1),
extract_with_schema(text, InvoiceV2)
)
discrepancy = (
v1_result.vendor != v2_result.vendor or
abs(v1_result.total_amount - v2_result.total_amount) > 0.01
)
if discrepancy:
log_discrepancy(text, v1_result, v2_result)
return {'v1': v1_result, 'v2': v2_result, 'discrepancy': discrepancy}Flagi funkcji przy wdrażaniu schematu
Należy użyć flag funkcji, aby kontrolować moment przełączenia potoku ze starego schematu na nowy. Umożliwia to stopniowe wdrażanie nowego schematu dla określonego odsetka ruchu, monitorowanie współczynników błędów i natychmiastowy rollback w razie problemów — bez ponownego wdrażania kodu. Sprawdzą się zarówno usługi flag funkcji, takie jak LaunchDarkly, jak i prosty rekord w bazie danych.
import os
def get_active_schema():
version = os.environ.get('EXTRACTION_SCHEMA_VERSION', '1.0')
schemas = {
'1.0': InvoiceV1,
'2.0': InvoiceV2,
}
return schemas.get(version, InvoiceV1)
async def extract_document(text: str):
SchemaClass = get_active_schema()
return await extract_with_schema(text, SchemaClass)Zgodność odbiorców z typami sumy
Odbiorcy downstream odczytujący wyodrębnione dane muszą poprawnie obsługiwać wiele wersji schematu. W kodzie odbiorcy należy użyć unii rozłącznej, która wybiera właściwą logikę parsowania na podstawie pola schema_version. Jest to rozwiązanie bardziej niezawodne niż warunkowe łańcuchy if-else i łatwiejsze do rozszerzania, gdy pojawi się wersja 3.
from pydantic import BaseModel
from typing import Union, Annotated
from typing import Literal
def parse_extraction(raw: dict) -> Union[InvoiceV1, InvoiceV2]:
version = raw.get('schema_version', '1.0')
if version == '1.0':
return InvoiceV1(**raw)
elif version == '2.0':
return InvoiceV2(**raw)
else:
raise ValueError(f'Unknown schema version: {version}')Testowanie zmian schematu przed wdrożeniem
Przed wdrożeniem nowego schematu należy uruchomić go na całym zestawie testów regresyjnych: wyselekcjonowanej kolekcji reprezentatywnych dokumentów ze znanymi oczekiwanymi wynikami. Należy porównać wyniki F1 dla każdego pola między starym i nowym schematem. Spadek F1 dla dowolnego pola oznacza, że opis nowego schematu wprowadził model w błąd — przed wdrożeniem należy poprawić opis tego pola.
def eval_schema_on_test_set(test_cases: list, SchemaClass) -> dict:
field_f1 = {}
for case in test_cases:
result = extract_with_schema(case['text'], SchemaClass)
for field in case['expected']:
expected = case['expected'][field]
actual = getattr(result, field, None)
# Update precision/recall counters
update_metrics(field_f1, field, expected, actual)
return {k: compute_f1(v) for k, v in field_f1.items()}Obsługa wycofywania schematu
Gdy dana wersja schematu nie jest już używana do nowych ekstrakcji, można ją wycofać. Wycofanie oznacza: zaprzestanie przyjmowania nowych rekordów w tej wersji, zachowanie możliwości odczytu starych rekordów oraz zaplanowanie daty wycofania, do której stare rekordy zostaną zmigrowane lub zarchiwizowane. Wycofanie należy opisać w changelogu, aby wszyscy odbiorcy wiedzieli, że muszą zaktualizować kod parsowania.
DEPRECATED_VERSIONS = {'1.0'}
SUNSET_DATE = '2026-09-01'
def warn_if_deprecated(version: str):
if version in DEPRECATED_VERSIONS:
import warnings
warnings.warn(
f'Schema version {version} is deprecated. '
f'It will be removed after {SUNSET_DATE}. '
'Migrate consumers to version 2.0.',
DeprecationWarning,
stacklevel=2
)Changelog i komunikacja
Każdej zmianie schematu musi towarzyszyć wpis w changelogu opisujący zakres i przyczynę zmiany, instrukcje migracji oraz przewidywany wpływ. Wpisy w changelogu należy udostępnić wszystkim zespołom korzystającym z wyodrębnionych danych przed wdrożeniem zmiany. Wiele katastrof podczas migracji schematów wynika nie z awarii technicznych, lecz z braku informacji dla odbiorców o nadchodzącej zmianie.
# CHANGELOG.md entry format:
# ## Schema v2.0 (2026-07-01)
# ### Changes
# - ADDED: vendor_tax_id (Optional[str]) - VAT/EIN extracted from header
# - ADDED: currency (str, default='USD') - detected from symbol/code
# ### Migration
# Run: python scripts/migrate_v1_to_v2.py --batch-size=500
# ### Consumers
# - billing-service: update parse_extraction() to handle v2
# - audit-service: query now supports currency filterSzybkie sprawdzenie
Sprawdź swoją wiedzę na temat ewolucji schematów i zgodności wstecznej w potokach ekstrakcji.
Podsumowanie lekcji
W tej lekcji dowiedzieli się Państwo, że: wersjonowanie schematów przechowuje identyfikator wersji wraz z każdym wyodrębnionym rekordem, dzięki czemu można przeprowadzać selektywne migracje, zmiany addytywne są bezpieczne, natomiast zmiana nazw lub typów pól wymaga starannej migracji, a równoległa walidacja umożliwia sprawdzenie nowego schematu przed wycofaniem starego. W następnej części zmierzymy opóźnienie LLM za pomocą metryk TTFT i TPOT.
Często zadawane pytania
Czy lekcja „Ewolucja schematów i zgodność wsteczna” jest bezpłatna?
Tak — pełny tekst „Ewolucja schematów i zgodność wsteczna” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Engineering Academy, przejdź na CoddyKit PRO. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Ewolucja schematów i zgodność wsteczna”?
Zarządzaj przełamującymi zmianami schematów w długo działających potokach ekstrakcji, wersjonując schematy, migrując historyczne ekstrakcje i uruchamiając równoległą walidację w okresach przejściowyc… Ćwiczysz AI Engineering Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć AI Engineering Academy?
Nie wymagamy żadnego doświadczenia. AI Engineering Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Ewolucja schematów i zgodność wsteczna”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji AI Engineering Academy?
Tak. Każda lekcja AI Engineering Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Instructor: ekstrakcja typowana z Pydantic
- Obsługa częściowych i brakujących danych
- Przetwarzanie wsadowe z async i kolejkami
- Ewolucja schematów i zgodność wsteczna