All Products
Search
Document Center

Vector Retrieval Service for Milvus:Buat leaderboard gaya hidup lokal dengan menggabungkan pencarian geospasial dan Fungsi AI di Alibaba Cloud Milvus

Last Updated:Aug 14, 2026

Alibaba Cloud Milvus menggabungkan tipe geospasial GEOMETRY dengan Fungsi AI sehingga satu kueri pencarian dapat melakukan penyaringan jangkauan geospasial dan pengambilan semantik sekaligus dalam satu Collection. Topik ini menjelaskan cara menjalankan penyaringan "di sekitar" dan pengambilan "sesuai selera" secara bersamaan, lalu menambahkan analisis sentimen dan pembuatan ringkasan untuk membangun leaderboard gaya hidup lokal yang dapat dijelaskan.

Ikhtisar solusi

"Apa yang enak dimakan di sekitar sini" adalah permintaan paling umum dalam skenario gaya hidup lokal. Untuk menjawabnya secara optimal, sistem harus memenuhi tiga kondisi sekaligus:

  • Di sekitar: toko berada dalam radius tertentu dari lokasi pengguna saat ini atau berada dalam kawasan bisnis tempat pengguna berada.

  • Sesuai selera: permintaan seperti "restoran Sichuan ramah keluarga" atau "kafe yang tenang untuk membicarakan urusan bisnis", yang tidak dapat dijabarkan secara tepat hanya dengan tag.

  • Dapat dipercaya: apa yang dikatakan ulasan nyata, seberapa tinggi tingkat ulasan positifnya, serta kelebihan dan keluhan apa yang muncul berulang kali.

    Implementasi tradisional biasanya menggabungkan tiga sistem terpisah: database spasial untuk penyaringan geospasial, database vektor untuk pengambilan semantik, dan layanan NLP untuk klasifikasi sentimen dan ringkasan. Setiap sistem menyimpan salinan datanya sendiri, sehingga operasi join lintas sistem, sinkronisasi data, dan pemeliharaan konsistensi menjadi mahal. Selain itu, satu kueri harus merangkai beberapa layanan dan beberapa putaran komunikasi jaringan.

Dimensi

Solusi tradisional (database spasial + database vektor + layanan NLP)

Alibaba Cloud Milvus

Jumlah sistem

3 atau lebih, masing-masing memiliki salinan data sendiri

1, dengan satu sumber data

Fusi geospasial dan semantik

Join lintas sistem atau beberapa putaran pengambilan

Satu panggilan search(): pencarian ANN + penyaringan ST_

Tinjau Pemahaman

Model sentimen dan ringkasan buatan sendiri atau eksternal

AI_SENTIMENT / AI_SUMMARIZE bawaan

Jalur vektorisasi

Aplikasi memanggil embedding terlebih dahulu, lalu menulis data

Vektorisasi saat insert

Konsistensi dan sinkronisasi

Dipelihara sendiri

Secara alami konsisten dalam satu database

Alibaba Cloud Milvus menggabungkan kemampuan-kemampuan ini ke dalam satu Collection dan satu pencarian:

Kemampuan

Implementasi

Penyaringan jangkauan geospasial

Simpan longitude dan latitude dalam bidang GEOMETRY, representasikan kawasan bisnis sebagai POLYGON, dan tulis fungsi relasi spasial seperti ST_WITHIN ke dalam ekspresi filter saat pencarian.

Pencarian semantik atas ulasan

Teks ulasan divectorisasi secara otomatis saat insert oleh AI_EMBEDDING, dan pencarian menjalankan kueri ANN terhadap vektor kueri yang dihasilkan oleh model yang sama.

Penyusunan ulang relevansi

Kandidat yang diambil disusun ulang berdasarkan relevansi terhadap kueri dengan AI_RERANK.

Kuantifikasi reputasi

Gunakan AI_SENTIMENT untuk mengklasifikasikan sentimen ulasan dan mengagregasi tingkat ulasan positif di sisi aplikasi, serta gunakan AI_SUMMARIZE untuk memadatkan ulasan toko menjadi ringkasan singkat.

Penyaringan geospasial dan pencarian semantik dijalankan dalam satu panggilan search(): pencarian vektor ANN menangani "sesuai selera", fungsi spasial dalam filter menangani "di sekitar", keduanya diselesaikan bersama dalam satu permintaan, dan penyusunan ulang diterapkan di atasnya untuk langsung menghasilkan kandidat leaderboard.

Topik ini menggunakan Fungsi AI berikut:

Fungsi

Fungsinya

Perannya dalam leaderboard

Cara pemanggilannya

AI_EMBEDDING

