AI Engineering Academy · درس

تطور المخطط والتوافق مع الإصدارات السابقة

أديروا تغييرات المخطط غير المتوافقة في مسارات الاستخراج طويلة التشغيل عبر إصدار المخططات، وترحيل عمليات الاستخراج التاريخية، وتشغيل التحقق المتوازي أثناء الانتقال.

الدرس 4 من 413 خطوة

تطور المخطط والتوافق مع الإصدارات السابقة درس مجاني في AI Engineering Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Engineering Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.

لماذا تتغير المخططات بمرور الوقت

مخططات الاستخراج ليست ثابتة. فمتطلبات الأعمال تتطور، وتظهر أنواع جديدة من المستندات، وتكتشفون حقولًا كان ينبغي التقاطها منذ البداية. يؤدي تغيير مخطط في خط أنابيب قيد التشغيل إلى إنشاء مشكلة توافق عكسي: تستخدم السجلات المستخرجة الحالية المخطط القديم، بينما تستخدم السجلات الجديدة المخطط الجديد. وإدارة هذا الانتقال بأمان هي جوهر تطور المخطط.

إدارة إصدارات المخططات

عيّنوا رقم إصدار لكل مخطط، وخزّنوه إلى جانب كل سجل مستخرج. عند تغيير المخطط، زيدوا رقم الإصدار. يتيح لكم ذلك الاستعلام عن السجلات حسب إصدار المخطط، وتشغيل عمليات الترحيل على السجلات القديمة، والحفاظ على منطق تحقق منفصل لكل إصدار. ويكفي وجود حقل نصي بسيط باسم schema_version في كل نموذج إخراج.

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 default

التغييرات الإضافية مقابل التغييرات الكاسرة

تُعد التغييرات الإضافية آمنة: فإضافة حقل Optional أو حقل ذي قيمة افتراضية لا تكسر كود الاستخراج القديم ولا السجلات القديمة. أما التغييرات الكاسرة فتنطوي على مخاطر: إذ يؤدي تغيير اسم حقل، أو تغيير نوعه من string إلى int، أو حذف حقل إلى تعطيل المستهلكين اللاحقين. فضّلوا دائمًا التغييرات الإضافية. وعندما يصبح التغيير الكاسر حتميًا، أنشئوا إصدارًا رئيسيًا جديدًا للمخطط ونفّذوا الترحيل بطريقة محكومة.

# 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 data

تخزين إصدار المخطط في قاعدة البيانات

أدرجوا إصدار المخطط في جدول نتائج الاستخراج حتى تعرفوا دائمًا الإصدار الذي أنشأ كل سجل. ويُعد عمود jsonb يخزن البيانات المستخرجة كاملة، إلى جانب عمود نصي schema_version، نمطًا شائعًا. يتيح لكم ذلك كتابة استعلامات تراعي الإصدار وترحيل السجلات الأقدم بشكل انتقائي خلال فترات انخفاض حركة المرور.

-- 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;

كتابة نصوص الترحيل

اكتبوا نص ترحيل لكل انتقال بين إصدارات المخطط، بحيث يقرأ السجلات القديمة، ويحوّلها إلى التنسيق الجديد، ثم يكتبها مجددًا بالإصدار الجديد. نفّذوا عمليات الترحيل على دفعات صغيرة باستخدام المعاملات حتى لا يؤدي أي إخفاق إلى ترك قاعدة البيانات في حالة ترحيل جزئي. احتفظوا دائمًا بالمخطط القديم متاحًا إلى أن يتم التحقق من اكتمال الترحيل.

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']
            )

التحقق المتوازي أثناء عمليات الانتقال

أثناء ترحيل المخطط، نفّذوا تحققًا متوازيًا: استخرجوا البيانات باستخدام المخططين القديم والجديد في الوقت نفسه لعينة من المستندات الواردة. قارنوا النتائج للتحقق من أن المخطط الجديد يلتقط كل ما كان يلتقطه القديم، إضافةً إلى الحقول الجديدة. لا توقفوا استخدام المخطط القديم إلا بعد أن يُظهر التحقق المتوازي تطابقًا مستقرًا على عينة ذات دلالة إحصائية.

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}

