💧 Chatbot con Amazon Bedrock Agent + Knowledge Base RAG — Lavandería Ecológica
Cómo construimos un asistente de IA conversacional para una lavandería ecológica usando Amazon Bedrock Agent, Knowledge Base RAG, AWS Lambda y una landing page 100% estática — sin servidores, sin frameworks pesados, sin complicaciones.
¿Por qué Amazon Bedrock Agent?
La mayoría de los chatbots simples solo responden con texto genérico. Un Bedrock Agent va más allá:
- Memoria de sesión nativa — el agente recuerda el contexto de la conversación sin que tengas que implementar nada
- Knowledge Base RAG integrada — conecta el agente a documentos propios (PDFs, FAQs, manuales) y responde con información real del negocio, no alucinaciones
- Orquestación automática — el agente decide cuándo consultar la KB, cuándo responder directamente y cómo combinar fuentes
- Modelo Nova-Lite — rápido, económico y suficientemente potente para casos de uso conversacionales en producción
- Sin infraestructura de ML — no hay modelos que mantener, no hay GPUs, no hay pipelines de entrenamiento
Para un negocio local como una lavandería, esto significa tener un asistente que conoce sus precios, horarios, procesos ecológicos y políticas — todo extraído automáticamente de sus propios documentos.
Arquitectura completa
Cliente (navegador)
│
▼
CloudFront CDN
┌─────────────────────────────────────────┐
│ / → S3 Bucket │
│ (index.html + widget JS) │
│ │
│ /chat → API Gateway HTTP API │
│ → Lambda (Flask) │
│ → Bedrock Agent Runtime │
│ ┌──────────────────────┐ │
│ │ Bedrock Agent │ │
│ │ (Nova-Lite v1) │ │
│ │ │ │ │
│ │ Knowledge Base RAG │ │
│ │ (OpenSearch + S3) │ │
│ └──────────────────────┘ │
└─────────────────────────────────────────┘
| Servicio AWS | Rol en el proyecto |
|---|---|
| Amazon Bedrock Agent | Orquesta la conversación, mantiene sesión, decide cuándo consultar la KB |
| Bedrock Knowledge Base | Indexa documentos del negocio, responde con RAG (Retrieval-Augmented Generation) |
| Amazon Nova-Lite v1 | Modelo de lenguaje — rápido y económico para chat en producción |
| AWS Lambda | Ejecuta el backend Flask sin servidores, escala a cero cuando no hay tráfico |
| API Gateway HTTP API | Expone el endpoint /chat con baja latencia y manejo de CORS |
| Amazon S3 | Almacena el frontend estático y los documentos fuente de la Knowledge Base |
| Amazon CloudFront | CDN global — sirve el frontend y enruta /chat al backend desde el mismo dominio |
| AWS IAM | Controla permisos mínimos entre Lambda y Bedrock |
Cómo funciona el flujo de una pregunta
Usuario escribe: "¿Cuánto cuesta lavar un traje?"
│
▼
chatbot-wiget.js → POST /chat { sessionId, message, context }
│
▼
Lambda (Flask) → bedrock_client.py
│
▼
invoke_agent(agentId, agentAliasId, sessionId, inputText)
│
▼
Bedrock Agent evalúa:
¿Tengo esta info en la KB? → Sí
│
▼
Knowledge Base RAG:
1. Embedding de la pregunta
2. Búsqueda semántica en OpenSearch
3. Recupera fragmentos relevantes del PDF de precios
4. Nova-Lite genera respuesta contextualizada
│
▼
Stream de respuesta → Lambda → API Gateway → Widget
Configuración del Bedrock Agent
Crear la Knowledge Base
# 1. Subir documentos fuente al bucket S3
aws s3 cp documentos/ s3://tintorerias-kb-docs/ --recursive
# 2. Crear la Knowledge Base (desde consola o CLI)
aws bedrock-agent create-knowledge-base \
--name "tintorerias-ecologicas-kb" \
--description "Documentos de servicios, precios y procesos ecológicos" \
--role-arn arn:aws:iam::ACCOUNT_ID:role/AmazonBedrockExecutionRoleForKnowledgeBase \
--knowledge-base-configuration '{
"type": "VECTOR",
"vectorKnowledgeBaseConfiguration": {
"embeddingModelArn": "arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v1"
}
}' \
--storage-configuration '{
"type": "OPENSEARCH_SERVERLESS",
"opensearchServerlessConfiguration": {
"collectionArn": "arn:aws:aoss:us-east-1:ACCOUNT_ID:collection/COLLECTION_ID",
"vectorIndexName": "tintorerias-index",
"fieldMapping": {
"vectorField": "embedding",
"textField": "text",
"metadataField": "metadata"
}
}
}'
Crear el Data Source y sincronizar
# Crear data source apuntando al bucket S3
aws bedrock-agent create-data-source \
--knowledge-base-id TU_KB_ID \
--name "documentos-tintorerias" \
--data-source-configuration '{
"type": "S3",
"s3Configuration": {
"bucketArn": "arn:aws:s3:::tintorerias-kb-docs"
}
}'
# Iniciar sincronización (indexación de documentos)
aws bedrock-agent start-ingestion-job \
--knowledge-base-id TU_KB_ID \
--data-source-id TU_DATA_SOURCE_ID
# Verificar estado de la sincronización
aws bedrock-agent get-ingestion-job \
--knowledge-base-id TU_KB_ID \
--data-source-id TU_DATA_SOURCE_ID \
--ingestion-job-id TU_JOB_ID
Crear el Agente y asociar la Knowledge Base
# Crear el agente
aws bedrock-agent create-agent \
--agent-name "asistente-tintorerias" \
--agent-resource-role-arn arn:aws:iam::ACCOUNT_ID:role/AmazonBedrockExecutionRoleForAgents \
--foundation-model "amazon.nova-lite-v1:0" \
--instruction "Eres el asistente virtual de una lavandería ecológica. Responde preguntas sobre servicios, precios, horarios y procesos de limpieza ecológica de manera amable y concisa. Usa la información de los documentos para dar respuestas precisas."
# Asociar la Knowledge Base al agente
aws bedrock-agent associate-agent-knowledge-base \
--agent-id TU_AGENT_ID \
--agent-version DRAFT \
--knowledge-base-id TU_KB_ID \
--description "Base de conocimiento de servicios y precios"
# Preparar y publicar el agente
aws bedrock-agent prepare-agent --agent-id TU_AGENT_ID
aws bedrock-agent create-agent-alias \
--agent-id TU_AGENT_ID \
--agent-alias-name "produccion"
Código del cliente Bedrock
Invocar el agente (con streaming)
import boto3
client = boto3.client("bedrock-agent-runtime", region_name="us-east-1")
def chat_con_agente(mensaje: str, session_id: str) -> str:
response = client.invoke_agent(
agentId="PPJ6T6QBDD",
agentAliasId="VQKXHLAVG1",
sessionId=session_id, # Bedrock mantiene el contexto por sessionId
inputText=mensaje,
)
# La respuesta llega como stream de eventos
texto_completo = ""
for event in response.get("completion", []):
chunk = event.get("chunk", {})
if "bytes" in chunk:
texto_completo += chunk["bytes"].decode("utf-8")
return texto_completo or "(sin respuesta)"
# Uso
respuesta = chat_con_agente("¿Cuánto cuesta lavar una chamarra de cuero?", "sesion-123")
print(respuesta)
Consultar la Knowledge Base directamente (sin agente)
def consultar_kb(pregunta: str, kb_id: str) -> str:
response = client.retrieve_and_generate(
input={"text": pregunta},
retrieveAndGenerateConfiguration={
"type": "KNOWLEDGE_BASE",
"knowledgeBaseConfiguration": {
"knowledgeBaseId": kb_id,
"modelArn": "amazon.nova-lite-v1:0",
"generationConfiguration": {
"promptTemplate": {
"textPromptTemplate": (
"Eres un asistente de lavandería ecológica. "
"Responde basándote en esta información:\n\n"
"$search_results$"
)
}
},
},
},
)
return response["output"]["text"]
Soporte multi-contexto (varios clientes, un backend)
# bedrock_client.py — patrón usado en este proyecto
CONFIGS = {
'aquamax': {
'type': 'agent',
'agent_id': 'PPJ6T6QBDD',
'agent_alias_id': 'VQKXHLAVG1',
},
'otro_cliente': {
'type': 'kb',
'kb_id': 'KB_ID_DEL_CLIENTE',
'model_arn': 'amazon.nova-lite-v1:0',
'prompt': "Eres el asistente de...\n\n$search_results$",
},
}
def chat(mensaje: str, session_id: str, contexto: str) -> str:
cfg = CONFIGS.get(contexto, CONFIGS['aquamax'])
if cfg['type'] == 'agent':
return _invoke_agent(mensaje, session_id, cfg)
return _retrieve_and_generate(mensaje, cfg)
Despliegue del backend en Lambda
Empaquetar y crear la función
cd tintorerias-chatbot
# Instalar dependencias en carpeta local
pip install flask flask-cors boto3 python-dotenv -t package/
cp app.py bedrock_client.py config.py lambda_handler.py package/
# Crear ZIP de despliegue
cd package && zip -r ../lambda_deployment.zip . && cd ..
# Obtener Account ID
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
# Crear rol IAM con permisos mínimos
aws iam create-role \
--role-name tintorerias-lambda-role \
--assume-role-policy-document '{
"Version":"2012-10-17",
"Statement":[{
"Effect":"Allow",
"Principal":{"Service":"lambda.amazonaws.com"},
"Action":"sts:AssumeRole"
}]
}'
aws iam attach-role-policy \
--role-name tintorerias-lambda-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam put-role-policy \
--role-name tintorerias-lambda-role \
--policy-name BedrockAccess \
--policy-document '{
"Version":"2012-10-17",
"Statement":[{
"Effect":"Allow",
"Action":[
"bedrock:InvokeAgent",
"bedrock:RetrieveAndGenerate",
"bedrock:Retrieve",
"bedrock:InvokeModel"
],
"Resource":"*"
}]
}'
# Crear la función Lambda
aws lambda create-function \
--function-name tintorerias-chatbot \
--runtime python3.11 \
--role arn:aws:iam::${ACCOUNT_ID}:role/tintorerias-lambda-role \
--handler lambda_handler.handler \
--zip-file fileb://lambda_deployment.zip \
--timeout 30 \
--memory-size 256 \
--environment Variables="{
KB_ID=TU_KB_ID,
MODEL_ARN=amazon.nova-lite-v1:0,
AWS_REGION=us-east-1
}"
Crear API Gateway y conectar con Lambda
# Crear HTTP API
API_ID=$(aws apigatewayv2 create-api \
--name "tintorerias-chat-api" \
--protocol-type HTTP \
--query 'ApiId' --output text)
# Crear integración Lambda
LAMBDA_ARN="arn:aws:lambda:us-east-1:${ACCOUNT_ID}:function:tintorerias-chatbot"
INTEGRATION_ID=$(aws apigatewayv2 create-integration \
--api-id $API_ID \
--integration-type AWS_PROXY \
--integration-uri $LAMBDA_ARN \
--payload-format-version "2.0" \
--query 'IntegrationId' --output text)
# Ruta POST /chat
aws apigatewayv2 create-route \
--api-id $API_ID \
--route-key "POST /chat" \
--target "integrations/$INTEGRATION_ID"
# Stage prod con auto-deploy
aws apigatewayv2 create-stage \
--api-id $API_ID \
--stage-name prod \
--auto-deploy
# Permiso para que API Gateway invoque Lambda
aws lambda add-permission \
--function-name tintorerias-chatbot \
--statement-id apigateway-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-east-1:${ACCOUNT_ID}:${API_ID}/*/*/chat"
# URL final del endpoint
aws apigatewayv2 get-api --api-id $API_ID --query 'ApiEndpoint' --output text
El widget de chat (frontend)
El widget es un archivo JS autocontenido que se inyecta en cualquier página HTML con un solo <script>:
<script src="chatbot-wiget.js"></script>
Internamente genera su propio DOM, estilos y lógica sin depender de ninguna librería:
// Configuración mínima necesaria
const CONFIG = {
apiUrl: 'https://TU_DOMINIO.cloudfront.net/chat',
requestTimeout: 30000,
context: 'aquamax', // identifica qué agente/KB usar en el backend
};
// Cada sesión de navegador tiene su propio UUID
// Bedrock Agent usa este ID para mantener el contexto de conversación
state.sessionId = crypto.randomUUID();
// Llamada al backend
fetch(CONFIG.apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sessionId: state.sessionId,
message: texto,
context: CONFIG.context,
}),
});
Probar el endpoint en producción
# Pregunta simple
curl -X POST https://TU_DOMINIO.cloudfront.net/chat \
-H "Content-Type: application/json" \
-d '{"sessionId":"test-1","message":"¿Cuáles son sus servicios?","context":"aquamax"}'
# Pregunta de seguimiento (mismo sessionId — el agente recuerda el contexto)
curl -X POST https://TU_DOMINIO.cloudfront.net/chat \
-H "Content-Type: application/json" \
-d '{"sessionId":"test-1","message":"¿Y cuánto tiempo tarda?","context":"aquamax"}'
# Respuesta esperada
# { "response": "Ofrecemos lavado en seco ecológico, lavado de ropa delicada..." }
Actualizar el despliegue
# Backend — reconstruir y subir Lambda
cd tintorerias-chatbot
pip install flask flask-cors boto3 python-dotenv -t package/
cp app.py bedrock_client.py config.py lambda_handler.py package/
cd package && zip -r ../lambda_deployment.zip . && cd ..
aws lambda update-function-code \
--function-name tintorerias-chatbot \
--zip-file fileb://lambda_deployment.zip
# Frontend — subir y limpiar caché de CloudFront
aws s3 cp index.html s3://$BUCKET_NAME/
aws s3 cp chatbot-wiget.js s3://$BUCKET_NAME/
aws cloudfront create-invalidation \
--distribution-id $DISTRIBUTION_ID \
--paths "/*"
Por qué este stack es ideal para negocios locales
- Costo casi cero en reposo — Lambda y API Gateway cobran por invocación, no por hora. Un chatbot con tráfico moderado cuesta centavos al mes.
- Sin mantenimiento de servidores — no hay EC2 que parchear, no hay contenedores que monitorear.
- Escala automática — de 0 a miles de usuarios simultáneos sin configuración adicional.
- Conocimiento del negocio real — la Knowledge Base RAG garantiza que el asistente responda con información actualizada del negocio, no con datos genéricos del modelo.
-
Reutilizable — el mismo backend soporta múltiples clientes cambiando solo el campo
contexten la petición.
Licencia
Proyecto privado — todos los derechos reservados.
Top comments (0)