DEV Community

Cover image for Crea tu asistente personal en los Meta Glasses.
Fernando Silva T for AWS Español

Posted on • Edited on

Crea tu asistente personal en los Meta Glasses.


Implementación de un asistente de IA con entrada de voz y visión para lentes inteligentes usando Android, Amazon Bedrock y Claude Sonnet 4.5, sin dependencia de la IA nativa de Meta ni restricciones geográficas.

Este proyecto aborda cuatro problemas técnicos concretos:

  1. Wake word detection sin librerías externas
  2. Conversión de frames del SDK de Meta a formatos compatibles con Bedrock
  3. Uso de la Converse API con input multimodal (imagen + texto)
  4. Coordinación de flujos asíncronos en Jetpack Compose

Stack: Kotlin · Jetpack Compose · Meta Wearables DAT SDK · Amazon Bedrock

Nivel: Intermedio

Repositorio: https://github.com/fernandosilvot/aws-bedrock-meta-glasses


Introducción

Todo comenzó con una conversación casual. Elizabeth Fuentes me comentó sobre el Meta Wearables DAT SDK — una API que Meta había liberado para desarrolladores que permitía integrar funcionalidades personalizadas a los Meta Smart Glasses, más allá de la IA nativa de Meta.

La idea me fascinó de inmediato: ¿Y si pudiera conectar estos lentes con Amazon Bedrock y usar Claude Sonnet 4.5 en lugar de la IA de Meta? ¿Podría crear un asistente que no solo escuchara, sino que también viera lo que yo veo?

El resultado es Meta-Rock, un asistente activado por voz que captura el frame actual de la cámara, lo envía junto al texto de la consulta y reproduce la respuesta mediante TTS.

Pantalla Principal


El problema y la visión

Los Meta Ray-Ban Smart Glasses vienen con IA integrada de Meta, pero tienen una limitación importante: solo está disponible en ciertos países. En Chile, donde vivo, no puedo acceder a esa funcionalidad nativa.

Además, incluso si estuviera disponible, quería explorar las posibilidades de integrar modelos de IA más avanzados y personalizables. ¿Qué podría lograr si conectara estos lentes directamente con Amazon Bedrock y Claude Sonnet 4.5?

Objetivo:

Construir un asistente hands-free que combine audio y visión, delegando toda la inferencia a Bedrock.


Arquitectura

Flujo de datos

Flujo de datos

Pipeline resumido:

  1. Wake word → SpeechRecognizer
  2. Captura de frame desde los lentes
  3. Conversión I420 → JPEG
  4. Request multimodal a Bedrock
  5. Respuesta → Text-to-Speech

Stack tecnológico

Capa Tecnología Justificación
UI Jetpack Compose Estado reactivo
Voz Android SpeechRecognizer API nativa
Video Meta Wearables DAT SDK Acceso directo a cámara
IA Amazon Bedrock Inferencia multimodal
Modelo Claude Sonnet 4.5 Razonamiento visual sólido

Demostración en video

Meta-Rock en ejecución

El video muestra el flujo completo:

  • Activación por wake word
  • Captura del frame desde los lentes
  • Envío multimodal a Bedrock
  • Respuesta hablada

📽️ Video de demostración:


Desafíos técnicos y decisiones de diseño

1. Wake word detection sin librerías externas

Decisión: usar SpeechRecognizer en escucha pasiva.

Motivación: evitar dependencias comerciales y mantener el stack 100% nativo.

Trade-offs:

  • Falsos positivos en ambientes ruidosos
  • Mayor consumo de batería
  • Latencia aproximada de 500 ms
