Pada tulisan sebelumnya, asisten AI WhatsApp untuk toko ini masih mengandalkan pencarian teks parsial (ILIKE) untuk mencocokkan nama produk — cukup untuk nama yang ditulis lengkap, namun mudah meleset ketika pelanggan menulis dengan kata yang berbeda dari nama SKU di database. Rencana menambahkan kolom keywords/alias untuk menutup gap tersebut telah disebutkan sebelumnya; berikut proses dan hasilnya.
前回の記事では、このWhatsApp AIアシスタントは商品名の部分一致検索(ILIKE)に依存していました。正式名称で問い合わせれば機能しますが、顧客がデータベース上のSKU名と異なる言葉で質問すると認識できないという課題がありました。そのギャップを埋めるため、keywords/alias列の追加を計画していると述べました。今回はその構築過程と結果についてです。
The previous post noted that this shop's WhatsApp AI assistant still relied on partial text search (ILIKE) to match product names — fine for fully spelled-out names, but easily confused when a customer phrased things differently from the SKU name in the database. A plan to add a keywords/alias column to close that gap was mentioned at the time; this post covers the implementation and the results.
Solusi: Tabel Embedding Terpisah 解決策:専用の埋め込みテーブル The Solution: A Separate Embedding Table
Tabel products_embedding ditambahkan di Supabase, terhubung ke tabel produk utama (items) lewat item_id. Setiap baris menyimpan:
Supabase上にproducts_embeddingテーブルを新設し、商品マスタテーブル(items)とitem_idで連携させました。各行には以下を格納します。
A products_embedding table was added in Supabase, linked to the main product table (items) via item_id. Each row stores:
keywords— daftar kata kunci dalam Bahasa Indonesia, dipisah komakeywords— インドネシア語のキーワードをカンマ区切りで列挙したものkeywords— a comma-separated list of keywords in Indonesianembedding— vektor 1536 dimensi dari kata kunci tersebut, untuk pencarian semantik lewat pgvectorembedding— そのキーワード文字列から生成した1536次元のベクトル(pgvectorによるセマンティック検索用)embedding— a 1536-dimension vector of that keyword string, for semantic search via pgvector
Targetnya: pelanggan bisa bertanya dengan kata sehari-hari ("ada tempat duduk plastik?") tanpa harus tahu nama SKU persis ("BANGKU ESPANA LOW STOOL"). 目的は、顧客がSKUの正式名称(例:「BANGKU ESPANA LOW STOOL」)を知らなくても、日常的な言葉(例:「プラスチックの椅子ある?」)で検索できるようにすることです。 The goal: customers can ask in everyday words ("got any plastic seats?") without knowing the exact SKU name ("BANGKU ESPANA LOW STOOL").
Cara Kerja 処理の流れ How It Works
items (nama produk + grup) | v Claude Haiku --> generate keyword list + confidence (HIGH/LOW) | |-- HIGH --> langsung diproses otomatis |-- LOW --> ditinjau manusia: Accept / Augment / Edit / Skip (PENDING) v string keyword final (dipisah koma) | v OpenAI text-embedding-3-small --> vektor 1536 dimensi | v Supabase pgvector --> products_embeddingitems(商品名 + グループ) | v Claude Haiku --> キーワードリスト生成 + 確信度判定(HIGH/LOW) | |-- HIGH --> 自動処理 |-- LOW --> 人間がレビュー:承認 / 追加 / 編集 / スキップ(PENDING) v 最終キーワード文字列(カンマ区切り) | v OpenAI text-embedding-3-small --> 1536次元ベクトル | v Supabase pgvector --> products_embeddingitems (product name + group) | v Claude Haiku --> generate keyword list + confidence (HIGH/LOW) | |-- HIGH --> processed automatically |-- LOW --> reviewed by a human: Accept / Augment / Edit / Skip (PENDING) v final keyword string (comma-separated) | v OpenAI text-embedding-3-small --> 1536-dimension vector | v Supabase pgvector --> products_embedding
Untuk ~3.000 SKU, generate keyword satu per satu secara manual tidak realistis. Claude Haiku menghasilkan tebakan awal untuk setiap produk dengan tingkat keyakinan HIGH atau LOW — singkatan jelas seperti "TEMP SAMPAH" → tempat sampah dapat diproses otomatis (HIGH), sementara kode model yang murni alfanumerik atau ambigu (LOW) dialihkan untuk ditinjau secara manual: menerima tebakan Claude, menambahkan kata kunci, mengedit secara manual, atau melewati (ditandai PENDING untuk ditinjau lagi nanti). 約3,000 SKUを一つずつ手作業でキーワード化するのは現実的ではありません。Claude Haikuが各商品の初期キーワード案をHIGH/LOWの確信度付きで生成します。「TEMP SAMPAH」→ ゴミ箱のような明確な略語は自動処理(HIGH)、英数字のみのモデルコードなど曖昧なものはLOWとして人手によるレビューに回されます — Claudeの案を承認、キーワードを追加、手動編集、またはスキップ(後で再レビューするためPENDINGとして保存)。 For ~3,000 SKUs, generating keywords one by one by hand isn't realistic. Claude Haiku produces an initial guess for each product with a HIGH or LOW confidence level — a clear abbreviation like "TEMP SAMPAH" → trash bin can be processed automatically (HIGH), while purely alphanumeric or ambiguous model codes (LOW) are routed for manual review: accept Claude's guess, add keywords, edit manually, or skip (flagged PENDING for a later pass).
Detail Teknis 技術詳細 Technical Details
- Model keyword generation: Claude Haiku (
claude-haiku-4-5-20251001)キーワード生成モデル:Claude Haiku(claude-haiku-4-5-20251001)Keyword generation model: Claude Haiku (claude-haiku-4-5-20251001) - Model embedding: OpenAI
text-embedding-3-small, 1536 dimensi埋め込みモデル:OpenAItext-embedding-3-small、1536次元Embedding model: OpenAItext-embedding-3-small, 1536 dimensions - Teks yang di-embed:
f"{nama_produk} {keyword_string}"埋め込み対象テキスト:f"{商品名} {キーワード文字列}"Text being embedded:f"{product_name} {keyword_string}" - Storage: Supabase Postgres + ekstensi pgvectorストレージ:Supabase Postgres + pgvector拡張Storage: Supabase Postgres + the pgvector extension
Sebelum bot produksi disentuh, harness pengujian retrieval terpisah dibangun — mengambil sampel produk dalam satu kategori, menghitung cosine similarity terhadap satu query uji, dan menyimpan hasil rangking untuk dianalisis secara manual. 本番のボットに触れる前に、検索精度を評価する専用テストハーネスを構築しました。あるカテゴリの商品群を対象に、テストクエリとのコサイン類似度を計算し、ランキング結果を保存して手動で分析します。 Before touching the production bot, a separate retrieval test harness was built — sampling products in one category, computing cosine similarity against a test query, and saving the ranked results for manual analysis.
Hasil: Campuran, Bukan Gagal Total 結果:「惜しい」結果 — 完全な失敗ではない Results: Mixed, Not a Total Failure
Pengujian dilakukan pada kategori bangku (bangku/kursi plastik) dan ember terhadap query yang sama: "tempat duduk". 同一クエリ「tempat duduk」(座る場所)に対して、bangku(プラスチック製の椅子・スツール)カテゴリとember(バケツ)カテゴリをテストしました。 The bangku (plastic stool/chair) and ember (bucket) categories were tested against the same query: "tempat duduk" (seat).
| Produk商品Product | KategoriカテゴリCategory | Similarity類似度Similarity |
|---|---|---|
| BANGKU ESPANA LOW STOOL | bangku | 0.4151 |
| BANGKU IMPALA | bangku | 0.4117 |
| … | bangku | … |
| Bangku baso 303 hijau | bangku | 0.2979 |
| EMBER 6 GL TUTUP BASIC HOME | ember | 0.3193 |
| EMBER 3 GLN ELEGANT TUTUP | ember | 0.3182 |
Match teratas memang benar — bangku jenis kursi (stool) yang relevan untuk query "tempat duduk". Sejauh ini bagus. 最上位の一致は正しく、座る場所のクエリに対して実際に椅子(スツール)が返っています。ここまでは良好です。 The top match is correct — a stool-type bangku relevant to the "tempat duduk" query. So far so good.
Masalahnya muncul di angka, bukan di urutan teratas: similarity tertinggi cuma 0.4151 dari skala maksimum 1.0 — bukan angka yang menunjukkan kecocokan kuat. Dan yang lebih krusial: skor ember tertinggi (0.3193, item yang sama sekali tidak berhubungan dengan tempat duduk) lebih tinggi dari beberapa item bangku yang justru relevan (0.2979–0.3056). Kalau sistem produksi mencari di seluruh katalog tanpa filter kategori — yang memang begitu cara kerja bot WhatsApp saat pelanggan tidak menyebut kategori — ember bisa saja mengalahkan bangku yang sebenarnya jadi jawaban yang benar. 問題は順位ではなく、数値の差にあります。最高類似度はわずか0.4151(最大値1.0のスケール)— 強い一致を示す数値ではありません。さらに重要なのは、座ることと全く関係のないバケツ(ember)の最高スコア(0.3193)が、本来関連性のある一部のbangku商品(0.2979〜0.3056)よりも高いという点です。本番システムがカテゴリで絞り込まずカタログ全体を検索する場合 — 顧客がカテゴリを明示しないWhatsAppボットでは実際にそうなります — バケツが本来正解であるはずの椅子よりも上位に来てしまう可能性があります。 The problem shows up in the numbers, not the top rank: the highest similarity is only 0.4151 out of a max of 1.0 — not a number that signals a strong match. More critically, the top ember score (0.3193, an item completely unrelated to seating) is higher than some genuinely relevant bangku items (0.2979–0.3056). If the production system searches the whole catalog without a category filter — which is exactly how the WhatsApp bot behaves when a customer doesn't mention a category — a bucket could outrank the chair that should have been the right answer.
Jadi: top-1 result lolos uji ini, tapi marginnya terlalu sempit untuk dipercaya di produksi dengan skala penuh ~3.000 SKU. つまり:このテストでは上位1件は正解でしたが、約3,000 SKU規模の本番環境で信頼するにはマージン(差)が狭すぎます。 So: the top-1 result passed this test, but the margin is too thin to trust in production at the full ~3,000 SKU scale.
Yang Saya Pelajari (Sejauh Ini) 現時点での学び What I've Learned (So Far)
- Menguji retrieval secara offline dengan harness sendiri jauh lebih murah dan cepat dibanding trial-and-error langsung di sesi WhatsApp pelanggan asli — temukan masalah sebelum pelanggan yang menemukannya.自前のテストハーネスでオフライン検証する方が、実際の顧客とのWhatsAppセッションで試行錯誤するよりもはるかに安価かつ迅速です — 顧客に発見される前に問題を発見できます。Testing retrieval offline with a custom harness is far cheaper and faster than trial-and-error in real customer WhatsApp sessions — find the problem before the customer does.
- Top-1 yang benar tidak berarti sistem aman. Yang menentukan keandalan adalah seberapa jauh jarak (margin) antara hasil yang relevan dan yang tidak relevan — bukan cuma apakah hasil nomor satu benar.上位1件が正解であることは、システムが安全である保証にはなりません。信頼性を決めるのは、関連する結果と無関係な結果との間の「距離(マージン)」であり、1位が正しいかどうかだけではありません。A correct top-1 result doesn't mean the system is safe. What determines reliability is the margin between relevant and irrelevant results — not just whether result number one happens to be right.
- Hipotesis kerja saat ini (belum final): representasi daftar kata kunci yang dipisah koma mungkin tidak memberi model embedding cukup struktur kalimat untuk memisahkan kategori produk secara tegas. Pendekatan berbeda sedang diuji untuk memvalidasi hipotesis ini.現在の作業仮説(未確定):カンマ区切りのキーワードリストという表現方法は、埋め込みモデルが商品カテゴリを明確に分離するために必要な文章構造を十分に提供していない可能性があります。これは現在、別のアプローチで検証中です。Current working hypothesis (not final): a comma-separated keyword list may not give the embedding model enough sentence structure to cleanly separate product categories. A different approach is being tested to validate this.
Keterbatasan Saat Ini 現在の限界 Current Limitations
- Pengujian baru mencakup 2 kategori (bangku, ember) sebagai sample dari ~3.000 SKU — belum representasi penuh katalog.テストは約3,000 SKUのうち2カテゴリ(bangku、ember)のサンプルのみ — カタログ全体を網羅した検証ではありません。Testing so far covers only 2 categories (bangku, ember) as a sample of ~3,000 SKUs — not yet a full catalog representation.
- Belum ada ambang batas (threshold) similarity yang ditentukan untuk produksi.本番運用のための類似度の閾値はまだ決定していません。No production similarity threshold has been set yet.
- Pendekatan alternatif untuk memperbaiki margin ini sedang diuji secara paralel — hasilnya (dengan angka before/after yang sudah divalidasi) akan dibagikan di tulisan berikutnya.このマージン問題を改善する別のアプローチを並行して検証中です。検証済みのbefore/after数値とともに、次回の記事で結果を共有します。An alternative approach to fix this margin problem is being tested in parallel — the results (with validated before/after numbers) will be shared in a future post.
Dibuat oleh developer AI independen berbasis di Bandung, Indonesia, yang membangun sistem AI custom untuk kebutuhan UMKM — termasuk semantic search, chatbot WhatsApp, dan otomasi workflow. Untuk kebutuhan serupa, hubungi Inti Sakura Solutions. バンドン(インドネシア)を拠点とする独立系AI開発者が、中小企業向けにセマンティック検索、WhatsAppチャットボット、ワークフロー自動化などのカスタムAIシステムを構築しています。同様の課題をお持ちの企業様は、Inti Sakura Solutionsまでご連絡ください。 Built by an independent AI developer based in Bandung, Indonesia, who builds custom AI systems for SMEs — including semantic search, WhatsApp chatbots, and workflow automation. If your business has a similar problem, get in touch with Inti Sakura Solutions.