All Products
Search
Document Center

Vector Retrieval Service for Milvus:Kurangi Biaya Pemanggilan AI di Alibaba Cloud Milvus dengan Embedding Cache dan AI Batch

Last Updated:Aug 14, 2026

Alibaba Cloud Milvus AI Function menyediakan dua fitur penghematan biaya: Embedding Cache yang menggunakan kembali vektor yang sudah ada untuk konten duplikat, dan AI Batch yang memproses pekerjaan besar yang dapat ditunda secara offline. Dalam tutorial ini, Anda akan mengonfigurasi kedua fitur tersebut, menentukan skenario penerapannya masing-masing, serta memverifikasi cache hit menggunakan kriteria yang andal.

Ikhtisar solusi

Setelah aplikasi AI berjalan, tekanan biaya umumnya berasal dari dua jenis pemborosan:

  • Konten yang sama di-embed berulang kali — judul produk disinkronkan berulang kali, FAQ dukungan dipublikasikan ulang, dan paragraf basis pengetahuan dimasukkan kembali ke model embedding melalui retry atau impor inkremental. Teks tersebut tidak berubah, sehingga vektornya umumnya juga tidak berubah, tetapi pemanggilan model dan waktu tunggunya tetap terjadi.

  • Pekerjaan yang bisa ditunda menggunakan pemanggilan real-time — pemanggilan real-time adalah jalur sinkron yang mengembalikan setiap hasil dalam respons, berbeda dengan batch offline yang dijalankan oleh AI Batch. Mengisi deskripsi produk, merangkum percakapan historis, menerjemahkan konten, dan menginisialisasi basis pengetahuan semuanya dapat dijalankan pada malam hari dan dikirimkan keesokan pagi tanpa memengaruhi pengalaman pengguna. Mengirim pekerjaan ini melalui pemanggilan real-time berarti membayar harga real-time sekaligus mengonsumsi kuota model yang dibutuhkan oleh pencarian online dan Q&A.

Dua pendekatan penghematan biaya yang sesuai adalah melakukan embedding konten yang sama hanya sekali dan memproses pekerjaan yang bisa ditunda dalam batch offline. Alibaba Cloud Milvus AI Function mencakup kedua kasus ini masing-masing melalui Embedding Cache dan AI Batch:

KemampuanFungsinyaPemborosan yang dihilangkan
Embedding CacheMencari vektor yang sudah ada berdasarkan kecocokan eksak pada teks dan konteks pemanggilan, lalu menggunakannya kembali jika ditemukan (cache hit).Konten yang sama di-embed hanya sekali, menghemat token duplikat dan waktu tunggu.
AI BatchPermintaan ditulis ke JSONL dan dikirimkan secara asinkron, lalu platform menjalankannya secara offline dalam batch.Pekerjaan besar yang bisa ditunda selesai dengan harga satuan lebih rendah, tanpa mengonsumsi kuota online.

Kedua fitur ini mengatasi jenis pemborosan yang berbeda. Tabel berikut memetakan karakteristik bisnis ke masing-masing fitur:

Karakteristik bisnisEmbedding CacheAI Batch
Pola dataTeks yang sama muncul berulang kaliVolume data besar diproses untuk pertama kalinya
Persyaratan responsDikembalikan secara onlineBoleh selesai belakangan
Sumber penghematanLebih sedikit pemanggilan model duplikatEksekusi batch offline dengan harga satuan lebih rendah
Skenario umumJawaban FAQ dan ground truth, judul dan atribut produk populer, paragraf basis pengetahuan yang diimpor berulang, istilah pencarian frekuensi tinggiMenghasilkan ringkasan atau tag untuk dokumen arsip, pemrosesan batch data produk yang ada, pembuatan deskripsi aset secara batch, evaluasi model dan pelabelan data, pembangunan ulang periodik dan pekerjaan malam hari

