AWS Builder Center
Building Agentic RAG with Terraform: Strands Agents, Bedrock AgentCore, ECR, S3, Bedrock Knowledge Bases, and OpenSearch Serverless

Building Agentic RAG with Terraform: Strands Agents, Bedrock AgentCore, ECR, S3, Bedrock Knowledge Bases, and OpenSearch Serverless

Explaining Agentic RAG Ingestion & Querying Steps; Building Agentic RAG architecture using Bedrock AgentCore, Docker Image for Agent, ECR, Bedrock Knowledge Bases, S3, and OpenSearch Serverless with Terraform; Implementing document ingestion and querying using a simple Streamlit app with embedding and reranker models, and enabling agents to call other agents as tools for critique and evaluation.

AI & Platform Engineer
In this post, we will implement an Agentic Retrieval-Augmented Generation (Agentic RAG) system on AWS using Terraform, Amazon S3, Amazon Bedrock Knowledge Bases (KB), Amazon OpenSearch Serverless, AWS Strands Agents, and Amazon Bedrock AgentCore Runtime.
If you are new to RAG or want to understand how to build the underlying infrastructure, please check out my previous post:
I won't cover the RAG implementation details again in this post. Instead, we will focus on how to use AI agents to retrieve information from Amazon Bedrock Knowledge Bases and how to improve RAG quality by combining Reranker models and Agentic RAG.
We will explore how a reranker improves retrieval precision by selecting the most relevant document chunks, while an AI agent dynamically generates retrieval queries, evaluates the retrieved context with agent-as-tool, and performs additional retrievals when necessary to improve answer completeness.
TLDR: We will build on top of the existing Simple RAG architecture and extend it with a Reranker model and Agentic RAG using Strands Agents and Amazon Bedrock AgentCore Runtime.

What is Agentic RAG?

Agentic Retrieval-Augmented Generation (Agentic RAG) is an AI architecture that combines Retrieval-Augmented Generation (RAG) with autonomous AI agents. Unlike traditional RAG, where the retrieval process follows a predefined sequence of steps, Agentic RAG allows an AI agent to decide when to retrieve information, which tools to use, and how to use the retrieved information to answer a user's question.
An AI agent can interact with external knowledge sources, execute retrieval tools, evaluate the retrieved information, and perform additional searches when the initial results are insufficient. This enables more flexible, iterative, and context-aware information retrieval.
Document Ingestion
Bash
- Upload documents → Amazon S3
- Split documents → Chunks
- Generate embeddings → Embedding Model
- Store embeddings + text + metadata → Vector Database
Agentic-RAG Query & Retrieval with Agent/s
Bash
- User submits a question
- AI Agent receives and analyzes the question
- Agent decides whether external knowledge is required
- Agent selects and invokes the appropriate retrieval tool
- Retrieve relevant document chunks → Amazon Bedrock Knowledge Bases
- Agent evaluates the retrieved context by calling another agent as tool
- Agent decides whether additional retrieval is needed
- If necessary, performs additional searches or tool calls
- Agent combines the retrieved information and reasoning
- Generate the final answer with relevant citations
Differences:
  • Traditional RAG: Follows a predefined retrieval pipeline (retrieve → augment → generate).
  • Agentic RAG: The agent dynamically controls the retrieval process (decide → retrieve → evaluate → iterate → generate).
In this project, Amazon S3 acts as the document source, while Amazon Bedrock Knowledge Bases (KB) manages the document ingestion pipeline.

When an ingestion job is triggered, the following steps take place:
  • User uploads a document through the Streamlit UI.
  • The application writes the document directly to the S3 docs bucket.
  • User clicks Sync Knowledge Base in the UI.
  • The application triggers Bedrock's StartIngestionJob API.
  • Bedrock KB reads the documents from S3 and splits their content into smaller pieces called chunks.
  • Bedrock KB generates embeddings for each chunk using the configured embedding model (Titan Text Embeddings v2).
  • Bedrock KB stores the resulting vectors, together with the original text and document metadata, in Amazon OpenSearch Serverless for vector search.

