# 1. Judul
**Blueprint Restrukturisasi Ingestion: Implementasi "Small-to-Big" Retrieval Architecture (Parent Document Retriever)**

# 2. Tanggal
Sabtu, 9 Mei 2026

# 3. Deskripsi
Dokumen ini merupakan cetak biru (*blueprint*) teknis untuk merombak alur ekstraksi informasi LLM di dalam modul *ingestion* `app/services/ingestion.py`. Tujuan dari dokumen ini adalah untuk menyediakan panduan implementasi komprehensif bagi peralihan dari metode *force-reading* (mengirimkan teks utuh dokumen yang digabungkan) ke metode *Targeted Semantic Retrieval*. Metode ini menggunakan pola "Small-to-Big" yang memastikan model bahasa (LLM) hanya membaca bagian dokumen yang 100% relevan, sehingga menghemat biaya API secara signifikan, meminimalkan latensi per dokumen (< 50 detik), dan mereduksi halusinasi.

# 4. Latar Belakang
Alur sistem saat ini (*existing system*) memotong dokumen PDF menjadi beberapa bagian (*chunks*) lalu menggabungkan *chunks* tersebut menjadi satu teks panjang (maksimal 25.000 karakter) untuk dikirim ke LLM (seperti Qwen3-Max). Terdapat kecacatan logika dan inefisiensi yang sangat mencolok dalam pendekatan ini:
1.  **Pemborosan Biaya (Token Bloat):** Membayar token untuk seluruh isi buku hanya untuk mengekstrak 5 paragraf informasi pilar.
2.  **Sindrom "Lost in the Middle":** Ketika LLM disuapi puluhan halaman teks sekaligus, model cenderung kehilangan fokus pada bagian tengah dokumen, yang mana justru seringkali memuat metodologi dan *gap* penelitian.
3.  **Ancaman Skalabilitas:** Sistem ini tidak *scalable* jika digunakan oleh banyak *user* secara bersamaan karena beban API LLM yang berat dan tidak efisien. Diperlukan sebuah sistem pencarian pintar yang memilah konteks sebelum dikirim ke LLM.

# 5. Metodologi
Restrukturisasi ini menerapkan arsitektur standar industri **"Parent Document Retriever" (Small-to-Big)** dengan optimasi untuk lingkungan *cloud* berskala besar:
1.  **Multi-Level Chunking:** Pembuatan dua lapis *chunk* (*Parent* untuk konteks utuh, *Child* untuk alat cari).
2.  **Cloud-Based Batch Embedding:** Pembuatan vektor *Child Chunk* menggunakan API eksternal (Gemini/OpenAI) agar CPU *backend server* tetap ringan, menggunakan sistem *batching* dengan *exponential backoff*.
3.  **In-Memory Semantic Search:** Penghitungan *Cosine Similarity* menggunakan pustaka *numpy* secara lokal (*in-memory*) agar kalkulasi berjalan dalam hitungan milidetik tanpa instalasi *database* vektor eksternal yang berat.
4.  **Deduplikasi Set:** Menggunakan struktur data *Set* untuk membuang *Parent ID* duplikat jika beberapa *query* pilar menemukan target paragraf yang sama.
5.  **"Stuffing" Context & Single LLM Call:** Semua paragraf *Parent* yang menang digabungkan (*stuffed*) dan dikirim dalam 1 kali pemanggilan API LLM (Qwen/OpenRouter) dengan pelindung *The 3-Layer Shield* (Pydantic Validator).

# 6. Keputusan (Decision Log)
1.  **Bypass Model Lokal:** Menggunakan API (Gemini/OpenAI) untuk proses *embedding Child Chunk* alih-alih model lokal (`sentence-transformers`), demi menjamin skalabilitas saat server diakses ratusan pengguna bersamaan. CPU hanya difokuskan pada operasi matematika dasar (*cosine similarity*).
2.  **Satu Panggilan LLM (Single Prompt Extraction):** Alih-alih melakukan *Map-Reduce* atau melakukan 5 panggilan API ekstraksi yang berbeda untuk 5 pilar, teks-teks *Parent Chunks* unik yang didapat akan digabungkan dan dikirim dalam satu instruksi *prompt* untuk menekan *overhead latency* dan menjaga konteks lintas-pilar.
3.  **Batasan Waktu Toleransi:** Proses dari pemotongan teks (*chunking*), *batch embedding*, perhitungan matematis, hingga ekstraksi JSON ditargetkan selesai dalam rentang waktu **< 50 detik per dokumen**.