Jawab dua pertanyaan berikut untuk memilih pendekatan yang tepat:

  • Apakah pengguna menunggu hasilnya? Jika ya, gunakan pemanggilan real-time. Jika tidak dan volumenya besar, pertimbangkan AI Batch.

  • Apakah kontennya muncul berulang kali? Jika ya, aktifkan Embedding Cache. Jika semua konten muncul untuk pertama kalinya, manfaat cache terbatas, sehingga fokuslah pada AI Batch dan pengendalian jumlah pemanggilan.

Skenario interaktif seperti balasan chat dan pengisian otomatis pencarian tetap menggunakan pemanggilan real-time secara default. Selama pengguna menunggu hasilnya, jangan alihkan permintaan ke AI Batch.

Kedua fitur ini tidak saling eksklusif, dan menggabungkannya merupakan praktik umum di lingkungan produksi. Pertimbangkan skenario e-commerce: produk baru terus di-embed secara real-time, sementara sinkronisasi berulang produk populer menggunakan kembali vektor melalui Embedding Cache. Ringkasan dan tag untuk beberapa juta produk arsip diserahkan ke AI Batch untuk diselesaikan pada malam hari.

Prasyarat

  • Instans Milvus 2.6. AI Function bergantung pada kernel 2.6, dan tidak memerlukan binding layanan model terpisah setelah instans dibuat.

  • Untuk mengakses instans melalui Internet, aktifkan Public Access pada tab Security Configuration halaman detail instans, dan tambahkan alamat IP egress klien ke daftar putih akses publik.

  • pymilvus terinstal untuk contoh Embedding Cache. Contoh dalam topik ini telah diverifikasi dengan pymilvus 3.0.0. Contoh AI Batch memanggil RESTful API dan hanya menggunakan pustaka standar Python.

Penting

RESTful API berbagi port 19530 dengan gRPC, jadi tentukan port secara eksplisit saat memanggilnya, misalnya http://c-xxx.milvus.aliyuncs.com:19530. Jika Anda menghilangkan port, permintaan akan menuju port 80 secara default dan koneksi akan timeout.

Embedding Cache: lakukan embedding konten yang sama hanya sekali

Cara kerja Embedding Cache dan tempat penerapannya

Embedding Cache pertama-tama mencari vektor yang sudah ada berdasarkan teks dan konteks pemanggilan, menggunakan kembali langsung jika ditemukan (cache hit), dan hanya memanggil model untuk konten yang muncul pertama kali. Cache menggunakan kecocokan eksak, sehingga dua potongan teks yang ditulis berbeda akan dihitung secara terpisah. Manfaatnya sepenuhnya berasal dari tingkat duplikasi konten: semakin tinggi duplikasi, semakin banyak permintaan model yang dihemat. Untuk perkiraan orde besaran, lihat Estimasi Biaya.

Jika cache tidak tersedia atau pembacaan timeout, Milvus tetap memanggil model, sehingga operasi tulis dan kueri Anda tidak terganggu.

Aktifkan Embedding Cache

Tambahkan konfigurasi cache ke params Fungsi Embedding. Atur ttl_hours sesuai seberapa sering konten berubah: perpanjang untuk konten yang jarang berubah, seperti judul produk dan FAQ, dan perpendek untuk konten yang cepat berubah agar versi baru segera dihitung ulang.

import json
import uuid
from urllib.error import HTTPError
from urllib.request import Request, urlopen

from pymilvus import DataType, Function, FunctionType, MilvusClient

MILVUS_URI = "http://c-xxx.milvus.aliyuncs.com:19530"  # Port harus 19530
MILVUS_TOKEN = "root:xxx"
MODEL_NAME = "text-embedding-v4"
VECTOR_DIM = 1024

# Konfigurasi cache: kecocokan eksak, backend Redis, TTL 24 jam
CACHE_CONFIG = json.dumps({
    "enabled": True,
    "exact_cache": {
        "enabled": True,
        "backend": "redis",
        "ttl_hours": 24,
    },
})