At query/retrieval time, instead of relying on a single retrieval operation, we use an AI agent built with Strands Agents and deployed on Amazon Bedrock AgentCore Runtime.
  • User submits a question through the Streamlit chat input.
  • The application invokes the AgentCore Runtime agent.
  • The agent analyzes the question and generates a focused retrieval query.
  • The agent calls its retrieve_kb(query) tool, which invokes the Bedrock Knowledge Bases Retrieve API.
  • Bedrock KB performs a vector search against OpenSearch Serverless, retrieves 20 candidate chunks, and applies the configured Reranker model (Amazon Rerank 1.0) to select the 5 most relevant chunks.
  • The agent calls its critique_context(question) tool to evaluate whether the retrieved information is sufficient to answer the original question.
  • If the context is insufficient, the agent identifies missing aspects, reformulates the retrieval query, and performs another retrieval.
  • This retrieval and evaluation loop can run up to 3 retrieval calls per question.
  • Once sufficient context is collected, the agent generates the final answer using the retrieved document chunks and includes source citations.

If you have read this far, we can go deeper 😊

Why Combine Reranking and Agentic RAG?

The Reranker model and Agentic RAG improve different aspects of the retrieval process:
  • Reranker, Improves Precision: Vector search retrieves candidate chunks based on embedding similarity. The reranker evaluates the question and candidate chunks together, re-scores their relevance, and selects the most useful chunks. In our implementation, it reduces 20 candidates to the best 5.
  • Agentic RAG, Improves Recall: Instead of relying on a single retrieval, the agent can generate multiple focused queries, evaluate the retrieved context, identify missing information, and perform additional retrievals when necessary.
By combining both approaches, we aim to retrieve more complete information while ensuring that the most relevant document chunks are passed to the generation model.
The key difference is that Simple RAG performs a single retrieval and reranking operation, while Agentic RAG dynamically controls an iterative retrieval and evaluation process, applying reranking to every retrieval.
Whether you're exploring to build your own system, this will give you a clear, practical starting point 😉
Code GitHub Link:  Project on GitHub 



Terraform: Bedrock AgentCore, ECR, CloudWatch, Bedrock KnowledgeBase, OpenSearch Serverless, S3, IAM

