AWS Bedrock Complete Course | Lambda, API Gateway, S3, RAG, Bedrock AgentCore & LangChain
This comprehensive guide covers architecting, building, and deploying production-ready Generative AI systems and autonomous AI agents on Amazon Web Services (AWS). It walks through core AWS cloud primitives—including Identity and Access Management (IAM), Amazon Simple Storage Service (S3), AWS Lambda, and Amazon API Gateway—and connects them with Amazon Bedrock, LangChain, Knowledge Base RAG pipelines, and the Bedrock AgentCore serverless runtime.
1. Executive Overview & Architecture Blueprint
Modern Generative AI engineering requires transitioning from local prototype scripts to scalable, secure, and decoupled cloud architectures. Amazon Bedrock provides managed foundation models (FMs) accessible through unified APIs, eliminating the overhead of managing self-hosted GPU clusters.
Core Technology Stack
2. IAM Governance: Users, Policies, Roles, and CLI Configuration
Security in AWS is strictly enforced through AWS Identity and Access Management (IAM). Root accounts should never be used for day-to-day operations or API orchestration; dedicated IAM entities must be configured instead.
Real-Life Analogy: University CRM Permissions
Think of IAM like a university CRM portal. A student logging into the portal can only view their own grades and fees. A Department Head (HOD) can see all students in their department, while the Dean or Director has system-wide administrative access.
- IAM Users are distinct identities (like a student or faculty account).
- Policies are the rulebooks defining allowed and denied actions (e.g., "allowed to view grades, denied from editing them").
- Roles represent delegated access between AWS services (e.g., granting an AWS Lambda function permission to call Amazon Bedrock on your behalf).
Policy vs. Role Distinction
- Policy: A JSON document that explicitly declares permissions (
Allow/Denyon specificActionsagainstResources). Policies are directly attached to IAM Users or Groups. - Role: An identity assumed by an AWS service (such as Lambda) to dynamically acquire short-term credentials, enabling inter-service communication without embedding static API keys.
Local Environment Setup via AWS CLI
To interface programmatic scripts and local developer tools with AWS, configure local access credentials:
# Verify AWS CLI installation
aws --version
# Configure programmatic credentials
aws configure
When prompted, input the generated IAM credentials:
- AWS Access Key ID:
AKIAIOSFODNN7EXAMPLE - AWS Secret Access Key:
wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY - Default region name:
us-east-1 - Default output format:
json
Note: The
us-east-1(N. Virginia) region provides the widest availability for all Amazon Bedrock foundation models, embedding endpoints, and AgentCore capabilities.
3. Serverless Compute with AWS Lambda & API Gateway Integration
AWS Lambda enables serverless compute execution where code executes strictly upon event triggers, scaling automatically from zero to thousands of concurrent executions.
Basic Event Handler Implementation
Every Lambda function defines a handler function accepting event and context parameters:
import json
def lambda_handler(event, context):
"""
Entry point for AWS Lambda executions.
:param event: Dict containing triggering payload, headers, and metadata.
:param context: Runtime object providing execution methods and properties.
"""
# Parse payload sent via API Gateway POST request
body = event.get("body", "{}")
if isinstance(body, str):
payload = json.loads(body)
else:
payload = body
question = payload.get("question", "Default Ping")
return {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": json.dumps({
"message": "Response from Lambda Handler",
"received_question": question
})
}
4. Deploying Custom Python Dependencies via Lambda Layers & S3
AWS Lambda runs in an isolated Linux environment containing standard Python libraries. To use third-party libraries such as langchain, langchain-aws, or custom packages, they must be bundled into an AWS Lambda Layer.
The Dependency Packaging Lifecycle
Local Environment (Virtualenv)
|
| pip install dependencies to target: layer/python/
v
Compress Directory
|
| zip -r layer.zip python/
v
Upload Artifact to Amazon S3
|
| s3://aws-bedrock-course-2026/lambda-layers/layer.zip
v
Create Lambda Layer & Attach to Target Lambda Function
Step-by-Step Layer Packaging
Due to compiled native binaries (C extensions, pydantic, numpy), packages must be installed targeting the Linux ARM64 or x86_64 architecture matching the Lambda runtime:
# 1. Create dedicated workspace
mkdir -p aws_lambda_layer
cd aws_lambda_layer
# 2. Define required dependencies
cat <<EOF > requirements.txt
langchain
langchain-aws
EOF
# 3. Target install into strict python/ subdirectory layout
pip install \
--platform manylinux2014_x86_64 \
--target=layer/python \
--implementation cp \
--python-version 3.12 \
--only-binary=:all: --upgrade \
-r requirements.txt
# 4. Remove unnecessary caching artifacts to minimize file size
find layer/python -type d -name "__pycache__" -exec rm -rf {} +
find layer/python -type d -name "*.dist-info" -exec rm -rf {} +
# 5. Compress into deployment package
cd layer
zip -r ../layer.zip python
cd ..
Because unpacked package layers frequently exceed the direct console upload threshold (50 MB), upload the ZIP file to Amazon S3:
# Upload to S3 bucket
aws s3 cp layer.zip s3://aws-bedrock-course-2026/lambda-layers/layer.zip
# Publish Lambda Layer from S3 object
aws lambda publish-layer-version \
--layer-name aws-langchain-bedrock-layer \
--description "LangChain and AWS Bedrock SDK Layer for Python 3.12" \
--content S3Bucket=aws-bedrock-course-2026,S3Key=lambda-layers/layer.zip \
--compatible-runtimes python3.12 \
--compatible-architectures x86_64
5. Amazon Bedrock: Foundation Models, Converse API, & LangChain Integration
Amazon Bedrock provides standardized access to top-tier foundation models via unified inference endpoints.
Bedrock Model Family Comparison
Converse API vs. Legacy InvokeModel
Earlier Bedrock implementations required custom JSON payloads for every provider via InvokeModel. The modern Converse API (BedrockRuntime.Client.converse) normalizes conversational payloads, tool calls, and system prompts across all supported models.
Local LangChain Implementation
import os
from dotenv import load_dotenv
from langchain_aws import ChatBedrockConverse
# Load environment configuration
load_dotenv()
# Initialize unified Converse client
llm = ChatBedrockConverse(
model="us.anthropic.claude-3-5-sonnet-20241022-v2:0",
region_name="us-east-1",
temperature=0.1,
max_tokens=2048
)
# Invoke conversation
query = "Explain the fundamental difference between an IAM Role and an IAM Policy."
response = llm.invoke(query)
print("Inference Content:", response.content)
print("Token Usage Metadata:", response.usage_metadata)
6. End-to-End Generative Q&A System on Serverless Infrastructure
Combining API Gateway, Lambda, Custom Dependency Layers, and Bedrock creates a resilient, serverless Q&A API.
Production Lambda Handler Implementation
import json
import os
from langchain_aws import ChatBedrockConverse
# Initialize Bedrock client globally for execution environment reuse
llm = ChatBedrockConverse(
model="us.anthropic.claude-3-5-sonnet-20241022-v2:0",
region_name=os.environ.get("AWS_REGION", "us-east-1"),
temperature=0.2,
max_tokens=1024
)
def lambda_handler(event, context):
"""
AWS Lambda entry point for Bedrock LLM Inference.
"""
try:
# Extract and parse request body safely
raw_body = event.get("body", "{}")
payload = json.loads(raw_body) if isinstance(raw_body, str) else raw_body
user_question = payload.get("question")
if not user_question:
return {
"statusCode": 400,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"error": "Field 'question' is required."})
}
# Invoke Bedrock via LangChain
response = llm.invoke(user_question)
return {
"statusCode": 200,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
"body": json.dumps({
"question": user_question,
"answer": response.content
})
}
except Exception as e:
return {
"statusCode": 500,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"error": str(e)})
}
7. Retrieval-Augmented Generation (RAG) Architecture & Mental Models
Large Language Models are limited by fixed knowledge cut-off dates and an inability to access private enterprise datasets.
Real-Life Analogy: The Open-Book Exam
Imagine taking a specialized medical exam.
- Without RAG: You must rely purely on what you memorized years ago (the LLM's pre-trained weights). If asked about a patient admitted yesterday, you cannot answer.
- The Naive Flawed Approach: Someone hands you a 1,000-page encyclopedia and demands you read all 1,000 pages within 30 seconds for every single question. You get overwhelmed, forget earlier pages, and run out of time.
- The RAG Approach: A research assistant instantly pinpoints the exact 2 relevant paragraphs from the encyclopedia and clips them to your exam sheet. You read only those 2 paragraphs and write down a precise, cited answer.
Mathematical Formulation of Vector Retrieval
Documents are split into discrete text chunks . An embedding model maps each chunk to a dense vector embedding:
When a user submits query , the query vector is generated:
The vector database calculates the cosine similarity metric across all indexed chunks to retrieve the top- most relevant segments:
8. Building Knowledge Bases with Amazon Bedrock & S3
Amazon Bedrock Knowledge Bases provide a managed RAG pipeline that automates document parsing, chunking, embedding generation, and vector index synchronization without requiring self-managed vector database infrastructure.
Knowledge Base Configuration Matrix
9. Serverless RAG Deployment via Bedrock Knowledge Base Retrievers
Once the Bedrock Knowledge Base completes data synchronization, integrate it directly into Python applications using AmazonKnowledgeBasesRetriever.
Complete End-to-End RAG Lambda Handler
import json
import os
from langchain_aws import ChatBedrockConverse, AmazonKnowledgeBasesRetriever
# 1. Initialize Bedrock Foundation Model
llm = ChatBedrockConverse(
model="us.anthropic.claude-3-5-sonnet-20241022-v2:0",
region_name="us-east-1",
temperature=0.0
)
# 2. Initialize Bedrock Managed Retriever
KNOWLEDGE_BASE_ID = os.environ.get("BEDROCK_KB_ID", "ABC123XYZ4")
retriever = AmazonKnowledgeBasesRetriever(
knowledge_base_id=KNOWLEDGE_BASE_ID,
retrieval_config={
"vectorSearchConfiguration": {
"numberOfResults": 4
}
}
)
def get_context(query: str) -> str:
"""
Queries Bedrock Knowledge Base and consolidates retrieved document chunks.
"""
docs = retriever.invoke(query)
context_accumulator = []
for doc in docs:
context_accumulator.append(doc.page_content)
return "\n\n".join(context_accumulator)
def execute_rag(query: str) -> str:
"""
Augments retrieved context into the LLM prompt and executes generation.
"""
context_text = get_context(query)
prompt = f"""You are a precise clinical assistant. Answer the user question strictly using the provided context.
Rule: Do not say 'Based on the provided context'. Provide direct answers only. If the answer cannot be found in the context, reply 'I do not know'.
Context:
{context_text}
Question:
{query}
"""
response = llm.invoke(prompt)
return response.content
def lambda_handler(event, context):
"""
Lambda Handler for RAG Pipeline.
"""
try:
raw_body = event.get("body", "{}")
payload = json.loads(raw_body) if isinstance(raw_body, str) else raw_body
query = payload.get("question")
if not query:
return {
"statusCode": 400,
"body": json.dumps({"error": "Missing required field: question"})
}
answer = execute_rag(query)
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({
"query": query,
"answer": answer
})
}
except Exception as e:
return {
"statusCode": 500,
"body": json.dumps({"error": str(e)})
}
10. Agentic AI & Amazon Bedrock AgentCore Architecture
Linear RAG chains execute deterministic sequences (Retrieve Augment Generate). Autonomous AI Agents, by contrast, operate via a dynamic reasoning loop: they evaluate user intent, select appropriate external tools, inspect interim execution results, and iterate until the task is solved.
Bedrock AgentCore Architectural Pillars
Amazon Bedrock AgentCore provides a fully managed, serverless runtime specifically designed to host, secure, and scale multi-turn agentic workloads built with any framework (LangChain, LangGraph, CrewAI).
- AgentCore Runtime: High-density MicroVM container runtime executing custom agent code.
- AgentCore Gateway: Managed endpoint orchestrating agent communication with Model Context Protocol (MCP) servers, Lambda tools, and internal microservices.
- Intelligent Memory: Stateful persistence layer handling short-term conversation context and long-term user preferences across multiple sessions.
11. Developing Tool-Calling Agents with LangChain & Nimble Search
Below is the implementation of an autonomous agent equipped with real-time Google Search capabilities via Nimble API.
Complete Agent Implementation (agent.py)
import os
from dotenv import load_dotenv
from langchain_aws import ChatBedrockConverse
from langchain_core.tools import tool
from langchain.agents import create_agent
from langchain_nimble import NimbleSearchAPIWrapper
load_dotenv()
# 1. Initialize Model
llm = ChatBedrockConverse(
model="us.anthropic.claude-3-5-sonnet-20241022-v2:0",
region_name="us-east-1",
temperature=0.0
)
# 2. Initialize Search Wrapper
nimble_wrapper = NimbleSearchAPIWrapper(
nimble_api_key=os.environ.get("NIMBLE_API_KEY"),
k=5
)
# 3. Define Tool with Comprehensive Docstrings
@tool
def search_tool(query: str) -> str:
"""
Searches Google for real-time information, recent events, company details,
and physical office locations.
Args:
query: The specific search query string.
"""
return nimble_wrapper.run(query)
tools = [search_tool]
# 4. Construct Autonomous Agent
system_prompt = (
"You are an expert research agent. When asked questions regarding real-time facts, "
"organizations, or locations, use the search_tool to verify information before answering. "
"If information is unavailable after search, state that you do not know."
)
agent = create_agent(
model=llm,
tools=tools,
system_prompt=system_prompt
)
12. Containerizing & Deploying Agents to Bedrock AgentCore Runtime
To deploy an agent to Bedrock AgentCore, the project is structured as an isolated container application exposing a standardized entry-point interface.
Project Layout
Entry-Point Wrapper (app.py)
import json
from bedrock_agentcore import BedrockAgentCoreApp
from agent import agent
# Initialize AgentCore application wrapper
agent_core_app = BedrockAgentCoreApp()
@agent_core_app.entrypoint
def handle_agent_invocation(event: dict, context: dict) -> dict:
"""
Standardized AgentCore execution handler.
"""
prompt = event.get("prompt", "")
if not prompt:
return {"answer": "No prompt provided."}
# Execute LangChain Agent
response = agent.invoke({
"messages": [
{"role": "user", "content": prompt}
]
})
# Extract terminal response message content
final_message = response["messages"][-1].content
return {
"answer": final_message
}
if __name__ == "__main__":
agent_core_app.run()
Production Multi-Architecture Dockerfile (Dockerfile)
FROM python:3.12-slim
# Install system utilities and uv package manager
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
build-essential \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir uv
# Set container working directory
WORKDIR /app
# Copy dependency specifications
COPY pyproject.toml .
# Install dependencies using uv into system environment
RUN uv pip install --system -r pyproject.toml
# Copy agent application source
COPY . .
# Expose standard container ports
EXPOSE 8080 8000 9000
# Run entry point
CMD ["python", "app.py"]
Local Debugging & Cloud Deployment Commands
# 1. Initialize AgentCore CLI project
agentcore create --name search_agent --no-agent
# 2. Link Container Definition
agentcore add-agent \
--name search_agent \
--build-type container \
--code-dir app/search_agent \
--entrypoint app.py \
--language python \
--framework langchain \
--model-provider bedrock
# 3. Test locally using the interactive inspector debugger
agentcore dev
# 4. Build and deploy container image to Amazon Elastic Container Registry (ECR)
# and spin up the AgentCore managed runtime
agentcore deploy
# 5. Check deployment status
agentcore status
13. Exposing AgentCore via Lambda & API Gateway with End-to-End Testing
Once deployed, the agent exposes an Amazon Resource Name (ARN). An AWS Lambda function acts as a secure intermediary, invoking the AgentCore runtime via boto3.
Client Lambda Handler for AgentCore
import json
import boto3
import os
# Initialize AgentCore Boto3 client
client = boto3.client(
"bedrock-agent-core",
region_name=os.environ.get("AWS_REGION", "us-east-1")
)
AGENT_RUNTIME_ARN = os.environ.get(
"AGENT_RUNTIME_ARN",
"arn:aws:bedrock-agent-core:us-east-1:123456789012:runtime/search_agent-abc123xyz"
)
def lambda_handler(event, context):
try:
# Parse payload
raw_body = event.get("body", "{}")
payload = json.loads(raw_body) if isinstance(raw_body, str) else raw_body
user_question = payload.get("question")
if not user_question:
return {
"statusCode": 400,
"body": json.dumps({"error": "Field 'question' is required."})
}
# Prepare AgentCore invocation payload
request_payload = json.dumps({"prompt": user_question})
# Invoke AgentCore
response = client.invoke_agent_runtime(
agentRuntimeArn=AGENT_RUNTIME_ARN,
payload=request_payload
)
# Parse streamed response payload
raw_response = response["response"].read().decode("utf-8")
parsed_response = json.loads(raw_response)
return {
"statusCode": 200,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
"body": json.dumps({
"question": user_question,
"answer": parsed_response.get("answer")
})
}
except Exception as e:
return {
"statusCode": 500,
"body": json.dumps({"error": str(e)})
}
14. Production Pitfalls, Error Handling, and Debugging Workflows
Debugging Matrix: Common Failure Modes & Solutions
15. Summary and Key Takeaways
- Strict IAM Governance: Never use root credentials for programmatic execution; always manage access using IAM Users with least-privilege policies for development and IAM Roles for inter-service communication.
- Unified Foundation Model Access: Amazon Bedrock's Converse API standardizes LLM invocation across Anthropic, Amazon, Meta, and Cohere through a single interface, eliminating vendor-specific SDK fragmentation.
- Serverless Compute Economics: AWS Lambda combined with API Gateway provides cost-efficient execution that scales dynamically from zero to thousands of requests without server management overhead.
- Modular Dependency Layers: Overcome Lambda storage and binary compilation limits by building clean Linux-targeted dependency packages and attaching them via Amazon S3-backed Lambda Layers.
- Deterministic RAG with Knowledge Bases: Bedrock Knowledge Bases automate the ingestion, chunking, embedding, and vector retrieval lifecycle, preventing hallucination by grounding LLM responses in verified S3 documents.
- Dynamic Autonomy with Agents: AI Agents break free from static RAG pipelines by leveraging an iterative reasoning loop (ReAct) to dynamically invoke external tools like web search APIs and databases.
- Containerized Agent Deployment via AgentCore: Amazon Bedrock AgentCore provides enterprise-grade infrastructure to build, containerize, and deploy tool-calling LangChain agents on a fully managed runtime.
- Production Resilience Best Practices: Always configure Lambda function timeouts to at least 50 seconds for LLM workloads, implement structured JSON payload parsing, and monitor trace logs using Amazon CloudWatch.