# Plan & Arsitektur — OCR Service Surat Sprint Kepolisian

## 1. Penilaian Jujur Tech Stack yang Diusulkan

Tech stack Anda (FastAPI + PaddleOCR + OpenCV/Pillow + Regex) **sudah bagus dan layak produksi**, tapi **belum tentu paling optimal akurasinya** untuk kasus surat sprint. Ada beberapa gap yang perlu diisi sebelum bisa disebut "terbaik".

### Yang sudah tepat
| Komponen | Alasan |
|---|---|
| **FastAPI** | Async native, Pydantic validation, OpenAPI docs otomatis, ideal untuk ML serving. |
| **PaddleOCR (PP-OCRv4/v5)** | Salah satu OCR open-source terbaik untuk dokumen campuran teks + tabel, mendukung Latin (cocok untuk Bahasa Indonesia), bisa jalan on-premise (penting untuk dokumen kepolisian yang sensitif — **cloud OCR seperti Google Vision/AWS Textract sebaiknya dihindari** karena masalah kerahasiaan). |
| **OpenCV + Pillow** | Standar industri untuk preprocessing. |
| **Regex/rule-based** | Cocok untuk dokumen terstruktur seperti sprint yang format-nya relatif baku. |

### Yang masih kurang / perlu ditambah

1. **Table extraction belum tertangani**
   Daftar personel di surat sprint hampir selalu berbentuk **tabel** (No, Pangkat, NRP, Nama, Jabatan, Keterangan). Regex pada teks linear dari OCR biasa **akan kacau** ketika baris tabel pecah atau kolom bergeser. Solusi: gunakan **PaddleOCR PP-Structure** (modul table recognition bawaan Paddle) atau model khusus seperti **TableTransformer (Microsoft)**.

2. **Document detection & dewarping untuk foto HP belum eksplisit**
   Foto HP bermasalah karena: perspektif miring, lipatan, bayangan, lighting tidak rata, fokus tidak merata. OpenCV crop + perspective transform manual saja sering gagal. Tambahkan:
   - **Document corner detection**: `DocTR` / `MobileSAM` / model edge-based, atau heuristik kontur OpenCV sebagai fallback.
   - **Dewarping**: `DocTr` / `DewarpNet` untuk halaman yang melengkung (lipatan).
   - **Shadow removal**: algoritma background division atau model spesialis.

3. **Strategi ekstraksi 100% regex itu rapuh**
   Surat sprint dari satuan berbeda (Polda, Polres, Polsek, Mabes) punya **variasi format**: header berbeda, urutan field berbeda, kadang pangkat disingkat (`AKP`, `IPDA`) kadang ditulis penuh. Regex murni akan butuh ratusan rule dan tetap miss kasus baru.
   **Rekomendasi pendekatan hybrid**:
   - **Layer 1 — Regex/rule** untuk field deterministik (Nomor sprint, tanggal, dasar hukum) yang format-nya baku.
   - **Layer 2 — Schema-aware extraction** menggunakan **LLM lokal** (Llama 3.1 8B / Qwen2.5 7B via Ollama atau vLLM) dengan structured output (JSON schema / Pydantic) untuk field yang variatif (jabatan, keterangan tugas).
   - **Layer 3 — Validation** terhadap master data (daftar pangkat valid, format NRP 8 digit, dll).

4. **Tidak ada confidence scoring & human-in-the-loop**
   Untuk dokumen kepolisian, **akurasi 100% otomatis itu mitos**. Sistem harus:
   - Mengeluarkan confidence score per field.
   - Otomatis flag dokumen low-confidence untuk review manusia.
   - Sediakan UI/endpoint koreksi yang feedback-nya bisa dipakai retraining.