Mengonversi teks ulasan menjadi vektor secara otomatis saat insert, sehingga aplikasi tidak perlu memanggil model terlebih dahulu.

Memungkinkan permintaan semantik seperti "restoran Sichuan ramah keluarga" dicocokkan melalui pencarian vektor.

Dideklarasikan sebagai Fungsi dalam skema Collection (FunctionType.TEXTEMBEDDING).

AI_RERANK

Menyusun ulang kandidat yang diambil berdasarkan relevansi terhadap kueri.

Memindahkan toko yang paling sesuai permintaan ke posisi teratas leaderboard.

Dilampirkan sebagai ranker dalam search() (FunctionType.RERANK), atau dipanggil melalui endpoint REST /v2/vectordb/ai/rerank.

AI_SENTIMENT

Menentukan apakah ulasan bersifat positif, negatif, atau netral.

Mengagregasi tingkat ulasan positif suatu toko.

Dipanggil melalui endpoint REST /v2/vectordb/ai/sentiment.

AI_SUMMARIZE

Memadatkan banyak ulasan menjadi ringkasan singkat.

Menghasilkan komentar satu baris yang ditampilkan di leaderboard.

Dipanggil melalui endpoint REST /v2/vectordb/ai/summarize.

AI_EMBEDDING dan AI_RERANK dapat dideklarasikan dalam Collection atau dilampirkan pada pencarian, sedangkan AI_SENTIMENT dan AI_SUMMARIZE hanya tersedia melalui antarmuka REST.

Prasyarat

  • Instans Milvus 2.6 telah dibuat. Tipe GEOMETRY dan Fungsi AI memerlukan kernel versi 2.6. Setelah membuat instans, Anda tidak perlu mengikat layanan model secara terpisah—deklarasikan Fungsi langsung dalam Collection.

  • Untuk mengakses instans melalui Internet, Public Network Access diaktifkan pada tab Security Configuration di halaman detail instans, dan alamat IP egress client ditambahkan ke daftar putih akses publik.

  • pymilvus telah diinstal. Contoh dalam topik ini telah diverifikasi dengan pymilvus 3.0.0.

Batasan dan catatan penggunaan

Tinjau perilaku berikut sebelum menyalin kode, karena beberapa di antaranya menghasilkan hasil kosong atau diam tanpa error:

  • Semantik batas geospasial — Ketika objek geometri berada tepat di batas poligon, ST_WITHIN mengembalikan false dan ST_INTERSECTS mengembalikan true. Misalnya, ketika lingkaran radius diaproksimasi sebagai poligon, toko yang berada tepat di kelilingnya tidak akan cocok dengan ST_WITHIN. Jika logika bisnis Anda harus menyertakan batas, gunakan ST_INTERSECTS sebagai gantinya. Untuk MULTIPOINT, ST_WITHIN mengembalikan false begitu salah satu titiknya berada di batas.

  • Akurasi aproksimasi radiuscircle_to_wkt mengonversi longitude dan latitude dengan aproksimasi planar, sehingga kesalahan meningkat seiring kenaikan latitude. Metode ini cocok untuk penyaringan radius kasar. Jika logika bisnis Anda sensitif terhadap presisi batas, tingkatkan jumlah segmen num_seg atau verifikasi jarak eksak kembali di sisi aplikasi.

  • Port antarmuka REST — Antarmuka RESTful berbagi port 19530 dengan gRPC, sehingga Anda harus menyertakan port secara eksplisit dalam alamat saat memanggil antarmuka REST, misalnya http://c-xxx.milvus.aliyuncs.com:19530. Jika Anda menghilangkan port, permintaan akan menuju port 80 secara default dan koneksi akan timeout.

  • Parameter rerank REST — Untuk antarmuka rerank REST, documents harus berupa array string dan query harus berupa string tingkat atas. Parameter top_n tidak didukung—meneruskannya akan mengembalikan param [top_n] is not supported. Antarmuka selalu mengembalikan satu skor per kandidat, sehingga jika Anda perlu memotong daftar, urutkan berdasarkan skor dan ambil N teratas di sisi aplikasi.

  • Sentimen tak tentuAI_SENTIMENT dapat mengembalikan nilai kosong, yang berarti model tidak dapat menentukan sentimennya. Filter nilai kosong sebelum mengagregasi tingkat ulasan positif, agar sampel tak tentu tidak dihitung dalam penyebut.

Prosedur

Jalankan langkah-langkah berikut secara berurutan dalam satu sesi yang sama. Potongan kode membentuk satu skrip berkelanjutan: langkah selanjutnya menggunakan kembali client, fungsi pembantu, dan variabel yang didefinisikan pada langkah sebelumnya (misalnya, circle dari Langkah 3 dan QUERY serta geo_filter dari Langkah 4). Jika Anda menyalin satu langkah secara terpisah, definisikan terlebih dahulu variabel hulu-nya.