# 7. Alur Diagram
```mermaid
flowchart TD
    classDef process fill:#f9f2f4,stroke:#333,stroke-width:2px;
    classDef cloud fill:#e1f5fe,stroke:#0277bd,stroke-width:2px;
    classDef ram fill:#fff3e0,stroke:#f57c00,stroke-width:2px;
    
    A[Dokumen PDF] --> B(Hybrid Parser)
    B --> C[Teks Markdown Bersih]
    
    C --> D{Multi-Level Chunking}:::process
    D -->|~1500 chars| P[Parent Chunks]
    D -->|~200 chars| CH[Child Chunks + Parent ID]
    
    CH --> E[Batch Embedding API<br>Gemini/OpenAI]:::cloud
    E --> V[Vektor Child Chunks]
    
    Q[5 Pertanyaan Pilar] --> QE[Embedding API]:::cloud
    QE --> QV[Vektor Query]
    
    V --> S{In-Memory Cosine Similarity}:::ram
    QV --> S
    
    S --> |Top-K Match| M[List of Child Chunks]
    M --> R{Deduplikasi Parent ID}:::process
    R --> |Ambil Teks Utuh| ST[Stuffed Context<br>Gabungan Parent Chunik]
    
    ST --> LLM[API Ekstraksi LLM<br>Qwen3-Max / OpenRouter]:::cloud
    LLM --> |The 3-Layer Shield| J[Valid JSON 5 Pilar]
    J --> N[(Neo4j Vector Graph)]
```

# 8. Struktur Folder (Tree)
Berikut adalah lokasi di mana skrip/logika ini akan ditempatkan:
```text
/home/daegir/Documents/PERCOBAAN/backend-sre-(Knowledge Extractor)/
├── app/
│   └── services/
│       ├── ingestion.py                 # File utama yang akan direfaktor
│       └── parser/
│           ├── smart_chunker.py         # Akan ditambahkan logika Multi-Level Chunking
│           └── semantic_retriever.py    # (FILE BARU) Berisi logika Batch Embed & Cosine Similarity
└── Markdown/
    └── small-to-big-retrieval/          # (FOLDER BARU)
        ├── Plan/
        │   └── small-to-big-retrieval-plan.md  # File dokumen ini
        └── Execution-Log/
```

# 9. Asumsi
1.  Kredensial API (Gemini/OpenAI) yang tertera di `.env` valid dan mendukung beban *request batching* (tidak langsung memblokir jika mendapat tembakan 100 teks sekaligus). Jika gagal, sistem *Exponential Backoff* yang ada harus mampu menanganinya.
2.  Operasi perhitungan *Cosine Similarity* menggunakan fungsi *dot product* dari pustaka *numpy* terbukti sangat cepat dan cukup stabil untuk ukuran *in-memory array* (< 2000 vektor per dokumen).
3.  Konsep *Zero-Vector Guard* dan *The 3-Layer Shield* (sebelumnya di `ingestion.py`) tidak dirombak dan dipertahankan posisinya untuk melindungi data dari halusinasi LLM.
4.  Pengiriman *chunks* mentah (untuk *backup* data) ke Supabase tetap menggunakan `generate_chunks` tunggal agar *database* Supabase tetap berisi teks yang kompatibel secara linear.

# 10. Konsep Tabel Metriks
| Metrik Pengukuran | Arsitektur Lama (Force-Reading) | Arsitektur Baru (Small-to-Big Retrieval) | Target Keberhasilan |
| :--- | :--- | :--- | :--- |
| **Beban API LLM (Tokens)** | ~25.000 karakter per PDF | ~3.000 hingga 5.000 karakter per PDF | Pengurangan konsumsi token hingga 80% |
| **Beban CPU / RAM Server** | Rendah (tidak ada kalkulasi matematis) | Sangat Ringan (hanya array *numpy* di RAM) | Tidak terjadi lonjakan CPU di atas 10% |
| **Kualitas Ekstraksi Pilar** | Berisiko terkena "Lost in the Middle" | Sangat Akurat (Zero-Noise Context) | Ekstraksi pilar (terutama *Gap* & *Methodology*) selalu akurat sesuai sumber. |
| **Waktu Total Pemrosesan** | < 30 detik per dokumen | < 50 detik per dokumen (ada tambahan API Call) | Proses selesai di bawah ambang batas (50s). |

# 11. Konsep Logika
1.  **Multi-Level Chunking (`smart_chunker.py`):** Sebuah fungsi baru `generate_parent_child_chunks(text)` akan memecah dokumen berdasarkan judul (*Parent*) lalu menggunakan *RecursiveCharacterTextSplitter* untuk memecah *Parent* menjadi kalimat/paragraf kecil (*Child*). Setiap *dict* pada *Child* wajib memiliki atribut `parent_id` dan `text`.
2.  **In-Memory Retrieval (`semantic_retriever.py`):** 
    *   *Batch Embedder*: Fungsi ini membungkus senarai (*list*) *Child Chunks* dan menembak API (Gemini).
    *   *Vector Searcher*: Menggunakan `numpy.dot` untuk menghitung kemiripan antara Vektor Query (seperti *"Apa gap penelitiannya?"*) dengan Vektor *Child Chunks*. Fungsi ini mengembalikan *Top-K* `parent_id` dengan nilai tertinggi.
3.  **Ingestion Orchestrator (`ingestion.py`):** Alur ekstraksi dimodifikasi. Sistem mengambil `parent_id` hasil *search*, menggunakan *Set* `set()` Python untuk menghilangkan *ID* yang tumpang tindih antar-pilar, mengambil teks asli dari *Parent Chunks*, menyatukannya (*join* dengan `\n\n`), lalu mengirimkannya ke LLM (Qwen3-Max) dengan *prompt* yang sudah dimodifikasi agar fokus pada teks "Stuffed" tersebut.

# 12. Tabel Eksekusi Task

| Check Box | Fase | Task | Detail Task | Deskripsi | Acceptance Criteria |
| :---: | :---: | :---: | :--- | :--- | :--- |
| [x] | 1 - Persiapan | Setup Struktur Direktori | Buat folder `Markdown/small-to-big-retrieval/Execution-Log` | Membuat struktur folder pelaporan agar eksekusi rapi. | Folder terbentuk dan siap digunakan. |
| [x] | 2 - Chunking | Buat Fungsi Multi-Level Chunking | Tambahkan fungsi `generate_parent_child_chunks()` di `smart_chunker.py` | Modifikasi pemotong agar menghasilkan dua himpunan data: `parent_chunks` (teks utuh, ID unik) dan `child_chunks` (potongan kecil, membawa ID parent-nya). | Fungsi dapat mengembalikan dua List: *Parents* (Teks besar) dan *Children* (Teks kecil + ID Parent). |
| [x] | 3 - Search Engine | Buat Modul `semantic_retriever.py` | Implementasikan Batch Embedding & Kalkulasi Cosine Similarity | Buat sebuah *class/function* di file baru yang mengambil list *Child Chunks*, meng-*embed* seluruhnya via API, lalu menghitung kedekatan matriks *Numpy* (Dot Product) terhadap 5 kalimat pertanyaan pilar. | Modul bisa menerima array kalimat, menghubungi Gemini/OpenAI API, dan mengembalikan `parent_id` dengan skor tertinggi. |
| [x] | 4 - Deduplikasi | Terapkan Logika Set Unik di `ingestion.py` | Proses hasil kembalian dari `semantic_retriever.py` | Tampung hasil *Top-K parent_id* dari setiap pertanyaan pilar, lalu masukkan ke dalam `set()` Python untuk menghilangkan duplikasi paragraf yang akan dikirim ke LLM. | Output dari proses ini adalah List berisi sekumpulan *Parent Chunk* yang 100% unik tanpa duplikat teks. |
| [x] | 5 - Orchestration | Refraktor `extract_pillars_with_llm` | Modifikasi pipeline pemrosesan *prompt* LLM | Ubah *prompt* awal di `ingestion.py` agar tidak lagi menerima 25.000 karakter *raw markdown*, melainkan menerima teks gabungan (*stuffed*) dari *Parent Chunks* yang unik. | Kode tereksekusi tanpa kesalahan; LLM mampu mengekstrak 5 pilar JSON hanya dari teks konteks gabungan yang pendek. |
| [x] | 6 - Pengujian | End-to-End Latency & Accuracy Test | Jalankan alur penuh (PDF -> LLM -> Neo4j) dengan pengukur waktu (`time.time()`). | Uji pemrosesan terhadap minimal 1 hingga 3 jurnal PDF untuk memastikan alur (dari pemotongan, pencarian lokal, hingga pembuatan Node di Neo4j) selesai dalam < 50 detik. | Sistem berhasil menghasilkan *Node* dan *Edge* yang valid di Neo4j tanpa *crash*, waktu di bawah 50s per dokumen. |
| [x] | 7 - Dokumentasi | Finalisasi Log Eksekusi | Susun laporan di `Execution-Log` | Catat *before-after* performa, kendala yang ditemukan (jika ada *rate limit*), dan solusi yang diimplementasikan selama koding berjalan. | Terdapat file laporan MarkDown yang dapat dibaca di folder `Execution-Log`. |
| [x] | 8 - Metriks & Reporting | Penambahan Metriks Pemantauan dan Laporan Final | Modifikasi `process_sync_pdf` untuk melacak `time.time()` granular dan menghitung `usage.prompt_tokens`. Susun Matriks Performa Standar Industri dan buat Laporan Final. | Matriks pemantauan mencetak latensi dan token per dokumen. Laporan Final tercetak rapi di folder `Report/`. |
