Filter transkrip itu proyeksi, bukan parameter
Mengubah aturan filter nggak harus menjalankan ulang whisper. Filter itu proyeksi dari stdout yang udah tersimpan, distamp sha256 tiap aturan berubah.
Ringkasan
Ganti aturan filter kata kasar bikin penulis hampir re-run whisper buat semua file audio, padahal boros banget. Ternyata filter cuma proyeksi dari output whisper yang udah tersimpan, jadi yang diulang cukup proyeksinya aja, bukan transkripsinya. Solusinya: simpan raw output, tempel filter_version berupa hash sha256 di frontmatter, refilter selesai detikan tanpa panggil model.
Aturan filter kata kasar di pipeline audio saya berubah. Cuma satu baris daftar frasa. Tapi refleks pertama yang muncul: jalankan ulang whisper untuk semua transkrip yang udah ada. Ratusan file audio, inferensi dibayar dua kali, padahal dari sisi audio nggak ada yang berubah sama sekali.
Desain pertama yang saya rancang emang kelihatan rapi di atas kertas: tempel filter_version ke cache key sidecar. Aturan berubah, key ikut berubah, semua hasil lama otomatis dianggap basi. Masalahnya di upgrade path: semua sumber lama harus re-transcribe manual. Untung ide ini dibalik di desain berikutnya, sebelum sempat dieksekusi.
Menjalankan ulang whisper itu bukan opsi murah
Kesalahan saya dimulai dari anggapan bahwa filter itu bagian dari transkripsi. Nyatanya bukan. Fungsi transcribe() di whisper membaca seluruh file audio, lalu memprosesnya dengan window bergeser 30 detik, dan tiap window diisi prediksi autoregressive sequence-to-sequence [1]. Dijalankan ulang cuma buat re-filter artinya inferensi penuh dari nol.
"Tinggal pakai model turbo dong, cepat katanya." Model turbo memang versi optimasi dari large-v3 yang lebih cepat dengan penurunan akurasi minimal [1]. Tetap aja itu run model sungguhan. Komputasinya sama beratnya untuk menghasilkan teks yang, dari sisi yang mau difilter, udah lengkap tersimpan di disk.
Filter itu proyeksi, bukan parameter transkripsi
Titik baliknya cuma satu: output whisper itu udah tersimpan. Stdout-nya ada di sidecar. Aturan filter cuma lapisan yang memproyeksikan stdout itu ke bentuk final. Kalau aturan berubah, yang perlu diulang cuma proyeksinya, bukan transkripsinya. Nol pemanggilan model.
Supaya tiap artefak turunan bisa divalidasi, aturan filter distamp dengan filter_version: hash sha256 dari daftar frasa, dihitung lewat hashlib.file_digest [2].
import hashlib
with open("phrases.txt", "rb") as f:
filter_version = hashlib.file_digest(f, "sha256").hexdigest()
Karena isinya hash dari konten daftar, marker ini order-independent (urutan frasa diacak pun nilainya sama) dan berganti otomatis setiap komponen aturan berubah. Tempatnya juga penting: filter_version hidup di event frontmatter, di samping datanya, bukan di cache key. Cache key transkripsi nggak disentuh, jadi hasil lama tetap valid dan nggak ada yang perlu dimigrasi. Checker frontmatter-nya juga ketat: semua window di sebuah sumber harus cocok dengan filter_version di barisnya, jadi campuran versi dalam satu sumber langsung ketahuan.
Desain finalnya punya satu fungsi _filter_projection yang dipakai bersama oleh transcribe, refilter, dan assemble. Tiga konsumen, satu definisi aturan, hasilnya byte-identical di semua jalur. Dari situ klaim determinismenya bisa dites beneran: refilter dengan versi yang sama itu idempotent, dan proyeksi ulang dari versi lama menghasilkan output identik byte-per-byte dengan transkripsi segar. Ada test-nya, bukan cuma harapan.
Penulisan hasil refilter juga atomik per sumber: tulis ke file .tmp dulu, lalu os.replace ke nama final. Fungsi itu menggantikan file tujuan diam-diam kalau ada, dan rename yang berhasil itu atomik, justru jaminan POSIX [3]. Kena kill di tengah refilter? Paling banter ada file .tmp nyangkut; transcript final nggak pernah ke-setengah-jadi.
Kalau producer-nya mahal, simpan raw output-nya
Pola umumnya: pipeline yang punya producer mahal dan konsumen murah atas output-nya, sebaiknya menyimpan raw output sekali, menurunkan derived view dari sana, lalu menempelkan stamp content-hash dari aturan yang menghasilkan tiap view. Mau ganti aturan? Proyeksikan ulang, entah ke berapa kalimat perintahnya. Mau audit? Bandingkan hash di frontmatter tiap artefak untuk tahu aturan mana yang menghasilkannya. Pertanyaan "transkrip ini difilter pakai aturan yang keberapa" jadi bisa dijawab tanpa menebak-nebak dari isi filenya.
Setelah kejadian ini saya jadi curiga setiap kali nemu desain yang solusinya "invalidasi cache dan hitung ulang semuanya". Kadang itu memang yang dibutuhkan. Tapi seringnya data mentahnya udah ada di disk, dan yang perlu diganti cuma cara memandangnya. Sekarang perubahan aturan filter di pipeline saya terasa biasa aja: edit daftar frasa, hash baru muncul, proyeksi ulang jalan, selesai dalam hitungan detik tanpa satu pun pemanggilan model. Jalurnya pendek, hasilnya bisa dites, dan nggak ada bagian sistem yang panik. Kayaknya memang harus dari awal gitu.