Langkah 0: Siapkan kode bersama (wajib)

Kode berikut berisi pengaturan koneksi, wrapper antarmuka REST, dan fungsi utilitas yang mengaproksimasi lingkaran radius sebagai poligon. Semua langkah selanjutnya bergantung pada kode ini. Ganti MILVUS_HOST dan MILVUS_TOKEN dengan informasi instans Anda sendiri.

from __future__ import annotations

import json
import math
import time
from typing import Any
from urllib.error import HTTPError
from urllib.request import Request, urlopen

from pymilvus import DataType, Function, FunctionType, MilvusClient

# ==================== Pengaturan koneksi ====================
MILVUS_HOST = "http://c-xxx.milvus.aliyuncs.com"
MILVUS_TOKEN = "root:xxx"
# Antarmuka RESTful berbagi port 19530 dengan gRPC. Sertakan port secara eksplisit,
# jika tidak, permintaan akan menuju port 80 secara default dan koneksi akan timeout.
MILVUS_REST_BASE_URL = f"{MILVUS_HOST}:19530"

client = MilvusClient(uri=MILVUS_HOST, token=MILVUS_TOKEN)
collection_name = "shop_reviews"

def post_json(path: str, body: dict[str, Any], timeout: int = 120,
              retries: int = 3) -> tuple[int, dict[str, Any]]:
    """Wrapper bersama untuk antarmuka REST AI_RERANK / AI_SENTIMENT / AI_SUMMARIZE."""
    last = None
    for _ in range(retries):
        request = Request(
            f"{MILVUS_REST_BASE_URL.rstrip('/')}{path}",
            data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
            headers={"Authorization": f"Bearer {MILVUS_TOKEN}",
                     "Content-Type": "application/json"},
            method="POST",
        )
        try:
            with urlopen(request, timeout=timeout) as response:
                status, data = response.status, json.loads(response.read().decode("utf-8"))
        except HTTPError as exc:
            status, data = exc.code, json.loads(exc.read().decode("utf-8"))
        last = (status, data)
        if status == 200 and data.get("code") == 0:
            return status, data
        time.sleep(1)
    return last

def circle_to_wkt(lng: float, lat: float, radius_km: float, num_seg: int = 32) -> str:
    """Aproksimasi lingkaran berpusat di (lng, lat) dengan radius radius_km sebagai poligon reguler dalam WKT.
    Catatan: longitude dan latitude adalah sudut. Konversi ini merupakan aproksimasi planar,
    sehingga kesalahan meningkat seiring kenaikan latitude."""
    d_lat = radius_km / 111.0
    d_lng = radius_km / (111.0 * math.cos(math.radians(lat)))
    pts = []
    for i in range(num_seg + 1):
        theta = 2 * math.pi * i / num_seg
        pts.append(f"{lng + d_lng * math.cos(theta):.6f} {lat + d_lat * math.sin(theta):.6f}")
    return f"POLYGON(({', '.join(pts)}))"

Nilai MILVUS_HOST digunakan sebagai URI client, sedangkan MILVUS_REST_BASE_URL menambahkan port 19530 untuk panggilan REST pada langkah selanjutnya. Persyaratan port eksplisit berlaku untuk antarmuka REST.

Langkah 1: Buat collection dengan bidang geospasial dan fungsi embedding

Bidang location menggunakan DataType.GEOMETRY untuk menyimpan titik koordinat toko. Bidang review membawa fungsi AI_EMBEDDING yang menghasilkan vektor berdimensi 1.024 secara otomatis saat insert. Pada saat yang sama, buat indeks HNSW untuk bidang vektor dan indeks R-Tree untuk bidang geospasial.

Peringatan

Kode berikut menjalankan drop_collection sebelum membuat Collection, yang secara permanen menghapus Collection bernama shop_reviews beserta seluruh datanya. Pada instans yang sudah berisi data bisnis, gunakan nama Collection uji terpisah atau hapus cabang drop_collection.

# ==================== Langkah 1: Buat collection ====================
if client.has_collection(collection_name):
    client.drop_collection(collection_name)

schema = MilvusClient.create_schema(auto_id=True, enable_dynamic_field=False)
schema.add_field("id", DataType.INT64, is_primary=True)
schema.add_field("shop_id", DataType.INT64)
schema.add_field("shop_name", DataType.VARCHAR, max_length=256)
schema.add_field("category", DataType.VARCHAR, max_length=64)
schema.add_field("location", DataType.GEOMETRY)                          # Simpan koordinat, WKT: POINT(lng lat)
schema.add_field("review", DataType.VARCHAR, max_length=4096)            # Teks ulasan mentah, digunakan sebagai input embedding
schema.add_field("review_embedding", DataType.FLOAT_VECTOR, dim=1024)    # Vektor ulasan

