Evolução de esquemas e compatibilidade retroativa
Gerencie alterações incompatíveis nos esquemas de pipelines de extração de longa duração versionando os esquemas, migrando extrações históricas e executando validação paralela durante as transições.
Evolução de esquemas e compatibilidade retroativa é uma aula grátis de AI Engineering Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Engineering Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Engineering Academy inclui 4 aulas no total.
Por que os esquemas mudam com o tempo
Os esquemas de extração não são estáticos. Os requisitos de negócio evoluem, novos tipos de documentos surgem e você descobre campos que deveria ter capturado desde o início. Alterar um esquema em um pipeline em produção cria um problema de compatibilidade retroativa: os registros extraídos existentes usam o esquema antigo, enquanto os novos usam o esquema novo. Gerenciar essa transição com segurança é o objetivo da evolução de esquemas.
Criando versões para seus esquemas
Atribua um número de versão a cada esquema e armazene-o junto de cada registro extraído. Ao alterar o esquema, incremente a versão. Isso permite consultar registros por versão do esquema, executar migrações em registros antigos e manter uma lógica de validação separada para cada versão. Um simples campo de texto schema_version em cada modelo de saída é suficiente.
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 defaultAlterações aditivas versus incompatíveis
Alterações aditivas são seguras: adicionar um campo Opcional ou um campo com valor padrão não quebra o código antigo de extração nem os registros antigos. Alterações incompatíveis são arriscadas: renomear um campo, alterar seu tipo de texto para inteiro ou removê-lo quebrará os consumidores subsequentes. Sempre prefira alterações aditivas. Quando uma alteração incompatível for inevitável, crie uma nova versão principal do esquema e faça a migração de maneira controlada.
# 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 dataArmazenando a versão do esquema no banco de dados
Inclua a versão do esquema na tabela de resultados da extração para saber sempre qual versão produziu cada registro. Uma coluna jsonb que armazene todos os dados extraídos, junto com uma coluna de texto schema_version, é um padrão comum. Isso permite escrever consultas cientes da versão e migrar seletivamente registros antigos durante períodos de baixo tráfego.
-- 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;Escrevendo scripts de migração
Escreva um script de migração para cada transição entre versões do esquema. Ele deve ler os registros antigos, transformá-los para o novo formato e gravá-los novamente com a nova versão. Execute as migrações em pequenos lotes com transações para que uma falha não deixe o banco de dados em um estado parcialmente migrado. Mantenha sempre o esquema antigo disponível até verificar que a migração foi concluída.
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']
)Validação paralela durante as transições
Durante uma migração de esquema, execute uma validação paralela: faça a extração simultaneamente com o esquema antigo e o novo para uma amostra dos documentos recebidos. Compare os resultados para verificar se o novo esquema captura tudo o que o antigo capturava, além dos novos campos. Só desative o esquema antigo depois que a validação paralela demonstrar uma paridade estável em uma amostra estatisticamente significativa.
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}Sinalizadores de recurso para disponibilizar o esquema
Use sinalizadores de recurso para controlar quando seu pipeline muda do esquema antigo para o novo. Isso permite disponibilizar gradualmente o novo esquema para uma porcentagem do tráfego, monitorar as taxas de erro e reverter instantaneamente se algo der errado — sem redistribuir o código. Serviços de sinalização de recursos como LaunchDarkly ou uma simples linha no banco de dados funcionam igualmente bem.
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)Compatibilidade dos consumidores com tipos união
Os consumidores subsequentes que leem os dados extraídos precisam lidar adequadamente com várias versões do esquema. Use uma união discriminada no código do consumidor para selecionar a lógica correta de análise com base no campo schema_version. Essa abordagem é mais robusta do que escrever cadeias condicionais de if-else e mais fácil de ampliar quando a versão 3 chegar.
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}')Testando alterações de esquema antes da implantação
Antes de implantar um novo esquema, execute-o em todo o seu conjunto de testes de regressão: uma coleção selecionada de documentos representativos com resultados esperados conhecidos. Compare as pontuações F1 de cada campo entre o esquema antigo e o novo. Uma regressão no F1 de qualquer campo significa que a descrição do novo esquema confundiu o modelo — corrija a descrição do campo antes de disponibilizá-lo.
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()}Lidando com a descontinuação de esquemas
Quando uma versão do esquema deixar de ser usada para novas extrações, você poderá descontinuá-la. Descontinuar significa: deixar de aceitar novos registros nessa versão, manter os registros antigos legíveis e programar uma data de encerramento para migrar ou arquivar os registros antigos. Documente a descontinuação em um registro de alterações para que todos os consumidores saibam que precisam atualizar seu código de análise.
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
)Registro de alterações e comunicação
Toda alteração de esquema deve ser acompanhada de uma entrada no registro de alterações que descreva o que mudou, por que mudou, as instruções de migração e o impacto esperado. Compartilhe as entradas com todas as equipes que consomem dados extraídos antes de implantar a alteração. Muitos desastres de migração de esquemas acontecem não por falhas técnicas, mas porque os consumidores não foram informados de que uma alteração estava por vir.
# 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 filterVerificação rápida
Teste sua compreensão sobre evolução de esquemas e compatibilidade retroativa em pipelines de extração.
Resumo da lição
Nesta lição, você aprendeu que o versionamento de esquemas armazena um identificador de versão junto de cada registro extraído para permitir migrações seletivas, que alterações aditivas são seguras, enquanto renomear ou alterar o tipo dos campos exige uma migração cuidadosa, e que a validação paralela permite verificar o novo esquema antes de desativar o antigo. A seguir, mediremos a latência de LLM com as métricas TTFT e TPOT.
Perguntas Frequentes
A aula “Evolução de esquemas e compatibilidade retroativa” é grátis?
Sim — o texto completo de “Evolução de esquemas e compatibilidade retroativa” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Engineering Academy, atualize para CoddyKit PRO. O curso de AI Engineering Academy inclui 4 aulas no total.
O que vou aprender em “Evolução de esquemas e compatibilidade retroativa”?
Gerencie alterações incompatíveis nos esquemas de pipelines de extração de longa duração versionando os esquemas, migrando extrações históricas e executando validação paralela durante as transições. Você pratica AI Engineering Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AI Engineering Academy?
Nenhuma experiência prévia é necessária. AI Engineering Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Evolução de esquemas e compatibilidade retroativa”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AI Engineering Academy?
Sim. Cada aula de AI Engineering Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Instructor: extração tipada com Pydantic
- Lidando com dados parciais e ausentes
- Processamento em lotes com operações assíncronas e filas
- Evolução de esquemas e compatibilidade retroativa