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:
| Kemampuan | Fungsinya | Pemborosan yang dihilangkan |
| Embedding Cache | Mencari 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 Batch | Permintaan 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 bisnis | Embedding Cache | AI Batch |
| Pola data | Teks yang sama muncul berulang kali | Volume data besar diproses untuk pertama kalinya |
| Persyaratan respons | Dikembalikan secara online | Boleh selesai belakangan |
| Sumber penghematan | Lebih sedikit pemanggilan model duplikat | Eksekusi batch offline dengan harga satuan lebih rendah |
| Skenario umum | Jawaban FAQ dan ground truth, judul dan atribut produk populer, paragraf basis pengetahuan yang diimpor berulang, istilah pencarian frekuensi tinggi | Menghasilkan 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.
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.
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
insertplusflushjauh 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:
| Pesanan | usage.total_tokens | request_id | Hasil |
| Request 1 | 20 | Memiliki nilai | Cache miss, model benar-benar dipanggil |
| Request 2 | 0 | Kosong | Cache hit |
| Request 3 | 0 | Kosong | Cache 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}}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.
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 cukupLangkah 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:
| Langkah | Operasi | Respons |
| Unggah | /v2/vectordb/ai/batch/files/upload | Mengembalikan input_file_id dalam bentuk file-batch-xxx |
| Buat | /v2/vectordb/ai/batch/jobs/create | Mengembalikan batch_id dalam bentuk batch_xxx |
| Polling | /v2/vectordb/ai/batch/jobs/describe | status dan request_counts (dengan jumlah total / selesai / gagal) |
| Unduh | /v2/vectordb/ai/batch/files/content | Aliran 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:
| Kemampuan | Dasar estimasi | Efek |
| Embedding Cache | 100.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 Batch | 100.000 entri per hari, 50 token input / 500 token output | Eksekusi 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:
| Gejala | Penyebab | Tindakan |
| 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. |