๐Ÿค– Android AI and Generative AI

Build an AI-Powered Search Feature for Android

Keyword search fails when users don't use the exact words in your content: 'cheap phone for my mom' won't match 'budget smartphone with large text'. AI-powered search understands meaning. Here's how to build it for an Android app, from embeddings to hybrid ranking and graceful fallbacks.

โœ๏ธ Yunok Tech ๐Ÿ“… Updated โฑ 3 min read ๐ŸŽฏ Intermediate
On this page
  1. Keyword vs semantic vs hybrid
  2. How semantic search works
  3. Where to run it
  4. On-device semantic search for personal notes
  5. Hybrid ranking with keyword fallback
  6. LLMs for query understanding
  7. Evaluating relevance
  8. Use cases
  9. Frequently asked questions
  10. Conclusion

Search is often the highest-intent feature in an app. Users who search know what they want. Semantic search matches by meaning rather than exact words, so it recovers queries that keyword search misses. Done carelessly, it also returns confident nonsense, so build it in layers.

Keyword vs semantic vs hybrid

ApproachHowGood atWeak at
Keyword (FTS)Match terms, rank by term frequencyExact names, SKUs, codes; fast and offlineSynonyms, intent, typos
SemanticCompare embedding vectors of query and itemsMeaning, paraphrases, natural languageExact identifiers; needs embeddings
HybridCombine both scoresBest overall relevanceMore complexity

How semantic search works

Semantic search with embeddings Offline, each catalogue item's text is turned into an embedding vector and stored in a vector index on the server. At query time, the user's query is embedded with the same model, the index finds the nearest item vectors by cosine similarity, results are optionally combined with keyword scores, and the top items are returned to the app. Indexing (once / on change) items' text embedding model vector index Query time "phone for mom" same model nearest neighbours top results
Items and queries must be embedded by the same model. Vectors from different models aren't comparable.

Where to run it

  • Server-side (most common): embed your catalogue with a cloud embedding model, store vectors in a vector-capable database, and expose /search?q=. The app stays simple, and the API key stays on the server.
  • On-device: for private or offline data such as the user's own notes, use a small on-device text embedder (for example MediaPipe Text Embedder with LiteRT) and brute-force cosine similarity for a few thousand items.

On-device semantic search for personal notes

NoteSemanticIndex.ktKotlin
/** Brute-force cosine search: fine for a few thousand notes. Vectors stored in Room as FloatArray blobs. */
class NoteSemanticIndex(private val embedder: TextEmbedderWrapper, private val dao: NoteVectorDao) {

    suspend fun index(note: Note) {
        val vector = embedder.embed("${note.title}\n${note.body}")
        dao.upsert(NoteVector(note.id, vector.normalized()))
    }

    suspend fun search(query: String, limit: Int = 10): List<String> = withContext(Dispatchers.Default) {
        val q = embedder.embed(query).normalized()
        dao.all()
            .map { it.noteId to dot(q, it.vector) }       // cosine similarity of normalised vectors
            .filter { it.second > 0.3f }                 // drop weak matches (tune on your data)
            .sortedByDescending { it.second }
            .take(limit)
            .map { it.first }
    }
}

fun FloatArray.normalized(): FloatArray {
    val norm = kotlin.math.sqrt(sumOf { (it * it).toDouble() }).toFloat().coerceAtLeast(1e-6f)
    return FloatArray(size) { this[it] / norm }
}
fun dot(a: FloatArray, b: FloatArray): Float { var s = 0f; for (i in a.indices) s += a[i] * b[i]; return s }

TextEmbedderWrapper wraps whichever on-device embedder you choose. Check the MediaPipe or LiteRT documentation for current setup.

Hybrid ranking with keyword fallback

HybridSearch.ktKotlin
suspend fun hybridSearch(query: String): List<String> = coroutineScope {
    val keyword = async { dao.ftsSearch(query).map { it.id } }        // Room FTS, always available offline
    val semantic = async { runCatching { semanticIndex.search(query) }.getOrDefault(emptyList()) }
    // Reciprocal rank fusion: simple, robust way to merge ranked lists
    val scores = mutableMapOf<String, Double>()
    listOf(keyword.await(), semantic.await()).forEach { list ->
        list.forEachIndexed { rank, id -> scores[id] = (scores[id] ?: 0.0) + 1.0 / (60 + rank) }
    }
    scores.entries.sortedByDescending { it.value }.map { it.key }
}

LLMs for query understanding

An LLM can turn "red running shoes under 3000 for women" into structured filters (category=shoes, color=red, maxPrice=3000, gender=women) via structured output. Apply those filters in your own search system, rather than letting the LLM pick products from memory.

Evaluating relevance

  • Build a set of real queries with the results you expect.
  • Measure whether the right item appears in the top 3 or top 10, before and after changes.
  • Track zero-result rate and click-through in analytics.

Use cases

Shopping

Server-side hybrid search with LLM-parsed filters.

Help centre

Semantic search over articles, the foundation of a RAG assistant.

Personal notes

On-device embeddings, so private notes never leave the phone.

Frequently asked questions

Do I need a vector database?

For a few thousand items, brute-force similarity is fine. For large catalogues, use a vector-capable database or search service on your backend.

What happens when the embedding model changes?

Re-embed everything with the new model. Vectors from different models can't be mixed.

Is semantic search always better?

No. Exact identifiers and names often work better with keyword search. That's why hybrid search is common.

Conclusion

AI-powered search combines embeddings for meaning, keyword search for precision, and optionally LLM query understanding for filters. Run it server-side for shared catalogues, on-device for private data, keep an offline FTS fallback, and measure relevance with real queries.

Next step: protect the AI endpoints behind these features with Integrating Cloud AI APIs Securely.

Official references

๐Ÿ“ฑ Apps built by Yunok Tech

Real Android apps on Google Play, free to download.

See all our apps โ†’

Ready to build real Android applications?

Every AI feature still runs inside an Android app that has to be fast, reliable and well structured. Strengthen the Kotlin, Jetpack Compose and architecture skills that AI integrations depend on, then apply these AI concepts with confidence.

Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.

Explore the Android course and take your next step as an Android developer.

Share: LinkedIn X / Twitter WhatsApp