Back to blog
    Construyendo un Asistente de IA en Dispositivo para tu App Móvil
    AI assistanton-device AIchatmobile AIimplementationsegment:mobile-builder

    Construyendo un Asistente de IA en Dispositivo para tu App Móvil

    Patrones de arquitectura para construir un asistente conversacional de IA que se ejecuta completamente en el dispositivo del usuario. Selección de modelo, gestión de conversaciones, patrones de UI y consideraciones de producción.

    EErtas Team·

    Un asistente de IA en dispositivo es una interfaz conversacional impulsada por un modelo de lenguaje que se ejecuta localmente en el teléfono del usuario. Sin API en la nube. Sin dependencia de red. Respuestas instantáneas. Privacidad completa.

    Esta guía cubre la arquitectura desde la selección del modelo hasta el despliegue en producción.

    Descripción general de la arquitectura

    El asistente en dispositivo tiene cuatro capas:

    1. Capa de modelo: llama.cpp carga y ejecuta el modelo GGUF
    2. Capa de conversación: Gestiona el historial de chat, system prompts y ventanas de contexto
    3. Capa de interfaz: UI de chat con visualización de tokens en streaming
    4. Capa de estado: Persiste las conversaciones, gestiona el ciclo de vida del modelo

    Selección del modelo para chat

    La IA conversacional se beneficia de modelos más grandes. Un modelo 3B con fine-tuning es el punto de partida recomendado:

    Tamaño del modeloCalidad de chatCoherencia multi-turnoRecomendado para
    1BAdecuada2-3 turnosQ&A simple, bots de FAQ
    3BBuena5-8 turnosAsistentes de chat completos

    Fine-tuning es esencial para chat. Un modelo base 3B generará respuestas genéricas. Un modelo 3B con fine-tuning habla con la voz de tu marca, conoce tu producto y maneja tus casos de uso específicos.

    Gestión de conversaciones

    La plantilla de prompt

    Cada familia de modelos tiene una plantilla de chat específica. Para Llama 3.2:

    <|begin_of_text|><|start_header_id|>system<|end_header_id|>
    
    Eres el asistente RecipeHelper. Ayuda a los usuarios a encontrar y modificar recetas. Siempre incluye tiempos de preparación y cocción.<|eot_id|><|start_header_id|>user<|end_header_id|>
    
    ¿Cena rápida para dos?<|eot_id|><|start_header_id|>assistant<|end_header_id|>
    

    Al hacer fine-tuning, el framework de entrenamiento maneja el formato de la plantilla automáticamente. En el momento de la inferencia, tu app debe formatear la conversación en esta plantilla antes de enviarla a llama.cpp.

    Gestión de la ventana de contexto

    Los modelos móviles típicamente usan ventanas de contexto de 2048-4096 tokens (configurables, pero ventanas más grandes usan más memoria y ralentizan la inferencia). Una conversación puede exceder esto rápidamente.

    Enfoque de ventana deslizante: Mantén el system prompt y los N turnos más recientes. Descarta los turnos más antiguos cuando la ventana se llena:

    func buildPrompt(systemPrompt: String, turns: [Turn], maxTokens: Int) -> String {
        var prompt = formatSystem(systemPrompt)
        var tokenCount = countTokens(prompt)
    
        // Agregar turnos del más nuevo al más antiguo, detenerse cuando la ventana esté llena
        var includedTurns: [Turn] = []
        for turn in turns.reversed() {
            let turnTokens = countTokens(formatTurn(turn))
            if tokenCount + turnTokens > maxTokens - 512 { break } // Reservar 512 para la respuesta
            includedTurns.insert(turn, at: 0)
            tokenCount += turnTokens
        }
    
        for turn in includedTurns {
            prompt += formatTurn(turn)
        }
    
        return prompt
    }
    

    Enfoque de resumen: Cuando la conversación excede la ventana, resume los turnos más antiguos en un contexto compacto y agrégalo al inicio del system prompt. Esto preserva la información clave mientras se mantiene dentro del presupuesto de tokens. Sin embargo, esto requiere una llamada de inferencia adicional.

    Persistencia de conversaciones

    Almacena las conversaciones en almacenamiento local (Core Data en iOS, Room en Android) para que los usuarios puedan retomar donde lo dejaron:

    • Guarda cada mensaje (rol, contenido, marca de tiempo)
    • Guarda metadatos de la conversación (título, fecha de creación, última actividad)
    • Limita las conversaciones almacenadas para prevenir crecimiento ilimitado del almacenamiento
    • Permite a los usuarios eliminar conversaciones

    UI con streaming

    Las interfaces de chat deben mostrar los tokens conforme se generan. Esto crea la percepción de un asistente rápido y responsivo.

    iOS (SwiftUI)

    struct ChatView: View {
        @StateObject var viewModel = ChatViewModel()
    
        var body: some View {
            ScrollView {
                ForEach(viewModel.messages) { message in
                    MessageBubble(message: message)
                }
            }
            .onChange(of: viewModel.streamingText) { _ in
                // Auto-scroll hacia abajo
            }
    
            HStack {
                TextField("Mensaje", text: $viewModel.input)
                Button("Enviar") { viewModel.send() }
            }
        }
    }
    

    Android (Compose)

    @Composable
    fun ChatScreen(viewModel: ChatViewModel) {
        val messages by viewModel.messages.collectAsState()
        val streaming by viewModel.streamingText.collectAsState()
    
        LazyColumn {
            items(messages) { message ->
                MessageBubble(message)
            }
            if (streaming.isNotEmpty()) {
                item { StreamingBubble(streaming) }
            }
        }
    
        Row {
            TextField(value = input, onValueChange = { input = it })
            Button(onClick = { viewModel.send(input) }) { Text("Enviar") }
        }
    }
    

    Cadencia de visualización de tokens

    llama.cpp genera tokens uno a la vez. Mostrar cada token individualmente puede causar parpadeo visual. Almacena en buffer 2-3 tokens antes de actualizar la UI para una apariencia de texto más fluida:

    private var tokenBuffer = StringBuilder()
    private var bufferCount = 0
    
    fun onToken(token: String) {
        tokenBuffer.append(token)
        bufferCount++
        if (bufferCount >= 3 || token.contains("\n")) {
            updateUI(tokenBuffer.toString())
            tokenBuffer.clear()
            bufferCount = 0
        }
    }
    

    Gestión del ciclo de vida del modelo

    Carga y descarga

    La carga del modelo toma 1-3 segundos dependiendo del tamaño del modelo y el dispositivo. La descarga es instantánea. Gestiona el ciclo de vida para equilibrar la capacidad de respuesta con la memoria:

    • Cargar en la primera interacción con IA: No cargues al iniciar la app. Carga cuando el usuario abre la función de chat.
    • Mantener cargado durante la sesión activa: Mientras el usuario esté en el chat, mantén el modelo en memoria.
    • Descargar al navegar fuera: Cuando el usuario abandona la pantalla de chat, descarga el modelo para liberar RAM.
    • Manejar advertencias de memoria: Regístrate para las advertencias de memoria del sistema y descarga el modelo si se activan.

    Indicador de carga

    Muestra un breve estado de carga (1-3 segundos) cuando el modelo se carga por primera vez. Después de eso, las respuestas comienzan a generarse inmediatamente. Los usuarios están acostumbrados a breves estados de carga para nuevas funciones.

    Fine-Tuning para tu asistente

    La brecha de calidad entre un modelo base y un modelo con fine-tuning es dramática para chat:

    MétricaBase 3BFine-Tuned 3B
    Respuestas dentro del tema60-70%92-96%
    Adherencia al formato55-65%94-98%
    Precisión de dominio50-60%88-94%
    Consistencia de tono40-50%90-95%

    Datos de entrenamiento para chat

    Crea ejemplos de entrenamiento que cubran:

    1. Preguntas comunes: Las 50-100 preguntas que los usuarios hacen con más frecuencia
    2. Casos extremos: Preguntas fuera de alcance, con respuestas de redirección elegante
    3. Patrones multi-turno: Conversaciones que abarcan 3-5 turnos mostrando seguimiento natural
    4. Ejemplos de estilo: Respuestas con la voz de tu marca y tu formato preferido

    500-2,000 conversaciones de entrenamiento producen un asistente de chat de alta calidad. Plataformas como Ertas manejan el pipeline de entrenamiento: sube ejemplos de conversación, haz fine-tuning con LoRA, exporta GGUF.

    Consideraciones de producción

    Guardarraíles de calidad de respuesta

    Los modelos en dispositivo pueden generar respuestas fuera de tema o incorrectas. Implementa guardarraíles ligeros:

    • Validación de entrada: Verifica entradas extremadamente largas o contenido que obviamente no es texto
    • Monitoreo de salida: Registra los temas de las respuestas (localmente) para identificar problemas de calidad
    • Mecanismo de retroalimentación: Permite a los usuarios reportar respuestas malas. Usa esta retroalimentación para mejorar los datos de entrenamiento

    Monitoreo de rendimiento

    Rastrea métricas en el dispositivo:

    • Tiempo hasta el primer token (debe ser menor a 300ms)
    • Tokens por segundo (debe mantenerse por encima de 10)
    • Tiempo de carga del modelo
    • Uso de memoria durante la inferencia
    • Tasa de fallos durante interacciones con IA

    Actualizaciones del modelo

    Distribuye mejoras del modelo a través de tu pipeline normal de entrega de modelos:

    • Busca actualizaciones al iniciar la app (cuando hay conexión)
    • Descarga en segundo plano
    • Intercambia el archivo del modelo al iniciar la siguiente sesión de chat
    • Mantén el modelo anterior como respaldo hasta que el nuevo esté validado

    El resultado es un asistente de chat que funciona en todas partes, responde al instante, no cuesta nada por conversación y mantiene los datos del usuario completamente privados.

    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