Setiap bisnis modern bertambah banyak dokumen yang harus diproses: invoice, kwitansi, surat kontrak, dan formulir. Banyak perusahaan langsung menggunakan layanan cloud OCR atau AI eksternal tanpa mempertimbangkan biaya jangka panjang, latensi, dan risiko penyebaran data. Di artikel ini, kita akan membangun pipeline pemrosesan dokumen AI yang sepenuhnya di-host di VPS sendiri, end-to-end, dengan prinsip produksi: aman, terukur, dan mudah di-debug.

Mengapa self-hosted lebih masuk akal daripada yang terlihat

Layanan eksternal memang cepat diaktifkan, tetapi Anda menyerahkan data mentah dokumen ke pihak ketiga. Untuk dokumen sensitif—invoice vendor, data SAP, atau dokumen hukum—bits kecil informasi bisa berakibat fatal. Di-premise berarti data tidak pernah meninggalkan infrastruktur Anda. Di samping keamanan, self-hosted menghilangkan biaya per-page, throttle rate, dan cold-start latency yang tidak terduga. Anda juga bisa menyesuaikan pipeline sesuai domain: rules khusus untuk format invoice Indonesia, e-invoice, atau format inland revenue.

Arsitektur sistem secara keseluruhan

Kita tidak hanya memasang OCR. Sistem kita adalah pipeline lima lapisan. Pertama, injeksi dokumen via API REST + dashboard web. Kedua, validasi file dan quarantining ekstensi berbahaya. Ketiga, preprocessing sebelum dikirim ke model: konversi format, kompresi, normalisasi arsip ZIP/RAR. Keempat, ekstraksi terstruktur menggunakan model generatif atau model spesifik OCR. Kelima, penyimpanan hasil terstruktur ke database dan file asli ke object storage atau disk dengan lifecycle policy.

Implementasi API FastAPI untuk ekstraksi invoice

FastAPI menjadi pilihan jelas karena performa asinkron, integrasi Pydantic, dan otomatisasi dokumentasi OpenAPI yang dibutuhkan tim backend lain. Kita membuat endpoint POST /api/v1/extract yang menerima multipart upload, menyimpan file ke direktori sementara, lalu meneruskan ke model ekstraksi. Jangan pernah mempercayai nama file dari pengguna; buat UUID baru dan normalisasi ekstensi.

Database SQLite dengan Prisma cukup memulai, tetapi untuk lingkungan produksi dengan konkurensi tinggi, pertimbangkan PostgreSQL. Tabel extractions minimal menyimpan filename asli, UUID internal, page_count, confidence_score, processing_time_ms, extracted_at, dan invoice_json untuk hasil terstruktur. Confidence score harus disimpan karena model LLM kadang yakin secara salah.

Dashboard Streamlit untuk human-in-the-loop review

Streamlit bukan cuma untuk prototype. Jika dikonfigurasi dengan benar, ia menjadi alat review yang cepat untuk tim operasional. Mount dashboard di host interface lokal 127.0.0.1:8502, lalu akses lewat reverse proxy. Jangan expose port Streamlit secara langsung karena WebSocket upgrade di proxy bisa menimbulkan risiko auth bypass jika tidak dikonfigurasi header Host dan X-Forwarded-Proto dengan benar.

Agar review efektif, dashboard menampilkan tiga hal: pratinjau gambar halaman yang diupload, JSON hasil ekstraksi yang bisa diedit secara langsung, dan tombol approve/reject yang menulis feedback ke database. Feedback ini nanti bisa menjadi dataset untuk fine-tuning model ekstraksi di tahap selanjutnya.

Reverse proxy, DNS, dan sertifikat TLS