أعلام الميزات لطرح المخطط

استخدموا أعلام الميزات للتحكم في موعد انتقال خط الأنابيب من المخطط القديم إلى الجديد. يتيح لكم ذلك طرح المخطط الجديد تدريجيًا على نسبة مئوية من حركة المرور، ومراقبة معدلات الأخطاء، والتراجع فورًا إذا حدث خطأ، من دون إعادة نشر الكود. وتعمل خدمات أعلام الميزات مثل LaunchDarkly أو صف بسيط في قاعدة البيانات على حد سواء.

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)

توافق المستهلكين باستخدام Union Types

يحتاج المستهلكون اللاحقون الذين يقرؤون البيانات المستخرجة إلى التعامل بسلاسة مع إصدارات المخطط المتعددة. استخدموا discriminated union في كود المستهلك لاختيار منطق التحليل الصحيح استنادًا إلى حقل schema_version. وهذا أكثر متانة من كتابة سلاسل شرطية من if-else، كما يسهل توسيعه عند وصول الإصدار 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}')

اختبار تغييرات المخطط قبل النشر

قبل نشر مخطط جديد، شغّلوه على كامل مجموعة اختبارات الانحدار: وهي مجموعة منتقاة من المستندات النموذجية ذات المخرجات المتوقعة والمعروفة. قارنوا درجات F1 لكل حقل بين المخطط القديم والجديد. ويعني تراجع F1 لأي حقل أن وصف المخطط الجديد أربك النموذج؛ لذا أصلحوا وصف الحقل قبل النشر.

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()}

التعامل مع إيقاف المخطط

بمجرد عدم استخدام إصدار من المخطط للاستخراجات الجديدة، يمكنكم إيقافه. ويعني الإيقاف: التوقف عن قبول سجلات جديدة بهذا الإصدار، والإبقاء على السجلات القديمة قابلة للقراءة، وجدولة تاريخ نهائي ستُرحّل فيه السجلات القديمة أو تُؤرشف. وثّقوا الإيقاف في سجل تغييرات حتى يعرف جميع المستهلكين ضرورة ترقية كود التحليل لديهم.

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.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 filter

تحقق سريع

اختبروا مدى فهمكم لتطور المخطط والتوافق العكسي في خطوط أنابيب الاستخراج.

مراجعة الدرس

تعلّمتم في هذا الدرس أن إدارة إصدارات المخطط تخزن معرّف إصدار إلى جانب كل سجل مستخرج، ما يتيح لكم الترحيل بشكل انتقائي، وأن التغييرات الإضافية آمنة، بينما يتطلب تغيير أسماء الحقول أو أنواعها ترحيلًا دقيقًا، وأن التحقق المتوازي يتيح لكم التحقق من المخطط الجديد قبل إيقاف القديم. في الجزء التالي، سنقيس زمن استجابة LLM باستخدام مقياسي TTFT وTPOT.

البدء مجانًا

تعلم Python مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
30
الدروس
120

الأسئلة الشائعة

هل درس «تطور المخطط والتوافق مع الإصدارات السابقة» مجاني؟

نعم — نص درس «تطور المخطط والتوافق مع الإصدارات السابقة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Engineering Academy، انتقل إلى CoddyKit PRO. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.

ماذا ستتعلم في «تطور المخطط والتوافق مع الإصدارات السابقة»؟

أديروا تغييرات المخطط غير المتوافقة في مسارات الاستخراج طويلة التشغيل عبر إصدار المخططات، وترحيل عمليات الاستخراج التاريخية، وتشغيل التحقق المتوازي أثناء الانتقال. تتمرن على AI Engineering Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ AI Engineering Academy؟

لا تُشترط خبرة سابقة. AI Engineering Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «تطور المخطط والتوافق مع الإصدارات السابقة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس AI Engineering Academy هذا؟

نعم. كل درس في AI Engineering Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. Instructor: الاستخراج الموصوف باستخدام Pydantic
  2. معالجة البيانات الجزئية والمفقودة
  3. المعالجة الدفعية باستخدام Async والطوابير
  4. تطور المخطط والتوافق مع الإصدارات السابقة
← العودة إلى AI Engineering Academy