# Vectorisasi review secara otomatis saat insert
schema.add_function(
    Function(
        name="embed_review",
        function_type=FunctionType.TEXTEMBEDDING,
        input_field_names=["review"],
        output_field_names=["review_embedding"],
        params={
            "provider": "aliyun_milvus",
            "model_name": "qwen3.7-text-embedding",
            "dim": 1024,
            "max_client_batch_size": 20,
            "max_concurrency": 1,
        },
    )
)

index_params = client.prepare_index_params()
index_params.add_index(
    field_name="review_embedding",
    index_type="HNSW",
    metric_type="COSINE",
    params={"M": 16, "efConstruction": 200},
)
# Buat indeks geospasial R-Tree untuk bidang GEOMETRY guna mempercepat kueri spasial
index_params.add_index(
    field_name="location",
    index_type="RTREE",
)
client.create_collection(
    collection_name=collection_name,
    schema=schema,
    index_params=index_params,
)

Dimensi review_embedding harus sesuai dengan nilai dim dalam parameter Fungsi. Membuat indeks RTREE untuk bidang GEOMETRY mempercepat kueri spasial ST_ dan direkomendasikan saat volume data besar.

Nilai model_name dalam topik ini (qwen3.7-text-embedding di sini, serta model rerank, sentimen, dan ringkasan pada langkah selanjutnya) bersifat contoh. Ganti masing-masing dengan model yang tersedia pada instans Anda sendiri.

Langkah 2: Masukkan data toko dan ulasan

Masukkan koordinat toko dan teks ulasan mentah. review_embedding dihasilkan secara otomatis oleh Fungsi.

# ==================== Langkah 2: Masukkan data toko dan ulasan ====================
# review_embedding dihasilkan secara otomatis oleh Fungsi, jadi Anda tidak perlu meneruskan vektor secara manual
rows = [
    {"shop_id": 1001, "shop_name": "Bashu Homestead Family Sichuan", "category": "Sichuan cuisine",
     "location": "POINT(116.4612 39.9088)",
     "review": "Datang bersama anak saya. Tersedia kursi tinggi dan mangkuk kecil, maoxuewang tidak terlalu pedas, pelayan sabar, dan tempatnya bersih."},
    {"shop_id": 1001, "shop_name": "Bashu Homestead Family Sichuan", "category": "Sichuan cuisine",
     "location": "POINT(116.4612 39.9088)",
     "review": "Anda harus antre meja akhir pekan, tetapi hidangannya konsisten. Cocok untuk makan keluarga, dan anak saya menikmatinya."},
    {"shop_id": 1002, "shop_name": "Mala Jianghu", "category": "Sichuan cuisine",
     "location": "POINT(116.4501 39.9101)",
     "review": "Surga bagi pecinta rasa kuat, shuizhuyu-nya mantap, tetapi ruangannya sempit dan bising, jadi tidak cocok untuk anak-anak."},
    {"shop_id": 1003, "shop_name": "Quiet Lane Coffee", "category": "Coffee",
     "location": "POINT(116.4550 39.9075)",
     "review": "Cukup tenang untuk membicarakan urusan bisnis dan pour-over-nya enak, tetapi tidak ada hubungannya dengan permintaan makanan Sichuan."},
]
client.insert(collection_name, rows)
client.flush(collection_name)
client.load_collection(collection_name)

Panggil flush() setelah memasukkan data, jika tidak pencarian yang dijalankan segera setelahnya mungkin mengembalikan hasil kosong. load_collection() memuat Collection ke memori, yang wajib dilakukan sebelum Anda dapat mencarinya.

Langkah 3: Penyaringan berdasarkan jangkauan geospasial

Pilih metode jangkauan berdasarkan bentuk area target Anda:

  • Kawasan administratif atau kawasan bisnis — Representasikan area sebagai POLYGON dan gunakan ST_WITHIN.

  • Area di sekitar lokasi pengguna saat ini — Aproksimasi lingkaran radius sebagai poligon dengan circle_to_wkt, lalu gunakan ST_WITHIN.

  • Logika yang harus menyertakan batas atau mendeteksi area tumpang tindih — Gunakan ST_INTERSECTS sebagai gantinya, karena geometri yang berada tepat di batas tidak akan cocok dengan ST_WITHIN.

    Kode berikut menunjukkan kedua jenis poligon: kawasan bisnis dan lingkaran radius. Lingkaran radius diaproksimasi sebagai poligon oleh circle_to_wkt.

