Panduan ini akan memandu Anda menerapkan penelusuran semantik di perangkat, yaitu teknik penelusuran yang menemukan konten relevan berdasarkan makna kueri, bukan hanya berdasarkan kata kunci. Penelusuran semantik menghilangkan kebutuhan untuk membuat dan memelihara tabel sinonim kustom atau taksonomi pemberian tag secara manual. Untuk menerapkan penelusuran semantik di perangkat, gunakan library berikut:
- ML Kit SDK, khususnya Embedding API, untuk menghasilkan embedding vektor
- AndroidX AppSearch untuk menyimpan embedding vektor, dan menelusuri data terstruktur menggunakan metrik jarak kosinus untuk menemukan konten berdasarkan semantiknya
Prasyarat
Untuk menggunakan penelusuran semantik di perangkat, Anda harus memenuhi persyaratan perangkat dan akun berikut:
- Perangkat Anda mendukung Android AICore. Sebagian besar perangkat yang didukung AICore menggunakan model yang diakselerasi NPU, sedangkan perangkat lainnya yang didukung akan beralih ke model CPU (dengan akselerasi XNNPACK).
- Akun Google di perangkat Anda adalah bagian dari program Pratinjau Developer AICore.
- Perangkat Anda memiliki versi terbaru APK Pratinjau Developer AICore: Di perangkat, buka halaman Google Play Store Android AICore dan ketuk Update untuk menginstal versi terbaru APK. Atau, buka Setelan perangkat, telusuri Layanan sistem atau Layanan Google, ketuk Android AICore (Beta), lalu ketuk Update jika update tersedia.
- Perangkat Anda memiliki versi terbaru aplikasi Private Compute Services: Di perangkat Anda, buka halaman Google Play Store Private Compute Services dan ketuk Update untuk menginstal versi terbaru aplikasi. Atau, buka Google Play Store > Kelola aplikasi & perangkat > Kelola > Private Compute Services dan ketuk Update jika update tersedia.
Model dan konfigurasi yang didukung
Embedding API ML Kit menggunakan rangkaian model EmbeddingGemma Google, yang dikelola oleh Android AICore. Secara khusus, Embedding API menggunakan
EmbeddingGemma 2
(embedding_gemma_v2) untuk menghasilkan embedding untuk input teks dan gambar.
Berikut adalah spesifikasi dan batasan input:
| Properti input | EmbeddingGemma 2 | Catatan penggunaan |
|---|---|---|
| Modalitas yang didukung | Teks dan gambar (Modality.TEXT, Modality.IMAGE) |
Untuk mengonfirmasi bahwa input gambar didukung, periksa saat runtime bahwa model.getMetadata()?.supportedModalities.contains(Modality.IMAGE) menampilkan true. |
| Token limit (batas token) | 256 token | Periksa menggunakan metode model.getTokenLimit(). Setiap gambar dienkode menjadi 70 token, sehingga menyisakan 186 token untuk teks yang menyertainya. |
| Resolusi gambar | Hingga 768 px di sisi yang lebih pendek | Konstruktor untuk class ImagePart menerima jenis argumen Bitmap, Uri, atau ByteArray. Gambar dengan dimensi yang lebih pendek dan lebih besar dari 768 px akan otomatis diperkecil oleh SDK dengan tetap mempertahankan rasio aspek. Untuk performa dan penggunaan memori yang optimal, hindari rasio aspek ekstrem. |
Dimensi vektor output adalah 768 (objek FloatArray dari float 32-bit). Anda dapat memeriksa dimensi vektor output saat runtime menggunakan metode model.getMetadata()?.dimension.
Mulai
Untuk mengonfigurasi project agar menggunakan penelusuran semantik, tambahkan dependensi berikut
ke file build.gradle.kts (atau build.gradle) level modul Anda.
Kotlin
dependencies {
// ...
implementation("com.google.mlkit:genai-embedding:1.0.0-alpha1")
implementation("androidx.appsearch:appsearch:1.2.0-alpha01")
implementation("androidx.appsearch:appsearch-local-storage:1.2.0-alpha01")
implementation("androidx.concurrent:concurrent-futures-ktx:1.3.0")
// ...
}
Groovy
dependencies {
// ...
implementation 'com.google.mlkit:genai-embedding:1.0.0-alpha1'
implementation 'androidx.appsearch:appsearch:1.2.0-alpha01'
implementation 'androidx.appsearch:appsearch-local-storage:1.2.0-alpha01'
implementation 'androidx.concurrent:concurrent-futures-ktx:1.3.0'
// ...
}
Untuk mengambil model yang tersedia, gunakan metode EmbeddingGeneration.getEmbeddingModels(EmbeddingOptions()).
Menentukan skema AppSearch
Untuk mengaktifkan penelusuran kesamaan semantik, Anda harus menentukan skema AppSearch tempat properti vektor dikonfigurasi secara eksplisit untuk pengindeksan kesamaan.
// Define the document schema
val documentSchema = AppSearchSchema.Builder("my_schema_type")
.addProperty(
AppSearchSchema.StringPropertyConfig.Builder("text")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
.build()
)
.addProperty(
AppSearchSchema.StringPropertyConfig.Builder("imageUri")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.build()
)
.addProperty(
AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(
AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
.setQuantizationType(
AppSearchSchema.EmbeddingPropertyConfig.QUANTIZATION_TYPE_8_BIT)
.build()
)
.build()
Mengindeks konten dokumen
Untuk mengindeks konten dokumen, Anda harus membuat embedding vektor untuk setiap
objek Content, menggabungkannya dalam instance GenericDocument, dan menyimpannya ke
database AppSearch.
Saat membuat objek EmbeddingVector, Anda juga harus meneruskan nilai modelSignature yang aktif. AppSearch menggunakan string modelSignature ini untuk mencegah
kueri penelusuran yang tidak kompatibel.
suspend fun syncDatabase(documents: List<Pair<String, Uri?>>) {
val modelSignature = model?.getModelSignature() ?: return
val docsWithEmbedding = mutableListOf<GenericDocument>()
for ((index, item) in documents.withIndex()) {
val (content, imageUri) = item
// 1. Generate embedding (text and/or image)
val parts = mutableListOf<Part>()
if (content.isNotBlank()) {
parts.add(TextPart(content))
}
var imagePart: ImagePart? = null
val vector = try {
if (imageUri != null && isImageSupported) {
imagePart = ImagePart(imageUri)
parts.add(imagePart)
}
if (parts.isEmpty()) continue
val request = EmbeddingRequest(
Content(parts)
)
model?.generateEmbedding(request)?.embedding?.vector
?: continue
} finally {
imagePart?.close()
}
// 2. Build the AppSearch document
val docBuilder = GenericDocument.Builder<GenericDocument.Builder<*>>(
"my_namespace",
"doc_$index",
"my_schema_type"
)
.setPropertyString("text", content)
// Associate the vector with the signature that generated it
.setPropertyEmbedding("embedding", EmbeddingVector(vector, modelSignature))
if (imageUri != null) {
docBuilder.setPropertyString("imageUri", imageUri.toString())
}
docsWithEmbedding.add(docBuilder.build())
}
// 3. Clear the old database by force-overriding the schema
val clearRequest = SetSchemaRequest.Builder().setForceOverride(true).build()
session?.setSchemaAsync(clearRequest)?.await()
val schemaRequest = SetSchemaRequest.Builder().addSchemas(documentSchema).build()
session?.setSchemaAsync(schemaRequest)?.await()
// 4. Index documents
val putRequest = PutDocumentsRequest.Builder().addGenericDocuments(docsWithEmbedding).build()
session?.putAsync(putRequest)?.await()
}
Menjalankan penelusuran semantik
Untuk melakukan kueri semantik, konversi kueri penelusuran (teks dan/atau gambar) menjadi vektor embedding, konfigurasikan parameter penelusuran vektor, dan buat kueri database AppSearch Anda.
Rekomendasi:
- Tetapkan metrik default ke konstanta
SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE. - Memberi peringkat hasil berdasarkan formula
"sum(this.matchedSemanticScores(getEmbeddingParameter(0)))". - Gunakan string
"semanticSearch(getEmbeddingParameter(0), $threshold)"logis untuk memfilter hasil yang kemiripannya memenuhi nilai minimum Anda (biasanya 0,5 atau lebih tinggi).
suspend fun search(
queryText: String,
imageUri: Uri? = null,
threshold: Double = 0.5
): List<Triple<String, Uri?, Double>> {
val modelSignature = model?.getModelSignature() ?: return emptyList()
// 1. Generate query embedding (text and/or image)
val parts = mutableListOf<Part>()
if (queryText.isNotBlank()) {
parts.add(TextPart(queryText))
}
var imagePart: ImagePart? = null
val queryVector = try {
if (imageUri != null && isImageSupported) {
imagePart = ImagePart(imageUri)
parts.add(imagePart)
}
if (parts.isEmpty()) return emptyList()
val request = EmbeddingRequest(
content = Content(parts)
)
model?.generateEmbedding(request)?.embedding?.vector
?: return emptyList()
} finally {
imagePart?.close()
}
// 2. Configure search specifications
val searchSpec = SearchSpec.Builder()
.addFilterSchemas("my_schema_type")
.setDefaultEmbeddingSearchMetricType(SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE)
.setRankingStrategy("sum(this.matchedSemanticScores(getEmbeddingParameter(0)))")
.setListFilterQueryLanguageEnabled(true)
.setResultCountPerPage(5)
// Add the query vector as parameter 0
.addEmbeddingParameters(EmbeddingVector(queryVector, modelSignature))
.build()
// 3. Execute search
val queryExpression = "semanticSearch(getEmbeddingParameter(0), $threshold)"
val results = session?.search(queryExpression, searchSpec) ?: return emptyList()
return try {
val page = results.nextPageAsync.await()
page.map {
val text = it.genericDocument.getPropertyString("text") ?: ""
val matchedImageUri = it.genericDocument.getPropertyString("imageUri")?.let(Uri::parse)
// rankingSignal contains the computed similarity score
Triple(text, matchedImageUri, it.rankingSignal)
}
} finally {
results.close()
}
}
Menggunakan indeks penelusuran campuran
Untuk pengalaman pengguna terbaik, sebaiknya gunakan strategi hibrid yang melibatkan penelusuran semantik dan kata kunci. Saat pengguna aktif mengetik di kotak penelusuran, gunakan penelusuran kata kunci untuk menampilkan saran pelengkapan otomatis instan; saat mereka memasukkan kueri lengkap, atau bahkan saat jeda kata, gunakan penelusuran semantik untuk membaca, memahami, dan mengambil kecocokan konseptual terbaik.
Mengindeks ulang database vektor Anda
Embedding yang dihasilkan oleh versi model yang berbeda berada dalam ruang vektor yang tidak kompatibel dan tidak dapat dibandingkan secara langsung. Anda harus mengindeks ulang database AppSearch setiap kali mengupdate model embedding.
Sebaiknya mulai mengindeks ulang embedding segera setelah model yang Anda gunakan memasuki periode penghentian penggunaan. Anda dapat menggunakan Embedding API untuk memeriksa jadwal penghentian penggunaan model. Panggil metode model.getMetadata() untuk mengambil
objek EmbeddingMetadata dan periksa properti tanggal siklus proses ini
(ditampilkan sebagai objek LocalDate dalam UTC):
- Nilai
deprecationDatemenandai awal periode penghentian penggunaan model. Anda harus mulai mengindeks ulang saat model terus berfungsi dan menyelesaikan pengindeksan ulang sebelum penonaktifan resminya. - Nilai
shutdownDateadalah waktu saat model akan dinonaktifkan dan tidak lagi disajikan. Setelah tanggal ini, model tidak dapat diakses untuk inferensi dan embedding yang ada dari model ini akan dipaksa untuk diindeks ulang dengan model terbaru yang tersedia.
Kami juga menginformasikan penghentian penggunaan model mendatang, linimasa migrasi, dan target tanggal penonaktifan melalui catatan rilis ML Kit dan update dokumentasi developer.
Praktik terbaik
Untuk pengalaman pengguna yang baik, ikuti praktik berikut saat mengintegrasikan embedding vektor ke dalam aplikasi Android Anda:
- Menjadwalkan workload pengindeksan: Selalu jadwalkan workload pengindeksan latar belakang menggunakan library WorkManager. Konfigurasikan tugas latar belakang Anda agar hanya berjalan saat perangkat sedang mengisi daya dan tidak ada aktivitas. ML Kit menerapkan batas kuota untuk penggunaan model, mirip dengan API LLM, dengan batas yang lebih longgar saat perangkat sedang diisi dayanya. Dalam mode Pratinjau Developer, Anda dapat mengabaikan batasan kuota untuk tujuan pengujian.
- Pengoptimalan penyimpanan dan kueri: Optimalkan jejak database dan latensi kueri Anda menggunakan kuantisasi dan pemotongan dimensi:
- Konfigurasikan skema
AppSearchSchemaAnda untuk menggunakan konstantaQUANTIZATION_TYPE_8_BIT, seperti yang ditunjukkan di Menentukan skema AppSearch. Hal ini mengurangi penggunaan memori dengan dampak minimal pada kualitas dengan memperkecil presisi setiap koordinat menjadi bilangan bulat 8-bit. - Gunakan Matryoshka Representation Learning (MRL), yang didukung oleh model EmbeddingGemma 2, untuk memangkas vektor embedding. Untuk mempelajari lebih lanjut, lihat EmbeddingGemma 2 di HuggingFace.
- Konfigurasikan skema
- Konsistensi tanda tangan: Jangan mengkueri ruang vektor menggunakan tanda tangan yang tidak cocok. Jika mengupgrade versi model embedding, Anda harus menghapus total database AppSearch dan membuat ulang embedding untuk semua dokumen menggunakan tanda tangan model baru.
- Keamanan dan moderasi konten: Anda bertanggung jawab untuk menerapkan
moderasi konten dan pemfilteran keamanan di aplikasi Anda. Tidak seperti API teks atau gambar
generatif, Embedding API ML Kit tidak menyertakan filter keamanan
bawaan. Karena model menghasilkan representasi vektor numerik
(koordinat
FloatArray) dari input yang diberikan pemanggil, bukan menghasilkan teks atau media baru, vektor numerik tidak secara inheren menimbulkan risiko keamanan konten generatif. Menghilangkan pemfilteran keamanan di perangkat juga mempertahankan performa pengindeksan dan penelusuran latensi rendah dengan menghindari pertentangan CPU selama pengindeksan dokumen dengan throughput tinggi dan kueri real-time. Untuk meningkatkan keamanan, ikuti panduan berikut:- Moderasi input yang tidak tepercaya dan konten yang diindeks: Jika aplikasi Anda mengindeks atau menelusuri konten buatan pengguna yang tidak tepercaya atau data eksternal, terapkan moderasi konten dan filter kebijakan ke teks atau gambar sumber sebelum mengindeksnya atau menampilkan hasil penelusuran.
- Lindungi pipeline AI generatif downstream: Jika dokumen atau bagian yang diambil diteruskan sebagai konteks ke model AI generatif downstream, pastikan filter keamanan dan kebijakan konten diaktifkan pada model downstream tersebut dan output-nya yang ditampilkan kepada pengguna.