[Catatan Pengembangan] Menemukan bahkan ketika istilah pencarian berbeda: Menambahkan Pencarian Vektor ke Log Percakapan

58.207.***.***
3

Artikel ini adalah bagian kedua dari seri tentang perkembangan fitur seCall — alat CLI untuk mengumpulkan dan mencari sesi percakapan agen AI. Karena dibuat sebagai bagian dari proyek pengujian vibe coding, saya tidak mengetahui dan tidak ingin tahu detail kodenya. Tidak perlu tahu, bahkan jika melihat tidak akan paham Namun saya menulis artikel ini karena ingin menjelaskan teknologi yang digunakan dalam fitur yang diterapkan setelah mencoba-coba, dan alasan pengenalan. (Bagian sebelumnya: Mencari Percakapan Sebelumnya Agen Terminal dengan SQLite Saja - Mengapa Harus Melakukan Analisis Morfologi Bahasa Korea)

Masalah: Hanya Bisa Menemukan Jika Katanya Sama

Di bagian sebelumnya, saya membuat pencarian BM25 dengan FTS5 + analisis morfologi bahasa Korea. Sekarang saya bisa menemukan "모델" dari "모델을". Tetapi saat menggunakannya, ada sesuatu yang terasa tidak tepat.

Ketika mencari "아키텍처", "설계" tidak terdeteksi. Ketika mencari "에러", "버그" tidak muncul. Manusia tahu bahwa kedua kata itu bermakna sama, tetapi BM25 menganggap mereka sebagai kata yang sama sekali berbeda jika hurufnya berbeda. Terutama dalam percakapan AI, bahasa Korea dan Inggris tercampur, sehingga percakapan di mana Anda bertanya "embedding" tetapi menerima jawaban "임베딩" hanya akan terdeteksi setengah jika dicari dengan cara apa pun.

Pada akhirnya, saya memerlukan pencarian dengan sumbu yang berbeda dari pencocokan teks.

Apa itu Embedding — Mengubah Kalimat Menjadi Angka

Inti dari pencarian vektor adalah embedding. Ini mengubah kalimat menjadi array angka (vektor) ratusan hingga ribuan elemen, dan di sini kalimat dengan makna serupa akan menghasilkan array angka yang serupa.

"아키텍처를 설계했다" dan "시스템 구조를 잡았다" memiliki huruf yang sama sekali berbeda, tetapi ketika melewati model embedding, mereka menghasilkan vektor yang menunjuk ke arah hampir sama. Jika saya mengukur jarak (kesamaan kosinus) antara dua vektor ini, saya dapat mengetahui dengan angka bahwa "kedua kalimat ini serupa".

Sederhananya — jika BM25 adalah pencarian kamus yang melihat "apakah ada kata yang sama", maka pencarian vektor adalah pencarian makna yang melihat "apakah konteksnya serupa".

Model Embedding: BGE-M3

Kualitas pencarian ditentukan oleh model apa yang digunakan untuk membuat vektor. seCall memilih BGE-M3.

  • Dukungan Multibahasa: Kuat dengan teks campuran Korea + Inggris. Percakapan AI memang sering mencampur Korea dan Inggris, jadi ini penting.

  • 1024 dimensi: Ukuran yang cukup untuk menangkap makna tanpa biaya penyimpanan dan pencarian yang berlebihan

  • Open Source: Siapa pun bisa mendapatkannya dari HuggingFace

Sebenarnya ada juga percobaan dan kesalahan di sini. Awalnya saya juga mencoba seri text-embedding-3 dari OpenAI. Kualitasnya bagus, tetapi memerlukan panggilan API, jadi jika Anda melakukan embedding ribuan percakapan, biayanya akan membengkak. Dan itu tidak sesuai dengan tujuan awal "menyelesaikan semuanya secara lokal". Ini juga tidak sesuai dengan filosofi saya "bahkan jika saya membayar Claude MAX200, saya tidak akan mengisi ulang API!"

Di Mana Menjalankan Embedding — Tiga Backend

Setelah model embedding ditentukan, saya perlu memutuskan di mana menjalankannya. Ini ternyata lebih membuat bingung dari yang dipikirkan.

