DEV Community

Cover image for AWS ChatBot
YUVARAJ. R
YUVARAJ. R

Posted on

AWS ChatBot

Chatbot with Amazon Bedrock Agent + Knowledge Base RAG — Ecological Laundry
How we built a conversational AI assistant for an eco-friendly laundry using Amazon Bedrock Agent, Knowledge Base RAG, AWS Lambda and a 100% static landing page — without servers, without heavy frameworks, without complications.

Why Amazon Bedrock Agent?
Most simple chatbots only respond with generic text. A Bedrock Agent it goes further:

Native session memory — the agent remembers the context of the conversation without you having to implement anything
Integrated RAG Knowledge Base — connect the agent to your own documents (PDFs, FAQs, manuals) and respond with real business information, not hallucinations
Automatic orchestration — the agent decides when to query the KB, when to respond directly, and how to combine sources
Nova-lite model — fast, economical and powerful enough for conversational use cases in production
No ML infrastructure — no models to maintain, no GPUs, no training pipelines
For a local business like a laundromat, this means having an assistant who knows your prices, schedules, green processes, and policies — all automatically pulled from your own documents.

Complete architecture
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) │ │
│ └──────────────────────┘ │
└─────────────────────────────────────────┘
AWS Service Role in the project
Amazon Bedrock Agent Orchestra the conversation, hold session, decide when to consult the KB
Bedrock Knowledge Base Indexes business documents, responds with RAG (Retrieval-Augmented Generation)
Amazon Nova-Lite v1 Fast and economical — language model for chat in production
AWS Lambda Runs the Flask backend without servers, scales to zero when there is no traffic
HTTP API Gateway API Exposes the endpoint /chat with low latency and CORS handling
Amazon S3 Stores the static frontend and source documents of the Knowledge Base
Amazon CloudFront global — CDN serves the frontend and routes /chat to the backend from the same domain
AWS IAM Control minimum permissions between Lambda and Bedrock
How the flow of a question works
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 Bedrock Agent configuration Create the 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"
}
}
}'
Create the Data Source and synchronize

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
Create the Agent and associate the 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"
Bedrock Customer Code
Invoke the agent (with 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)"

Enter fullscreen mode Exit fullscreen mode




Uso

respuesta = chat_con_agente("¿Cuánto cuesta lavar una chamarra de cuero?", "sesion-123")
print(respuesta)
Consult the Knowledge Base directly (without agent)
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"]
Multi-context support (multiple clients, one 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)
Deployment of the backend in Lambda
Package and create the function
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
}"
Create API Gateway and connect with 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
The chat widget (frontend)
The widget is a self-contained JS file that is injected into any HTML page with a single :</p> <script src="chatbot-wiget.js">

Internally it generates its own DOM, styles and logic without depending on any library:

// 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,
}),
});
Test the endpoint in production

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..." }

Update the deployment

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 "/*"

Why this stack is ideal for local businesses
Almost zero cost at rest — Lambda and API Gateway charge per invocation, not per hour. A chatbot with moderate traffic costs pennies a month.
No server maintenance — no EC2 to patch, no containers to monitor.
Automatic scale — from 0 to thousands of simultaneous users without additional configuration.
Knowledge of real business — the RAG Knowledge Base ensures that the assistant responds with up-to-date business information, not generic model data.
Reusable — the same backend supports multiple clients by changing only the field context in the petition.

Top comments (0)