5. **Alternatif end-to-end yang patut dipertimbangkan**
   Jika nanti volume dokumen besar dan format relatif stabil, fine-tuning model **Document Understanding** end-to-end bisa lebih akurat:
   - **Donut** (OCR-free, langsung image → JSON).
   - **LayoutLMv3** (kombinasi teks + layout + visual).
   - **Surya OCR** (newer, sangat bagus untuk dokumen).
   Untuk MVP, tetap pakai PaddleOCR. Donut/LayoutLM adalah opsi V2 setelah ada labeled dataset cukup (~500–1000 dokumen).

### Verdict
Stack Anda **bisa mencapai ~85–92% akurasi field-level** untuk surat sprint dengan kualitas scan baik, dan **~70–80%** untuk foto HP, **kalau ditambah** komponen di atas. Tanpa table extraction + dewarping + hybrid extraction, akurasinya akan jatuh di kondisi nyata.

---

## 2. Arsitektur yang Direkomendasikan

### 2.1 Diagram Logis

```
┌────────────────────────────────────────────────────────────────────┐
│                         Client (Web/Mobile)                        │
└──────────────────────────────┬─────────────────────────────────────┘
                               │ HTTPS (multipart upload)
                               ▼
┌────────────────────────────────────────────────────────────────────┐
│                    FastAPI Gateway (stateless)                     │
│   - Auth (JWT/API key)   - Rate limit   - Request validation       │
└──────────────────────────────┬─────────────────────────────────────┘
                               │ enqueue job
                               ▼
┌────────────────────────────────────────────────────────────────────┐
│              Job Queue (Redis + Celery / RQ / Dramatiq)            │
└──────────────────────────────┬─────────────────────────────────────┘
                               ▼
┌────────────────────────────────────────────────────────────────────┐
│                    OCR Worker Pipeline (GPU/CPU)                   │
│  ┌────────────┐  ┌──────────────┐  ┌───────────┐  ┌────────────┐   │
│  │ 1. Ingest  │→ │ 2. Preproc   │→ │ 3. OCR +  │→ │ 4. Extract │   │
│  │  & detect  │  │ (deskew,     │  │  Layout   │  │ (regex +   │   │
│  │  PDF/IMG   │  │  dewarp,     │  │  PP-Struct│  │  LLM +     │   │
│  │            │  │  denoise)    │  │  + Table) │  │  validate) │   │
│  └────────────┘  └──────────────┘  └───────────┘  └─────┬──────┘   │
│                                                         │          │
│                          ┌──────────────────────────────┘          │
│                          ▼                                         │
│                   ┌─────────────┐                                  │
│                   │ 5. Confidence│ → low conf? flag for review    │
│                   │   scoring    │                                 │
│                   └──────┬───────┘                                 │
└──────────────────────────┼─────────────────────────────────────────┘
                           ▼
┌────────────────────────────────────────────────────────────────────┐
│           Storage: PostgreSQL (metadata) + MinIO/S3 (file)         │
│           + Vector store opsional (untuk dedup / search)           │
└────────────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────────────────┐
│           Review UI (optional) — koreksi manual + audit trail      │
└────────────────────────────────────────────────────────────────────┘
```

### 2.2 Pipeline Detail per Tahap

**Tahap 1 — Ingest & Document Detection**
- PDF: render setiap halaman jadi image @ 300 DPI (`pdf2image` / `PyMuPDF`).
- Image (foto HP): deteksi sudut dokumen → crop → perspective transform.
  - Library: OpenCV `findContours` (cepat) sebagai fallback, **DocTR document detector** (lebih akurat) sebagai utama.

**Tahap 2 — Preprocessing**
- Deskew (rotation correction) — Hough transform atau model.
- Dewarp (untuk foto buku/lipatan) — `DewarpNet` atau model RNN.
- Adaptive thresholding (untuk foto dengan lighting tidak rata).
- Shadow removal (background division).
- Denoise (Non-Local Means).
- Resize ke ukuran optimal OCR (~1500–2500 px sisi panjang).

