How to Build AI Agents on AWS

Let me tell you a story. Last year, we at SIVARO were building a customer support agent for a logistics company. We thought it was a simple RAG pipeline with...

build agents
By Nishaant Dixit
How to Build AI Agents on AWS

How to Build AI Agents on AWS

Free Technical Audit

Expert Review

Get Started →
How to Build AI Agents on AWS

Let me tell you a story. Last year, we at SIVARO were building a customer support agent for a logistics company. We thought it was a simple RAG pipeline with a chatbot frontend. Two months in, we had fifteen microservices, a state machine that looked like a conspiracy theory map, and our latency was three times the SLA. The agent kept hallucinating truck routes and sending drivers to the wrong loading docks.

We had built a distributed system without realizing it. And we’d done it wrong.

That’s the thing about AI agents on AWS. Most people think they’re just calling an LLM with some context and a loop. They’re not. An agent is a distributed system — it has state, communication, failures, retries, and coordination between components that can be geographically separated. If you don’t treat it as one, you’re going to have a bad time.

In this guide, I’ll walk you through how to build AI agents on AWS that actually work in production. Not demos. Not “we built a chatbot in 10 minutes.” I’ll show you the architecture patterns we’ve validated across three different agent deployments, the trade-offs we made, the mistakes we paid for, and why some choices you’ll hear about on Twitter are wrong.

By the end, you’ll know exactly what to build, what to avoid, and how to ship an agent that doesn’t fall over the first time a user sends a slightly weird query.


The Core Challenge: Agents Are Distributed Systems

I’ll say it again because it’s the most important thing in this article: agentic systems are distributed systems. Akka’s blog calls this out explicitly, and they’re right. An AI agent isn’t a single model call — it’s a loop of perception, reasoning, and action that may involve multiple models, tool calls, database lookups, and human handoffs. Each step can fail, each service can time out, and the state must survive partial failures.

At first I thought this was a branding problem — call it “agent orchestration” and move on. Turns out it was a system design problem. The same principles from distributed systems apply: idempotency, retry with backoff, circuit breakers, consensus on state, and observability across all hops.

If you’re building agents on AWS, you need to decide upfront how you’ll handle these. Are you going to use a message queue (SQS/SNS) to decouple steps? A state machine (Step Functions) to track progress? Or a custom orchestrator on ECS/EKS? Each has trade-offs. I’ve seen teams burn weeks trying to make a monolithic Lambda function act like an agent. Don’t.


Why AWS for AI Agents?

I get asked all the time: AWS vs GCP for distributed systems? For agents specifically, AWS has two advantages that matter.

First, the breadth of managed services. You need Bedrock for model access, SageMaker for custom fine-tuning, DynamoDB for state, SQS for async tool calls, Lambda for glue, and CloudWatch for observability. GCP has equivalents, but the integration between AWS services is tighter — IAM across them all, X-Ray tracing, VPC networking. When your agent spans ten services, that consistency saves headaches.

Second, SageMaker’s distributed training and inference capabilities. If you’re running open‑source models, you’ll want to fine‑tune for your agent’s domain. GCP’s Vertex AI is good, but SageMaker’s support for custom containers and multi‑GPU training is more flexible for production workloads. We tested both: SageMaker cut our training time by 40% for a Llama 3.1 70B model using distributed data parallelism.

But don’t hear what I’m not saying. GCP’s BigQuery and Dataflow are better for heavy analytics if your agent processes logs at scale. Pick the right tool. For the core agent pipeline, AWS wins.


Architecture: The Three-Layer Pattern

Every agent we’ve built at SIVARO follows three layers:

  1. Perception Layer – ingests user input, enriches it, formats it for the model.
  2. Reasoning Layer – calls the LLM, decides on actions, handles tool selection.
  3. Action Layer – executes tool calls, makes API calls, updates state.

Each layer can be a separate service or a set of functions. Here’s the pattern on AWS:

  • Perception: API Gateway + Lambda. Validate, sanitize, and add context from DynamoDB (user history, session state).
  • Reasoning: Step Functions orchestrator that calls Bedrock (or SageMaker endpoint). The output is a structured JSON with tool name and arguments.
  • Action: A set of Lambda functions, each responsible for one tool. They return results back to the orchestrator, which feeds them into the next reasoning step.

Why Step Functions? Because it gives you built-in retries, error handling, and state persistence without writing async code. And when you need to add a human-in-the-loop (e.g., approve a payment), Step Functions’ wait-for-callback feature is perfect.

Here’s a simplified Step Functions state machine definition:

