Lewati ke konten utama

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.

ArahTopic atau map defaultPemilik yang menghasilkanPemilik yang mengonsumsiTujuan
Mitra ke Aceh RAGdoc.uploadedBackend mitraKafka worker Aceh RAGMemberi tahu Aceh RAG bahwa byte tersedia di bawah storageKey.
Aceh RAG ke mitrakafka.stage_topicsKafka worker Aceh RAGBackend mitraMelaporkan 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.

PengaturanDefaultKonfirmasi deployment yang diperlukan
kafka.enabledfalsePastikan ini aktif di tempat integrasi mitra seharusnya berjalan.
kafka.bootstrap_serverslocalhost:9092Konfirmasikan daftar broker yang sebenarnya.
kafka.doc_uploaded_topicdoc.uploadedKonfirmasikan nama input topic.
kafka.stage_topicsMap stage-ke-topic lengkapKonfirmasikan setiap rute dan sediakan setiap destination.
kafka.consumer_groupaceh-rag-ingestionKonfirmasikan consumer group yang ditetapkan untuk Aceh RAG.
kafka.default_source_versionm2-kafkaPengaturan legacy; ingress memakai sourceVersion jika tersedia, selain itu uploadedAt yang tetap wajib.
kafka.max_retries3Konfirmasikan anggaran retry untuk kegagalan fetch dan pipeline yang bersifat sementara.
kafka.retry_backoff_seconds2.0Konfirmasikan 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 eventKey konfigurasi internalWire stage/statusProgressTujuan default
Byte sumber diambilfetchedextracting/done10doc.extracted
Ekstraksi / parse selesaiparsedextracting/done25doc.extracted
Percobaan OCR aktual dimulaiocr_pendingocr_pending/started25doc.ocr.pending
Percobaan OCR selesaiocrocr_done/done45doc.ocr.done
Chunk dibuatchunkedchunking/done65doc.chunked
Embedding disimpanembeddedembedding/done85doc.embedded
Index berhasil atau perlu ditinjauindexedindexed/done atau indexed/needs_review100doc.indexed
Kegagalan terminalfailedfailed/failed100doc.indexed
No-op sumber terstruktur atau duplikatskippedindexed/skipped100doc.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​

FieldWajib?TypeValidasi, default, dan makna
jobIdYaUUID stringIdentitas IngestionJob mitra. Digunakan untuk idempotensi redelivery, key status-event Kafka, dan korelasi.
documentIdYaUUID stringIdentitas dokumen/versi mitra. Identitas lock dan source_key default jika sourceKey tidak tersedia.
registryIdTidakUUID string | nullIdentitas registry mitra. Jika tidak dikirim atau bernilai null pada event yang valid, memakai documentId yang valid; UUID non-null yang tidak valid ditolak.
storageKeyYastringHarus tidak kosong. Key object storage yang dibaca Aceh RAG untuk mengambil byte dokumen.
fileNameTidakstring | nullNama 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).
mimeTypeYastringMIME type yang didukung. Harus sesuai dengan ekstensi storageKey.
collectionIdYaUUID stringIdentitas collection mitra. Disimpan sebagai metadata sumber.
collectionNameYastringDinormalisasi menjadi JDIH, OpenData, PPID, SatuData, atau upload. Mapping otoritatif masih menunggu persetujuan mitra.
uploadedByYaUUID stringIdentitas aktor atau layanan mitra. Disimpan sebagai metadata sumber.
uploadedAtYaISO-8601 timestampWaktu upload dengan timezone; source_version default jika sourceVersion tidak tersedia. Tetap wajib.
sourceTypeTidakstring enum | nullHarus sesuai dengan nama collection sumber yang dikenal; "JDIH" eksplisit diterima untuk collection wilayah seperti "Provinsi Aceh".
formatTidakstring | nullHanya untuk kompatibilitas legacy. Jika non-null, harus sesuai dengan mimeType dan storageKey.
sourceSystemTidakstring | nullLabel sistem sumber opsional yang tidak kosong, misalnya jdih-portal atau ppid-crawler. Jika non-null, disimpan sebagai metadata sumber.
skpaUnitTidakstring | nullUnit SKPA atau organisasi opsional yang tidak kosong. Jika non-null, disimpan sebagai metadata sumber.
classificationTidakstring | nullLabel klasifikasi opsional yang tidak kosong. Jika non-null, disimpan sebagai metadata sumber.
categoryTidakstring | nullLabel kategori mitra opsional yang tidak kosong. Jika non-null, disimpan sebagai metadata sumber.
sourceKeyTidakstring | nullIdentitas sumber tetap untuk sinkronisasi terjadwal, misalnya jdih-<portal uuid>. Menggantikan documentId untuk source_key dan identitas lock.
sourceVersionTidakstring | nullMenggantikan uploadedAt sebagai source_version; nilai baru menyimpan file yang berubah sebagai edisi baru.
sourceMetadataTidakobject | nullProvenance 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 formatEkstensi 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.

KegagalanCoba lagi?Stage eventInterpretasi mitra
JSON tidak valid, payload bukan objek, atau jobId hilang/tidak validTidakTidak ada status yang dapat dikorelasikanPeriksa log producer dan rekonsiliasikan job.
Input wajib, registryId eksplisit, mapping collection, atau string opsional tidak validTidakfailed/failed hanya dengan ID job dan registry yang dapat dipulihkan; aiDocumentId: nullPerbaiki dan terbitkan ulang; rekonsiliasikan jika event tidak dapat dikorelasikan.
Fetch object storage kehabisan retryDibatasi secara internalfailed/failedPerbaiki ketersediaan objek sebelum menerbitkan ulang.
Pipeline kehabisan retry setelah fetchDibatasi secara internalextracting/done, lalu failed/failed; pembaruan awal OCR mungkin juga sudah terjadiPerbaiki 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:

  1. Deteksi duplikat dalam proses menggunakan jobId.
  2. Provenance tersimpan memakai source_key = sourceKey or documentId dan source_version = sourceVersion or uploadedAt. Upload admin tanpa field baru tetap memakai dedup SHA-256 + versi dan judul fileName.

