Fitur semantic search sering terasa seperti search bar understands you: pengguna menulis kata yang tidak persis sama dengan dokumen, tetapi hasilnya tetap relevan. Masalahnya, ketika backend pencarian rusak di level embedding atau indexing, gejalanya tidak selalu terlihat sebagai error API. Endpoint tetap 200 OK, latency masih normal, tetapi ranking hasil tiba-tiba aneh, CTR turun, dan query yang sebelumnya bagus berubah menjadi acak.

Dalam studi kasus ini, akar masalahnya adalah embedding tak sinkron: sebagian data masih memakai embedding lama, sementara query sudah dihitung dengan model baru atau dimensi vektor baru. Variasi lain yang sama berbahayanya adalah pipeline reindex asynchronous tidak tuntas, sehingga index berisi campuran representasi lama dan baru. Dari luar terlihat seperti bug ranking, padahal masalah utamanya adalah inkonsistensi data vektor.

Inspirasi teknisnya mirip tren produk pencarian modern yang mencoba memahami maksud pengguna, bukan sekadar mencocokkan kata. Namun di sisi backend, kemampuan itu bergantung pada konsistensi embedding, metadata, dan proses indexing.

Gejala Nyata Setelah Deploy

Kasus seperti ini biasanya muncul beberapa jam setelah deploy model, perubahan pipeline indexing, atau migrasi vector store. Tim produk melaporkan CTR turun pada halaman hasil pencarian. Tim backend melihat log API normal. Tidak ada lonjakan 5xx, tidak ada timeout berarti, dan query ke database/vector store tetap berjalan.

Gejala yang sering muncul:

  • Query populer tiba-tiba menampilkan hasil yang terasa tidak nyambung.
  • Hasil pencarian menjadi terlalu acak atau terlalu umum.
  • Dokumen yang seharusnya berada di top 3 turun jauh, meski datanya tidak berubah.
  • Query serupa memberi hasil yang tidak stabil antar request atau antar shard/index.
  • Log aplikasi normal, tetapi metrik bisnis seperti CTR, conversion from search, atau zero-result dissatisfaction memburuk.

Ini tanda penting: jika performa teknis tampak sehat tetapi kualitas ranking turun setelah deploy, curigai data retrieval dan representasi embedding lebih dulu, bukan hanya bug pada controller atau endpoint.

Kenapa Embedding Tak Sinkron Bisa Membuat Query Salah

1. Query embedding dan document embedding berasal dari model berbeda

Dalam semantic search, query dan dokumen harus berada pada ruang vektor yang kompatibel. Jika query sudah di-embed dengan model baru, sementara sebagian dokumen masih memakai model lama, skor similarity kehilangan makna. Secara matematis vektornya mungkin tetap bisa dihitung, tetapi secara semantik ranking menjadi rusak.

2. Dimensi vektor berubah, tetapi validasi longgar

Beberapa sistem akan gagal keras saat dimensi berbeda. Namun ada juga pipeline buatan internal yang secara tidak sengaja melakukan fallback, padding, truncation, atau serialisasi yang membuat data tetap tersimpan tetapi salah. Akibatnya request tidak error, namun hasil similarity tidak lagi valid.

3. Reindex async tidak selesai atau berhenti di tengah

Ini skenario yang sangat masuk akal setelah deploy. Aplikasi mulai memakai model embedding baru untuk query, lalu worker reindex berjalan di background untuk memperbarui embedding dokumen. Jika queue macet, job gagal diam-diam, atau rollout dipotong di tengah, index berisi campuran data lama dan baru. Dari sisi API semuanya tampak hidup, tetapi ranking menjadi tidak konsisten.

4. Alias index dipindahkan terlalu cepat

Dalam migrasi yang lebih rapi, biasanya dibuat index baru lalu alias diarahkan setelah reindex selesai. Bug muncul saat alias dipindahkan sebelum seluruh corpus siap, atau ada layanan yang masih menulis ke index lama sementara layanan query membaca index baru.

Langkah Investigasi: Dari Gejala ke Root Cause

Mulai dari query yang diketahui rusak

Jangan mulai dari log umum. Ambil 5-10 query nyata yang CTR-nya turun atau dikeluhkan pengguna. Untuk tiap query, bandingkan:

  • hasil sebelum deploy vs sesudah deploy, jika ada snapshot atau log ranking,
  • embedding model yang dipakai untuk query,
  • index atau collection yang dibaca,
  • metadata dokumen teratas: versi embedding, waktu index, shard/collection, dan source pipeline.