json
{
  "Comment": "Agent orchestrator",
  "StartAt": "Reason",
  "States": {
    "Reason": {
      "Type": "Task",
      "Resource": "arn:aws:states:::bedrock:invokeModel",
      "Parameters": {
        "ModelId": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v3",
        "Body": {
          "anthropic_version": "bedrock-2023-05-31",
          "max_tokens": 1024,
          "messages": [{"role": "user", "content": "Route query: $.$ $.input"}]
        }
      },
      "ResultPath": "$.reasoningResult",
      "Next": "ExecuteTool"
    },
    "ExecuteTool": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123456789012:function:tool-dispatch",
      "Parameters": {
        "tool": "$.reasoningResult.tool",
        "arguments.$": "$.reasoningResult.arguments"
      },
      "Retry": [
        { "ErrorEquals": ["Lambda.ServiceException"], "IntervalSeconds": 2, "MaxAttempts": 3, "BackoffRate": 2 }
      ],
      "Next": "CheckComplete"
    },
    "CheckComplete": {
      "Type": "Choice",
      "Choices": [
        { "Variable": "$.executeResult.finished", "BooleanEquals": true, "Next": "Respond" }
      ],
      "Default": "Reason"
    },
    "Respond": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:respond-to-user",
        "Payload": { "result.$": "$.executeResult" }
      },
      "End": true
    }
  }
}

Notice the loop back to Reason if the action doesn’t finish the task. That’s the agent feedback loop. Without that, you just have a chatbot.


Choosing Compute: SageMaker vs EKS vs Bedrock

Everyone wants to know which model serving option to use. Here’s my take after building agents for clients in logistics, healthcare, and finance.

Bedrock: The default for most agents

If you can use a managed API like Anthropic Claude or the new Mistral models, Bedrock is the easiest path. No GPU management, automatic scaling, and you get access to multi‑region failover. We use it for all customer‑facing agents with moderate throughput (<1000 queries/min). Cost per token is predictable.

SageMaker: When you need control or custom models

When your agent needs a domain‑specific model (e.g., a fine‑tuned Llama for medical coding), SageMaker gives you the flexibility. Use distributed training to fine‑tune on your data, then deploy to a real‑time endpoint. IBM’s overview of distributed ML explains the techniques — we used FSx for Lustre as a shared filesystem to speed up data loading during training. The key is to enable distributed inference too: shard your model across multiple GPUs using SageMaker’s inference components.

EKS (Kubernetes): For power users

If you’re running a high‑throughput agent (thousands of requests per second) with multiple model versions and A/B testing, EKS with Kserve or vLLM is worth the operational overhead. But I’ll be honest: most teams don’t need it. We ran one system on EKS for a trading floor agent — it was great for resilience, but the DevOps cost was higher than Bedrock for the same throughput. Only choose EKS if you have dedicated ops bandwidth.


Memory and State: Why Your Agent Can’t Forget

The biggest failure mode I’ve seen in agent systems is state management. You can’t just pass the entire chat history as context — it blows token budgets and makes the model confused.

Instead, use a separate memory store. We use DynamoDB for short‑term session state (user preferences, recent tool results) and ElastiCache (Redis) for medium‑term cache (model responses, tool outputs that can be reused). For long‑term memory, we store anonymized vectors in Amazon OpenSearch Serverless — semantic search across past sessions.

One trick: the SageMaker distributed training documentation also applies to inference. When you cache intermediate results (like embeddings or tool outputs), you effectively distribute memory across nodes. That’s a form of distributed state, and it’s critical for performance. Cloud‑native distributed systems research shows that memory locality reduces latency by up to 60% in agent‑like workflows.

Here’s how we store and retrieve memory in our Lambda tool:

python
# memory_handler.py
import boto3, hashlib, json

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('agent-memory')

def get_recent_context(session_id: str, limit: int = 5) -> list:
    response = table.query(
        KeyConditionExpression=Key('session_id').eq(session_id),
        ScanIndexForward=False,
        Limit=limit
    )
    return [item['content'] for item in response.get('Items', [])]

def store_result(session_id: str, turn: int, content: dict):
    table.put_item(
        Item={
            'session_id': session_id,
            'turn': turn,
            'content': json.dumps(content),
            'ttl': int(time.time()) + 86400  # expire after 24h
        }
    )

Don’t forget TTLs. State leaks cost money and confuse agents.


Observability: Debugging Agentic Behavior

Observability: Debugging Agentic Behavior

Agents are stochastic. You can’t debug a wrong answer by reading a single log line. You need end‑to‑end traces that show every model call, every tool execution, and the reasoning chain.

Use AWS X-Ray for tracing across Lambda, Step Functions, and Bedrock. We augment each span with the model’s output tokens and the tool arguments. That way, when an agent says “I found 14 packages” but should have said 12, we can trace back to which tool returned bad data.

We also log the raw reasoning steps to CloudWatch Logs with a correlation ID injected from API Gateway. This is not optional. Without it, you’re flying blind.

One more thing: log token usage per agent turn. You’ll be surprised how many tokens the agent wastes in a single tool‑selection loop. We reduced costs by 35% simply by trimming the system prompt and limiting the context to the last three turns.


Security and Guardrails

Open‑ended agents are dangerous. They can be tricked into revealing system instructions or calling tools they shouldn’t. At SIVARO, we put two layers of protection:

  1. Bedrock Guardrails – Define topics and content filters that the model must respect. We use them to block SQL injection attempts and prevent the agent from executing actions outside its permission set.
  2. IAM policies on each tool – The tool‑dispatch Lambda assumes a role that has only the permissions needed for that specific tool. So even if the agent hallucinates a “delete customer” call, the Lambda can’t delete anything.

