Amazon Bedrock AI agents are within reach of any Java team that knows Spring Boot. Spring AI and Amazon Bedrock AgentCore let developers build agents that plan multi-step work, call tools and APIs, and remember context across conversations — using the same patterns they already use for REST APIs, security, and data access. The managed platform underneath supplies the runtime, memory, browser automation, code execution, and identity production demands, so the prototype-to-production gap is a set of patterns to adopt, not a new stack to learn.
AWS’s reference implementation builds two working systems: a single-agent travel assistant demonstrating tool calling and memory, and a multi-agent document processing platform with five specialists, a rules engine, and alerting. Both run as Spring Boot applications on AgentCore runtime.
What turns a model call into an agent
A chat completion call can generate text, but as the AWS post puts it, “it can’t decide what to do next, reach into external systems, or remember what happened yesterday.” An agent can, because it unites three capabilities. Planning breaks a goal into steps and revises them as new information arrives. Tool use lets the model invoke external functions — APIs, databases, code interpreters — to act in the world. Memory carries context across turns and sessions, so each decision builds on what came before. A retrieval-augmented generation application has retrieval but no planning loop. An agent has all three, operating in a goal-directed loop until the task is complete or it determines it cannot proceed.
The reference architecture sketches how the loop could look in production, as a conceptual eleven-stage pipeline: a client submits a document and a question; a proposed runtime gateway would handle authentication and rate limiting, and input guardrails would screen the request before a router classifies the document and routes it to a specialist agent, which plans its workflow and calls tools, the model, memory, and a rules engine as needed. Low-confidence or flagged results go to a human review queue before the grounded answer returns. Resilience, observability, logging, and least-privilege IAM apply across all stages. Treat this as the target architecture rather than the code as shipped: the gateway and guardrail stages are proposed production controls, while the sample itself ships only a per-request in-process guardrail demo.
Amazon Bedrock AI agents: the stack and the SDK
Amazon Bedrock AgentCore is the platform underneath: framework-agnostic and model-agnostic, it provides the production infrastructure — managed runtime, memory, browser automation, code execution, and identity management — while you bring your preferred framework and foundation model. The Spring AI AgentCore SDK, an open-source library at github.com/spring-ai-community/spring-ai-agentcore, maps those capabilities onto idiomatic Spring: annotations, auto-configuration, and composable advisors. Developers write standard Spring AI code while AgentCore handles the infrastructure; the SDK has reached general availability.
The application layer uses Spring AI components: @AgentCoreInvocation for endpoint wiring, ChatClient for model interaction, @Tool callbacks, and advisors for memory and guardrails. The infrastructure layer provides AgentCore runtime, memory, Browser, Code Interpreter, and Evaluations, and the spring-ai-agentcore SDK bridges the two, talking to Bedrock through the Converse API. To follow the reference implementation you need JDK 21, Spring Boot 4.1 with Spring AI 2.0 and spring-ai-agentcore 2.0, Maven 3.9 or later, Node.js 18 or later, Terraform 1.5 or later, Docker with Buildx, and the AWS CLI v2 with credentials.
A single autonomous agent: the travel assistant