Aturan mitra:

  • Terbitkan ulang jobId yang sama hanya untuk redelivery atau retry IngestionJob tersebut.
  • Konfirmasikan apakah documentId dan uploadedAt immutable untuk satu versi dokumen sebelum deployment production.
  • Lacak IngestionJob secara eksklusif berdasarkan jobId.

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​

FieldTipeMakna
vintegerVersi envelope, selalu 1.
jobIdstringKorelasi IngestionJob mitra dan key Kafka.
registryIdstring | nullKorelasi 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.
aiDocumentIdstring | nulldocuments.id AI-engine; null sebelum persistensi atau pada kegagalan sebelum persistensi.
stageenumWire stage mitra, berbeda dari key routing internal.
statusenumHasil wire; progress 100 saja tidak berarti berhasil.
progressintegerPersentase milestone 0–100; hasil terminal memakai 100.
errorstring | nullDetail kegagalan; null jika tidak ada error.
metaobject | nullMetadata, null jika tidak berlaku. ocrConfidence dan chunkCount berada di dalamnya, bukan key tingkat atas.
tsstringTimestamp 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";
StatusTerminal?Makna
startedTidakAdapter saat ini hanya mengirim ocr_pending/started sebelum OCR aktual, dengan progress 25 serta aiDocumentId, error, dan meta null.
doneHanya pada indexedMilestone selesai; hanya indexed/done menandai penyelesaian berhasil.
failedYafailed/failed; tampilkan error dan perbaiki sebelum menerbitkan ulang.
skippedYaindexed/skipped; no-op sumber terstruktur atau duplikat byte.
needs_reviewYaindexed/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.

JalurPembaruan wire
Born-digital berhasilextracting/done (10), extracting/done (25), chunking/done (65), embedding/done (85), indexed/done (100)
OCR berhasilextracting/done (10), ocr_pending/started (25), extracting/done (25), ocr_done/done (45), chunking/done (65), embedding/done (85), indexed/done (100)
Review OCRMilestone OCR yang sama, diakhiri indexed/needs_review (100)
Fetch gagalfailed/failed (100); tanpa pembaruan penyelesaian fetch
Pipeline gagal setelah fetchextracting/done (10), pembaruan awal OCR jika ada, failed/failed (100)
Skip sumber terstrukturindexed/skipped (100), dengan aiDocumentId null
Skip duplikat kontenextracting/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 consumerStage event?Penanganan offsetAturan pelacakan mitra
indexedYa, berakhir dengan indexed/doneDi-commitTandai versi dokumen selesai.
needs_reviewYa, berakhir dengan indexed/needs_reviewDi-commitTandai sebagai terminal perlu ditinjau, bukan selesai.
errorYa, biasanya failed/failedDi-commitTandai gagal untuk delivery ini. Terbitkan ulang hanya setelah memperbaiki penyebabnya.
skippedindexed/skipped untuk no-op sumber terstruktur atau duplikat konten; tidak ada event baru untuk redelivery yang sudah diprosesDi-commitPerlakukan event indexed/skipped sebagai terminal. Untuk redelivery yang sudah dilewati oleh penyimpanan deduplikasi dalam proses, pertahankan status dari delivery sebelumnya.
deferredTidak ada stage event keberhasilanTidak segera di-commit untuk record tersebutWorker 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:

  1. Konfirmasikan input topic aktif, map stage-topic lengkap, consumer group, daftar broker, dan deployment Kafka worker bersama tim platform.
  2. Pastikan byte objek tersedia di storageKey sebelum menerbitkan event.
  3. Gunakan UUID jobId yang stabil untuk redelivery IngestionJob yang sama, dan UUID documentId untuk versi dokumen.
  4. Kirim uploadedAt yang menyertakan timezone dan jangan mengganti salah satu ID mitra dengan ID AI-engine di masa depan.
  5. Kirim mimeType yang sesuai dengan ekstensi storageKey dan collectionName yang dikenali.
  6. Konfirmasikan bucket OBS yang dikonfigurasi, principal IAM worker, dan prefix key yang diizinkan; tidak ada presigned URL atau lookup database mitra yang digunakan.
  7. Hilangkan field opsional yang tidak dikenal, alih-alih mengirim null atau string kosong.
  8. 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:

  1. Jadikan state job mitra sepenuhnya bergantung pada jobId. registryId mengorelasikan registry mitra; aiDocumentId yang nullable adalah provenance AI-engine, bukan key pembaruan.
  2. Perlakukan semua field dalam skema sebagai wajib. Simpan field nullable sebagai nullable, bukan sebagai field yang hilang.
  3. Terapkan pembaruan secara idempotent berdasarkan envelope lengkap, termasuk progress; dua milestone extracting/done yang valid memiliki progress 10 dan 25.
  4. 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.
  5. Tandai indexed/done sebagai selesai.
  6. Tandai indexed/needs_review sebagai terminal yang perlu ditinjau dan arahkan ke tinjauan manusia.
  7. Tandai failed/failed sebagai gagal dan tampilkan error kepada operator.
  8. Tandai indexed/skipped sebagai status no-op terminal. Jangan menunggu status event baru setelah redelivery duplikat yang sudah diproses; status delivery sebelumnya adalah otoritatif untuk versi tersebut.
  9. 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.