Transmisión de respuestas de IA en React Native: reducción de la latencia con AppSync + Bedrock

Transmisión de respuestas de IA en React Native: reducción de la latencia con AppSync + Bedrock

Las tres causas fundamentales

Cuando se llama a un modelo de lenguaje grande directamente desde una aplicación de React Native —sin orquestación, sin streaming ni caché—, ocurren tres cosas al mismo tiempo: la interfaz de usuario se bloquea a la espera de una respuesta completa, el coste de los tokens aumenta con cada mensaje del historial de la conversación y se están realizando inferencias que se podrían haber evitado.

Este artículo describe una capa de orquestación basada en AWS AppSync, Lambda y Bedrock que aborda cada uno de estos aspectos a nivel de infraestructura, no a nivel de producto.

  • Latencia: Una llamada directa a `InvokeModel` desde un cliente de React Native añade varios segundos al TTFT (tiempo hasta el primer token). Sin streaming, la interfaz de usuario queda bloqueada hasta que la respuesta completa está lista.
  • Coste del token: Cada ida y vuelta incluye el historial completo de la conversación. Sin una capa de almacenamiento en caché o de reducción del contexto, el uso de tokens aumenta de forma lineal con la duración de la conversación.
  • Conclusión innecesaria: Las preguntas duplicadas que no encuentran resultados en la caché se traducen directamente en horas de GPU desperdiciadas y en un aumento de la huella de carbono.
Requisitos previos
— AWS CLI configurada
— Acceso a modelos de Bedrock habilitado
— Amplify CLI instalada
— React Native y Node.js instalados

La capa de orquestación: qué problemas resuelve

No se trata de un problema de rendimiento de React Native. Es un problema de arquitectura. La capa de orquestación resuelve cada causa raíz a nivel de infraestructura:

  • Latencia: La transmisión de tokens a través de las suscripciones de AppSync muestra los primeros caracteres en la interfaz de usuario en menos de un segundo, sin necesidad de esperar a recibir la respuesta completa.
  • Coste del token: El filtrado en el borde de Lambda elimina el contexto irrelevante antes de que llegue a Bedrock; solo se envía la carga útil relevante.
  • Conclusión innecesaria: Una capa de caché en Lambda intercepta las consultas repetidas antes de que lleguen al modelo.
  • Protección de datos: Bedrock se ejecuta dentro de tu cuenta de AWS a través de una VPC; tus datos nunca salen de ella para entrenar modelos públicos.

Cada capa tiene una única responsabilidad. Esta es la estructura:

  • React Native (Frontend): Nuestra interfaz única para iOS, Android y web.
  • Amazon Cognito (Identidad): Asigna roles de IAM que restringen qué entidades pueden invocar los puntos de conexión de Bedrock.
  • AWS AppSync (GraphQL): GraphQL permite al cliente solicitar solo los campos que necesita, lo que reduce el tamaño de la carga útil y el consumo de ancho de banda en dispositivos móviles.
  • AWS Lambda (Lógica): Funciones que se ejecutan solo cuando es necesario, realizando un filtrado del contexto y comprobaciones de la caché antes de invocar Bedrock
  • Amazon Bedrock (AI): El servicio gestionado de AWS que nos da acceso a modelos como Claude o Llama a través de una API privada y segura.
  • Amazon RDS / Aurora PostgreSQL: Para la persistencia de datos relacionales complejos que la IA necesita consultar (RAG - Retrieval Augmented Generation).

Configuración del entorno

Toda la infraestructura se define mediante código. No hay que realizar ningún paso manualmente en la consola de AWS.

En amplificar init, configura dos entornos independientes: dev y prod. Nunca pruebes las instrucciones ni las llamadas de modelo en el entorno de producción.

# Instalar el Amplify CLI para gestionar el backend
npm install -g @aws-amplify/cli

# Inicializar el proyecto React Native con TypeScript
npx react-native init GreenAIApp --template react-native-template-typescript

# Inicializar el entorno AWS
amplify init

Aplicación

Autenticación segura (Cognito)

Cognito se encarga de la autenticación y asigna roles de IAM a cada usuario. Esto es lo que determina qué entidades pueden invocar los puntos de conexión de Bedrock.

amplificar añadir auth

En el código de React Native, envolvemos nuestra aplicación:

import { withAuthenticator } from 'aws-amplify-react-native';

const App = () => {
  return (
    
      Asistente de AI v1.0 - Acceso seguro
    
  );
};

export default withAuthenticator(App);

Manejador Lambda: comprobación de caché + invocación de Bedrock

La función Lambda ejecuta dos pasos antes de invocar Bedrock: una consulta a la caché y una consulta RAG en PostgreSQL. Si se encuentra el dato en la caché, Bedrock nunca se invoca.