Tujuan awalnya bukan langsung memperbaiki, tetapi membuktikan apakah query salah karena retrieval atau karena tahap ranking berikutnya.

Cek metadata embedding pada dokumen hasil teratas

Setiap vektor dokumen idealnya menyimpan metadata minimal seperti:

  • embedding_model
  • embedding_version
  • vector_dimension
  • indexed_at
  • source_revision atau checksum konten

Jika metadata ini tidak ada, debugging akan jauh lebih sulit. Dalam banyak insiden, root cause baru terlihat ketika tim memeriksa 20 hasil teratas dan menemukan campuran versi embedding di index yang sama.

Contoh metadata dokumen yang sehat:
{
  "document_id": "prd_1842",
  "embedding_model": "text-embedding-v2",
  "embedding_version": "2026-08-15",
  "vector_dimension": 1024,
  "indexed_at": "2026-08-20T10:14:22Z",
  "content_hash": "8d7f..."
}

Kalau sebagian hasil punya embedding_version lama, sementara aplikasi query sudah memakai versi baru, Anda sudah punya petunjuk kuat.

Bandingkan distribusi versi di index

Jangan hanya memeriksa beberapa dokumen. Ambil gambaran besar: berapa persen dokumen di index yang memakai model lama vs baru. Jika vector store Anda mendukung filter metadata, buat agregasi sederhana. Jika tidak, ambil sampel besar dan hitung di aplikasi.

SELECT embedding_version, COUNT(*)
FROM search_documents
GROUP BY embedding_version
ORDER BY COUNT(*) DESC;

Jika penyimpanan vektor tidak memakai SQL, prinsipnya sama: pastikan Anda bisa menjawab apakah corpus sudah sepenuhnya direindex atau masih campur.

Periksa worker queue dan status reindex

Banyak tim terlalu cepat fokus ke query path, padahal masalahnya di pipeline belakang. Verifikasi:

  • berapa total job reindex yang dijadwalkan, sukses, gagal, dan tertunda,
  • apakah ada dead-letter queue, retry storm, atau worker berhenti,
  • apakah job lama dan job baru berjalan bersamaan lalu saling menimpa,
  • apakah ada rate limit dari penyedia embedding yang membuat proses berhenti sebagian.

Contoh sinyal yang patut dicurigai:

  • jumlah dokumen di index baru hanya 60-80% dari corpus aktif,
  • job sukses tetapi dokumen tidak benar-benar terupdate karena upsert key salah,
  • worker menulis ke collection lama karena environment variable belum sinkron.

Bandingkan dimensi vektor yang tersimpan

Jika Anda mengganti model embedding, pastikan dimensi vektornya memang sesuai dengan schema index. Ini penting terutama saat migrasi antar provider atau antar model.

if len(query_vector) != expected_dimension:
    raise ValueError("query vector dimension mismatch")

Validasi seperti ini sebaiknya ada di boundary service. Tanpa validasi keras, bug bisa lolos sebagai masalah kualitas hasil, bukan error sistem.

Audit jalur penulisan dan pembacaan index

Dalam sistem yang dipisah antara layanan ingest dan layanan query, kesalahan konfigurasi sering terjadi setelah deploy:

  • writer menulis ke index documents_v2, reader masih membaca documents_current yang menunjuk ke documents_v1,
  • query service memakai model embedding baru, tetapi worker ingest belum diperbarui,
  • satu region sudah rollout, region lain belum, sementara traffic dibagi lintas region.

Ini menjelaskan kenapa hasil pencarian bisa terasa acak hanya untuk sebagian pengguna.

Query dan Log yang Perlu Dicek

Untuk insiden seperti ini, log request biasa tidak cukup. Anda butuh log diagnostik yang menghubungkan query, model embedding, dan index yang dipakai.

Log request pencarian

{
  "event": "search_request",
  "query": "sepatu lari ringan buat maraton",
  "query_embedding_model": "text-embedding-v2",
  "query_embedding_version": "2026-08-15",
  "target_index": "products_semantic_current",
  "top_k": 20,
  "request_id": "req_01..."
}

Log hasil pencarian yang diringkas

{
  "event": "search_response_debug",
  "request_id": "req_01...",
  "top_results": [
    {
      "document_id": "prd_1842",
      "score": 0.81,
      "embedding_version": "2026-08-15",
      "vector_dimension": 1024
    },
    {
      "document_id": "prd_0091",
      "score": 0.80,
      "embedding_version": "2026-06-02",
      "vector_dimension": 768
    }
  ]
}

Jika hasil teratas sudah menunjukkan campuran versi atau dimensi, Anda tidak perlu menebak terlalu jauh.

Pengecekan integritas index

Verifikasi juga hal-hal berikut:

  • jumlah total dokumen aktif di sumber data vs jumlah dokumen terindex,
  • jumlah dokumen per embedding version,
  • dokumen terbaru apakah sudah punya embedding terbaru,
  • apakah ada dokumen duplikat dengan embedding versi berbeda.

Studi Kasus Root Cause yang Masuk Akal

Misalkan tim Anda mengganti model embedding untuk meningkatkan kualitas semantic search. Deploy dilakukan dalam dua bagian:

  1. service query mulai membuat embedding query dengan model baru,
  2. worker background menjalankan reindex seluruh dokumen.

Masalah terjadi karena worker reindex berjalan async dan berhenti di 70% akibat retry yang tidak pernah pulih. Sebagian corpus kini punya embedding baru, sebagian masih lama. Karena vector store masih memakai collection yang sama, hasil pencarian menjadi campuran. Query tertentu kebetulan tetap bagus, tetapi query lain sangat rusak karena nearest neighbors berasal dari ruang vektor yang tidak kompatibel.

Variasi lain: index baru dibuat dengan dimensi berbeda, tetapi ada layanan yang tetap membaca alias lama. Hasilnya bukan error total, melainkan fallback ke lexical search untuk sebagian request dan vector search untuk request lain. Dari luar terlihat seperti ranking berubah-ubah.

Perbaikan yang Aman: Jangan Patch Ranking Dulu

Kesalahan umum saat panic debugging adalah menambah rule boosting, filter manual, atau reranker darurat untuk menutupi hasil aneh. Ini jarang menyelesaikan masalah inti. Jika akar masalahnya embedding tak sinkron, prioritasnya adalah memulihkan konsistensi corpus.

1. Bekukan versi model untuk query dan dokumen

Pastikan query service dan indexing service menggunakan kontrak versi yang sama. Idealnya versi embedding dikonfigurasi eksplisit, bukan implicit dari environment yang bisa berbeda antar service.

EMBEDDING_MODEL=text-embedding-v2
EMBEDDING_VERSION=2026-08-15
VECTOR_DIMENSION=1024
TARGET_INDEX=products_semantic_v2

Konfigurasi ini harus tervalidasi saat startup. Jika service query memakai VECTOR_DIMENSION yang berbeda dari index target, service sebaiknya gagal start atau menolak request internal.

2. Rebuild index baru, jangan campur di tempat yang sama

Pendekatan paling aman biasanya:

  1. buat index/collection baru khusus versi embedding baru,
  2. reindex seluruh dokumen ke sana,
  3. verifikasi coverage dan kualitas sampling,
  4. baru pindahkan alias read secara atomik.

Dengan cara ini Anda menghindari corpus campuran di satu index produksi.

3. Gunakan alias atau pointer read/write yang terpisah

Pisahkan konsep write target dan read target. Writer boleh menulis ke index baru selama backfill berjalan, tetapi reader tetap membaca index lama sampai validasi selesai. Setelah siap, lakukan cutover terkontrol.

4. Jalankan reindex idempoten

Job reindex harus aman dijalankan ulang. Gunakan upsert dengan key dokumen yang konsisten, dan sertakan metadata versi embedding agar pembaruan mudah diverifikasi.

5. Bersihkan dokumen lama setelah cutover

Setelah pembacaan berpindah ke index baru dan observasi stabil, barulah hapus index lama. Menghapus terlalu cepat menghilangkan jalur rollback.

Migrasi Aman untuk Semantic Search

Strategi dual-index

Untuk perubahan besar seperti model embedding baru atau dimensi baru, dual-index hampir selalu lebih aman daripada in-place migration. Biayanya lebih tinggi sementara waktu, tetapi jauh mengurangi risiko kualitas hasil rusak tanpa terlihat.

  • Kapan dipilih: model berubah, dimensi berubah, atau corpus besar dan reindex makan waktu lama.
  • Kelebihan: rollback mudah, validasi lebih jelas, tidak ada campuran versi di satu index.
  • Kekurangan: butuh storage tambahan dan orkestrasi lebih disiplin.

Cutover berbasis readiness, bukan waktu

Jangan memindahkan traffic hanya karena job sudah berjalan beberapa jam. Cutover harus menunggu syarat yang terukur:

  • 100% dokumen aktif sudah terindex,
  • 0 job gagal yang belum diremediasi,
  • sampling query penting lulus evaluasi manual atau offline relevance check,
  • distribusi metadata embedding seragam sesuai target versi.

