DEV Community

Cover image for AWS Bedrock in 2026: what it actually is and how to build your first AI agent on it
Aj
Aj

Posted on • Originally published at cloudedventures.com

AWS Bedrock in 2026: what it actually is and how to build your first AI agent on it

Most AWS Bedrock content is either marketing copy or academic papers. Neither is useful if you're trying to actually build something.

This is the architecture, the real code patterns, and the things that actually trip people up in production.


What AWS Bedrock actually is

AWS Bedrock is Amazon's managed AI inference platform. It gives you API access to foundation models — Claude, Llama, Titan, Mistral — without managing the infrastructure those models run on.

The reason it's become the foundation of most enterprise AI deployments is simple: it sits inside your existing AWS infrastructure. Your IAM roles, VPCs, CloudWatch, Lambda functions — they all work with Bedrock the same way they work with S3 or DynamoDB.

Four primitives make it powerful beyond raw model access:

  • Knowledge Bases — managed RAG. Connect an S3 bucket of documents, Bedrock handles embedding, vector storage, and retrieval automatically.
  • Agents — managed agentic orchestration. Define tools (Lambda functions) the model can call, Bedrock manages conversation history and the agentic loop.
  • Guardrails — content filtering, PII detection, topic restrictions at the platform level, not in your application code.
  • Prompt Management — versioned, managed prompts. Test variants, roll back bad changes, maintain consistency across environments.

The core pattern: how a Bedrock agent works

Every Bedrock agent follows the same underlying pattern:

User sends message
    ↓
Model receives message + tool definitions + conversation history
    ↓
Model returns: stopReason = "tool_use" OR "end_turn"
    ↓
If tool_use: execute the Lambda, append result, loop again
If end_turn: return response to user
Enter fullscreen mode Exit fullscreen mode

The model does not execute tools — it requests them. Your code executes them and feeds the result back.

Here is the full implementation:

import boto3
import json

client = boto3.client("bedrock-runtime", region_name="us-east-1")

tools = [
    {
        "toolSpec": {
            "name": "get_s3_file_list",
            "description": "List files in an S3 bucket. Use when the user asks about files or data stored in S3.",
            "inputSchema": {
                "json": {
                    "type": "object",
                    "properties": {
                        "bucket_name": {
                            "type": "string",
                            "description": "The S3 bucket name to list"
                        },
                        "prefix": {
                            "type": "string",
                            "description": "Optional prefix to filter results"
                        }
                    },
                    "required": ["bucket_name"]
                }
            }
        }
    }
]


def execute_tool(tool_name: str, tool_input: dict) -> str:
    if tool_name == "get_s3_file_list":
        s3 = boto3.client("s3")
        response = s3.list_objects_v2(
            Bucket=tool_input["bucket_name"],
            Prefix=tool_input.get("prefix", "")
        )
        files = [obj["Key"] for obj in response.get("Contents", [])]
        return json.dumps({"files": files, "count": len(files)})
    return json.dumps({"error": f"Unknown tool: {tool_name}"})


def run_bedrock_agent(user_message: str) -> str:
    messages = [{"role": "user", "content": [{"text": user_message}]}]
    system = [{"text": "You are an AWS assistant. Use tools to get real data rather than guessing."}]

    for _ in range(10):
        response = client.converse(
            modelId="anthropic.claude-3-sonnet-20240229-v1:0",
            system=system,
            messages=messages,
            toolConfig={"tools": tools}
        )

        stop_reason = response["stopReason"]
        output = response["output"]["message"]
        messages.append(output)

        if stop_reason == "end_turn":
            for block in output["content"]:
                if "text" in block:
                    return block["text"]

        elif stop_reason == "tool_use":
            tool_results = []
            for block in output["content"]:
                if "toolUse" not in block:
                    continue
                tool = block["toolUse"]
                result = execute_tool(tool["name"], tool["input"])
                tool_results.append({
                    "toolResult": {
                        "toolUseId": tool["toolUseId"],
                        "content": [{"text": result}]
                    }
                })
            messages.append({"role": "user", "content": tool_results})

    return "Agent reached iteration limit"


print(run_bedrock_agent("How many files are in my data-lake-prod bucket?"))
Enter fullscreen mode Exit fullscreen mode

stopReason is your only control signal. "tool_use" means keep looping. "end_turn" means stop. Everything else is implementation detail.


What Bedrock adds over raw API access

If you are already calling Claude's API directly, three things matter in production:

IAM-native access control — Use IAM roles instead of API keys. No secrets to rotate, no credential leaks. Your Lambda gets a role, that role gets a Bedrock policy.

Built-in observability — Every Bedrock call shows up in CloudWatch automatically. Latency, token usage, error rates, throttling — zero instrumentation required from you.

VPC isolation — Model calls can run inside your VPC. For healthcare, finance, or regulated workloads this is the difference between a viable architecture and a compliance non-starter.


The three bugs that break Bedrock agents in production

Bug 1 — Checking content type instead of stopReason

# This breaks silently on complex tasks
if response["output"]["message"]["content"][0]["type"] == "text":
    return  # Agent stops before the tool ever runs

# This is correct
if response["stopReason"] == "end_turn":
    return
Enter fullscreen mode Exit fullscreen mode

Claude returns explanatory text alongside tool_use blocks. If you check content type to detect completion, you terminate the loop before the tool executes.

Bug 2 — Missing assistant message before tool results

# Missing a critical step
messages.append(tool_result_message)

# Correct — append assistant response first, then the tool result
messages.append(output)               # The assistant's tool_use request
messages.append(tool_result_message)  # Then the result
Enter fullscreen mode Exit fullscreen mode

Without the assistant message in history, the model cannot connect the tool result back to its own request. You get confused or looping responses.

Bug 3 — Using the iteration cap as your primary stop

The for _ in range(10) is a safety net against runaway loops — not your stopping condition. stopReason is how the model signals it is finished. Cap is a ceiling, not a controller.


What comes next after the basics

Once the core loop works, the natural Bedrock progression is:

  • Knowledge Bases — connect documents, let the model ground answers in your actual data
  • Bedrock Guardrails — content filters and PII detection the model cannot be prompted around
  • Multi-agent coordination — coordinator agent delegating to specialist subagents via tool calls, each with an isolated context window
  • CloudWatch observability — latency distribution, token cost, tool call frequency, error rates per agent

Getting hands-on

Understanding the architecture is step one. Building intuition for what breaks in production — failed tools, bad tool inputs, sessions that run longer than expected — requires actually running the system.

If you want to work through these patterns in a real AWS Bedrock sandbox — agentic loops, tool sequencing, Guardrails, CloudWatch dashboards — Cloud Edventures has a hands-on track covering exactly this. The CCA-001: Claude Certified Architect track runs in isolated AWS sandboxes with automated validation. No AWS account needed.

👉 cloudedventures.com/labs/track/claude-certified-architect-cca-001

What is the most confusing part of Bedrock for you? Drop a comment — happy to go deeper on any section.

Top comments (0)