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:
- Wake word detection sin librerías externas
- Conversión de frames del SDK de Meta a formatos compatibles con Bedrock
- Uso de la Converse API con input multimodal (imagen + texto)
- 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.
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
Pipeline resumido:
- Wake word →
SpeechRecognizer - Captura de frame desde los lentes
- Conversión I420 → JPEG
- Request multimodal a Bedrock
- 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()
}
}
}
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)
}
// ...
}
}
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()
}
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()
}
}
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
)
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
- Repositorio GitHub: aws-bedrock-meta-glasses
- Repo original de Meta: CameraAccess Sample
- Meta Wearables SDK: Documentación oficial
- Amazon Bedrock: Converse API
- Claude Sonnet 4.5: Modelo en Bedrock
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)
wow ! Estamos a así 🤏 de tener nuestro propio agente tipo "jarvis" de iron man