Guardrail yang Sebaiknya Ada di Backend

Validasi dimensi dan versi saat write

Saat dokumen di-upsert ke vector store, backend harus memverifikasi bahwa panjang vektor dan metadata versi sesuai kontrak index. Jika tidak, gagal keras. Lebih baik menolak data salah daripada menerima data yang diam-diam merusak ranking.

Validasi versi saat read

Di response internal debug atau sampling pipeline, sertakan metadata versi embedding pada top results. Ini sangat membantu mendeteksi hasil campuran sejak awal.

Health check kualitas, bukan hanya availability

Health check biasa hanya menjawab apakah service hidup. Untuk semantic search, tambahkan quality canary ringan: beberapa query kontrol dengan ekspektasi dokumen kategori tertentu. Ini bukan evaluasi sempurna, tetapi cukup untuk mendeteksi degradasi kasar setelah deploy.

Fail closed untuk mismatch fatal

Jika query embedding version tidak kompatibel dengan index target, jangan lanjutkan secara diam-diam. Pilih salah satu:

  • kembalikan error internal yang jelas ke caller internal, atau
  • fallback terkontrol ke lexical search dengan logging dan alert yang tegas.

Fallback boleh, tetapi harus eksplisit. Fallback diam-diam sering membuat insiden lebih sulit didiagnosis.

Monitoring yang Relevan

Untuk mencegah kejadian serupa, monitoring semantic search harus menggabungkan metrik sistem dan metrik kualitas.

Metrik sistem

  • jumlah job reindex sukses/gagal/tertunda,
  • durasi rata-rata embedding generation,
  • error rate dari provider embedding atau worker internal,
  • coverage index: dokumen aktif vs dokumen terindex,
  • distribusi embedding version per index.

Metrik kualitas

  • CTR hasil pencarian per segmen query,
  • reformulation rate: pengguna langsung mencari ulang dengan query berbeda,
  • zero-click search atau pogo-sticking bila tersedia,
  • stabilitas top results untuk query canary,
  • persentase hasil top-N dengan metadata versi yang benar.

Yang sering terlewat adalah monitor distribusi embedding version. Metrik ini sederhana tetapi sangat efektif mendeteksi corpus campuran.

Contoh Alur Investigasi yang Efektif

  1. Ambil 10 query yang CTR-nya turun setelah deploy.
  2. Bandingkan top 10 hasil sebelum dan sesudah deploy.
  3. Untuk hasil sesudah deploy, tarik metadata embedding tiap dokumen.
  4. Hitung distribusi embedding_version pada top results dan keseluruhan index.
  5. Cek status queue reindex dan dokumen yang belum terproses.
  6. Verifikasi query service memakai model/versi/dimensi yang sama dengan target index.
  7. Jika corpus campuran terkonfirmasi, hentikan cutover atau rollback ke index lama.
  8. Rebuild index baru sampai lengkap, lalu lakukan switch alias secara atomik.

Urutan ini bekerja karena cepat memisahkan masalah kualitas retrieval dari masalah API umum.

Checklist Pencegahan untuk Developer Backend

  • Simpan metadata embedding_model, embedding_version, dan vector_dimension di setiap dokumen.
  • Jangan migrasi model embedding besar secara in-place tanpa kebutuhan kuat.
  • Gunakan dual-index untuk perubahan model atau dimensi.
  • Pastikan job reindex idempoten dan bisa diaudit progresnya.
  • Tambahkan validasi keras untuk mismatch dimensi dan versi.
  • Pisahkan target write dan target read dengan alias yang jelas.
  • Jangan anggap API 200 OK berarti ranking sehat.
  • Monitor CTR, reformulation, dan distribusi versi embedding setelah deploy.
  • Siapkan query canary dan sampling manual untuk evaluasi cepat.
  • Sediakan rollback plan sebelum mengganti model embedding di produksi.

Penutup

Debug backend pencarian semantik sering menipu karena gejalanya muncul sebagai penurunan relevansi, bukan error sistem. Saat ranking tiba-tiba aneh setelah deploy, sementara log API normal, salah satu tersangka utama adalah embedding tak sinkron: query memakai representasi baru, tetapi dokumen masih lama, atau reindex async berhenti di tengah.

Kuncinya adalah memperlakukan embedding sebagai kontrak data, bukan detail implementasi tersembunyi. Dengan metadata yang benar, migrasi dual-index, validasi versi/dimensi, serta monitoring kualitas yang memadai, Anda bisa mencegah bug “query salah karena embedding tak sinkron” berubah menjadi insiden produksi yang mahal.