API event mitra
Consumer menerima doc.uploaded tanpa aiDocumentId. Aceh RAG membuat ID tersebut setelah menyimpan baris documents dan mengembalikannya dalam event status tahapan; ID ini bukan persyaratan input.
Halaman ini adalah kontrak mandiri untuk integrasi backend mitra di /docs/api/partner-events.
Consumer adalah backend mitra sisi server yang mengelola crawler, DMS, key object storage, dan pelacakan job Kafka. Ini bukan API browser. Jangan kirim event ini dari kode browser.
Transportasi dan kepemilikan
Aceh RAG mengonsumsi event pengunggahan dokumen dari Kafka, membaca byte yang dirujuk dari object storage yang dikonfigurasi, menjalankan pipeline parse, OCR, chunk, embed, dan index yang ada, lalu mengirim stage event kembali ke Kafka untuk pelacakan job mitra.
| Arah | Topic atau map default | Pemilik yang menghasilkan | Pemilik yang mengonsumsi | Tujuan |
|---|---|---|---|---|
| Mitra ke Aceh RAG | doc.uploaded | Backend mitra | Kafka worker Aceh RAG | Memberi tahu Aceh RAG bahwa byte tersedia di bawah storageKey. |
| Aceh RAG ke mitra | kafka.stage_topics | Kafka worker Aceh RAG | Backend mitra | Melaporkan kemajuan tahapan per dokumen dan hasil terminal pada destination yang dipilih oleh key routing internal. |
Kafka dikendalikan oleh konfigurasi dan default-nya nonaktif untuk proses lokal, test, dan CI. Deployment production harus mengaktifkan dan menjalankan proses Kafka worker khusus secara eksplisit (produksi: worker dibuat ulang pada image API 2cc9a417 — audit produksi firstmate 2026-10-07). Nama topic, daftar broker, dan consumer group adalah nilai konfigurasi, jadi konfirmasikan nilai aktif untuk environment target sebelum menghubungkan.
| Pengaturan | Default | Konfirmasi deployment yang diperlukan |
|---|---|---|
kafka.enabled | false | Pastikan ini aktif di tempat integrasi mitra seharusnya berjalan. |
kafka.bootstrap_servers | localhost:9092 | Konfirmasikan daftar broker yang sebenarnya. |
kafka.doc_uploaded_topic | doc.uploaded | Konfirmasikan nama input topic. |
kafka.stage_topics | Map stage-ke-topic lengkap | Konfirmasikan setiap rute dan sediakan setiap destination. |
kafka.consumer_group | aceh-rag-ingestion | Konfirmasikan consumer group yang ditetapkan untuk Aceh RAG. |
kafka.default_source_version | m2-kafka | Pengaturan legacy; ingress memakai sourceVersion jika tersedia, selain itu uploadedAt yang tetap wajib. |
kafka.max_retries | 3 | Konfirmasikan anggaran retry untuk kegagalan fetch dan pipeline yang bersifat sementara. |
kafka.retry_backoff_seconds | 2.0 | Konfirmasikan jeda retry dasar. |
Kontrak ini tidak menentukan pengaturan keamanan broker. Gunakan profil koneksi Kafka yang dideploy oleh tim platform.
Map stage-topic default
kafka.stage_topics adalah mapping lengkap: konfigurasi worker gagal sebelum startup jika rute hilang, kosong, atau menggunakan key yang tidak didukung. Mitra memiliki nilai DMS sebenarnya dan dapat menimpa default ini tanpa mengubah publisher.
| Kondisi event | Key konfigurasi internal | Wire stage/status | Progress | Tujuan default |
|---|---|---|---|---|
| Byte sumber diambil | fetched | extracting/done | 10 | doc.extracted |
| Ekstraksi / parse selesai | parsed | extracting/done | 25 | doc.extracted |
| Percobaan OCR aktual dimulai | ocr_pending | ocr_pending/started | 25 | doc.ocr.pending |
| Percobaan OCR selesai | ocr | ocr_done/done | 45 | doc.ocr.done |
| Chunk dibuat | chunked | chunking/done | 65 | doc.chunked |
| Embedding disimpan | embedded | embedding/done | 85 | doc.embedded |
| Index berhasil atau perlu ditinjau | indexed | indexed/done atau indexed/needs_review | 100 | doc.indexed |
| Kegagalan terminal | failed | failed/failed | 100 | doc.indexed |
| No-op sumber terstruktur atau duplikat | skipped | indexed/skipped | 100 | doc.indexed |
Mitra harus membuat enam destination DMS yang disepakati dan memberikan izin producer kepada worker sebelum deployment; pembuatan otomatis broker tidak diasumsikan.
Event input doc.uploaded
Terbitkan satu objek JSON per dokumen ke input topic yang dikonfigurasi. Field payload menggunakan camelCase.
Tabel field
| Field | Wajib? | Type | Validasi, default, dan makna |
|---|---|---|---|
jobId | Ya | UUID string | Identitas IngestionJob mitra. Digunakan untuk idempotensi redelivery, key status-event Kafka, dan korelasi. |
documentId | Ya | UUID string | Identitas dokumen/versi mitra. Identitas lock dan source_key default jika sourceKey tidak tersedia. |
registryId | Tidak | UUID string | null | Identitas registry mitra. Jika tidak dikirim atau bernilai null pada event yang valid, memakai documentId yang valid; UUID non-null yang tidak valid ditolak. |
storageKey | Ya | string | Harus tidak kosong. Key object storage yang dibaca Aceh RAG untuk mengambil byte dokumen. |
fileName | Tidak | string | null | Nama file asli yang digunakan sebagai judul dokumen setelah spasi di awal/akhir dihapus dan whitespace berurutan diringkas menjadi satu spasi. Untuk ingestion baru, jika tidak dikirim, kosong, atau bukan string, judul memakai nama file yang diturunkan dari storage key; refresh byte identik mempertahankan judul tersimpan. Field ini tidak mengubah storage key atau nama file untuk parser; namanya masih sementara menunggu kesepakatan mitra (kafka_ingestion.PARTNER_FILENAME_FIELD). |
mimeType | Ya | string | MIME type yang didukung. Harus sesuai dengan ekstensi storageKey. |
collectionId | Ya | UUID string | Identitas collection mitra. Disimpan sebagai metadata sumber. |
collectionName | Ya | string | Dinormalisasi menjadi JDIH, OpenData, PPID, SatuData, atau upload. Mapping otoritatif masih menunggu persetujuan mitra. |
uploadedBy | Ya | UUID string | Identitas aktor atau layanan mitra. Disimpan sebagai metadata sumber. |
uploadedAt | Ya | ISO-8601 timestamp | Waktu upload dengan timezone; source_version default jika sourceVersion tidak tersedia. Tetap wajib. |
sourceType | Tidak | string enum | null | Harus sesuai dengan nama collection sumber yang dikenal; "JDIH" eksplisit diterima untuk collection wilayah seperti "Provinsi Aceh". |
format | Tidak | string | null | Hanya untuk kompatibilitas legacy. Jika non-null, harus sesuai dengan mimeType dan storageKey. |
sourceSystem | Tidak | string | null | Label sistem sumber opsional yang tidak kosong, misalnya jdih-portal atau ppid-crawler. Jika non-null, disimpan sebagai metadata sumber. |
skpaUnit | Tidak | string | null | Unit SKPA atau organisasi opsional yang tidak kosong. Jika non-null, disimpan sebagai metadata sumber. |
classification | Tidak | string | null | Label klasifikasi opsional yang tidak kosong. Jika non-null, disimpan sebagai metadata sumber. |
category | Tidak | string | null | Label kategori mitra opsional yang tidak kosong. Jika non-null, disimpan sebagai metadata sumber. |
sourceKey | Tidak | string | null | Identitas sumber tetap untuk sinkronisasi terjadwal, misalnya jdih-<portal uuid>. Menggantikan documentId untuk source_key dan identitas lock. |
sourceVersion | Tidak | string | null | Menggantikan uploadedAt sebagai source_version; nilai baru menyimpan file yang berubah sebagai edisi baru. |
sourceMetadata | Tidak | object | null | Provenance sumber bernilai string yang digabung ke metadata.source, termasuk tahun, nomor, wilayah, dan URL untuk sitasi. |
fileName dan ketiga field sinkronisasi dibaca secara toleran. Untuk sourceKey, sourceVersion, dan setiap nilai sourceMetadata, nilai bukan string, kosong, null, dan "-" setelah trim dianggap tidak tersedia; sourceMetadata bukan objek juga diabaikan. Key yang tidak tersedia tidak menghapus nilai tersimpan. Field opsional lainnya menerima nilai yang tidak dikirim atau null; nilai non-null harus string tidak kosong dan memenuhi batasannya. registryId default ke documentId, sourceType dan format yang tidak tersedia diinferensikan, dan metadata opsional yang tidak tersedia tidak disimpan.
Hanya key deskriptif sourceMetadata berikut yang diterima: judul, tahun, nomor, jenis, wilayah, status_berlaku, bidang_hukum, url, pdf_url, detail_url, bentuk_singkat, catalogue_id, catalogue_source, content_type, year. Key lain dibuang dengan satu peringatan terstruktur berisi nama key saja, tanpa nilainya. Field ini tidak dapat mengatur workspace, penggantian dokumen, penyimpanan, transport, atau akses, baik saat ingestion maupun refresh byte identik; field transport/akses tetap diprioritaskan.
Field tingkat atas yang tidak dikenal diabaikan oleh consumer saat ini dan tidak disimpan sebagai metadata sumber. Jangan menaruh state job mitra yang wajib hanya di field yang tidak dikenal.
Nilai source type
type DocumentSourceType = "JDIH" | "OpenData" | "PPID" | "SatuData" | "upload";
Tabel field di atas menjelaskan penentuan source type dan aturan kompatibilitas sourceType eksplisit dengan nama collection wilayah. Source type yang bertentangan membuat event malformed dan menghasilkan stage event failed.
Nilai format
Format dokumen yang didukung adalah:
type DocumentFormat = "txt" | "md" | "html" | "csv" | "docx" | "xlsx" | "pptx" | "pdf" | "image";
format adalah petunjuk, bukan byte dokumen. Derivasi nama file Kafka saat ini menggunakan petunjuk tersebut untuk memaksa ekstensi berikut ketika storageKey tidak memiliki ekstensi:
Petunjuk format | Ekstensi turunan |
|---|---|
pdf | .pdf |
docx | .docx |
xlsx | .xlsx |
csv | .csv |
html | .html |
image | .png |
Untuk txt, md, dan pptx ketika format tidak disertakan, pastikan basename storageKey sudah memiliki ekstensi yang dapat di-parse, seperti records/qanun.txt, records/page.md, atau records/slides.pptx. Key tanpa ekstensi hanya valid jika mimeType yang didukung dan dideklarasikan menyelesaikan formatnya. Ekstensi tidak didukung yang tidak kosong, MIME yang tidak didukung, atau MIME/ekstensi/format legacy yang bertentangan ditolak saat ingress dan menghasilkan failed/failed berkorelasi sebelum fetch OBS apa pun.
Event wajib
{
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"documentId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"storageKey": "documents/a1b2c3d4e5f6/perda-no-12-2026.pdf",
"mimeType": "application/pdf",
"collectionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"collectionName": "JDIH",
"uploadedBy": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"uploadedAt": "2026-08-05T09:12:03.412Z"
}
Event ini valid. Aceh RAG membaca objek OBS di bucket yang dikonfigurasi menggunakan storageKey tanpa modifikasi, menormalisasi JDIH, dan menyimpan source_key=documentId serta source_version=uploadedAt.
Contoh event lengkap
{
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"documentId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"storageKey": "jdih/2026/qanun-aceh-001",
"mimeType": "application/pdf",
"collectionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"collectionName": "JDIH",
"uploadedBy": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"uploadedAt": "2026-07-23T10:15:30Z",
"sourceType": "JDIH",
"format": "pdf",
"registryId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"sourceSystem": "jdih-portal",
"skpaUnit": "dinas-keuangan",
"classification": "public",
"category": "qanun",
"sourceVersion": "2026-07-23T10:15:30Z"
}
Karena mimeType dan format legacy sama-sama pdf, basename storageKey tanpa ekstensi diperlakukan sebagai nama file PDF untuk dispatch parser.
Validasi input dan perilaku kegagalan
Pesan malformed dicatat dan diperlakukan sebagai terminal untuk penanganan offset. Event failed/failed hanya diterbitkan jika jobId dan registryId yang valid dapat dipulihkan; jika registryId tidak dikirim, documentId yang valid dapat digunakan. Pemulihan pesan malformed ini berbeda dari parsing yang berhasil: registry ID null/tidak valid yang dikirim secara eksplisit tidak diganti. Kegagalan tanpa identitas yang dapat dikorelasikan tidak menerbitkan status dan memerlukan rekonsiliasi operator.
| Kegagalan | Coba lagi? | Stage event | Interpretasi mitra |
|---|---|---|---|
JSON tidak valid, payload bukan objek, atau jobId hilang/tidak valid | Tidak | Tidak ada status yang dapat dikorelasikan | Periksa log producer dan rekonsiliasikan job. |
Input wajib, registryId eksplisit, mapping collection, atau string opsional tidak valid | Tidak | failed/failed hanya dengan ID job dan registry yang dapat dipulihkan; aiDocumentId: null | Perbaiki dan terbitkan ulang; rekonsiliasikan jika event tidak dapat dikorelasikan. |
| Fetch object storage kehabisan retry | Dibatasi secara internal | failed/failed | Perbaiki ketersediaan objek sebelum menerbitkan ulang. |
| Pipeline kehabisan retry setelah fetch | Dibatasi secara internal | extracting/done, lalu failed/failed; pembaruan awal OCR mungkin juga sudah terjadi | Perbaiki penyebab sebelum menerbitkan ulang. |
Pesan malformed permanen gagal dengan cepat. Error object storage dan pipeline yang bersifat sementara di-retry dengan exponential backoff menggunakan pengaturan retry yang dikonfigurasi. Percobaan retry tidak menghasilkan stage event retry terpisah.
Identitas dokumen, versioning, dan idempotensi
Gunakan jobId sebagai identitas job lintas layanan dan documentId sebagai identitas dokumen sumber. aiDocumentId hanya dibuat ketika Aceh RAG membuat row documents-nya.
Aceh RAG menggunakan dua identitas yang berkaitan:
- Deteksi duplikat dalam proses menggunakan
jobId. - Provenance tersimpan memakai
source_key = sourceKey or documentIddansource_version = sourceVersion or uploadedAt. Upload admin tanpa field baru tetap memakai dedup SHA-256 + versi dan judulfileName.
Aturan mitra:
- Terbitkan ulang
jobIdyang sama hanya untuk redelivery atau retry IngestionJob tersebut. - Konfirmasikan apakah
documentIddanuploadedAtimmutable untuk satu versi dokumen sebelum deployment production. - Lacak
IngestionJobsecara eksklusif berdasarkanjobId.
jobId duplikat dalam proses worker yang sama tidak diproses ulang dan tidak menerbitkan stage event baru. Migration 0007 memberlakukan (sourceType, source_version, source_key); migration 0006 menjaga checksum + versi secara terpisah. Dengan sourceKey, row corpus bersama yang selesai (indexed, needs_review, atau archived) dengan type/key/byte sama pada versi mana pun dilewati tanpa ingestion ulang, tetapi sourceMetadata digabung, judul fileName yang valid diperbarui, dan label pencarian judul dibangun ulang. Identitas, chunk, kolom akses, dan metadata lain tidak berubah. Hasil terminal adalah indexed/skipped, memakai aiDocumentId lama dan chunkCount: 0. Byte berubah mengikuti aturan lama: versi baru membuat edisi baru; identitas sama dikoreksi di tempat. Tidak ada tautan supersede otomatis. Lock memakai doc-uploaded:<sourceKey or documentId>; lock dan state job default tetap dalam proses.
Kirim tahun/wilayah sebagai string untuk filter dan sedikitnya satu dari pdf_url, detail_url, atau url untuk URL sitasi bibliografi. judul hanya metadata, bukan judul tampilan. Kontrak backend §3 memuat satu query read-only untuk cek keberadaan ("sourceType" dan metadata->>'source_key' tingkat atas, semua versi); tidak ada endpoint baru. Deploy kontrak AI-engine ini sebelum menjalankan sinkronisasi: worker lama mengabaikan field baru. Consumer wajib membedakan status, bukan topic atau progress; hanya indexed/done berarti sukses.
Event output StageStatusEvent
Serializer v1 selalu mengirim seluruh sepuluh key envelope di bawah, termasuk nilai nullable. Key record Kafka adalah jobId dalam UTF-8. Routing memakai key konfigurasi internal pada map stage-topic, bukan hanya wire stage: dua pembaruan extracting/done mewakili fetch dan parse, dengan progress 10 dan 25. Tidak ada urutan total lintas topic.
Skema JSON
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "StageStatusEvent",
"type": "object",
"additionalProperties": false,
"required": [
"v",
"jobId",
"registryId",
"aiDocumentId",
"stage",
"status",
"progress",
"error",
"meta",
"ts"
],
"properties": {
"v": {
"const": 1
},
"jobId": {
"type": "string",
"minLength": 1
},
"registryId": {
"type": [
"string",
"null"
]
},
"aiDocumentId": {
"type": [
"string",
"null"
]
},
"stage": {
"type": "string",
"enum": [
"extracting",
"ocr_pending",
"ocr_done",
"chunking",
"embedding",
"indexing",
"indexed",
"failed"
]
},
"status": {
"type": "string",
"enum": [
"started",
"done",
"failed",
"skipped",
"needs_review"
]
},
"progress": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"error": {
"type": [
"string",
"null"
]
},
"meta": {
"type": [
"object",
"null"
],
"properties": {
"ocrConfidence": {
"type": "number"
},
"chunkCount": {
"type": "integer",
"minimum": 0
}
}
},
"ts": {
"type": "string",
"format": "date-time"
}
},
"allOf": [
{
"if": {
"anyOf": [
{
"properties": {
"stage": {
"const": "failed"
},
"status": {
"const": "failed"
}
}
},
{
"properties": {
"stage": {
"const": "indexed"
},
"status": {
"enum": [
"done",
"skipped",
"needs_review"
]
}
}
}
]
},
"then": {
"properties": {
"registryId": {
"type": "string",
"minLength": 1
}
}
}
}
]
}
Makna field
| Field | Tipe | Makna |
|---|---|---|
v | integer | Versi envelope, selalu 1. |
jobId | string | Korelasi IngestionJob mitra dan key Kafka. |
registryId | string | null | Korelasi registry mitra; input valid memakai documentId jika tidak dikirim atau bernilai null; pemulihan pesan malformed hanya memakai fallback tersebut jika tidak dikirim. Wajib non-null untuk event terminal. |
aiDocumentId | string | null | documents.id AI-engine; null sebelum persistensi atau pada kegagalan sebelum persistensi. |
stage | enum | Wire stage mitra, berbeda dari key routing internal. |
status | enum | Hasil wire; progress 100 saja tidak berarti berhasil. |
progress | integer | Persentase milestone 0–100; hasil terminal memakai 100. |
error | string | null | Detail kegagalan; null jika tidak ada error. |
meta | object | null | Metadata, null jika tidak berlaku. ocrConfidence dan chunkCount berada di dalamnya, bukan key tingkat atas. |
ts | string | Timestamp producer ISO-8601 dengan timezone. |
meta.ocrConfidence adalah confidence per-chunk minimum di semua chunk yang memilikinya, termasuk chunk parked (non-index) (_emit_success_stages, src/aceh_rag/kafka/kafka_ingestion.py:947-967) — pindaian dengan .95 terindex dan .40 parked memancarkan .40. Percobaan OCR yang kembali ke prosa native dapat ditutup dengan metadata null. meta.chunkCount berisi total chunk untuk chunking, jumlah chunk terindex untuk embedding dan indexing terminal, serta 0 untuk skip sumber terstruktur atau duplikat byte. Review dapat mempertahankan sebagian chunk terindex; jumlahnya tidak selalu nol.
Tahapan
type Stage = "extracting" | "ocr_pending" | "ocr_done" | "chunking" | "embedding" | "indexing" | "indexed" | "failed";
extracting mewakili penyelesaian fetch dan parse. ocr_pending memulai percobaan OCR aktual; ocr_done menutupnya. chunking dan embedding melaporkan milestone selesai. indexing diterima oleh envelope v1 tetapi tidak diterbitkan adapter milestone saat ini. indexed membawa keberhasilan, skip, atau review; failed membawa kegagalan terminal.
Makna status
type StageStatus = "started" | "done" | "failed" | "skipped" | "needs_review";
| Status | Terminal? | Makna |
|---|---|---|
started | Tidak | Adapter saat ini hanya mengirim ocr_pending/started sebelum OCR aktual, dengan progress 25 serta aiDocumentId, error, dan meta null. |
done | Hanya pada indexed | Milestone selesai; hanya indexed/done menandai penyelesaian berhasil. |
failed | Ya | failed/failed; tampilkan error dan perbaiki sebelum menerbitkan ulang. |
skipped | Ya | indexed/skipped; no-op sumber terstruktur atau duplikat byte. |
needs_review | Ya | indexed/needs_review; memerlukan tinjauan manusia, termasuk scan yang sebagian diindex. Jangan tandai selesai. |
Contoh serialisasi berhasil
{
"v": 1,
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"registryId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"aiDocumentId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"stage": "indexed",
"status": "done",
"progress": 100,
"error": null,
"meta": {
"chunkCount": 12
},
"ts": "2026-10-07T09:12:03Z"
}
Lifecycle berurutan
Penyelesaian fetch langsung diterbitkan. Awal OCR diterbitkan sekali per percobaan aktual, termasuk retry, sebelum pekerjaan OCR. Pembaruan penyelesaian parse dan milestone lain diterbitkan setelah pipeline selesai; awal OCR dapat mendahului pembaruan penyelesaian parse. Lintas topic, rekonsiliasikan berdasarkan job dan progress tanpa mengasumsikan urutan global.
| Jalur | Pembaruan wire |
|---|---|
| Born-digital berhasil | extracting/done (10), extracting/done (25), chunking/done (65), embedding/done (85), indexed/done (100) |
| OCR berhasil | extracting/done (10), ocr_pending/started (25), extracting/done (25), ocr_done/done (45), chunking/done (65), embedding/done (85), indexed/done (100) |
| Review OCR | Milestone OCR yang sama, diakhiri indexed/needs_review (100) |
| Fetch gagal | failed/failed (100); tanpa pembaruan penyelesaian fetch |
| Pipeline gagal setelah fetch | extracting/done (10), pembaruan awal OCR jika ada, failed/failed (100) |
| Skip sumber terstruktur | indexed/skipped (100), dengan aiDocumentId null |
| Skip duplikat konten | extracting/done (10), indexed/skipped (100), dengan aiDocumentId yang sudah ada |
Semantik retry, deferred, skipped, dan publish-error
State ini memengaruhi pelacakan job mitra meskipun tidak semuanya terlihat sebagai event output.
| Hasil consumer | Stage event? | Penanganan offset | Aturan pelacakan mitra |
|---|---|---|---|
indexed | Ya, berakhir dengan indexed/done | Di-commit | Tandai versi dokumen selesai. |
needs_review | Ya, berakhir dengan indexed/needs_review | Di-commit | Tandai sebagai terminal perlu ditinjau, bukan selesai. |
error | Ya, biasanya failed/failed | Di-commit | Tandai gagal untuk delivery ini. Terbitkan ulang hanya setelah memperbaiki penyebabnya. |
skipped | indexed/skipped untuk no-op sumber terstruktur atau duplikat konten; tidak ada event baru untuk redelivery yang sudah diproses | Di-commit | Perlakukan event indexed/skipped sebagai terminal. Untuk redelivery yang sudah dilewati oleh penyimpanan deduplikasi dalam proses, pertahankan status dari delivery sebelumnya. |
deferred | Tidak ada stage event keberhasilan | Tidak segera di-commit untuk record tersebut | Worker lain memegang lock per dokumen. Pertahankan job sebagai pending, rekonsiliasikan sebagai dokumen tanpa status terminal, dan jalankan ulang secara eksplisit sesuai kebijakan operasional yang disepakati. |
deferred adalah hasil internal non-terminal untuk kontensi lock. Hasil ini tidak menerbitkan stage keberhasilan dan melewati commit segera untuk record yang mengalami kontensi. Pemrosesan consumer saat ini tidak memberikan jaminan redelivery pada partition yang sama: jika record berikutnya dari partition yang sama di-commit, commit tersebut dapat memajukan offset partition melewati record yang deferred. Sistem mitra harus merekonsiliasi dokumen yang tidak mencapai indexed/done, indexed/needs_review, atau failed/failed, lalu menjalankan ulang secara eksplisit berdasarkan kebijakan operasional yang disepakati.
Jika penerbitan stage-status event itu sendiri gagal, Aceh RAG mencatatnya dan melanjutkan pemrosesan dokumen. Dokumen tetap dapat disimpan dan ditandai telah diproses meskipun satu atau beberapa status event terlewat. Consumer mitra sebaiknya membuat pembaruan stage idempotent dan menyediakan jalur rekonsiliasi operator untuk dokumen yang telah diproduksi tetapi tidak memiliki status event terbaru.
Checklist producer untuk doc.uploaded
Sebelum menghasilkan event:
- Konfirmasikan input topic aktif, map stage-topic lengkap, consumer group, daftar broker, dan deployment Kafka worker bersama tim platform.
- Pastikan byte objek tersedia di
storageKeysebelum menerbitkan event. - Gunakan UUID
jobIdyang stabil untuk redelivery IngestionJob yang sama, dan UUIDdocumentIduntuk versi dokumen. - Kirim
uploadedAtyang menyertakan timezone dan jangan mengganti salah satu ID mitra dengan ID AI-engine di masa depan. - Kirim
mimeTypeyang sesuai dengan ekstensistorageKeydancollectionNameyang dikenali. - Konfirmasikan bucket OBS yang dikonfigurasi, principal IAM worker, dan prefix key yang diizinkan; tidak ada presigned URL atau lookup database mitra yang digunakan.
- Hilangkan field opsional yang tidak dikenal, alih-alih mengirim
nullatau string kosong. - Simpan event asli lengkap di log mitra agar delivery yang malformed atau gagal dapat diperbaiki dan diterbitkan ulang.
Checklist consumer untuk StageStatusEvent
Saat mengonsumsi status event:
- Jadikan state job mitra sepenuhnya bergantung pada
jobId.registryIdmengorelasikan registry mitra;aiDocumentIdyang nullable adalah provenance AI-engine, bukan key pembaruan. - Perlakukan semua field dalam skema sebagai wajib. Simpan field nullable sebagai nullable, bukan sebagai field yang hilang.
- Terapkan pembaruan secara idempotent berdasarkan envelope lengkap, termasuk progress; dua milestone
extracting/doneyang valid memiliki progress 10 dan 25. - Pertahankan urutan dalam setiap destination topic, tetapi toleransi stage perantara yang hilang dan jangan mengasumsikan urutan total di seluruh stage topic karena penerbitan stage bersifat best-effort.
- Tandai
indexed/donesebagai selesai. - Tandai
indexed/needs_reviewsebagai terminal yang perlu ditinjau dan arahkan ke tinjauan manusia. - Tandai
failed/failedsebagai gagal dan tampilkanerrorkepada operator. - Tandai
indexed/skippedsebagai status no-op terminal. Jangan menunggu status event baru setelah redelivery duplikat yang sudah diproses; status delivery sebelumnya adalah otoritatif untuk versi tersebut. - Rekonsiliasikan job pending yang tidak memiliki status event terminal dan jalankan ulang secara eksplisit sesuai kebijakan operasional yang disepakati; jangan mengandalkan kontensi lock deferred untuk mengirim ulang record Kafka asli.