Bedrock AgentCore, ECR-Repository, Agent-Core Runtime, Runtime IAM Policy:
tf
# AWS ECR to store Agent Docker Image
resource "aws_ecr_repository" "agent" {
name = "${var.project_name}-agent"
image_tag_mutability = "MUTABLE"
force_delete = true
image_scanning_configuration {
scan_on_push = true
}
}
# IAM Policy for AgentCore Runtime
data "aws_iam_policy_document" "agent_runtime_trust" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["bedrock-agentcore.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "aws:SourceAccount"
values = [data.aws_caller_identity.current.account_id]
}
condition {
test = "ArnLike"
variable = "aws:SourceArn"
values = ["arn:aws:bedrock-agentcore:${var.aws_region}:${data.aws_caller_identity.current.account_id}:*"]
}
}
}
resource "aws_iam_role" "agent_runtime_execution" {
name = "${var.project_name}-agent-runtime-role"
assume_role_policy = data.aws_iam_policy_document.agent_runtime_trust.json
}
data "aws_iam_policy_document" "agent_runtime_permissions" {
statement {
sid = "BedrockModelInvoke"
effect = "Allow"
actions = ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"]
resources = [
"arn:aws:bedrock:*::foundation-model/*",
"arn:aws:bedrock:*:${data.aws_caller_identity.current.account_id}:inference-profile/*",
]
}
statement {
sid = "KnowledgeBaseRetrieve"
effect = "Allow"
actions = ["bedrock:Retrieve"]
resources = [aws_bedrockagent_knowledge_base.this.arn]
}
# Retrieve calls with a rerankingConfiguration also need Rerank on the caller.
statement {
sid = "Rerank"
effect = "Allow"
actions = ["bedrock:Rerank"]
resources = ["*"]
}
statement {
sid = "EcrPull"
effect = "Allow"
actions = ["ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "ecr:BatchCheckLayerAvailability"]
resources = [aws_ecr_repository.agent.arn]
}
statement {
sid = "EcrAuth"
effect = "Allow"
actions = ["ecr:GetAuthorizationToken"]
resources = ["*"]
}
statement {
sid = "Logs"
effect = "Allow"
# PutResourcePolicy lets AgentCore deliver the agent's spans into its own log group.
actions = ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogStreams", "logs:PutResourcePolicy"]
resources = ["arn:aws:logs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/bedrock-agentcore/*"]
}
statement {
sid = "LogsDescribe"
effect = "Allow"
actions = ["logs:DescribeLogGroups"]
resources = ["arn:aws:logs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:log-group:*"]
}
statement {
sid = "Xray"
effect = "Allow"
actions = ["xray:PutTraceSegments", "xray:PutTelemetryRecords", "xray:GetSamplingRules", "xray:GetSamplingTargets"]
resources = ["*"]
}
# AgentCore Observability metrics (sessions, latency, tokens) from ADOT.
statement {
sid = "Metrics"
effect = "Allow"
actions = ["cloudwatch:PutMetricData"]
resources = ["*"]
condition {
test = "StringEquals"
variable = "cloudwatch:namespace"
values = ["bedrock-agentcore"]
}
}
}
resource "aws_iam_role_policy" "agent_runtime_permissions" {
name = "${var.project_name}-agent-runtime-permissions"
role = aws_iam_role.agent_runtime_execution.id
policy = data.aws_iam_policy_document.agent_runtime_permissions.json
}
# AgentCore Runtime
resource "aws_bedrockagentcore_agent_runtime" "agent" {
count = var.agent_container_image_uri == "" ? 0 : 1
agent_runtime_name = replace("${var.project_name}_agent", "-", "_")
role_arn = aws_iam_role.agent_runtime_execution.arn
agent_runtime_artifact {
container_configuration {
container_uri = var.agent_container_image_uri
}
}
network_configuration {
network_mode = "PUBLIC"
}
environment_variables = {
AWS_REGION = var.aws_region
KNOWLEDGE_BASE_ID = aws_bedrockagent_knowledge_base.this.id
GENERATION_MODEL_ID = var.generation_model_id
RERANK_MODEL_ARN = var.enable_reranking ? local.rerank_model_arn : ""
}
}
# AgentCore Observability, CloudWatch
resource "aws_cloudwatch_log_delivery_source" "agent_traces" {
count = length(aws_bedrockagentcore_agent_runtime.agent)
name = "${var.project_name}-agent-traces"
log_type = "TRACES"
resource_arn = aws_bedrockagentcore_agent_runtime.agent[0].agent_runtime_arn
}
resource "aws_cloudwatch_log_delivery_destination" "agent_traces" {
count = length(aws_bedrockagentcore_agent_runtime.agent)
name = "${var.project_name}-agent-traces"
delivery_destination_type = "XRAY"
}
resource "aws_cloudwatch_log_delivery" "agent_traces" {
count = length(aws_bedrockagentcore_agent_runtime.agent)
delivery_source_name = aws_cloudwatch_log_delivery_source.agent_traces[0].name
delivery_destination_arn = aws_cloudwatch_log_delivery_destination.agent_traces[0].arn
}
IAM Policies for Bedrock KnowledgeBase, Bedrock Invoke Models:
tf
data "aws_iam_policy_document" "bedrock_kb_trust" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["bedrock.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "aws:SourceAccount"
values = [data.aws_caller_identity.current.account_id]
}
}
}
resource "aws_iam_role" "bedrock_kb" {
name = "${var.project_name}-kb-role"
assume_role_policy = data.aws_iam_policy_document.bedrock_kb_trust.json
}
data "aws_iam_policy_document" "bedrock_kb_permissions" {
statement {
sid = "S3Read"
actions = ["s3:GetObject", "s3:ListBucket"]
resources = [aws_s3_bucket.docs.arn, "${aws_s3_bucket.docs.arn}/*"]
}
statement {
sid = "InvokeEmbeddingModel"
actions = ["bedrock:InvokeModel"]
resources = ["arn:aws:bedrock:${var.aws_region}::foundation-model/${var.embedding_model_id}"]
}
# Reranking inside a Retrieve call runs as this service role (not the caller).
statement {
sid = "Rerank"
actions = ["bedrock:Rerank"]
resources = ["*"]
}
statement {
sid = "InvokeRerankModel"
actions = ["bedrock:InvokeModel"]
resources = [local.rerank_model_arn]
}
statement {
sid = "OpenSearchServerlessAccess"
actions = ["aoss:APIAccessAll"]
resources = [aws_opensearchserverless_collection.kb.arn]
}
}
resource "aws_iam_role_policy" "bedrock_kb" {
name = "${var.project_name}-kb-policy"
role = aws_iam_role.bedrock_kb.id
policy = data.aws_iam_policy_document.bedrock_kb_permissions.json
}
S3 Bucket, Bucket Policy:
tf
resource "aws_s3_bucket" "docs" {
bucket = var.docs_bucket_name != null ? var.docs_bucket_name : "${var.project_name}-docs-${data.aws_caller_identity.current.account_id}"
}
resource "aws_s3_bucket_versioning" "docs" {
bucket = aws_s3_bucket.docs.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_public_access_block" "docs" {
bucket = aws_s3_bucket.docs.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
Bedrock KnowledgeBase for Vector Database Config (embedding model, embedding dimensions, opensearch serverless collection, chunking), Data Source (S3 Bucket, Vector Ingestion, Chunking Strategy):
tf
locals {
generation_model_arn = "arn:aws:bedrock:${var.aws_region}:${data.aws_caller_identity.current.account_id}:inference-profile/${var.generation_model_id}"
embedding_model_arn = "arn:aws:bedrock:${var.aws_region}::foundation-model/${var.embedding_model_id}"
rerank_model_arn = "arn:aws:bedrock:${var.aws_region}::foundation-model/${var.rerank_model_id}"
}
resource "aws_bedrockagent_knowledge_base" "this" {
name = "${var.project_name}-kb"
role_arn = aws_iam_role.bedrock_kb.arn
knowledge_base_configuration {
type = "VECTOR"
vector_knowledge_base_configuration {
embedding_model_arn = local.embedding_model_arn
embedding_model_configuration {
bedrock_embedding_model_configuration {
dimensions = var.embedding_dimension
}
}
}
}
storage_configuration {
type = "OPENSEARCH_SERVERLESS"
opensearch_serverless_configuration {
collection_arn = aws_opensearchserverless_collection.kb.arn
vector_index_name = opensearch_index.kb_vector_index.name
field_mapping {
vector_field = "bedrock-knowledge-base-default-vector"
text_field = "AMAZON_BEDROCK_TEXT_CHUNK"
metadata_field = "AMAZON_BEDROCK_METADATA"
}
}
}
depends_on = [aws_iam_role_policy.bedrock_kb, time_sleep.wait_for_index]
}
resource "aws_bedrockagent_data_source" "docs" {
knowledge_base_id = aws_bedrockagent_knowledge_base.this.id
name = "${var.project_name}-docs-source"
data_source_configuration {
type = "S3"
s3_configuration {
bucket_arn = aws_s3_bucket.docs.arn
}
}
vector_ingestion_configuration {
chunking_configuration {
chunking_strategy = "FIXED_SIZE"
fixed_size_chunking_configuration {
max_tokens = var.chunk_max_tokens
overlap_percentage = var.chunk_overlap_percentage
}
}
}
}
OpenSearch Serverless Config for security (encryption, network), VectorDB config (embedding dimension, hnsw, faiss, knn_vector):
tf
resource "aws_opensearchserverless_security_policy" "encryption" {
name = "${var.project_name}-encrypt"
type = "encryption"
policy = jsonencode({
Rules = [{
ResourceType = "collection"
Resource = ["collection/${var.project_name}-kb"]
}]
AWSOwnedKey = true
})
}
resource "aws_opensearchserverless_security_policy" "network" {
name = "${var.project_name}-network"
type = "network"
policy = jsonencode([{
Rules = [{
ResourceType = "collection"
Resource = ["collection/${var.project_name}-kb"]
}]
AllowFromPublic = true
}])
}
resource "aws_opensearchserverless_collection" "kb" {
name = "${var.project_name}-kb"
type = "VECTORSEARCH"
depends_on = [
aws_opensearchserverless_security_policy.encryption,
aws_opensearchserverless_security_policy.network,
]
}
resource "aws_opensearchserverless_access_policy" "kb" {
name = "${var.project_name}-access"
type = "data"
policy = jsonencode([{
Rules = [
{
ResourceType = "collection"
Resource = ["collection/${var.project_name}-kb"]
Permission = ["aoss:*"]
},
{
ResourceType = "index"
Resource = ["index/${var.project_name}-kb/*"]
Permission = ["aoss:*"]
}
]
Principal = [
aws_iam_role.bedrock_kb.arn,
var.admin_principal_arn != null ? var.admin_principal_arn : data.aws_caller_identity.current.arn,
]
}])
}
resource "time_sleep" "wait_for_access_policy" {
depends_on = [aws_opensearchserverless_access_policy.kb]
create_duration = "60s"
}
provider "opensearch" {
url = aws_opensearchserverless_collection.kb.collection_endpoint
healthcheck = false
aws_region = var.aws_region
sign_aws_requests = true
}
resource "opensearch_index" "kb_vector_index" {
name = "${var.project_name}-index"
index_knn = true
mappings = jsonencode({
properties = {
"bedrock-knowledge-base-default-vector" = {
type = "knn_vector"
dimension = var.embedding_dimension
method = {
name = "hnsw"
engine = "faiss"
space_type = "l2"
}
}
"AMAZON_BEDROCK_TEXT_CHUNK" = { type = "text" }
"AMAZON_BEDROCK_METADATA" = { type = "text", index = false }
}
})
# Bedrock adds its own metadata fields to the mapping during ingestion
# (x-amz-bedrock-kb-*). Without this, every later apply would see drift and
# replace the index, wiping all embeddings.
lifecycle {
ignore_changes = [mappings]
}
depends_on = [time_sleep.wait_for_access_policy]
}
resource "time_sleep" "wait_for_index" {
depends_on = [opensearch_index.kb_vector_index]
create_duration = "30s"
}
Also, app_config.tf, providers.tf, variables.tf, output.tf implemented, you can reach from =>  Terraform Codes . We can run these TF files with terraform plan, apply, destroy.



Agent Dockerfile, Agent.py

Agent runs on Bedrock AgentCore as container. It has agent tools as functions: retrieve_kb(query: str), critique_context(question: str). It generally retrieves and reranks the chunks of data.
To run on Agentcore, needs to create Docker image with Arm64 based.
Dockerfile
FROM --platform=linux/arm64 python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
# opentelemetry-instrument (ADOT) sends traces to AgentCore Observability.
CMD ["opentelemetry-instrument", "python3", "agent.py", "--serve"]
Only here the main part, please have a look from here for the details:  Agent Code 
Agent, System Prompt, Tools:
Python
def run(question: str) -> dict:
state: dict = {"question": question, "trace": [], "chunks": []}
agent = Agent(
model=BedrockModel(
model_id=_config()["model_id"], temperature=0.2, region_name=_config()["region"]
),
tools=_make_tools(state),
system_prompt=(
"You are an agentic RAG assistant. Answer the user's question ONLY "
"using information retrieved via retrieve_kb.\n\n"
"Process:\n"
"1. Call retrieve_kb with a focused query. For compound questions "
"covering multiple topics, call it once per topic.\n"
"2. Call critique_context(question) to check if you have enough to "
"answer fully.\n"
"3. If not sufficient and you still have retrieval budget, "
"reformulate the query (or target a missing aspect) and retrieve "
"again, then critique again.\n"
"4. Once sufficient, or your retrieval budget is exhausted "
f"(max {MAX_RETRIEVALS} retrieve_kb calls total), write the final "
"answer using only retrieved content, citing sources by URI. If "
"nothing relevant was found, say so plainly instead of guessing."
),
)
answer = str(agent(question)).strip()
sources = sorted({c["uri"] for c in state["chunks"] if c["uri"]})
return {"answer": answer, "citations": sources, "trace": state["trace"]}
Agent Main:
Python
if __name__ == "__main__":
if "--serve" in sys.argv:
from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()
@app.entrypoint
def _invoke(payload: dict, context=None) -> dict:
question = payload.get("question") or payload.get("prompt") or DEFAULT_QUESTION
result = run(question)
result["runtime"] = {
"session_id": getattr(context, "session_id", None),
"host": socket.gethostname(),
}
return result
app.run()
else:
result = run(" ".join(sys.argv[1:]) or DEFAULT_QUESTION)
print(json.dumps(result, indent=2))



Streamlit App: Uploading, Digest & Querying

App has 4 main parts as utils: agentcore_ops.py, aws_clients.py, bedrock_ops.py, s3_ops.py.
App Code that includes utils and main:  App Code 
Start_ingestion_job, get_ingestion_job_status, querying, vector_search with reranker model: bedrock-ops.py:
Python
from utils.aws_clients import get_bedrock_agent_client, get_bedrock_agent_runtime_client, get_config
NUM_CANDIDATES = 20
NUM_RESULTS = 5
def start_ingestion_job() -> str:
cfg = get_config()
resp = get_bedrock_agent_client().start_ingestion_job(
knowledgeBaseId=cfg["knowledge_base_id"],
dataSourceId=cfg["data_source_id"],
)
return resp["ingestionJob"]["ingestionJobId"]
def get_ingestion_job_status(job_id: str) -> dict:
cfg = get_config()
resp = get_bedrock_agent_client().get_ingestion_job(
knowledgeBaseId=cfg["knowledge_base_id"],
dataSourceId=cfg["data_source_id"],
ingestionJobId=job_id,
)
job = resp["ingestionJob"]
return {"status": job["status"], "failure_reasons": job.get("failureReasons", [])}
def _vector_search_configuration() -> dict:
rerank_model_arn = get_config()["rerank_model_arn"]
if not rerank_model_arn:
return {"numberOfResults": NUM_RESULTS}
return {
"numberOfResults": NUM_CANDIDATES,
"rerankingConfiguration": {
"type": "BEDROCK_RERANKING_MODEL",
"bedrockRerankingConfiguration": {
"modelConfiguration": {"modelArn": rerank_model_arn},
"numberOfRerankedResults": NUM_RESULTS,
},
},
}
def query(question: str) -> dict:
cfg = get_config()
resp = get_bedrock_agent_runtime_client().retrieve_and_generate(
input={"text": question},
retrieveAndGenerateConfiguration={
"type": "KNOWLEDGE_BASE",
"knowledgeBaseConfiguration": {
"knowledgeBaseId": cfg["knowledge_base_id"],
"modelArn": cfg["generation_model_arn"],
"retrievalConfiguration": {
"vectorSearchConfiguration": _vector_search_configuration(),
},
},
},
)
citations = []
for citation in resp.get("citations", []):
for ref in citation.get("retrievedReferences", []):
uri = ref.get("location", {}).get("s3Location", {}).get("uri")
if uri:
citations.append(uri)
return {"answer": resp["output"]["text"], "citations": citations}
Query Agentic from agentcore_ops.py:
Python
def query_agentic(question: str, session_id: str) -> dict:
cfg = get_config()
if not is_agentic_configured():
raise RuntimeError(
"AGENT_RUNTIME_ARN is not a Bedrock AgentCore runtime ARN. Deploy the agent "
"with ./deploy.sh; it writes the ARN into app/.env."
)
# The same runtimeSessionId for every question in one browser session, so
# AgentCore Observability groups them as one session.
started = time.monotonic()
resp = get_bedrock_agentcore_client().invoke_agent_runtime(
agentRuntimeArn=cfg["agent_runtime_arn"],
runtimeSessionId=session_id,
qualifier="DEFAULT",
payload=json.dumps({"question": question}).encode(),
)
result = json.loads(resp["response"].read())
# Proof of execution on AgentCore: IDs returned by the AWS API, plus what
# the agent reported from inside its container.
result["agentcore"] = {
"runtime_arn": cfg["agent_runtime_arn"],
"session_id": resp.get("runtimeSessionId", session_id),
"trace_id": resp.get("traceId"),
"request_id": resp["ResponseMetadata"]["RequestId"],
"status_code": resp.get("statusCode"),
"latency_s": round(time.monotonic() - started, 1),
"container": result.pop("runtime", None),
"console_url": (
f"https://{cfg['region']}.console.aws.amazon.com/bedrock-agentcore/observability"
f"?region={cfg['region']}"
),
}
return result



All Code & Demo

GitHub Link:  Project on GitHub ****
Deploy.sh includes image build, push to ECR. Details:  deploy.sh 
Run:
Bash
./deploy.sh
All images with  better resolution 
After applying the TF, view/copy outputs to link with app.py:

Uploading files (popular latest GenAI papers under docs/pdf):

Simple vs Agentic RAG:

Monitoring Agent:

Querying & Retrieval Data:



DON't Forget to delete Resources:
Run:
Bash
./deploy.sh destroy



ConclusionIn this post, we mentioned:

  • how to create Agentic RAG using Bedrock AgentCore, Agent DockerImage, ECR, Bedrock KnowledgeBase, S3, OpenSearch Serverless with Terraform,
  • how to digest, query using simple streamlit app with embedding and reranker models,
  • how agent calls agent-as-tool for critique evaluation,
  • how to enable with permission roles,
  • how to use AWS Claude models (e.g. Sonnet).
If you found the tutorial interesting, I’d love to hear your thoughts in the blog post comments. Feel free to share your reactions or leave a comment. I truly value your input and engagement 😉
For other posts 👉  https://dev.to/omerberatsezer  🧐

References

Your comments 🤔 I’d love to hear your experience and know-how.
  • Do you know the detail of RAG that handled by Bedrock Knowledge Base?
  • How do you implement your Agentic RAG in production?
Drop your thoughts, feedback, or improvements in the comments, always curious to learn from different approaches. ☺️
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article