1. Ollama (Nilai Default)

Ollama, yang terkenal sebagai runtime LLM lokal, juga dapat menjalankan model embedding. Cukup satu baris ollama pull bge-m3 dan selesai. Jika Anda sudah menggunakan Ollama, ini adalah yang paling nyaman. Nilai default seCall juga ini.

2. ONNX Runtime (Inferensi Lokal)

Saya ingin agar berfungsi tanpa Ollama. Dengan ONNX Runtime, saya dapat memuat file model BGE-M3 (.onnx) secara langsung dan melakukan inferensi dalam Rust. Saat pertama kali dijalankan, model secara otomatis diunduh dari HuggingFace (~600MB), dan setelah itu berfungsi secara offline. Keuntungannya adalah tidak perlu menjalankan layanan terpisah seperti Ollama.

3. OpenAI API (Cloud)

Ini adalah opsi yang saya siapkan untuk kasus di mana tidak ada GPU lokal atau diperlukan kecepatan tinggi. Cukup masukkan kunci API. Namun ada biayanya, dan karena harus mengirim percakapan ke server eksternal, secara pribadi saya tidak menyukainya. Ditolak

Karena saya tidak tahu apa yang disukai pengguna, saya menyiapkan semuanya. Dapat diubah dengan satu baris file konfigurasi. Ini adalah pola yang sama dengan pilihan Lindera/Kiwi di bagian sebelumnya. Dalam alat pribadi, struktur yang dapat mengganti backend seperti ini cukup berguna — jika lingkungan berubah, Anda hanya perlu mengubah konfigurasi.

Pengalaman Menyakitkan dengan sqlite-vec

Ini adalah cerita yang dijanjikan di akhir bagian sebelumnya. Rencana awalnya seperti ini:

FTS5 (pencarian teks) + sqlite-vec (pencarian vektor) = Selesai dengan SQLite saja

sqlite-vec adalah ekstensi SQLite yang mendukung pencarian vektor. Semua orang menggunakannya, dan secara konseptual sangat rapi. Tetapi ketika saya menjalankan build, saya mendapat kesalahan kompilasi C di MacBook Air kecil saya yang berharga. Setelah mencari, ternyata sqlite-vec yang saya percayai masih dalam tahap alpha (0.1) dengan kompatibilitas platform yang tidak stabil.

Di sini ada dua pilihan:

  • Langsung perbaiki masalah build C → Itu agak... (jika saya memperbaikinya sendiri, nanti jika badan utama diperbarui, sayapnya bisa bertambah lebih besar dari badannya)

  • Pindahkan proyek ke Windows atau Linux dan kerjakan dari sana (?)

  • Cari cara untuk mengatasinya dengan cara lain

  • Bunuh Trump 🚀

Tentu saja saya memilih yang terakhir. 😇 (Upaya membunuh Trump juga gagal)

Jalan Keluar: Penyimpanan BLOB + usearch

Pada akhirnya, penyimpanan vektor hanya menyelaraskan array angka ke dalam kolom BLOB biasa di SQLite sebagai byte. Meskipun saya melepaskan fitur tabel virtual sqlite-vec, data itu sendiri masih berada dalam satu file SQLite, jadi saya menjaga prinsip "database tunggal".

Kecepatan pencarian diselesaikan dengan perpustakaan bernama usearch. usearch adalah perpustakaan native Rust yang mengimplementasikan algoritma HNSW (Hierarchical Navigable Small World).

Apa itu HNSW (bukan perangkat lunak Hacker News) — ketika menemukan "yang paling dekat" dari ribuan vektor, membandingkan semuanya lambat. HNSW menghubungkan vektor menjadi grafik dan mencari dengan mengikuti tetangga terdekat. Ini adalah algoritma tetangga terdekat perkiraan (ANN) yang mengorbankan akurasi demi kecepatan dramatis. Dengan 1000 vektor, Anda tidak perlu membandingkan semuanya; cukup dengan puluhan lompatan untuk menemukan yang paling mirip.

Jadi struktur akhirnya adalah:

  • Penyimpanan Vektor: Kolom BLOB SQLite (persisten)

  • Indeks Pencarian Vektor: File usearch HNSW (performa)

  • Jika indeks HNSW rusak atau ketinggalan zaman, ada fallback untuk perbandingan menyeluruh dari BLOB