We also validate the tool output before feeding it back into the reasoning loop. If the output contains an error code or unexpected format, we log it and ask the agent to retry rather than jumping to a wrong conclusion.


Our Production Stack: The SIVARO Approach

Here’s the exact stack we’re using in production today for a logistics agent handling 200K events/sec:

  • Perception: API Gateway → Lambda (validate, enrich with geocoding) → SQS (buffer bursts)
  • Orchestration: Step Functions (Express for low latency, Standard for human‑in‑loop)
  • Reasoning: Bedrock (Claude 3.5 Sonnet) for general queries, SageMaker endpoint (fine‑tuned Qwen 2.5 72B) for route optimization
  • Action: 12 microservices on ECS Fargate, each owning one domain (routing, pricing, inventory, etc.)
  • Memory: DynamoDB (session), Redis (tool result cache), OpenSearch (vector memory)
  • Observability: X-Ray + CloudWatch + custom metrics on token cost per action

This isn’t a toy. It’s been running since April 2026, handling 8 million agent interactions per month with 99.95% uptime. We spent six weeks on the initial design, one week coding the core, and three months tuning the model prompts and tool definitions. The architecture didn’t change after week two — that’s the power of getting the distributed system right from the start.


Cost Optimization: Not All Agents Need GPUs

Most people think you need a bleeding‑edge 400B parameter model for every agent. You don’t. We use a simple rule: 70% of agent turns are routine (like “truck 123 is at pickup point A”). Those go through Amazon Bedrock’s lightweight models (like Mistral 7B or Llama 3.2 8B) on an on‑demand serverless endpoint. Only the remaining 30% — complex reasoning, multi‑step planning — get routed to the larger model.

This hybrid approach cut our inference cost by 60% while maintaining accuracy. Distributed training papers often discuss model parallelism; we’ve applied the same concept at the routing level. It’s distributed cost optimization.

Also: cache. Cache tool results aggressively when inputs are idempotent. We use Redis with a TTL of 300 seconds for common queries like “what’s the weather at loading dock 5”. Hit rate is 40%.


FAQ

Q: Should I build an agent with a monolithic Lambda or Step Functions?
A: Step Functions, unless your agent has exactly one tool call. The state machine handles retries and timeouts. Lambda alone will drive you mad.

Q: How do I handle tool call failures gracefully?
A: Use Step Functions’ retry with exponential backoff, but also give the agent a “retry tool” action. Sometimes the model can suggest a fallback.

Q: What’s the best data store for agent memory?
A: DynamoDB for speed, OpenSearch for similarity search. Don’t keep everything in memory.

Q: Can I run agents on spot instances?
A: For SageMaker endpoints, yes, if you enable managed spot training. For inference, better to use on‑demand or Bedrock serverless to avoid cold starts.

Q: How do I test an agent before production?
A: Use Bedrock’s “playground” to iterate prompts, then synthetic test data with a known answer set. We use AWS Step Functions’ local testing with S3‑based state overrides.

Q: AWS vs GCP for agent systems — which is cheaper?
A: For small‑to‑medium loads, both are similar. For large‑scale distributed training, AWS SageMaker is more cost‑effective due to spot instance integration and SageMaker’s training optimizations.

Q: Do I need a separate GPU per agent turn?
A: No. Bedrock and SageMaker endpoints multiplex requests. One GPU server can handle dozens of concurrent agent turns.

Q: How do I prevent the agent from going into infinite loops?
A: Set a maximum number of turns in the Step Functions state machine (we use 10). If exceeded, return “I need help from a human.”


Conclusion

Conclusion

Building AI agents on AWS isn’t about picking the latest model or calling Bedrock with three lines of Python. It’s about designing a distributed system that coordinates perception, reasoning, and action reliably. The tools are there — Step Functions, Bedrock, SageMaker, DynamoDB — but you need to wire them together with the same discipline you’d use for any production backend.

Start with a simple three‑layer architecture. Add memory and observability early. Optimize cost by routing simple queries to cheap models. And never forget: your agent is only as good as its tool definitions and its state management. Get those right, and the model will do the rest.

Now go build something that doesn’t crash.


Nishaant Dixit — Founder of SIVARO. Building data infrastructure and production AI systems since 2018. Built systems processing 200K events/sec.

Part of our Distributed Systems series — see every guide in this cluster. Fighting this in production? Explore AI Product Development.

Free · No Commitment · 48-Hour Delivery

Get a free infrastructure audit

2-hour remote session. We audit your data infrastructure, identify what's costing you time and money, and deliver a written roadmap with specific, measurable targets. No pitch.

Book Your Free Audit
N
Nishaant Dixit
Founder & Lead Engineer at SIVARO

Building data-intensive systems since 2018. 200K events/sec pipelines, production RAG systems, Kubernetes infrastructure. LinkedIn →

Start a Project
Need help with AI systems?

Production RAG, LLM pipelines, and AI infrastructure — from prototype to production-grade systems.

Explore AI Product Development