**Tahap 3 — OCR + Layout Analysis**
- **PaddleOCR PP-Structure** dijalankan sekali → menghasilkan:
  - Bounding boxes + teks + confidence per word/line.
  - Table region detection + table-to-HTML/JSON.
  - Layout type per region (title, paragraph, table, figure).
- Output ditampung sebagai struktur intermediate (mirip hOCR / ALTO XML).

**Tahap 4 — Information Extraction**
- **4a. Header parsing (regex)**: Nomor sprint, tanggal, satuan penerbit, dasar hukum, perihal. Format relatif baku → regex sangat cocok.
- **4b. Personnel table extraction**: ambil dari hasil PP-Structure table → mapping kolom (Pangkat, NRP, Nama, Jabatan, Keterangan).
- **4c. LLM fallback**: untuk field yang regex/table miss, kirim chunk teks + JSON schema ke LLM lokal (Ollama / vLLM) dengan **structured output** (Pydantic via `outlines` / `instructor`).
- **4d. Validation layer**:
  - NRP: 8 digit numerik.
  - Pangkat: harus ada di daftar master pangkat Polri.
  - Tanggal: parse + sanity check.
  - Cross-check: jumlah personel di body = jumlah baris tabel.

**Tahap 5 — Confidence Scoring & Routing**
- Aggregate confidence: weighted average dari OCR confidence + validation pass/fail + LLM logprob (kalau pakai).
- Threshold (mis. < 0.85) → status `NEEDS_REVIEW`.
- Threshold tinggi (≥ 0.95) + semua validasi pass → status `AUTO_APPROVED`.

### 2.3 API Endpoint (FastAPI)

```
POST   /api/v1/documents              # upload, kembalikan job_id
GET    /api/v1/documents/{job_id}     # poll status + hasil
GET    /api/v1/documents/{job_id}/raw # raw OCR output (debug)
PATCH  /api/v1/documents/{job_id}     # koreksi manual (HITL)
GET    /api/v1/health                 # liveness
GET    /api/v1/metrics                # Prometheus
```

Response shape (contoh):
```json
{
  "job_id": "uuid",
  "status": "completed | processing | needs_review | failed",
  "confidence": 0.92,
  "data": {
    "nomor_sprint": "Sprin/123/IV/2025",
    "tanggal": "2025-04-21",
    "satuan_penerbit": "Polres Bandung",
    "dasar": ["...", "..."],
    "perihal": "...",
    "personel": [
      {"no": 1, "pangkat": "AKP", "nrp": "12345678", "nama": "...", "jabatan": "Kasat Reskrim", "confidence": 0.97},
      ...
    ],
    "ttd": {"pejabat": "...", "pangkat": "...", "nrp": "..."}
  },
  "review_flags": []
}
```

### 2.4 Tech Stack Final yang Direkomendasikan

| Layer | Pilihan | Catatan |
|---|---|---|
| API | **FastAPI** + Uvicorn/Gunicorn | sesuai usulan |
| Validation | **Pydantic v2** | wajib |
| Queue | **Redis + Celery** atau **Dramatiq** | OCR berat, jangan blocking request |
| OCR | **PaddleOCR PP-OCRv4 + PP-Structure** | tambah PP-Structure untuk tabel |
| Preprocessing | **OpenCV + Pillow** + **DocTR** (detection) | DocTR untuk foto HP |
| Extraction | **Regex + Ollama (Llama 3.1 8B / Qwen2.5 7B)** + **instructor/outlines** | hybrid |
| Storage | **PostgreSQL** (metadata) + **MinIO** (file blob) | self-hosted, sesuai compliance |
| Observability | **Prometheus + Grafana + Loki** | wajib produksi |
| Container | **Docker + docker-compose** (dev) → **Kubernetes** (prod) | |
| GPU | NVIDIA T4/A10 (1× cukup untuk MVP) | PaddleOCR jauh lebih cepat di GPU |

---

## 3. Roadmap Pengembangan (Bertahap)