client = MilvusClient(uri=MILVUS_URI, token=MILVUS_TOKEN)
collection_name = "ai_embedding_cache_demo"

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("content", DataType.VARCHAR, max_length=4096)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=VECTOR_DIM)
schema.add_function(
    Function(
        name="embed_content_with_cache",
        function_type=FunctionType.TEXTEMBEDDING,
        input_field_names=["content"],
        output_field_names=["embedding"],
        params={
            "provider": "aliyun_milvus",
            "model_name": MODEL_NAME,
            "dim": VECTOR_DIM,
            "cache": CACHE_CONFIG,        # ← Aktifkan Embedding Cache
        },
    )
)

index_params = client.prepare_index_params()
index_params.add_index(field_name="embedding", index_type="AUTOINDEX", metric_type="COSINE")
client.create_collection(collection_name=collection_name, schema=schema,
                        index_params=index_params)

# Konten yang mengenai cache saat penulisan tidak menghasilkan pemanggilan model
row = {"content": "Milvus is an open-source vector database."}
client.insert(collection_name, [row])
client.flush(collection_name)

Konten yang mengenai cache saat penulisan tidak menghasilkan pemanggilan model. Untuk mengonfirmasi bahwa cache berlaku, lihat Verifikasi Cache Hit.

Verifikasi cache hit

Konfirmasi cache hit dari respons titik akhir embedding RESTful, yang mengembalikan usage.total_tokens dan request_id. Saat terjadi cache hit, tidak ada pemanggilan model yang benar-benar terjadi, sehingga konsumsi token turun menjadi nol dan tidak ada request ID yang dihasilkan di sisi model.

Penting

Baik perbandingan vektor maupun latensi penulisan bukan kriteria yang valid untuk cache hit. Langkah ini mudah salah.

Dua pemeriksaan intuitif tidak berfungsi:

  • Perbandingan vektor — model mengembalikan vektor yang sama untuk input yang sama. Dalam pengujian untuk topik ini, menulis teks yang sama dua kali dengan cache dinonaktifkan sepenuhnya tetap menghasilkan perbedaan elemen-per-elemen sebesar 0.

  • Latensi penulisan — overhead inheren dari satu operasi insert plus flush jauh melebihi satu pemanggilan model, dan dalam pengujian penulisan kedua bahkan bisa lebih lambat daripada yang pertama.

Kode berikut mengirimkan teks baru yang sama tiga kali dan melaporkan hasil setiap permintaan. Jalankan dalam skrip atau sesi yang sama dengan langkah sebelumnya, karena kode ini menggunakan kembali impor dan konstanta yang telah didefinisikan.

# ==================== Verifikasi apakah cache benar-benar terkena ====================
# Kriteria: apakah usage.total_tokens turun menjadi nol dan apakah request_id kosong.
# Jika keduanya terpenuhi, tidak ada pemanggilan model yang benar-benar terjadi, artinya cache hit.