# ==================== Langkah 3: Penyaringan jangkauan geospasial ====================
# 3.1 Kueri toko dalam poligon kawasan bisnis
cbd_polygon = ("POLYGON((116.448 39.905, 116.465 39.905, 116.465 39.914, "
               "116.448 39.914, 116.448 39.905))")
hits = client.query(
    collection_name=collection_name,
    filter=f"ST_WITHIN(location, '{cbd_polygon}')",
    output_fields=["shop_name", "category", "location"],
    limit=100,
)
print("Catatan ulasan dalam kawasan bisnis:")
for h in hits:
    print(f"  - {h['shop_name']} ({h['category']})  {h['location']}")

# 3.2 Kueri dalam radius 3 km: aproksimasi lingkaran sebagai poligon, lalu periksa apakah toko berada di dalamnya
center_lng, center_lat = 116.4600, 39.9090
circle = circle_to_wkt(center_lng, center_lat, radius_km=3.0)
nearby = client.query(
    collection_name=collection_name,
    filter=f"ST_WITHIN(location, '{circle}')",
    output_fields=["shop_name", "category", "location"],
    limit=200,
)
print(f"Catatan ulasan dalam radius 3 km dari titik pusat ({center_lng}, {center_lat}):")
for n in nearby:
    print(f"  - {n['shop_name']} ({n['category']})  {n['location']}")

Data sampel memiliki 4 catatan ulasan dari 3 toko (Bashu Homestead memiliki 2 ulasan), sehingga kedua kueri mengembalikan 4 catatan ulasan. Untuk menghasilkan satu baris per toko, lakukan deduplikasi berdasarkan shop_id di sisi aplikasi.

Langkah 4: Gabungkan pencarian semantik dengan penyaringan geospasial

Langkah ini menggabungkan "di sekitar" (fungsi spasial dalam filter) dan "sesuai selera" (pencarian vektor ANN) dalam satu panggilan search(). Langkah ini menggunakan kembali circle dari Langkah 3.

# ==================== Langkah 4: Gabungkan pencarian semantik dengan penyaringan geospasial ====================
# Pencarian ANN atas vektor ulasan menangani "sesuai selera", fungsi spasial dalam filter menangani "di sekitar",
# dan keduanya diselesaikan bersama dalam satu permintaan
QUERY = "restoran Sichuan ramah keluarga di sekitar sini"
geo_filter = f"category == 'Sichuan cuisine' && ST_WITHIN(location, '{circle}')"
results = client.search(
    collection_name=collection_name,
    data=[QUERY],
    anns_field="review_embedding",
    filter=geo_filter,
    limit=20,
    output_fields=["shop_id", "shop_name", "category", "location", "review"],
)
print(f"Kueri: {QUERY}")
for rank, hit in enumerate(results[0], 1):
    e = hit["entity"]
    print(f"  {rank}. [kemiripan {hit['distance']:.4f}]  {e['shop_name']} | {e['review'][:24]}")

Contoh berikut menunjukkan hasil yang dikembalikan. "Bashu Homestead Family Sichuan" berada di peringkat pertama karena ulasannya menyebut "kursi tinggi" dan "pelayan sabar", yang secara semantik dekat dengan "ramah keluarga":

1. [kemiripan 0.6926]  Bashu Homestead Family Sichuan | Datang bersama anak saya. Kur
2. [kemiripan 0.6591]  Mala Jianghu | Surga bagi pecinta rasa 
3. [kemiripan 0.6091]  Bashu Homestead Family Sichuan | Anda harus antre meja akhi

Langkah 5: Susun ulang kandidat dengan AI_RERANK

Kemiripan vektor mencerminkan kedekatan semantik, yang tidak persis sama dengan relevansi hasil terhadap kueri. Menyusun ulang kandidat yang diambil dengan AI_RERANK memindahkan toko yang lebih sesuai maksud kueri ke posisi depan. Langkah ini menggunakan kembali QUERY dan geo_filter dari Langkah 4.

Tersedia dua opsi; pilih salah satu sesuai skenario Anda:

  • Penyusunan ulang dalam pipeline pengambilan — Lampirkan ranker dalam search() (digunakan dalam 5.1). Model menyusun ulang kandidat yang diambil oleh pencarian ANN dari Collection.

  • Penyusunan ulang daftar teks yang sudah ada — Panggil antarmuka REST tanpa status /v2/vectordb/ai/rerank (digunakan dalam 5.2). Jalur ini menerima daftar teks secara langsung dan tidak bergantung pada pencarian.

    Dua subbagian di bawah ini bersifat alternatif, bukan langkah berurutan.