The travel assistant is the recommended starting point: one Spring Boot application with tool calling for weather lookups and itinerary search, streaming responses, conversation memory, and resilience patterns. Spring AI orchestrates calls to Amazon Bedrock through the Converse API and resolves tool invocations locally inside the same process, while server-sent events stream tokens to the client as they are generated.
AgentCore runtime manages agent lifecycle and scaling, routing requests and monitoring health. Your agent must expose POST /invocations (JSON or streamed responses) and GET /ping (Healthy or HealthyBusy); long-running tasks must signal busy status, or the runtime might scale the agent down mid-task. With the SDK, a single annotation handles the whole contract:
@Service
public class TravelAgent {
private final ChatClient chatClient;
public TravelAgent(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@AgentCoreInvocation
public Flux<String> chat(PromptRequest request) {
return chatClient.prompt()
.user(request.prompt())
.stream()
.content();
}
}Tool calling is how the agent acts: annotate a method with @Tool and the model decides when to call it and with what arguments:
@Tool(description = "Get a short weather forecast for a city")
public String getWeather(
@ToolParam(description = "City name") String city) {
return forecastService.lookup(city);
}Register tools on the ChatClient: the model receives the definitions as context, reasons about when a tool call would help, and returns a structured tool call response. Spring AI invokes the annotated method and feeds the result back for further reasoning. For structured output, request a Java record type and Spring AI parses the reply back into the Java type. The AWS example was verified with Claude Sonnet 4.5 on Bedrock; where the schema matters, add validation and retry in your application code.
Memory is what makes the assistant a continuing conversation rather than a series of isolated prompts. The SDK integrates with AgentCore memory through the advisor pattern — interceptors that enrich prompts with context before they reach the model:
chatClient.prompt()
.advisors(agentCoreMemory.advisors)
.advisors(a -> a.param(
ChatMemory.CONVERSATION_ID, "user:" + sessionId))
.user(prompt)
.stream()
.content();AgentCore memory supplies short- and long-term context, so the agent retains history without external state management. The session ID becomes a conversation ID in actor:session form; for user-facing deployments, derive the actor from a verified OAuth 2.0 (JWT) identity, not a client-supplied value. Long-term memory extraction is asynchronous and optional, using four strategies: semantic (facts about users), user preference (explicit settings), summary (condensed history), and episodic (past interactions and lessons).
Coordinating specialists: the multi-agent document system

The second system, intelligent document processing, shows what changes when several agents work together. Documents route through five specialists — Lawyer, Mortgage, Insurance, Tax, and General — each built on a shared AbstractDocumentAgent template. A ClassifierAgent forwards each document to the right specialist, which extracts structured JSON with confidence scores via the multimodal Converse API. A rules engine evaluates the extraction, and low-confidence documents go to human review.
Coordination determines the system’s behaviour, and the sample uses three patterns. The router has a lightweight orchestrator dispatch each request to one specialist; it holds no domain logic. The evaluator passes specialist findings through a rules engine that applies severity levels and dispatches alerts or blocks for human review. Parallel fan-out is an optional extension: dispatch to specialists concurrently and aggregate results first, so wall-clock time is bounded by the slowest single call rather than the sum of all calls.
The rules engine is what makes the evaluator pattern operable without code changes. Rules live in application.yml as Spring Expression Language conditions evaluated against the extraction:
idp:
review:
enabled: ${REVIEW_ENABLED:true}
confidence-threshold: ${REVIEW_CONFIDENCE_THRESHOLD:0.7}
rules:
- id: low-confidence
agentType: ALL
condition: "confidence < 0.6"
severity: WARN
message: "Low extraction confidence; consider human verification."Severity controls behaviour: INFO and WARN are logged, ALERT dispatches through a pluggable AlertService, and BLOCK holds the document for manual review. The default alert service logs and emits a metric; substitute Amazon SNS, Slack, or email with your own bean. Because rules are operator configuration rather than end-user input, supported extraction rules can be changed in application.yml without changing their Java implementation.
Deploying to AgentCore runtime
Both systems ship as one immutable ARM64 image, promoted unchanged to production:
git clone https://github.com/aws-samples/sample-build-autonomous-ai-agents-with-amazon-bedrock-and-spring-ai.git
cd sample-build-autonomous-ai-agents-with-amazon-bedrock-and-spring-ai/code/autonomous-agent
./scripts/deploy.sh devThe deployment script creates an ECR repository, builds and pushes the container image, creates an IAM execution role, and deploys the AgentCore runtime. Three Terraform modules provision the ECR repository, the IAM role, and the runtime, configured in terraform.tfvars — the sample uses model global.anthropic.claude-sonnet-4-5-20250929-v1:0.
AgentCore runtime scales to zero and bills per invocation, so an idle agent costs almost nothing; the main variable cost is Bedrock token usage, plus a small amount for CloudWatch and ECR storage. Check current AgentCore and Bedrock pricing before estimating — rates change, and the runtime returns HTTP 429 when traffic exceeds your account and Region quotas. When finished, tear the infrastructure down with the provided cleanup script and delete any AgentCore memory resources and ECR images you no longer need.
Production readiness: resilience, security and guardrails
The production patterns are where this sample earns its keep, because they are the parts prototypes typically skip. On resilience, every model call uses a Resilience4j retry with exponential backoff, a circuit breaker, and a timeout; streaming paths emit a fallback message instead of a raw error. Configuration is best kept externalised: values such as Region, model ID, memory ID, timeouts, rate limits and system prompt belong outside the code, bound to a typed @ConfigurationProperties class so environments differ by configuration rather than by build. The application is stateless and ships as one immutable image.
On security, AgentCore runtime uses IAM authentication by default, and the execution role follows least privilege: ECR pull, scoped CloudWatch Logs and X-Ray, Bedrock InvokeModel, scoped workload access tokens, and memory actions limited to the specific memory resource ARN. For user-facing agents, add an OAuth 2.0 (JWT) authoriser such as Amazon Cognito and derive the memory actor ID from the verified identity. AgentCore Gateway enforces inbound authentication as an OAuth 2.0 resource server validating JWT bearer tokens through Cognito, Okta, Auth0, or your own OIDC provider, with configurable rate limiting — but those limits are fail-open by default, so they complement your core security controls rather than replace them.
On responsible AI, agents that take real-world actions need safety controls as a first-class concern. The sample ships a stand-in in-process pre-check called GuardrailService, which screens prompts for disallowed intent and prompt injection and is toggled per request. Production deployments should use Amazon Bedrock Guardrails as a standard control applied to every request: content filtering, denied topics, PII filters for requirements such as GDPR and HIPAA, and grounding checks to reduce hallucination risk before the agent acts. To wire Bedrock Guardrails into Spring AI, use the advisor pattern to apply the guardrail on every request. Because policies live in the guardrail, security teams can update filters independently of the application release cycle.
On observability, the example instruments with the Micrometer Observation API (@Observed), producing timers that become trace spans with a tracing bridge. It emits domain metrics — idp.documents.processed, idp.extraction.confidence, idp.alerts.sent, idp.processing.duration — through /actuator/prometheus, and structured JSON logs to standard output, captured in CloudWatch Logs. An automated evaluation loop is left as an extension: persist metrics, traces, audit events, and human-review outcomes as an evaluation dataset, then use AgentCore Evaluations or a separate pipeline to compare versioned prompts, models, tools, and routing rules — and promote changes only after evaluation and human approval.
Limits and what to build next
The sample is a foundation, not a finished product. It has no automated evaluation loop, and distributed tracing is not configured out of the box. The demo guardrail service is a stand-in, not a production control, and structured output maps to Java records in the sample, verified with Claude Sonnet 4.5 — verify it against the model you deploy, and add validation and retry in your application where the schema matters.
The next steps are incremental, not a rewrite: connect enterprise systems through AgentCore Gateway and the Model Context Protocol, add Bedrock Knowledge Bases and web search, implement Cognito JWT authentication with per-user memory isolation, customise Guardrails policies, and use AgentCore Evaluations to score production traces. One open question is where the managed AgentCore harness fits for teams starting now, versus the SDK path this article follows. The key takeaway, in the AWS post’s own words, is that for Java teams “you do not need to leave your environment to build production-quality agents.” The patterns you already trust — dependency injection, configuration properties, advisors, Actuator, Micrometer — carry directly into the agentic world, and AgentCore supplies the runtime, memory, identity, and governance that production demands.