Caddy atau Nginx keduanya bisa digunakan. Caddy lebih ramah karena auto-HTTPS, tetapi kadang kaku saat menangani subpath tertentu di belakang reverse_proxy. Nginx memberikan kontrol lebih besar terhadap rewrite, timeout, dan header. Dalam panduan ini kita gunakan Nginx karena fleksibel menangani /api/* ke port 8001 dan dashboard ke port 8502 di dalam satu server block.

Pastikan izin direktori sertifikat aman. Jangan pernah menyimpan certbot konfigurasi di direktori yang bisa dibaca publik. Nonaktifkan TLS renegotiation yang diinisiasi client (potensi vektor DoS/MITM), dan gunakan rotasi certificate/key yang pendek untuk meminimalisir dampak jika key ter-compromise.

Keamanan yang harus diterapkan sejak hari pertama

Model AI Anda akan memproses dokumen apa pun yang diupload. Batasi tipe MIME dan ukuran file secara ketat. Tolak PDF terenkripsi, ZIP berpassword, dan format exotik seperti DJVU kecuali Anda siap menanganinya. Terapkan rate limit per-IP dan per-user. Jangan hardcode API key Gemini OpenAI Anthropic di kode atau environment file tanpa protesi; gunakan vault atau secret manager dengan rotation policy. Jangan pernah menampilkan API key di dashboard.

Untuk API key Gemini, pastikan hanya visibility tertentu: milik project yang benar, dibatasi lokasi geografis, dengan quota harian. Rotate key minimal setiap 90 hari. Jangan gunakan API key yang sama antara staging dan production. Jika memungkinkan, jalankan model lokal dengan Ollama atau vLLM untuk dokumen yang sangat sensitif.

Observability: logging, tracing, dan error classification

Log harian berisi ratusan baris yang tidak berguna jika tidak terstruktur. Terapkan structured logging JSON dengan field wajib: request_id, filename_hash, page_count, model_version, processing_ms, confidence, error_code. Tidak semua kegagalan sama. Bayangkan dokumen diproses selama 78 detik sebelum timeout, padahal dokumen tersebut seharusnya ditolak sejak awal karena ukuran melebihi batas. Error classification membantu operasional tahu apakah kegagalan di model, di preprocessing, di storage, atau di jaringan.

Ke depan, tambahkan tracing OpenTelemetry untuk melacak satu dokumen dari upload hingga disimpan. Metrik penting: upload queue depth, processing latency p99, confidence histogram, storage I/O util, dan error rate per error class.

Scaling: dari satu VPS ke arsitektur terdistribusi

Single VPS cukup menangani ratusan dokumen per hari jika diatur dengan benar, tetapi pertumbuhan pertama biasanya membuat single-node gagal pada scaling vertically: disk habis karena file PDF bervolume tinggi, RAM penuh karena model LLM memuat terlalu banyak konteks, atau thread pool CPU penuh karena OCR berjalan paralel tanpa kontrol.

Langkah pertama adalah memisahkan layers. Jadikan extraction API sebagai worker stateless yang hanya butuh model inference dan disk sementara, bukan database utama. Gunakan Redis untuk antrian upload agar API listener tetap responsif meskipun 50 dokumen datang bersamaan. Kedua, pisahkan storage: object storage untuk file asli, PostgreSQL untuk metadata, cache untuk hasil ekstraksi yang sering diakses.

Ketika throughput bertambah, pertimbangkan horizontal scaling untuk FastAPI worker di belakang load balancer, dan GPU worker khusus untuk inferensi jika model berada di cloud hybrid. Tetap pertahankan reverse proxy sebagai single ingress agar dashboard dan API tetap punya URL tunggal untuk tim operasional.

Kesalahan yang sering terjadi dan cara menghindarinya

Kesalahan pertama adalah mempercayai output model sepenuhnya. Hasil OCR dari model generatif bisa terlihat rapuh, tetapi pengguna kadang tidak memperhatikan error_code kosong atau confidence di bawah 0.7. Selalu sediakan mode edit di dashboard. Kesalahan kedua adalah mengabaikan PDF terenkripsi: banyak API mengembalikan error abstrak yang membuat debugging sulit. Validasi awal menangani kasus ini. Kesalahan ketiga adalah timeout default yang terlalu pendek. Dokumen 40 halaman bisa butuh waktu lebih dari 30 detik. Ubah timeout proxy dan FastAPI minimal 120 detik untuk endpoint ekstraksi, dan beri indikator progres streaming agar pengguna tahu sistem masih bekerja.

Penutup: roadmap pengembangan sistem

Setelah fondasi bekerja, prioritas berikutnya adalah feedback loop: anotasi manusia di dashboard untuk memperbaiki model, periodic re-training dengan data yang sudah disetujui, dan alerting otomatis saat confidence turun secara signifikan di suatu jenis dokumen. Transformasi dokumen dari file pasif menjadi data terstruktur adalah perjalanan berkelanjutan. VPS Anda adalah fondasi yang kendiri; model dan interface adalah bagian yang berkembang.