### Fase 0 — Persiapan (1 minggu)
- Kumpulkan **dataset sampel**: minimal 50 surat sprint (campur PDF scan + foto HP) dari beragam satuan.
- Buat **ground truth labelling** untuk 20 dokumen (untuk evaluasi).
- Definisikan **schema output final** (JSON) bersama stakeholder.

### Fase 1 — MVP Pipeline Sinkron (2 minggu)
- Setup FastAPI skeleton + Pydantic schemas.
- Integrasi PaddleOCR PP-OCRv4 (CPU dulu, GPU menyusul).
- Preprocessing dasar: deskew + denoise + resize.
- Regex extraction untuk field header.
- Endpoint sinkron `POST /documents` (untuk dev/testing saja).
- **Evaluasi akurasi** terhadap 20 ground truth.

### Fase 2 — Robustness untuk Foto HP (2 minggu)
- Integrasi document detection (DocTR atau OpenCV contour).
- Perspective transform + dewarping.
- Shadow removal.
- Re-evaluasi akurasi pada subset foto HP.

### Fase 3 — Table Extraction (1.5 minggu)
- Integrasi PP-Structure untuk personnel table.
- Mapping kolom + validation (NRP, pangkat).
- Master data tabel pangkat Polri.

### Fase 4 — Async + Production Ready (1.5 minggu)
- Pindahkan ke arsitektur async dengan Celery + Redis.
- Storage MinIO + PostgreSQL.
- Auth, rate limit, logging, metrics.
- Docker compose untuk deployment.

### Fase 5 — LLM Hybrid Extraction (2 minggu)
- Setup Ollama / vLLM dengan model lokal.
- Structured output via `instructor`.
- Confidence scoring + routing ke review.

### Fase 6 — HITL Review UI (opsional, 2 minggu)
- Endpoint koreksi.
- Simple web UI (Next.js) untuk reviewer.
- Audit trail & feedback loop.

### Fase 7 — Optimasi Lanjutan (ongoing)
- Fine-tune PaddleOCR detection/recognition pada dataset internal.
- Eksplorasi Donut/LayoutLMv3 jika dataset sudah cukup.
- Batch processing & GPU optimization.

**Total estimasi MVP fungsional (Fase 1–4): ~7 minggu** dengan 1 backend engineer + 1 ML engineer.

---

## 4. Risiko & Mitigasi

| Risiko | Mitigasi |
|---|---|
| Data sensitif (kepolisian) bocor | Wajib on-prem; tidak ada cloud OCR; enkripsi at-rest (LUKS/pgcrypto) + in-transit (mTLS); audit log lengkap. |
| Variasi format antar satuan | Hybrid extraction (regex + LLM); kumpulkan sample dari banyak satuan sejak awal. |
| Foto HP kualitas buruk | Validasi kualitas image di client (resolusi minimal, blur detection) sebelum upload. |
| Akurasi tidak sampai target | HITL review wajib untuk dokumen low-confidence; jangan deploy fully-automatic. |
| Tanggung jawab hukum atas hasil OCR | Selalu simpan original document + flag bahwa hasil ekstraksi adalah "draft, perlu verifikasi manusia". |

---

## 5. Pertanyaan Sebelum Implementasi

Sebelum saya lanjut ke implementasi, mohon konfirmasi:

1. **Volume**: berapa dokumen/hari yang ditargetkan? (mempengaruhi pilihan async vs sync, GPU vs CPU)
2. **Deployment target**: on-prem mutlak, atau private cloud (GovCloud) boleh?
3. **Source dokumen**: apakah ada akses ke 20–50 sample surat sprint untuk dijadikan dataset awal?
4. **Integrasi**: service ini akan dipanggil sistem apa? (mempengaruhi auth & API contract)
5. **HITL**: apakah ada SDM untuk review manual dokumen low-confidence?
6. **Hardware**: sudah ada server GPU, atau perlu sizing rekomendasi?
7. **Format output final**: ada schema yang sudah dipakai sistem downstream?