# ==================== Langkah 5: Susun ulang kandidat dengan AI_RERANK ====================
RERANK_MODEL = "qwen3-rerank"

# 5.1 Lampirkan ranker RERANK dalam search(): setelah pengambilan ANN, model menyusun ulang kandidat
reranker = Function(
    name="rerank_reviews",
    function_type=FunctionType.RERANK,
    input_field_names=["review"],
    params={
        "reranker": "model",
        "provider": "aliyun_milvus",
        "model_name": RERANK_MODEL,
        "queries": [QUERY],
        "timeout_sec": 10,
    },
)
results = client.search(
    collection_name=collection_name,
    data=[QUERY],
    anns_field="review_embedding",
    filter=geo_filter,
    limit=20,
    output_fields=["shop_id", "shop_name", "review"],
    ranker=reranker,
)
for rank, hit in enumerate(results[0], 1):
    e = hit["entity"]
    print(f"  {rank}. [skor rerank {hit['distance']:.4f}]  {e['shop_name']} | {e['review'][:24]}")

# 5.2 Anda juga dapat menggunakan antarmuka REST tanpa status /v2/vectordb/ai/rerank
rerank_docs = [
    "Datang bersama anak saya. Tersedia kursi tinggi dan mangkuk kecil, maoxuewang tidak terlalu pedas, pelayan sabar, dan tempatnya bersih.",
    "Surga bagi pecinta rasa kuat, shuizhuyu-nya mantap, tetapi ruangannya sempit dan bising, jadi tidak cocok untuk anak-anak.",
]
status, data = post_json(
    "/v2/vectordb/ai/rerank",
    {
        "model_name": RERANK_MODEL,
        "query": QUERY,
        "documents": rerank_docs,
        "params": {"timeout_sec": 10},
    },
)
assert status == 200 and data.get("code") == 0, data
ranked = sorted(data["data"]["output"]["results"],
                key=lambda x: x["relevance_score"], reverse=True)
for rank, item in enumerate(ranked, 1):
    print(f"  {rank}. [relevansi {item['relevance_score']:.4f}]  {rerank_docs[item['index']][:28]}")

Contoh berikut menunjukkan output setelah penyusunan ulang. Kedua ulasan "Bashu Homestead Family Sichuan" berada di atas "Mala Jianghu":

1. [skor rerank 0.6986]  Bashu Homestead Family Sichuan | Datang bersama anak saya. Kur
2. [skor rerank 0.5389]  Bashu Homestead Family Sichuan | Anda harus antre meja akhi
3. [skor rerank 0.5214]  Mala Jianghu | Surga bagi pecinta rasa 

Antarmuka REST mengembalikan hasilnya dalam data.output.results, di mana setiap item berkorespondensi dengan satu kandidat dan berisi index (posisi kandidat dalam parameter permintaan documents) dan relevance_score (skor relevansi). Mengurutkan berdasarkan skor secara menurun memberikan hasil yang telah disusun ulang:

1. [relevansi 0.6986]  Datang bersama anak saya. Tersedia kursi tinggi
2. [relevansi 0.5214]  Surga bagi pecinta rasa kuat,

Teks kandidat harus mencakup setiap dimensi semantik kueri. Kueri dalam contoh ini, "restoran Sichuan ramah keluarga di sekitar sini", mencakup dimensi "ramah keluarga" dan "masakan Sichuan", dan kandidat yang kehilangan salah satunya akan kehilangan skor signifikan. Ambil kandidat pertama sebagai contoh:

Teks kandidat

Skor relevansi

Datang bersama anak saya. Tersedia kursi tinggi dan mangkuk kecil, pelayan sabar. (tidak ada informasi masakan Sichuan)

0.4723

Datang bersama anak saya. Tersedia kursi tinggi dan mangkuk kecil, maoxuewang tidak terlalu pedas, pelayan sabar.

0.6666

Datang bersama anak saya. Tersedia kursi tinggi dan mangkuk kecil, maoxuewang tidak terlalu pedas, pelayan sabar, dan tempatnya bersih.

0.6986

Menambahkan satu nama hidangan Sichuan meningkatkan skor dari 0.4723 menjadi 0.6666. Sebaliknya, "Surga bagi pecinta rasa kuat, shuizhuyu-nya mantap ... tidak cocok untuk anak-anak." mendapat skor pada dimensi masakan Sichuan karena menyebut "shuizhuyu", dan relevansinya tetap di 0.5214. Jadi jika kandidat pertama tidak memiliki informasi masakan Sichuan, posisinya justru akan lebih rendah.