Sayang tidak bisa pakai sqlite-vec, tetapi hasilnya justru lebih cepat karena memisahkan indeks HNSW. Kegagalan tidak selalu buruk. Rasanya tidak seperti ada kompromi besar (mohon)

Chunking — Memotong Percakapan ke Ukuran yang Sesuai

Model embedding memiliki batasan panjang input. Sering kali satu putaran percakapan AI melebihi ribuan karakter, dan jika dimasukkan secara utuh, makna bagian belakang akan memudar.

Jadi saya memotong percakapan menjadi chunk berukuran sesuai. seCall memotong sekitar 3600 karakter, dengan bagian yang tumpang tindih sekitar 15% di antara chunk. Tumpang tindih (overlap) ini membantu mengurangi hilangnya konteks di batas tempat pemotongan.

Setiap chunk juga dilengkapi dengan metadata "chunk ini adalah putaran ke berapa dari sesi mana". Ini diperlukan agar nantinya dari hasil pencarian bisa kembali ke percakapan asli.

Berapa Lama Sebenarnya Prosesnya

Bicara realistis — embedding itu lambat. Menjalankan BGE-M3 dengan ONNX Runtime pada CPU memerlukan ratusan milidetik per chunk. Memproses 600 percakapan memakan waktu sekitar 40 menit. (Ada seorang pengguna di versi awal yang menjalankan 3GB percakapan yang katanya butuh 3 hari... Dengan kesempatan ini saya minta maaf lagi)

Namun ini hanya dilakukan pada indexing awal, dan setelah itu hanya percakapan baru yang diproses. Dan pencarian itu sendiri selesai dalam hitungan milidetik berkat HNSW. Ini adalah pola penulisan sekali pembacaan berkali-kali yang tipikal: indexing lambat tetapi pencarian cepat.

Melihat hasil sebenarnya, beberapa jam untuk indexing awal layak dilakukan. Jika uang berlimpah, cukup tanyakan ke Opus atau Sonnet untuk membuat wiki, dan riwayat otobiografi kejahatan akan keluar sebagai seri

Ringkasan

Teknologi

Peran

Alasan Pemilihan

BGE-M3

Model Embedding

Multibahasa kuat dengan campuran Korea-Inggris, open source

ONNX Runtime

Inferensi Lokal

Berfungsi offline tanpa layanan eksternal

Ollama

Backend Embedding Default

Paling nyaman bagi mereka yang sudah menggunakannya

usearch (HNSW)

Indeks Pencarian Vektor

Alternatif sqlite-vec, native Rust, cepat

SQLite BLOB

Penyimpanan Vektor

Mempertahankan file DB tunggal

Intinya adalah menjalankan pencarian berbasis makna secara lokal, tanpa layanan eksternal. Meskipun tidak bisa pakai sqlite-vec, jalan keliling yang diambil justru menghasilkan kombinasi BLOB + HNSW yang lebih fleksibel dan cepat. Kegagalan juga terkadang memberikan hasil yang baik (mohon)

Di bagian berikutnya, saya akan membicarakan cara menggabungkan dua hasil pencarian ini — BM25 (pencocokan teks) dan vektor (pencocokan makna) — menjadi satu, tentang pencarian hybrid dan algoritma RRF. Jika ada yang tertarik, saya akan melanjutkan ceritanya.

Pos ini ditulis oleh non-vibe. Anda dapat membacanya dengan tenang 😁 Pengembangan agentic bukan tentang membenturkan kepala ke tanah kosong. Ini adalah kolaborasi tiga orang: pengetahuan domain kecerdasan manusia berdasarkan pengalaman yang luas dan pekerjaan yang akurat dan tak lelah dari kecerdasan buatan yang dipasang dengan peralatan yang solid.

로그인한 회원만 댓글 등록이 가능합니다.

개발한당

KR | ID | EN
  • IDR
  • KOR
8.36 0.01

2026.07.10 KEB 하나은행 고시회차 1036회

다가오는 한인 행사일정

  • 등록 된 일정이 없어요!