Qdrant
Spesifikasi teknis Vector Database Qdrant di Nexa, arsitektur koleksi 'articles_dedup', metrik Cosine Similarity, HNSW indexing, integrasi deduplikasi semantik C2, dan sinkronisasi payload D1 Webhook.
Qdrant Vector Database (Semantic Engine)
Qdrant bertindak sebagai Database Vektor Terdistribusi (Vector Database) berkecepatan tinggi dalam ekosistem Nexa Intelligence Platform. Berjalan pada port REST 6333 dan gRPC 6334, Qdrant mengemban dua peran arsitektural utama:
- Gatekeeper Deduplikasi Semantik (
nexa-c2): Memfilter berita sindikasi dan artikel duplikat secara instan menggunakan pencarian Approximate Nearest Neighbor (ANN) dengan ambang batas Cosine Similarity $\ge 0.90$, sehingga menghemat hingga 40% biaya inferensi LLM. - Memori Semantik & RAG (AIOS / D1 AI Worker): Menjadi repositori vektor dense 768-dimensi yang dihubungkan secara deterministik (
UUIDv5) dengan dokumen berita di PostgreSQL dan Elasticsearch, serta menerima pembaruan status inferensi dari callback Webhook AIOS.
Spesifikasi Teknis Klaster & Koleksi
Klaster Qdrant dipasang secara terdedikasi pada klaster komputasi Nexa dengan parameter konfigurasi produksi berikut:
| Parameter Konfigurasi | Nilai Produksi | Keterangan Teknis |
|---|---|---|
| Cluster Endpoint (REST) | http://10.43.49.184:6333 | Endpoint komunikasi HTTP REST API untuk query dan health check. |
| Cluster Endpoint (gRPC) | http://10.43.49.184:6334 | Endpoint berkinerja tinggi untuk high-throughput bulk upsert dari C2. |
| Primary Collection | articles_dedup | Koleksi utama penyimpanan seluruh vektor artikel berita nasional. |
| Vector Dimension | 768 (Dense Vector) | Dimensi output model embedding teks berbahasa Indonesia (bge-large-id / indobert). |
| Distance Metric | Cosine | Metrik pengukuran sudut kosinus antar-vektor (skor rentang $0.0$ s.d. $1.0$). |
| Index Type | HNSW (Hierarchical Navigable Small World) | Graf vektor terindeks untuk pencarian ANN berskala jutaan titik. |
| HNSW $M$ | 16 | Jumlah koneksi dua-arah (bidirectional links) per simpul vektor. |
| HNSW $ef_construct$ | 128 | Kedalaman evaluasi tetangga terdekat saat konstruksi indeks vektor. |
| On-Disk Vectors & Payload | true | Vektor dan payload disimpan pada media NVMe untuk menekan pemakaian RAM. |
1. Alur Deduplikasi Semantik (nexa-c2)
Setiap artikel berita yang telah dibersihkan oleh worker C1 Content Sanitizer dipancarkan ke antrean Kafka queue-normalize-data. Worker C2 Semantic Deduplication mengonsumsi antrean tersebut dan mengeksekusi gatekeeping semantik ke Qdrant:
[Artikel Bersih C1] ──▶ [Text Embedding 768-D] ──▶ [Qdrant Cosine Similarity Search]
│
┌───────────────────────────────┴───────────────────────────────┐
▼ ▼
Skor Similarity >= 0.90 Skor Similarity < 0.90
[DUPLIKAT / SINDIKASI] [ARTIKEL UNIK]
│ │
├─▶ Tandai flag duplikat di PostgreSQL ├─▶ Upsert Point Vektor ke Qdrant
├─▶ Naikkan counter trending media ├─▶ Ingest ke Kafka `queue-ai-inference`
└─▶ Buang dari pipeline LLM mahal └─▶ Siap diekstraksi oleh Worker D1Logika Query Kemiripan Semantik (Python Client)
Worker C2 membatasi pemindaian kemiripan hanya pada jendela waktu 7 hari terakhir (published_at >= now-7d) untuk menghindari perbandingan dengan berita lama yang topiknya kebetulan serupa:
from qdrant_client import QdrantClient
from qdrant_client.http import models
client = QdrantClient(url="http://10.43.49.184:6333", timeout=5.0)
# Cari 1 tetangga terdekat (Nearest Neighbor) dengan threshold 0.90
search_results = client.search(
collection_name="articles_dedup",
query_vector=embedding_vector_768,
limit=1,
score_threshold=0.90,
query_filter=models.Filter(
must=[
models.FieldCondition(
key="published_at",
range=models.DatetimeRange(
gte="now-7d/d" # Jendela geser 7 hari kalender
)
)
]
)
)
if search_results:
duplicate_match = search_results[0]
print(f"Artikel Duplikat! Induk: {duplicate_match.payload['article_id']}, Skor: {duplicate_match.score:.4f}")
# Rute ke penanganan sindikasi (tanpa memanggil LLM)
else:
# Simpan vektor artikel unik ke Qdrant dan teruskan ke D1
client.upsert(
collection_name="articles_dedup",
points=[
models.PointStruct(
id=deterministic_point_id,
vector=embedding_vector_768,
payload={
"article_id": str(article_uuid),
"domain": domain_name,
"title": article_title,
"published_at": published_iso_date,
"status": "pending_ai"
}
)
]
)2. Sinkronisasi Deterministik UUIDv5 & Webhook D1
Salah satu aspek kritis arsitektur data Nexa adalah penjaminan konsistensi ID titik vektor antara worker C2 (Python/FastEmbed) dan worker D1 (Python/AIOS).
Skema ID Deterministik (UUIDv5)
Alih-alih membuat ID acak, titik vektor Qdrant dibuat menggunakan algoritma UUIDv5 berdasar DNS namespace dari document_id / article_id:
import uuid
# Formula ID Deterministik yang dipakai seragam di C2 dan D1:
point_id = str(uuid.uuid5(uuid.NAMESPACE_DNS, str(document_id)))Dengan mekanisme ini, worker D1 dapat langsung memperbarui (in-place update) metadata titik vektor di Qdrant tanpa perlu melakukan query pencarian ID terlebih dahulu.
Pembaruan Payload Asinkron dari Webhook D1
Setelah proses ekstraksi kognitif AIOS (klasifikasi 5 pilar taksonomi, sentimen, dan ekstraksi entitas) selesai, endpoint Webhook D1 (nexa-d1/webhook.py) mengeksekusi pembaruan payload titik vektor ke Qdrant:
import requests
from datetime import datetime
# Payload pembaruan dari Webhook nexa-d1
qdrant_payload = {
"status": "completed",
"job_id": job_id,
"agent": (taxonomy_result or "umum").lower(),
"taxonomy": taxonomy_result,
"confidence": float(confidence_val),
"processed_at": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
}
update_url = "http://10.43.49.184:6333/collections/articles_dedup/points/payload?wait=true"
response = requests.post(
update_url,
json={
"points": [point_id],
"payload": qdrant_payload
},
timeout=5
)3. Struktur Lengkap Titik Vektor (Payload Schema)
Berikut adalah contoh utuh rekaman titik vektor di Qdrant setelah melewati tahap deduplikasi C2 dan pengayaan Webhook D1:
{
"id": "c1f7a01e-4501-49b8-a73c-7c093a123456",
"version": 2,
"score": null,
"vector": [
0.021384,
-0.045219,
0.119284,
0.008912,
"... 768 nilai dense float32 ..."
],
"payload": {
"article_id": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
"domain": "kompas.com",
"title": "KPK Dalami Aliran Dana Dugaan Korupsi Proyek Pengadaan di Kemenhub",
"published_at": "2026-09-24T08:30:00Z",
"status": "completed",
"job_id": "job_d1_extract_982341",
"agent": "politik",
"taxonomy": "Politik & Hukum / Korupsi Pengadaan",
"confidence": 0.942,
"processed_at": "2026-09-24T08:31:12Z"
}
}4. Konfigurasi Indeks Payload & Optimasi Memori
Untuk memastikan query filter berjalan sub-milidetik pada puluhan juta vektor tanpa melakukan full-scan payload:
-
Payload Schema Indexing:
- Kolom
published_atdiindeks dengan tipe datadatetimeuntuk akselerasi rentang waktu 7 hari. - Kolom
statusdanagentdiindeks dengan tipekeyworduntuk pemilahan status pemrosesan.
PUT /collections/articles_dedup/index Host: 10.43.49.184:6333 Content-Type: application/json { "field_name": "published_at", "field_schema": "datetime" } - Kolom
-
On-Disk Vector Storage:
- Vektor dense 768 dimensi menggunakan konfigurasi
on_disk: true. Indeks HNSW tetap berada di RAM untuk pencarian kilat, sedangkan raw float array dialokasikan pada disk NVMe sehingga footprint RAM berkurang hingga 70%.
- Vektor dense 768 dimensi menggunakan konfigurasi
-
Snapshots & Disaster Recovery:
- Snapshot koleksi diambil berkala dan diunggah ke Object Storage (MinIO) pada bucket
nexa-backups/qdrant/melalui REST endpointPOST /collections/articles_dedup/snapshots.
- Snapshot koleksi diambil berkala dan diunggah ke Object Storage (MinIO) pada bucket
MinIO
Spesifikasi teknis Object Storage MinIO (S3 Compatible) di Nexa, partisi folder bucket 'nexa', siklus hidup raw payload, presigned URL, dan backup snapshot.
National Overview
Spesifikasi teknis antarmuka National Overview (/dashboard), ringkasan situasi lintas sektor, serta pemetaan database PostgreSQL, Elasticsearch, dan antrean Kafka.