def post_json(path, body, timeout=180):
    request = Request(
        f"{MILVUS_URI.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:
            return response.status, json.loads(response.read().decode("utf-8"))
    except HTTPError as exc:
        return exc.code, json.loads(exc.read().decode("utf-8"))

# Gunakan teks yang benar-benar baru agar permintaan pertama pasti miss
text = f"cache hit verification {uuid.uuid4().hex[:12]}"
body = {
    "model_name": MODEL_NAME,
    "texts": [text],
    "params": {"dim": VECTOR_DIM, "cache": CACHE_CONFIG},
}

for i in (1, 2, 3):
    status, data = post_json("/v2/vectordb/ai/embedding", body)
    assert status == 200 and data.get("code") == 0, data
    usage = data["data"].get("usage", {})
    request_id = data["data"].get("request_id", "")
    total_tokens = usage.get("total_tokens")
    hit = (total_tokens == 0) and not request_id
    print(f"Request {i}: total_tokens={total_tokens} "
          f"request_id={'(empty)' if not request_id else request_id} "
          f"-> {'cache hit' if hit else 'cache miss, the model was invoked'}")

Tabel berikut mencantumkan hasil pengukuran dari tiga permintaan berturut-turut untuk teks baru yang sama:

Pesananusage.total_tokensrequest_idHasil
Request 120Memiliki nilaiCache miss, model benar-benar dipanggil
Request 20KosongCache hit
Request 30KosongCache hit

Kedua bidang tersebut tersedia langsung dalam badan respons dan tidak memerlukan izin tambahan. Untuk validasi silang di sisi server, pantau perubahan jumlah pemanggilan model embedding di Konsol.

AI Batch: proses pekerjaan yang bisa ditunda secara offline

Cara kerja AI Batch dan tempat penerapannya

AI Batch menerima permintaan yang ditulis dalam format JSONL, menerimanya secara asinkron, dan menjalankannya secara offline dalam batch. Model operasional khas di e-commerce menyisihkan kuota model selama hari kerja untuk pengguna yang sedang mencari dan mengajukan pertanyaan. Data produk dan percakapan dukungan yang terakumulasi sepanjang hari kemudian diproses pada malam hari: menghasilkan deskripsi produk secara massal, mengisi tag, dan mengubah percakapan panjang menjadi ringkasan. Hasilnya ditulis kembali ke sistem bisnis sebelum hari kerja berikutnya, tanpa membayar harga real-time untuk data tersebut.

Siapkan file input dan jalankan pekerjaan batch

Input berupa JSONL, dengan satu baris per pekerjaan. Gunakan custom_id untuk menghubungkan setiap pekerjaan dengan data aslinya sehingga hasilnya dapat dipetakan kembali ke catatan tertentu:

{"custom_id":"article-001","method":"POST","url":"/v1/chat/completions","body":{"model":"qwen3.7-max","messages":[{"role":"user","content":"Generate a one-sentence summary for this knowledge base article..."}],"enable_thinking":false}}
{"custom_id":"article-002","method":"POST","url":"/v1/chat/completions","body":{"model":"qwen3.7-max","messages":[{"role":"user","content":"Generate a one-sentence summary for this knowledge base article..."}],"enable_thinking":false}}
Penting

Nilai body.model pada setiap baris harus sesuai dengan model_name yang dideklarasikan saat unggah, atau pekerjaan akan gagal.

Proses lengkap terdiri dari empat langkah: unggah file JSONL, buat pekerjaan, polling status, dan unduh hasil. AI Batch menggunakan RESTful API, sehingga blok berikut mendefinisikan konstanta dan fungsi pembantu untuk permintaan terautentikasi, unggah multi-bagian, dan pengunduhan hasil. Jalankan blok ini terlebih dahulu, karena keempat langkah tersebut menggunakan kembali fungsi-fungsi ini.

Fungsi pembantu dan konstanta untuk RESTful API AI Batch

import json
import shutil
import tempfile
import time
import uuid
from pathlib import Path
from urllib.error import HTTPError
from urllib.request import Request, urlopen

# AI Batch menggunakan RESTful API. Setel base_url ke alamat instans (port 19530)
MILVUS_BASE_URL = "http://c-xxx.milvus.aliyuncs.com:19530"
MILVUS_TOKEN = "root:xxx"
MODEL_NAME = "qwen3.7-max"     # Harus sesuai dengan body.model di setiap baris input.jsonl
PROVIDER = "aliyun_milvus"
ENDPOINT = "/v1/chat/completions"

def post_json(path, body, timeout=180):
    request = Request(
        f"{MILVUS_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:
            return response.status, json.loads(response.read().decode("utf-8"))
    except HTTPError as exc:
        return exc.code, json.loads(exc.read().decode("utf-8"))

def upload_input_file(input_file):
    """Unggah input.jsonl sebagai multipart"""
    boundary = f"----milvus-ai-batch-{uuid.uuid4().hex}"
    fields = {"provider": PROVIDER, "model_name": MODEL_NAME,
              "endpoint": ENDPOINT, "purpose": "batch"}
    with tempfile.TemporaryFile(mode="w+b") as payload:
        for name, value in fields.items():
            payload.write(f"--{boundary}\r\n".encode())
            payload.write(f'Content-Disposition: form-data; name="{name}"\r\n\r\n'.encode())
            payload.write(value.encode())
            payload.write(b"\r\n")
        payload.write(f"--{boundary}\r\n".encode())
        payload.write(b'Content-Disposition: form-data; name="file"; filename="input.jsonl"\r\n')
        payload.write(b"Content-Type: application/jsonl\r\n\r\n")
        with open(input_file, "rb") as fh:
            shutil.copyfileobj(fh, payload)
        payload.write(b"\r\n")
        payload.write(f"--{boundary}--\r\n".encode())

        length = payload.tell()
        payload.seek(0)
        request = Request(
            f"{MILVUS_BASE_URL.rstrip('/')}/v2/vectordb/ai/batch/files/upload",
            data=payload,
            headers={"Authorization": f"Bearer {MILVUS_TOKEN}",
                     "Content-Type": f"multipart/form-data; boundary={boundary}",
                     "Content-Length": str(length)},
            method="POST",
        )
        try:
            with urlopen(request, timeout=600) as response:
                return response.status, json.loads(response.read().decode("utf-8"))
        except HTTPError as exc:
            return exc.code, json.loads(exc.read().decode("utf-8"))

def download_batch_file(batch_id, file_type, output_file):
    """Unduh file hasil. Catatan: gunakan batch_id + file_type, bukan file_id"""
    request = Request(
        f"{MILVUS_BASE_URL.rstrip('/')}/v2/vectordb/ai/batch/files/content",
        data=json.dumps({"provider": PROVIDER, "batch_id": batch_id,
                         "file_type": file_type}, ensure_ascii=False).encode("utf-8"),
        headers={"Authorization": f"Bearer {MILVUS_TOKEN}",
                 "Content-Type": "application/json"},
        method="POST",
    )
    with urlopen(request, timeout=600) as response, output_file.open("wb") as out:
        shutil.copyfileobj(response, out)

Langkah 1: Unggah file input

Unggah input.jsonl dan simpan input_file_id yang dikembalikan, yang mengidentifikasi input pekerjaan yang akan Anda buat pada langkah berikutnya.

# 1) Unggah file input JSONL
status, data = upload_input_file("input.jsonl")
input_file_id = (data.get("data") or {}).get("id")
if status != 200 or not input_file_id:
    raise SystemExit(f"Upload failed: HTTP={status} message={data.get('message')}")
print(f"input_file_id = {input_file_id}")

Langkah 2: Buat pekerjaan batch

Buat pekerjaan dari input_file_id dan deklarasikan jendela penyelesaian. Dalam pengujian, pekerjaan jenis ini dapat memakan waktu beberapa jam dari pembuatan hingga penyelesaian, sehingga completion_window biasanya diatur ke 24h.

# 2) Buat pekerjaan Batch dan deklarasikan jendela penyelesaian
status, data = post_json("/v2/vectordb/ai/batch/jobs/create", {
    "provider": PROVIDER,
    "input_file_id": input_file_id,
    "endpoint": ENDPOINT,
    "completion_window": "24h",
})
batch_id = (data.get("data") or {}).get("id")
if status != 200 or not batch_id:
    raise SystemExit(f"Creation failed: HTTP={status} message={data.get('message')}")
print(f"batch_id = {batch_id}")

Langkah 3: Polling status pekerjaan

Pekerjaan batch adalah pekerjaan asinkron berskala jam, jadi jangan lakukan polling intensif di foreground. Dalam pengujian, pekerjaan yang dikirimkan tetap dalam status in_progress dalam waktu lama. Di lingkungan produksi, catat batch_id setelah pengiriman dan lakukan polling nanti dari tugas terjadwal, bukan dengan memblokir thread bisnis.

# 3) Polling status pekerjaan. Pekerjaan batch berjalan asinkron berskala jam. Di produksi,
#    catat batch_id dan lakukan polling nanti dari tugas terjadwal, bukan polling intensif di foreground.
while True:
    status, data = post_json("/v2/vectordb/ai/batch/jobs/describe",
                             {"provider": PROVIDER, "batch_id": batch_id})
    batch = data.get("data") or {}
    batch_status = batch.get("status")
    print(f"{batch_status}  {batch.get('request_counts')}")
    if batch_status in {"completed", "failed", "expired", "cancelled"}:
        break
    time.sleep(300)      # Polling setiap 5 menit sudah cukup

Langkah 4: Unduh hasil

Jangan unduh hasil sampai status bernilai completed. Baris yang gagal dikumpulkan dalam error_file_id dan dapat dicoba ulang secara terpisah.

# 4) Unduh hasil setelah selesai. Baris yang gagal ada di error_file_id dan dapat dicoba ulang secara terpisah
if batch_status == "completed":
    download_batch_file(batch_id, "output", Path("output.jsonl"))
    if batch.get("error_file_id"):
        download_batch_file(batch_id, "error", Path("error.jsonl"))

Konfirmasi dan gunakan hasil

Tabel berikut mencantumkan respons terukur dari keempat operasi:

LangkahOperasiRespons
Unggah/v2/vectordb/ai/batch/files/uploadMengembalikan input_file_id dalam bentuk file-batch-xxx
Buat/v2/vectordb/ai/batch/jobs/createMengembalikan batch_id dalam bentuk batch_xxx
Polling/v2/vectordb/ai/batch/jobs/describestatus dan request_counts (dengan jumlah total / selesai / gagal)
Unduh/v2/vectordb/ai/batch/files/contentAliran file hasil. Melaporkan output_file_id is empty jika pekerjaan belum selesai

Operasi unduh menemukan file berdasarkan batch_id ditambah file_type, bukan berdasarkan ID file, yang mudah salah.

Setelah pekerjaan selesai, file hasil tetap membawa custom_id asli, sehingga aplikasi Anda dapat menulis setiap ringkasan kembali ke catatan yang sesuai. Jika sebagian data gagal, unduh hanya file error yang sesuai dengan error_file_id dan coba ulang baris yang gagal. Hasil yang telah selesai tetap dipertahankan, dan tidak perlu menjalankan ulang seluruh batch.

Estimasi biaya

Embedding Cache dan AI Batch menghemat biaya dengan cara yang berbeda, sehingga estimasinya juga berbeda:

KemampuanDasar estimasiEfek
Embedding Cache100.000 pemanggilan embedding per hari, rata-rata 50 token per pemanggilan, rasio cache hit 50%Bagian yang mengenai cache tidak menghasilkan pemanggilan model, dan konsumsi token keseluruhan turun kira-kira separuhnya
AI Batch100.000 entri per hari, 50 token input / 500 token outputEksekusi batch offline, sehingga biaya pemanggilan model dihitung dengan harga satuan lebih rendah

Angka-angka ini hanya menggambarkan sumber penghematan dan orde besarnya. Biaya aktual bergantung pada model, wilayah, dan harga saat ini, serta input dan output biasanya memiliki harga satuan berbeda, jadi rujuk halaman harga resmi dan tagihan aktual Anda.

Pemecahan masalah

Tabel berikut mencantumkan error yang paling sering terjadi saat mengonfigurasi Embedding Cache dan AI Batch:

GejalaPenyebabTindakan
Koneksi timeout saat memanggil RESTful API.Port dihilangkan, sehingga permintaan menuju port 80 secara default.Tentukan port 19530 secara eksplisit, misalnya http://c-xxx.milvus.aliyuncs.com:19530.
Pekerjaan batch gagal.Nilai body.model pada baris input.jsonl tidak sesuai dengan model_name yang dideklarasikan saat unggah.Samakan body.model pada setiap baris dengan model_name yang Anda deklarasikan saat mengunggah file.
Permintaan unduh melaporkan output_file_id is empty.Pekerjaan belum selesai.Lakukan polling /v2/vectordb/ai/batch/jobs/describe hingga status bernilai completed, lalu unduh hasilnya.
Permintaan unduh tidak menemukan file hasil.Permintaan mengidentifikasi file berdasarkan ID file.Panggil /v2/vectordb/ai/batch/files/content dengan batch_id ditambah file_type.
Dua penulisan teks yang sama mengembalikan vektor identik, atau penulisan kedua lebih lambat daripada yang pertama.Perbandingan vektor dan latensi penulisan bukan kriteria untuk cache hit.Periksa usage.total_tokens dan request_id dalam respons titik akhir embedding RESTful.