Oleh karena itu, dalam produksi, kirimkan teks asli lengkap sebagai kandidat untuk penyusunan ulang, dan jangan memangkas informasi penting yang mungkin membawa relevansi, seperti nama hidangan atau kata kategori, hanya untuk memperpendek teks. Saat mengevaluasi kualitas penyusunan ulang, gunakan juga teks lengkap yang sama seperti yang digunakan online.

Langkah 6: Hitung tingkat ulasan positif dan hasilkan ringkasan ulasan

Gunakan AI_SENTIMENT untuk mengklasifikasikan sentimen semua ulasan suatu toko dan mengagregasi tingkat ulasan positif di sisi aplikasi. Lalu gunakan AI_SUMMARIZE untuk memadatkan ulasan menjadi komentar satu baris. Langkah ini membaca data yang dimasukkan di Langkah 2.

# ==================== Langkah 6: Analisis sentimen untuk tingkat ulasan positif + pembuatan ringkasan ====================
shop_id = 1001
reviews = [r["review"] for r in client.query(
    collection_name=collection_name,
    filter=f"shop_id == {shop_id}",
    output_fields=["review"],
    limit=1000,
)]

# 6.1 Klasifikasi sentimen dan agregasi tingkat ulasan positif
status, sent = post_json("/v2/vectordb/ai/sentiment", {
    "model_name": "qwen3.7-max",
    "texts": reviews,
    "params": {"categories": ["positive", "negative", "neutral"], "temperature": 0},
})
assert status == 200 and sent.get("code") == 0, sent
labels = sent["data"]["output"]["outputs"]
valid = [x for x in labels if x]              # Nilai kosong dianggap tak tentu dan dikecualikan dari agregasi
positive = sum(1 for x in valid if x == "positive")
good_rate = positive / len(valid) if valid else 0.0
print(f"Tingkat ulasan positif: {good_rate:.0%}   (sampel valid {len(valid)}/{len(labels)})")

# 6.2 Hasilkan ringkasan ulasan toko
status, summ = post_json("/v2/vectordb/ai/summarize", {
    "model_name": "qwen3.7-max",
    "texts": ["\n".join(reviews)],
    "params": {"max_words": 40, "temperature": 0,
               "prompt": "Summarize the overall reviews of this store in English, highlighting the signature dishes, the diners it suits, and the atmosphere."},
})
assert status == 200 and summ.get("code") == 0, summ
print(f"Ringkasan toko: {summ['data']['output']['outputs'][0]}")

Ambil shop_id 1001 sebagai contoh: kedua ulasan diklasifikasikan sebagai positif, tingkat ulasan positifnya 100%, dan ringkasan yang dihasilkan adalah "Tempatnya bersih dan maoxuewang-nya lembut serta enak. Pelayanannya sabar dan tersedia peralatan makan anak, sehingga cocok untuk makan keluarga bersama anak, meski Anda harus antre meja akhir pekan."

Agregasi mencocokkan setiap label dengan nilai yang Anda teruskan dalam parameter permintaan categories. AI_SENTIMENT juga dapat mengembalikan nilai kosong ketika model tidak dapat menentukan sentimennya; filter nilai kosong sebelum mengagregasi, seperti yang ditunjukkan oleh daftar valid.

Tipe geometri dan fungsi spasial

Bagian ini menjelaskan tipe GEOMETRY dan fungsi relasi spasial ST_ yang digunakan dalam prosedur.

GEOMETRY dan WKT

Jika Anda menyimpan longitude dan latitude sebagai dua bidang DOUBLE, database memperlakukannya sebagai dua angka dan tidak dapat menentukan "mana yang berada di dalam mana" atau "apakah dua area saling berpotongan". GEOMETRY adalah tipe geospasial yang disediakan oleh Milvus 2.6. Tipe ini menyimpan titik, garis, dan poligon sebagai objek geometri, yang memungkinkan operasi relasi spasial dan memungkinkan indeks geospasial mempercepat kueri.

Nilai bidang GEOMETRY menggunakan format WKT (Well-Known Text). Tipe umumnya sebagai berikut:

Tipe WKT

Contoh

Makna

Padanan dalam topik ini

POINT

POINT(116.4612 39.9088)

Titik tunggal

Koordinat satu toko

LINESTRING

LINESTRING(116.46 39.90, 116.47 39.91)

Polyline

Segmen jalan atau rute pengiriman

POLYGON

POLYGON((116.448 39.905, ..., 116.448 39.905))

Area tertutup yang koordinat awal dan akhirnya identik

Kawasan bisnis atau area pengiriman

MULTIPOINT

MULTIPOINT((116.46 39.90), (116.47 39.91))

Beberapa titik

Beberapa toko dari satu merek

MULTIPOLYGON

MULTIPOLYGON(((...)),((...)))

Beberapa area

Beberapa kawasan administratif

