Nama field tidak berubah tetapi artinya telah berubah, dan downstream diam-diam membuat kesalahan: tambahkan nomor versi ke hasil ekstraksi
`amount` minggu lalu mewakili "sen", tapi minggu ini diubah menjadi "yuan", tetapi nama bidangnya tidak berubah. Pemeriksaan jenis masih lolos, namun hasil bisnisnya mungkin dua kali lipat lebih buruk. Peningkatan yang paling berbahaya pada antarmuka ekstraksi sering kali adalah tidak melaporkan kesalahan secara eksplisit, namun secara diam-diam mengubah maknanya.
Artikel ini ditulis untuk pengembang dan tim data hilir yang mengelola antarmuka ekstraksi dokumen. Setelah hasil ekstraksi ditingkatkan, nama field mungkin tidak berubah, namun maknanya akan berbeda. Misalnya, jika amount diubah dari "fen" menjadi "yuan", pemeriksaan jenis tetap lolos, namun nomor bisnisnya mungkin berbeda dua kali lipat.
Berikut ini penjelasan cara menggunakan nomor versi, menghapus fungsi migrasi, dan catatan migrasi untuk menangani jenis masalah ini. Setelah membaca ini, Anda dapat menentukan pemutakhiran mana yang memerlukan peralihan versi, dan informasi apa yang harus disimpan selama migrasi.
Contoh dalam artikel ini menggunakan sampel sintetis, dan kodenya telah diperiksa dengan contoh di lingkungan yang terisolasi. Ini tidak mewakili jaminan atas semua file atau efek produk yang sebenarnya. Sebelum memproses dokumen penting, harap simpan dokumen asli dan tinjau sesuai dengan batasan dan item penerimaan dalam teks.
Nama field tidak berubah tetapi maknanya berubah. Bagaimana cara menghadapinya? Kesimpulan satu kalimat
Beri label setiap keluaran dengan schema_version dan gunakan fungsi migrasi eksplisit untuk mengubah versi data yang berbeda, bukan hanya memberi label pada versi baru.
| situasi Anda | Praktik yang direkomendasikan | alasan |
|---|---|---|
| Cukup tambahkan bidang | Tambahkan bidang baru dan catat versinya | Menambah bidang tidak sama dengan mengubah satuan |
| Unit bidang atau semantik berubah | Pilih validator dan fungsi migrasi berdasarkan versi | Arti bidang dengan nama yang sama akan berubah secara diam-diam |
| Catatan lama memiliki unit yang tidak ditentukan | Klarifikasi kegagalan, simpan data lama dan tambahkan bukti | Fungsi migrasi tidak boleh hanya menebak-nebak |
| Hilir telah menggunakan antarmuka lama | Jadwalkan peralihan versi | Hilir tidak dapat diharapkan untuk secara otomatis mendeteksi perubahan semantik numerik |
| Periksa terjemahannya dengan hasil ekstraksi | Pertahankan informasi versi protokol untuk bidang yang dihasilkan | Lebih mudah untuk memeriksa "mengapa kali ini berbeda dari sebelumnya" |
Nomor versi harus sesuai dengan semantiknya
Menambah suatu bidang tidak sama dengan mengubah satuan. Anda dapat memberikan setiap output schema_version dan memilih validator dan fungsi migrasi berdasarkan versi.
Nomor versi saja tidak melindungi sistem. Jika semua versi mengikuti logika penguraian yang sama dan tidak dapat dibedakan, itu hanyalah bidang dekoratif.
Kunci penguraian berversi
- Pilih aturan penguraian berdasarkan versi: Versi berbeda menggunakan validatornya sendiri.
- Migrasi adalah transformasi data eksplisit: ini bukan tentang memberi label langsung pada data lama dengan versi baru.
- Perubahan semantik harus dapat dilacak: aturan seperti satuan dan presisi harus dapat ditinjau.
Contoh berikut menunjukkan cara mengonversi bilangan bulat poin menjadi string jumlah desimal.
Fungsi migrasi yang dapat diaudit
from decimal import Decimal
def migrate(record):
if record.get("schema_version") != "v1":
raise ValueError("unexpected input version")
cents = record.get("amount_cents")
if type(cents) is not int:
raise ValueError("integer cents required")
return {"schema_version": "v2",
"amount": format(Decimal(cents) / Decimal(100), ".2f"),
"currency": record["currency"]}
old = {"schema_version": "v1", "amount_cents": 12345, "currency": "USD"}
new = migrate(old)
assert new["amount"] == "123.45"
assert old["amount_cents"] == 12345
assert migrate({**old, "amount_cents": -25})["amount"] == "-0.25"
try:
migrate({**old, "amount_cents": True})
except ValueError:
pass
else:
raise AssertionError("bool is not a monetary integer here")
Contoh ini mengasumsikan bahwa mata uang masukan menggunakan hubungan satuan persen dan keluarannya memiliki dua tempat desimal. Ini bukan konverter universal yang mencakup semua unit mata uang.
type(cents) is int sengaja mengecualikan nilai Boolean sebagai subkelas integer dengan Python untuk menghindari penerimaan True sebagai satu sen.
📊 Perbandingan inti: hanya nomor versi dan migrasi berversi
| praktik | Memengaruhi | keterbatasan |
|---|---|---|
Hanya tambahkan bidang schema_version | Output ditandai dengan versi | Jika semua versi masih mengikuti logika parsing yang sama, nomor versi hanyalah hiasan |
| Pilih validator dan fungsi migrasi berdasarkan versi | Bidang dengan nama yang sama memiliki penjelasan yang jelas dalam versi yang berbeda | Penting untuk menuliskan aturan konversi untuk setiap versi |
Apa yang tersisa di catatan migrasi?
Disarankan untuk mencatat informasi berikut selama migrasi untuk memfasilitasi proses backtracking dan offload.
| informasi | menggunakan | risiko hilang |
|---|---|---|
| Versi masukan dan keluaran | Pilih aturan interpretasi yang tepat | Arti bidang dengan nama yang sama membingungkan |
| Versi program migrasi | Mereproduksi hasil konversi | Hasil yang tidak dapat dijelaskan setelah pembaruan |
| Identitas rekaman asli | Ambil sumber | Data baru dan lama tidak bisa disamakan |
| Alasan kegagalan | Pemrosesan manual pengalihan | Data yang tidak memenuhi syarat diisi dengan nilai default |
Jangan terlalu kompatibel dengan kesalahan sejarah
Jika unit rekaman lama tidak pasti, fungsi migrasi tidak boleh menebak. Pendekatan yang lebih tepat adalah mengembalikan kegagalan yang jelas, mempertahankan data lama, dan meminta bukti tambahan.
Untuk pengguna hilir yang sudah menggunakan antarmuka lama, peralihan versi juga harus diatur, daripada mengharapkannya secara otomatis menemukan bahwa nilai semantik telah berubah.
Referensi: Kata kunci umum Skema JSON
Verifikasi pasca-terjemahan juga memerlukan kesadaran versi
Saat menggunakan bidang yang diekstraksi untuk memeriksa terjemahan OmniDoc (portal produk), Anda harus menunjukkan protokol mana yang menghasilkan bidang tersebut. Perubahan pada file sumber, aturan ekstraksi, atau bahasa target dapat mempengaruhi metode perbandingan.
Mempertahankan informasi versi dapat membuat "mengapa kali ini berbeda dari sebelumnya" menjadi masalah yang dapat dipecahkan alih-alih hanya menghubungkannya dengan ketidakstabilan model.
🎯 Metode mana yang cocok untuk Anda?
Jika Anda baru saja menambahkan kolom baru
Tambahkan bidang baru secara langsung dan catat schema_version di output.
Jika Anda ingin mengubah satuan atau arti suatu bidang
Tulis validator independen dan fungsi migrasi untuk versi baru, dan jangan gunakan kembali logika penguraian lama.
Jika unit data historis Anda tidak jelas
Perjelas bahwa migrasi gagal, simpan data asli, dan tambahkan bukti sebelum diproses.
❓ Pertanyaan yang Sering Diajukan (FAQ)
1. Apakah cukup menambahkan nomor versi saja ke output?
tidak cukup. Nomor versi hanyalah sebuah label, dan tidak dapat melindungi makna bisnis jika semua versi masih menggunakan logika parsing yang sama.
2. Mengapa fungsi migrasi mengecualikan nilai Boolean?
Nilai Boolean dalam Python adalah subkelas bilangan bulat, dan type(cents) is int mencegah True diterima sebagai satu sen.
3. Apa yang harus Anda lakukan jika unit pencatatan lama tidak pasti?
Fungsi migrasi tidak boleh ditebak. Kegagalan yang jelas harus dikembalikan, data lama dipertahankan, dan bukti tambahan diminta.
4. Informasi apa yang perlu disimpan dalam catatan migrasi?
Minimal, sertakan versi input dan output, versi program migrasi, identitas rekaman asli, dan alasan kegagalan.
📝 Ringkasan
Fakta bahwa nama bidang tetap tidak berubah tidak berarti maknanya tetap tidak berubah. Membuat versi keluaran dan menangani perubahan semantik dengan fungsi migrasi eksplisit dan catatan migrasi dapat secara signifikan mengurangi risiko kesalahan diam-diam di hilir.


