
Busqueda semantica en el dispositivo: busqueda con IA sin servidor
Como construir busqueda semantica que se ejecuta completamente en el telefono del usuario. Embeddings locales, similaridad vectorial y consultas en lenguaje natural sobre contenido del usuario sin servidor ni API.
La busqueda por palabras clave falla cuando los usuarios no conocen las palabras exactas. "Ese correo sobre la reunion del presupuesto de la semana pasada" no coincide con un correo con asunto "Revision Financiera Q3." La busqueda semantica entiende significado, no solo palabras clave.
El enfoque estandar coloca la busqueda semantica en un servidor con una base de datos vectorial. Pero para apps moviles donde el contenido del usuario es local (notas, mensajes, fotos, documentos), enviar ese contenido a un servidor anula el proposito de mantenerlo en el dispositivo.
La busqueda semantica en el dispositivo mantiene todo local. El modelo de embeddings se ejecuta en el telefono. El indice vectorial vive en almacenamiento local. La consulta de busqueda nunca sale del dispositivo.
Como funciona la busqueda semantica
- Indexacion: Cada pieza de contenido se convierte en un vector de embedding (una lista de numeros que representa su significado) usando un modelo pequeno
- Almacenamiento: Los vectores de embedding se almacenan junto al contenido en una base de datos local
- Consulta: La consulta de busqueda del usuario se convierte en un vector de embedding usando el mismo modelo
- Comparacion: El vector de consulta se compara contra todos los vectores almacenados usando similaridad coseno
- Clasificacion: Los resultados se devuelven clasificados por puntuacion de similaridad
La magia esta en los embeddings. Dos piezas de texto sobre el mismo tema producen vectores similares, incluso si no comparten palabras clave.
El modelo de embedding
Los modelos de embedding en el dispositivo son pequenos y rapidos. A diferencia de los LLMs generativos (600MB-1.7GB), los modelos de embedding tipicamente ocupan 20-80MB:
| Modelo | Tamano | Dimensiones | Velocidad (iPhone 15) |
|---|---|---|---|
| all-MiniLM-L6-v2 | 23MB | 384 | 500+ embeddings/seg |
| nomic-embed-text-v1.5 | 55MB | 768 | 200+ embeddings/seg |
| bge-small-en-v1.5 | 33MB | 384 | 400+ embeddings/seg |
A 200-500 embeddings por segundo, indexar 1,000 notas toma 2-5 segundos. El embedding de consulta es casi instantaneo (menos de 5ms).
Ejecutando el modelo de embedding
Puedes ejecutar modelos de embedding via:
ONNX Runtime Mobile: Soporta modelos de embedding en formato ONNX. Disponible para iOS (via Swift) y Android (via Kotlin). La opcion mas madura para inferencia de embeddings en movil.
// iOS con ONNX Runtime
let session = try ORTSession(env: env, modelPath: embeddingModelPath)
let inputTensor = try ORTValue(tensorData: tokenizedInput, shape: shape)
let outputs = try session.run(withInputs: ["input_ids": inputTensor])
let embedding = outputs["embeddings"]!.tensorData()
Modo de embedding de llama.cpp: llama.cpp puede generar embeddings desde modelos GGUF usando la bandera de embedding. Esto te permite usar el mismo motor de inferencia para generacion y embedding.
Almacenamiento vectorial
SQLite con extension personalizada
El enfoque mas simple para movil: almacenar vectores como BLOBs en SQLite y calcular similaridad en codigo de aplicacion.
// Android: Almacenar embedding
fun storeEmbedding(db: SQLiteDatabase, contentId: Long, embedding: FloatArray) {
val blob = ByteBuffer.allocate(embedding.size * 4)
embedding.forEach { blob.putFloat(it) }
db.execSQL(
"INSERT INTO embeddings (content_id, vector) VALUES (?, ?)",
arrayOf(contentId, blob.array())
)
}
// Buscar por similaridad
fun search(db: SQLiteDatabase, queryEmbedding: FloatArray, limit: Int): List<SearchResult> {
val cursor = db.rawQuery("SELECT content_id, vector FROM embeddings", null)
val results = mutableListOf<SearchResult>()
while (cursor.moveToNext()) {
val blob = cursor.getBlob(1)
val stored = FloatArray(blob.size / 4)
ByteBuffer.wrap(blob).asFloatBuffer().get(stored)
val similarity = cosineSimilarity(queryEmbedding, stored)
results.add(SearchResult(cursor.getLong(0), similarity))
}
return results.sortedByDescending { it.similarity }.take(limit)
}
Esto es simple y funciona para colecciones de hasta ~10,000 elementos. Mas alla de eso, el escaneo lineal se vuelve lento.
SQLite con extension vectorial
Para colecciones mas grandes, usa una extension vectorial de SQLite que soporte busqueda de vecino mas cercano aproximado (ANN):
- sqlite-vss: Extension de SQLite que usa Faiss para busqueda vectorial. Soporta iOS y Android.
- sqlite-vec: Extension de busqueda vectorial ligera disenada para uso embebido.
Estas extensiones crean un indice sobre los vectores, habilitando busqueda en sub-milisegundos sobre cientos de miles de elementos.
El pipeline completo
Paso 1: Indexar contenido
Cuando el usuario crea o modifica contenido (nota, mensaje, documento), genera y almacena su embedding:
func indexContent(_ content: Content) async {
let embedding = await embeddingModel.encode(content.text)
database.storeEmbedding(contentId: content.id, vector: embedding)
}
Ejecuta la indexacion en segundo plano. Los usuarios no deberian esperar a que se calculen los embeddings.
Paso 2: Buscar
Cuando el usuario ingresa una consulta de busqueda:
func search(query: String) async -> [Content] {
let queryEmbedding = await embeddingModel.encode(query)
let results = database.similaritySearch(queryEmbedding, limit: 10)
return results.map { fetchContent($0.contentId) }
}
La busqueda devuelve resultados clasificados por similaridad semantica. "Notas de la reunion del presupuesto" coincide con "Revision Financiera Q3" porque los embeddings capturan la relacion semantica.
Paso 3: Busqueda hibrida
Combina busqueda semantica con busqueda por palabras clave para mejores resultados:
- Ejecuta busqueda por palabras clave (SQLite FTS5) para coincidencias exactas
- Ejecuta busqueda semantica para coincidencias basadas en significado
- Combina y elimina duplicados de los resultados
- Clasifica por puntuacion combinada (coincidencias exactas con prioridad)
Esto maneja tanto consultas exactas ("reunion con Juan") como consultas difusas ("ese correo sobre el cronograma del proyecto").
Presupuesto de rendimiento
| Componente | Almacenamiento | RAM | Velocidad |
|---|---|---|---|
| Modelo de embedding | 23-55MB | 50-100MB durante inferencia | 200-500 embeddings/seg |
| Indice vectorial (10K items, 384d) | ~15MB | ~15MB | Menos de 5ms por busqueda |
| Indice vectorial (100K items, 384d) | ~150MB | ~30MB (con indice ANN) | Menos de 10ms por busqueda |
Huella adicional total para busqueda semantica: 40-200MB almacenamiento, 65-130MB RAM durante busqueda. Esto es una fraccion de lo que requiere un LLM generativo, haciendolo practico incluso en dispositivos limitados.
Casos de uso
Apps de notas
Busca en todas las notas por significado. "Notas de la reunion de la semana pasada sobre el lanzamiento del producto" encuentra notas relevantes independientemente de las palabras exactas.
Clientes de correo
Encuentra correos por tema, no solo por remitente o asunto. "Conversacion sobre la renovacion del contrato" muestra el hilo correcto.
Apps de fotos
Combina con subtitulado de imagenes (en el dispositivo) para habilitar busqueda de fotos por texto. "Atardecer en la playa" encuentra fotos coincidentes incluso sin etiquetas manuales.
Gestores de documentos
Busca en PDFs, documentos y archivos por contenido y significado.
Combinando con LLMs en el dispositivo
La busqueda semantica se combina naturalmente con modelos generativos en el dispositivo. Usa los resultados de busqueda como contexto para el LLM:
- El usuario hace una pregunta
- La busqueda semantica recupera contenido relevante de sus datos
- El LLM genera una respuesta usando el contenido recuperado como contexto
Esto es RAG en el dispositivo. Sin servidor necesario. Todo el pipeline (embedding, busqueda, generacion) se ejecuta localmente.
Para el componente generativo, fine-tunea un modelo con tus datos de dominio usando una plataforma como Ertas. El modelo fine-tuned combinado con busqueda semantica local crea un asistente de IA poderoso y completamente privado.
Ship AI that runs on your users' devices.
Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.
Keep reading

Building an On-Device AI Assistant for Your Mobile App
Architecture patterns for building a conversational AI assistant that runs entirely on the user's device. Model selection, conversation management, UI patterns, and production considerations.

On-Device Text Classification for Mobile Apps
How to build fast, accurate text classification that runs on the user's phone. Sentiment analysis, content categorization, intent detection, and spam filtering without an API call.

On-Device Content Generation: AI Drafts That Work Offline
How to build AI-powered drafting features that work without internet. Email replies, message suggestions, note expansion, and content templates generated entirely on the user's device.