Koordinat diurutkan longitude terlebih dahulu lalu latitude, yaitu POINT(lng lat), yang merupakan kebalikan dari urutan lisan "latitude ... longitude ...". Semua koordinat dalam topik ini menggunakan sistem koordinat WGS84.

Fungsi relasi spasial ST_

Fungsi yang diawali ST_ menentukan relasi spasial antara dua objek geometri. Anda menuliskannya dalam ekspresi filter dan mengembalikan true atau false. Topik ini terutama menggunakan tiga fungsi berikut:

Fungsi

Deskripsi

Penggunaan dalam topik ini

ST_WITHIN(A, B)

Apakah A berada sepenuhnya di dalam B.

Menentukan apakah toko berada di dalam poligon kawasan bisnis, atau dalam radius tertentu.

ST_CONTAINS(A, B)

Apakah A sepenuhnya memuat B, kebalikan dari ST_WITHIN.

Menentukan apakah kawasan bisnis memuat toko tertentu, untuk analisis atribusi.

ST_INTERSECTS(A, B)

Apakah A dan B memiliki irisan atau kontak apa pun.

Menentukan apakah area pengiriman toko beririsan dengan sel grid tempat pengguna berada.

Fungsi berikut juga didukung dan dapat digunakan sesuai kebutuhan:

Fungsi

Deskripsi

Skenario

ST_EQUALS

Dua objek geometri identik.

Deduplikasi geometri.

ST_TOUCHES

Hanya batas yang bersentuhan dan interior tidak tumpang tindih.

Menentukan apakah kawasan bisnis bersebelahan saling berbatasan.

ST_OVERLAPS

Tumpang tindih parsial tanpa keterkandungan.

Menentukan apakah area pengiriman saling bersilangan.

ST_CROSSES

Melintasi persimpangan.

Menentukan apakah jalan melewati kawasan bisnis.

Logika peringkat leaderboard

Merangkai langkah-langkah di atas menghasilkan satu alur lengkap pembuatan leaderboard: penyaringan geospasial mempersempit kandidat menjadi "di sekitar", pencarian semantik mencocokkan "sesuai selera", penyusunan ulang menyesuaikan urutan, dan sentimen plus ringkasan melengkapi "reputasi". Untuk menghasilkan satu baris per toko, lakukan deduplikasi kandidat berdasarkan shop_id di sisi aplikasi.

Posisi akhir dapat menggabungkan tiga sinyal:

  • Sinyal geospasial: apakah toko berada dalam radius atau kawasan bisnis bertindak sebagai ambang batas penerimaan, dan toko yang tidak memenuhi syarat langsung difilter. Toko yang lolos kemudian mendapatkan bobot dasar sesuai jarak.

  • Relevansi semantik: relevance_score yang dikembalikan oleh AI_RERANK, yang mengukur seberapa baik ulasan toko sesuai permintaan pengguna.

  • Reputasi: tingkat ulasan positif yang diagregasi oleh AI_SENTIMENT.

    Anda dapat menggabungkan sinyal-sinyal ini dengan fusi berbobot dalam bentuk w1 · skor relevansi + w2 · tingkat ulasan positif + w3 · peluruhan jarak, dan sesuaikan bobotnya sesuai tujuan bisnis Anda. Dengan fusi ini, toko dapat berada di peringkat tinggi secara keseluruhan karena sangat sesuai dengan semantik "ramah keluarga" dan kombinasi tersebut diperkuat oleh tingkat ulasan positif yang tinggi serta jarak yang lebih dekat, bukan hanya berdasarkan peringkat saja.

Ekstensi lanjutan

  • Pencarian toko multimodal: gunakan qwen3-vl-embedding untuk vectorisasi foto toko dan foto hidangan guna mendukung pencarian toko berbasis gambar seperti "tempat di sekitar dengan dekorasi seperti ini", yang juga dapat digabungkan dengan penyaringan geospasial dalam satu pencarian.

  • Prahitung reputasi offline: ketika toko memiliki banyak ulasan, gunakan metode batch seperti AI_BATCH untuk menghitung sebelumnya tingkat ulasan positif dan ringkasan ulasan secara offline dan menyimpannya, sehingga kueri leaderboard hanya membaca hasilnya, yang menyeimbangkan biaya dan latensi.

  • Relasi spasial yang lebih kaya: gunakan ST_INTERSECTS untuk menentukan apakah area pengiriman toko beririsan dengan sel grid tempat pengguna berada, dan gunakan ST_CONTAINS untuk analisis atribusi kawasan bisnis, memperluas fusi "geospasial + AI" dari pencarian toko hingga pengiriman, pemilihan lokasi, dan langkah lainnya.