class SpeechManager(
    private val context: Context,
    private val onWakeWord: () -> Unit,
    private val onTranscript: (String) -> Unit
) {
    private val speechRecognizer = SpeechRecognizer.createSpeechRecognizer(context)

    fun startPassiveListening() {
        val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
            putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, 
                    RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
            putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true)
        }
        speechRecognizer.startListening(intent)
    }

    private fun checkForWakeWord(text: String) {
        val normalized = text.lowercase().trim()
        if (normalized.contains("hey friday") || normalized.contains("oye viernes")) {
            onWakeWord()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Aceptable para prototipo funcional y pruebas reales.


2. Envío constante de contexto visual

Decisión clave: enviar siempre el frame de la cámara junto al texto.

Motivación técnica:

  • Clasificadores por palabras clave son frágiles
  • Consultas ambiguas requieren visión (“¿y esto?”)
  • El modelo puede ignorar la imagen si no es relevante
private fun onTranscriptReady(transcript: String) {
    Log.d(TAG, "Transcript: $transcript")
    val frame = currentFrameProvider?.invoke() // Siempre captura el frame

    _uiState.update { 
        it.copy(
            state = NovaState.PROCESSING, 
            transcript = transcript, 
            sentWithImage = frame != null
        ) 
    }

    processingJob = viewModelScope.launch(Dispatchers.IO) {
        val systemPrompt = Prompts.system(language.locale.language)
        val response = if (frame != null) {
            bedrockClient.askWithImage(transcript, frame, systemPrompt)
        } else {
            bedrockClient.askTextOnly(transcript, systemPrompt)
        }
        // ...
    }
}
Enter fullscreen mode Exit fullscreen mode

Costo: mayor consumo de tokens y ancho de banda

Beneficio: lógica simple y UX consistente


3. Conversión de formatos de video

El SDK de Meta entrega frames en formato I420 (YUV).

Bedrock requiere imágenes codificadas (JPEG).

Pipeline implementado:

I420 → NV21 → Bitmap → JPEG (85%)

private fun i420ToNV21(i420: ByteArray, width: Int, height: Int): ByteArray {
    val frameSize = width * height
    val nv21 = ByteArray(frameSize * 3 / 2)

    // Copiar Y
    System.arraycopy(i420, 0, nv21, 0, frameSize)

    // Intercalar U y V para NV21
    var uvIndex = frameSize
    var uIndex = frameSize
    var vIndex = frameSize + frameSize / 4

    for (i in 0 until frameSize / 4) {
        nv21[uvIndex++] = i420[vIndex++]
        nv21[uvIndex++] = i420[uIndex++]
    }

    return nv21
}

private fun bitmapToBytes(bitmap: Bitmap): ByteArray {
    val stream = ByteArrayOutputStream()
    bitmap.compress(Bitmap.CompressFormat.JPEG, 85, stream)
    return stream.toByteArray()
}
Enter fullscreen mode Exit fullscreen mode

El 85% de compresión mantiene legibilidad de texto con menor latencia.


4. Integración con Bedrock (Converse API)

Se utiliza una única llamada con input multimodal:

  • Texto del usuario
  • Imagen capturada
  • System prompt dependiente del idioma
class BedrockClient {
    private val client = BedrockRuntimeClient {
        region = "us-east-1"
        credentialsProvider = StaticCredentialsProvider(
            accessKeyId,
            secretAccessKey
        )
    }

    suspend fun askWithImage(prompt: String, frame: Bitmap, systemPrompt: String): String {
        val imageBytes = bitmapToBytes(frame)
        val request = ConverseRequest {
            modelId = "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
            system = listOf(SystemContentBlock.Text(systemPrompt))
            messages = listOf(
                Message {
                    role = ConversationRole.User
                    content = listOf(
                        ContentBlock.Image(
                            ImageBlock {
                                format = ImageFormat.Jpeg
                                source = ImageSource.Bytes(imageBytes)
                            }
                        ),
                        ContentBlock.Text(prompt)
                    )
                }
            )
            inferenceConfig {
                maxTokens = 300
                temperature = 0.7F
            }
        }
        return client.converse(request).output!!.asMessage().content.first().asText()
    }
}
Enter fullscreen mode Exit fullscreen mode

La inferencia se realiza directamente desde Android usando el SDK oficial de AWS para Kotlin.


5. Gestión de estado en la UI

Se definen estados explícitos para coordinar audio, video e inferencia:

enum class NovaState {
    IDLE,           // Esperando "Hey Viernes"
    LISTENING,      // Escuchando pregunta
    PROCESSING,     // Enviando a Bedrock
    RESPONDING      // Mostrando/leyendo respuesta
}

data class NovaUiState(
    val state: NovaState = NovaState.IDLE,
    val transcript: String? = null,
    val response: String? = null,
    val sentWithImage: Boolean = false
)
Enter fullscreen mode Exit fullscreen mode

Esto reduce errores visuales y facilita el debugging.


Casos de uso validados

  • Identificación visual (objetos, colores, texto)
  • Lectura de pantallas
  • Consultas generales con o sin contexto visual

El modelo decide dinámicamente si usar la imagen.


Resultados

  • Latencia end-to-end: 2–3 segundos
  • Precisión wake word: ~95% en ambientes silenciosos
  • Consumo de batería: ~15% por hora
  • Tamaño del APK: 12.4 MB

Lecciones técnicas

  • Optimizar demasiado temprano complica la arquitectura
  • JPEG al 85% es un buen punto de equilibrio
  • Wake word sin SDK dedicado tiene límites claros
  • Claude maneja bien inputs multimodales ruidosos

Próximos pasos

  • Clasificador local ligero para decidir envío de imagen
  • Optimización de consumo energético
  • Nuevos flujos de análisis visual continuo

Conclusión

Construir Meta-Rock fue un desafío técnico fascinante que surgió de una necesidad real: acceder a funcionalidades de IA en los Meta Ray-Ban que no están disponibles en mi región.

Pero más allá de resolver esa limitación, este proyecto me permitió explorar las posibilidades de integrar wearables con modelos de IA de última generación. ¿Qué más se puede lograr cuando tienes acceso directo a la cámara de unos lentes inteligentes y a modelos como Claude Sonnet 4.5?

Lo más emocionante es que esto es solo el comienzo. Planeo seguir mejorando Meta-Rock, explorando nuevas funcionalidades y documentando cada avance en futuros blogs. Desde análisis de escenas en tiempo real hasta traducción visual instantánea, las posibilidades son infinitas.

Este proyecto me enseñó que las limitaciones geográficas o de plataforma no tienen por qué detenerte. Con las herramientas adecuadas (AWS, SDKs abiertos, creatividad), puedes construir soluciones que no solo resuelven tus necesidades, sino que abren puertas a experiencias completamente nuevas.

Recursos

Agradecimientos

Este proyecto no hubiera sido posible sin:

  • Elizabeth Fuentes por compartir el descubrimiento del Meta Wearables DAT SDK y motivarme a explorar esta tecnología
  • El equipo de Meta por abrir el SDK de los Ray-Ban a la comunidad de desarrolladores
  • AWS por hacer Bedrock accesible y proporcionar modelos de IA de clase mundial
  • La comunidad de AWS Community Builders por el apoyo constante y el intercambio de conocimientos

¿Tienes preguntas o ideas para mejorar Meta-Rock? Déjalas en los comentarios o contáctame en fernandosilvot.cl

Made with ❤️ in Chile 🇨🇱

Top comments (1)

Collapse
 
ensamblador profile image
ensamblador AWS Español

wow ! Estamos a así 🤏 de tener nuestro propio agente tipo "jarvis" de iron man