Ejemplo de lógica Lambda (Node.js):
const { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } = require("@aws-sdk/client-bedrock-runtime");
const { Client } = require('pg');

exports.handler = async (event) => {
  const pg = new Client({ /* RDS credentials via Secrets Manager */ });
  await pg.connect();

  // 1. Cache check — skip Bedrock if already answered
  const cached = await pg.query(
    'SELECT response FROM cache WHERE question_hash = $1',
    [event.questionHash]
  );
  if (cached.rows.length > 0) {
    await pg.end();
    return { source: 'cache', response: cached.rows[0].response };
  }

  // 2. RAG — fetch relevant context from knowledge base
  const context = await pg.query(
    'SELECT content FROM knowledge_base WHERE topic = $1 LIMIT 3',
    [event.topic]
  );
  await pg.end();

  // 3. Invoke Bedrock with Messages API
  const bedrock = new BedrockRuntimeClient({ region: "us-east-1" });
  const command = new InvokeModelWithResponseStreamCommand({
    modelId: "anthropic.claude-3-haiku-20240307-v1:0",
    contentType: "application/json",
    accept: "application/json",
    body: JSON.stringify({
      anthropic_version: "bedrock-2023-05-31",
      max_tokens: 300,
      messages: [{
        role: "user",
        content: `Context: ${context.rows.map(r => r.content).join('\n')}\n\nQuestion: ${event.question}`
      }]
    })
  });

  const stream = await bedrock.send(command);
  return { source: 'bedrock', stream };
};

Carga de archivos en S3 para análisis de imágenes

Los archivos subidos para el análisis de visión se almacenan en S3 con una lista de controles de acceso (ACL) privada, a la que solo puede acceder el rol de IAM del usuario autenticado y que nunca se hace pública.

// Amplify v6: utiliza el almacenamiento de 'aws-amplify/storage' en su lugar
import { Storage } from 'aws-amplify';

const uploadDocument = async (fileUri: string) => {
  try {
    const response = await fetch(fileUri);
    const blob = await response.blob();
    await Storage.put(`uploads/${Date.now()}.jpg`, blob, {
 level: 'private',
 contentType: 'image/jpeg',
    });
  } catch (error) {
    console.error("Error al subir a S3:", error);
  }
};

Buenas prácticas

Reducción del tamaño de la carga útil con GraphQL

Con GraphQL a través de AppSync, el cliente solo solicita los campos que necesita. En el caso de una respuesta larga de Bedrock, al solicitar solo los resumen El uso de un campo en lugar del objeto completo reduce considerablemente el tamaño de la carga útil, lo cual es importante en las conexiones móviles.

Selección del modelo: adaptar el tamaño del modelo a la tarea

Reserva los modelos grandes para el razonamiento complejo. Para clasificación, detección de intenciones, o breves resúmenes, los modelos más pequeños disponibles en Bedrock son considerablemente más baratos y rápidos. Elige el modelo en función de la tarea, no del marketing.

Almacenamiento en caché del lado del cliente

Utiliza React Query o la caché de Apollo en React Native para almacenar las respuestas localmente. Si se formula la misma pregunta dos veces, muéstrala desde la caché — sin invocación de Lambda, no hay llamada de Bedrock.

Implementación y CI/CD

  • Amplificar el alojamiento: Conecta tu repositorio de GitHub. Cada Pull Request generará un entorno backend efímero para que el equipo de control de calidad pruebe las funciones de IA sin romper la producción.
  • Monitorización con CloudWatch: Configura las alertas de presupuesto. Si el consumo de la API de Bedrock supera los $50 en un día, la función Lambda debería desactivarse automáticamente para evitar un aumento descontrolado de los costes.

Ventajas e inconvenientes

CaracterísticaReact Native + AWS StackAlternativa (Firebase/Heroku)
Control del modelo AIControl total (modelos privados a través de Bedrock)Limitado (dependencia de API externas)
SostenibilidadMenos llamadas de inferencia innecesarias gracias a la caché y a la selección de modelosMedio
Curva de aprendizajeAlto (requiere conocimientos de IAM y VPC)Bajo
Coste a escalaMuy bajo (verdadero pago por uso)Puede volverse impredecible

Conclusión

Los problemas de latencia y coste que aquí se describen no son problemas del producto, sino de la arquitectura. La capa de orquestación creada con AppSync, Lambda y Bedrock resuelve cada uno de ellos a nivel de infraestructura.

Si estás empezando a explorar esta pila, un primer paso práctico es desplegar una única función Lambda que llame a Bedrock, medir el tiempo de respuesta y compararlo con una llamada directa a la API. La diferencia hará que el caso sea mejor que cualquier diagrama.

El objetivo no es crear una aplicación que funciona - es construir uno que sea rápido, barato de mantener y responsable por diseño.

Más entradas

Comparte: