Building production agents on AWS, and the control you keep

Pull-quote: “Every rung up the AWS agent stack buys you operational leverage and charges you one specific control. The trade is only bad when nobody wrote down which control it was.”
The decision that shapes a production agent on AWS is not which model you call, it is how much of the reasoning loop you hand to the platform. AWS offers four distinct positions on that question, from a bare model API where you write the loop yourself to a managed loop you configure in a JSON document. Each position removes work and removes a specific control, and the trade is predictable enough to write down before you build.
This is for an engineer who has a working agent on a laptop and now has to run it for other people. It assumes you know what a tool call is, not that you know Amazon Bedrock AgentCore contains thirteen separate services. By the end you can place any AWS agent design on the ladder, name what that rung costs, and pick a frontend contract that survives changing your mind later.
What you will be able to do
- Name the four AWS positions for running an agent loop and say who owns the loop in each.
- List what AgentCore harness cannot do, so a config-first decision is made with the exceptions in view.
- Distinguish the thirteen AgentCore services by the problem each solves.
- Enforce a tool permission boundary outside the agent’s own code, where prompt injection cannot reach it.
- Deploy an AG-UI server so your frontend is not coupled to the agent framework behind it.
- Choose a starting rung from two questions about your workload rather than a preference.
What AWS services do you actually need to run an agent in production?
You need four things, and AWS sells each separately: a model endpoint, a loop, a place for the loop to run, and a governed boundary around the tools the loop can reach. Everything else optimises one of those four.
Amazon Bedrock is the model endpoint, a managed service providing access to more than a hundred foundation models through one set of APIs, including Converse for a provider-neutral message exchange and InvokeModel for raw provider payloads (Amazon Bedrock user guide). It returns tokens. Calling it inside a while block is not the same as having an agent.
Strands Agents is the loop: an open-source agent SDK from AWS, installed with pip install strands-agents or npm install @strands-agents/sdk. It implements the standard cycle, invoke the model, check whether it asked for a tool, execute, feed the result back, repeat until it stops asking (Strands agent loop documentation).
Amazon Bedrock AgentCore is the place to run it plus the boundary: an agentic platform of thirteen modular services, framework-agnostic and model-agnostic, usable together or independently (AgentCore developer guide). Two of them, Runtime and harness, hold the loop. The other eleven are the boundary and the instrumentation.
Who owns the reasoning loop at each level of the AWS stack?
Ownership moves in four discrete steps, and each step transfers one more part of the loop from your repository to AWS configuration.

Rung one, Bedrock plus your own loop. You call Converse, parse the tool-use blocks, dispatch, append results, and decide when to stop. You own every line, including the ones you have not written yet: retries, checkpointing, a trajectory log.
Rung two, Strands Agents. You inherit a tested loop and keep the process. Two details matter more than the syntax. Runs end with a named stop reason, among them end_turn, limit_turns, limit_total_tokens, and guardrail_intervention, so an ending is classified rather than guessed at. And limits are arguments, not prompt instructions:
from strands import Agent
agent = Agent(tools=[search_logs])
result = agent(
"Find every timeout error in the last six hours and open one ticket",
limits={"turns": 5, "output_tokens": 2000, "total_tokens": 10000},
)
if result.stop_reason == "limit_turns":
escalate(result)
Hooks are the second reason to be on this rung. A BeforeToolCallEvent handler inspects the arguments the model produced and cancels the call with a message the model then sees, which is how you put a policy in front of a write without asking the prompt nicely.
Rung three, AgentCore Runtime. You keep the loop and give away the infrastructure. Wrap your agent with the AgentCore SDK’s BedrockAgentCoreApp entrypoint, package it as an ARM64 container, push it to Amazon ECR, and deploy. AWS supplies session isolation, scaling, auth gating, and observability plumbing, while the orchestration loop stays code you wrote (harness versus Runtime). Each session gets a dedicated microVM with isolated CPU, memory, and filesystem, sanitised when the session ends. Runs last up to 8 hours, payloads reach 100 MB, and the service speaks MCP, A2A, and AG-UI (AgentCore Runtime documentation).
Rung four, AgentCore harness. You give away the loop. The harness is a managed agent loop powered by Strands Agents: declare the model, system prompt, tools, memory, and limits as configuration and AWS runs the cycle (AgentCore harness documentation). Providers switch mid-session without losing context, and execution limits are configuration too, through maxIterations, timeoutSeconds, maxTokens, and idle and lifetime ceilings. The harness runs inside Runtime, which is why CloudTrail records its operations under AWS::BedrockAgentCore::Runtime.
What do you give up when you let AWS run the loop?
Four capabilities, and the documentation names them plainly rather than burying them. AgentCore harness does not support your choice of agent framework, bidirectional streaming, non-agent-loop patterns such as graph or workflow topologies, and hooks.

The hooks omission is the one that catches teams. On Strands, a before-tool-call hook is where a hard business rule lives: refuse a refund above a threshold, require a citation before a write, block a delete outside a maintenance window. Move to a configured harness and that code has nowhere to go inside the loop. Move the rule instead to a boundary the loop cannot bypass, which is what AgentCore Policy exists for.
The framework omission matters if your agent is already a LangGraph state machine or a CrewAI crew. Runtime accepts those unchanged. The harness does not, because the harness is the loop.
Two things make the choice reversible: a harness exports to Strands code and runs on Runtime when configuration stops being enough, and it can be called as a step inside an AWS Step Functions workflow through the InvokeHarness state.
| Rung | You give up | Pick it when |
|---|---|---|
| Bedrock plus your own loop | Everything AWS would have supplied | The loop design is the product |
| Strands Agents | Nothing yet, hosting is still yours | A hard rule must run before a tool |
| AgentCore Runtime | Control of the execution environment | Many users, long asynchronous work |
| AgentCore harness | Framework choice, hooks, graphs, duplex streams | The loop is standard, the tools are the work |
What does Amazon Bedrock AgentCore add that an agent framework does not?
It adds the eleven services that are not the loop, and those are the ones a framework never covers. A framework gives you orchestration. It does not give the agent its own identity, an audited boundary in front of every tool call, or a catalogue of what your colleagues already built.
They divide by problem. Gateway turns existing APIs, Lambda functions, and services into MCP-compatible tools, and connects to MCP servers you already run. Identity supplies the agent’s identity and an outbound token vault, working with Cognito, Okta, or Microsoft Entra ID. Memory holds short-term conversation state and long-term memory across sessions. Browser and Code Interpreter are managed sandboxes. Observability emits OpenTelemetry-compatible traces, Evaluations scores sessions, traces, and spans, and Optimization turns those traces into prompt and tool-description recommendations validated by A/B tests. Registry catalogues agents, MCP servers, tools, and skills, and Payments handles microtransactions with spending limits over the x402 protocol.
Policy is the answer to the hooks problem above. You create a policy engine, associate it with an AgentCore Gateway, and it intercepts all agent traffic through that gateway, evaluating each request before the tool runs. Rules are written in Cedar, or in natural language that is translated to Cedar, validated against the tool schema, and checked by automated reasoning for rules that are overly permissive, overly restrictive, or unsatisfiable (AgentCore Policy documentation).
Enforcement outside the agent’s code is the property that matters. It holds regardless of how the agent is implemented and cannot be talked out of by a manipulated prompt, the argument made in defending an agent against prompt injection. A guardrail inside the context window is a request. A guardrail at the gateway is a decision.
How do you build an interactive agent frontend without locking it to one backend?
Put AG-UI between them. AG-UI is an open, event-based protocol that standardises how an agent backend talks to a user-facing application, sitting alongside MCP for agent-to-tool communication and A2A for agent-to-agent coordination (AG-UI documentation). Your application subscribes to a typed event stream rather than to a framework’s SDK, so the backend can be Strands, LangGraph, CrewAI, or Pydantic AI without the frontend noticing.

AgentCore Runtime supports AG-UI as a first-party protocol and proxies in front of your container. The contract is specific: the container listens on port 8080, serves /invocations for HTTP and Server-Sent Events, /ws for WebSocket, and /ping for health checks. Requests from the InvokeAgentRuntime API pass through unmodified while AgentCore handles SigV4 or OAuth 2.0 authentication, session isolation, and scaling (deploying AG-UI servers on AgentCore Runtime). Ports separate the protocols: AG-UI and plain HTTP use 8080, MCP uses 8000, A2A uses 9000.
Deployment is then two commands:
agentcore configure -e my_agui_server.py --protocol AGUI
agentcore deploy
Confirm the wiring before writing any frontend code. Run the server locally and post an AG-UI request to http://localhost:8080/invocations with a threadId, a runId, and a messages array. The response should be an SSE stream containing RUN_STARTED, then one or more TEXT_MESSAGE_CONTENT events carrying deltas, then RUN_FINISHED. Those three event types in order mean the contract holds.
How do you choose which level to start on?
Two questions decide it, and neither is about scale.
First: does your control flow branch on anything other than the model’s next tool choice? Approval gates, fan-out to parallel workers, a state machine with named transitions, and a plan that gets revised are all graph shapes, and graph shapes are a Runtime workload. A single loop that calls tools until it is done is a harness workload.
Second: do you have a hard rule that must hold on every write, and where does it live today? If it lives in code that runs before the tool executes, decide now whether it moves to AgentCore Policy at the gateway or stays a Strands hook in your own process. Discovering that after configuring a harness means rewriting the enforcement path under time pressure.
If either answer is unclear, start one rung lower. Moving up is a configuration rewrite, moving down is an export AWS supports directly, and both are cheaper than finding out mid-incident that the control you needed was the one you traded away.
Where this goes wrong
The rule lived in the prompt. The symptom is an agent that respects a spending cap in testing and violates it under an unusual phrasing. Instruction text is advisory by construction. Move the rule to a Strands before-tool-call hook if you own the process, or a Cedar policy on the gateway if you do not, then confirm the denial appears in CloudWatch rather than only in the model’s reply.
Nobody checked the region list. The symptom is a deployment that works in one account and fails in another with a service exception rather than a permission error. AgentCore capabilities roll out per region and the set is not uniform. Check the regions page for the specific capability, not for AgentCore in general.
The frontend was built against a framework SDK. The symptom is that replacing the agent framework becomes a UI project, because the client imports framework types instead of subscribing to a protocol. AG-UI makes that swap invisible, and adopting it afterwards costs more than adopting it first.
Observability was assumed rather than configured. The symptom is an agent that misbehaved yesterday and a log group holding only the final answer. AgentCore Observability emits traces of each step, but a trace you never route to a queryable backend is not evidence. Reproduce one failed run end to end before the first real user arrives.
Common questions
Is AgentCore harness the same thing as AgentCore Runtime?
No. Runtime is serverless hosting for an agent whose orchestration loop you wrote, and harness is a managed loop you configure rather than write. The harness runs inside Runtime, which is why CloudTrail records harness operations under AWS::BedrockAgentCore::Runtime, but they are separate choices about who owns the loop.
Do I have to use Amazon Bedrock models with AgentCore?
No. AgentCore Runtime works with any model, including OpenAI, Google Gemini, and Anthropic Claude models called outside Bedrock, and the harness supports Bedrock, OpenAI, Google Gemini, and any LiteLLM-compatible provider. The harness can switch provider mid-session without losing conversation context, which makes a price-performance comparison a configuration change.
Can I run Strands Agents without AWS?
Yes. Strands Agents is an open-source SDK for Python and TypeScript that runs anywhere those runtimes run, including a laptop or a non-AWS host, and it calls models from any supported provider. Its AWS integration is a convenience rather than a requirement.
How long can an agent run on AgentCore Runtime?
Up to 8 hours per session, which covers asynchronous work and multi-agent collaboration that a request-response function cannot. Runtime bills CPU against actual active processing, so the waiting periods while a model responds typically do not accumulate compute charges the way a reserved container would.
What is AG-UI and do I need it?
AG-UI is an open event-based protocol for the connection between an agent backend and a user-facing application, the third member of a set that includes MCP for tools and A2A for agent-to-agent messages. You need it when a human watches the agent work, because streaming, interrupts, shared state, and approvals all cross that boundary and you would otherwise invent a private version of each.
Next steps
Place your current agent on the ladder before you change anything. Write down which rung it sits on, then the single control you would lose by moving up one, and check whether anything in production depends on that control today. Most teams find one dependency they had not made explicit, and it is almost always the tool permission check.
Then read where the agent loop lives on serverless runtimes, which takes the hosting question underneath rung three and asks what holds state between steps. If the framework question is open, choosing an agent framework, or writing the loop yourself compares what each option costs in control, and the agent harness, not the model, decides if your agent works sets out the nine components every rung must provide.
We keep the tool boundary outside the agent’s own reasoning in production work, including the federal compliance platform ComplyGrid. What we test in public sits in AI Fieldwork.
