Skip to content

New serverless pattern - lambda-durable-agentcore-springai-sam-java - #3264

Open
parasjain01 wants to merge 3 commits into
aws-samples:mainfrom
parasjain01:jainpar-feature-lambda-durable-agentcore-springai-java
Open

New serverless pattern - lambda-durable-agentcore-springai-sam-java#3264
parasjain01 wants to merge 3 commits into
aws-samples:mainfrom
parasjain01:jainpar-feature-lambda-durable-agentcore-springai-java

Conversation

@parasjain01

@parasjain01 parasjain01 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Description

Human-in-the-loop AI review with AWS Lambda durable functions and Amazon Bedrock AgentCore, in Java.

A Lambda durable function orchestrates a Spring AI agent hosted on AgentCore Runtime. The agent drafts a summary of a submitted document and flags what a reviewer should verify. The workflow then suspends at a callback until a human approves or rejects, and on approval asks the agent for a final version that folds in the reviewer's comments. No compute is billed while suspended, and the completed analyze step is served from its checkpoint on resume rather than re-invoking the model.

Two AWS services: Lambda and Amazon Bedrock AgentCore.

Workflow state — step results, the pending callback, and the handler's return value — is checkpointed by the durable execution service and read back with get-durable-execution and get-durable-execution-history, so the pattern provisions no database. Submission and approval are both driven from the AWS CLI.

Why this is useful

Agentic workflows tend to need a human somewhere in the loop, and that turns out to be awkward to build: the workflow has to wait an unbounded amount of time for a person, without holding an execution open or paying for idle compute, and without re-running the expensive model call every time it wakes up. This pattern shows the whole shape end to end, so a reader can take three specific things from it:

  • How to pause a workflow for a person, in Java. waitForCallback suspends the execution and resumes it when a decision arrives. The pattern shows the Java API for it, including details that are easy to get wrong - operation names are mandatory and must be stable across deployments, WaitForCallbackConfig nests a CallbackConfig rather than taking a timeout directly, and the execution timeout has to exceed the callback timeout or the workflow expires while still waiting.

  • How to avoid paying twice for an agent call. Because the callback resumes into a fresh invocation that replays the handler from the top, a naive implementation re-invokes the model on every resume. The pattern shows what makes replay skip completed work, and the unit tests assert it rather than just describing it.

  • How to run a Java agent on AgentCore, and call it from Lambda. AgentCore hosts source code directly only for Python and Node, so a Java agent has to be an ARM64 container - the pattern includes the build script and the CloudFormation for it. It also covers two things the SDK does not make obvious: InvokeAgentRuntimeResponse has no payload() accessor because the body is streamed, and reusing one runtimeSessionId across both calls is what lets the agent keep its earlier turn in context across the approval gate.

For readers weighing modelling choices, it also takes a position on two that caused real bugs while building it: approve and reject are both callback successes carrying the verdict as data, with failure reserved for "no decision could be obtained"; and the result keeps outcome separate from decision so "the reviewer said no" is never confused with "nobody answered".

It sits alongside lambda-durable-bedrock-agentcore-async, which pairs durable functions with AgentCore but uses waitForCallback as a machine-to-machine rendezvous with no human step. The two are complementary: that one shows an async agent callback, this one a synchronous agent step behind a human gate.

A note on dependencies

The agent uses spring-ai-agentcore-runtime-starter, which auto-configures the POST /invocations and GET /ping endpoints AgentCore requires. It is a Spring AI Community project under org.springaicommunity, published to Maven Central and Apache-2.0 licensed - not an official Spring AI or AWS module - and it requires Spring Boot 4.1 or later. Flagging it in case that matters for inclusion. The README says the same, and documents how to write the two endpoints by hand instead if a reader would rather not take the dependency.

Testing

  • mvn test in orchestrator/ runs 4 unit tests against the durable execution SDK's in-memory runner, covering the approve, reject and timeout paths, and asserting that replay skips completed steps rather than calling the agent again. No AWS account required.
  • Verified end to end on AWS: the AgentCore runtime reaches READY, both agent calls succeed against Bedrock, and analyze-document records exactly one StepStarted across two InvocationCompleted events. Approve and reject paths both confirmed, including that finalize-document is skipped on rejection.
  • sam validate --lint, sam build and the repo's scripts/validate.js schema validator all pass.

Checklist

  • One pattern directory only
  • README.md, example-pattern.json and template.yaml present, based on _pattern-model
  • SAM policy templates and scoped IAM used where possible
  • No architecture diagram image (per PUBLISHING.md, the website team creates it)
  • Branch named {username}-feature-{description}

The ServerlessLand URL placeholder in the README is left as << Add the live URL here >> pending publication.

Human-in-the-loop AI review with AWS Lambda durable functions and Amazon Bedrock
AgentCore, in Java.

A Lambda durable function orchestrates a Spring AI agent hosted on AgentCore
Runtime. The agent drafts a summary of a submitted document, the workflow
suspends at a callback until a human approves or rejects it, then resumes and
asks the agent for a final version if approved. No compute is billed while
suspended, and the completed analyze step is served from its checkpoint on
resume rather than re-invoking the model.

Two services: Lambda and AgentCore. Workflow state - step results, the pending
callback and the handler's return value - is checkpointed by the durable
execution service and read back with get-durable-execution and
get-durable-execution-history, so there is no table to provision. Submission and
approval are both driven from the AWS CLI.

The agent ships as an ARM64 container image because AgentCore hosts source code
directly only for Python and Node runtimes; scripts/build-agent-image.sh builds
and pushes it. The spring-ai-agentcore-runtime-starter auto-configures the
POST /invocations and GET /ping endpoints AgentCore requires, so the agent is a
single method annotated with @AgentCoreInvocation.

Includes unit tests using the durable execution SDK's in-memory runner covering
the approve, reject and timeout paths, and asserting that replay skips completed
steps instead of calling the agent again.

Verified end to end on AWS: the AgentCore runtime reaches READY, both agent calls
succeed against Bedrock, and analyze-document records exactly one StepStarted
across two InvocationCompleted events.
@parasjain01

Copy link
Copy Markdown
Contributor Author

Submission issue: #3265

Effect: Allow
# GetAuthorizationToken cannot be scoped to a repository.
Action: ecr:GetAuthorizationToken
Resource: '*'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make this more restrictive than '*'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately ecr:GetAuthorizationToken does not support resource-level permissions — AWS IAM does not allow scoping it to a specific repository ARN. The ECR IAM documentation lists it as an action that requires Resource: "*". The comment in the template already notes this. Lines 71 and 72 (Bedrock model permissions) have been tightened in response to the other comments.

Comment thread lambda-durable-agentcore-springai-sam-java/example-pattern.json Outdated

The durable function asks the agent to draft a summary of a document, then suspends. It resumes only when a human sends a decision, and asks the agent for a final version if the review was approved. While suspended the function consumes no compute and can wait for days.

Learn more about this pattern at Serverless Land Patterns: << Add the live URL here >>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update the URL

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL placeholder is intentional — the Serverless Land URL is assigned by the website team after the PR merges. I'll update it once the pattern is live.

Comment thread lambda-durable-agentcore-springai-sam-java/README.md
Comment thread lambda-durable-agentcore-springai-sam-java/README.md
Comment thread lambda-durable-agentcore-springai-sam-java/README.md
--durable-execution-name review-001 \
--query 'DurableExecutions[0].DurableExecutionArn' --output text)

aws lambda get-durable-execution-history \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got the following error:

aws: [ERROR]: An error occurred (ParamValidation): Parameter validation failed: Invalid length for parameter DurableExecutionArn, value: 0, valid min length: 1

Comment thread lambda-durable-agentcore-springai-sam-java/README.md Outdated
* [Java 21](https://docs.aws.amazon.com/corretto/latest/corretto-21-ug/downloads-list.html) and [Apache Maven](https://maven.apache.org/install.html) 3.9 or later
* [Docker](https://docs.docker.com/get-docker/), [Finch](https://runfinch.com/), nerdctl or Podman, to build the agent container image
* An Amazon Bedrock model available in the target Region. The default is `us.anthropic.claude-sonnet-4-5-20250929-v1:0`. Check availability with `aws bedrock get-foundation-model-availability --model-id <id> --region <region>`; if it reports anything other than `AUTHORIZED`, enable it under **Model access** in the Amazon Bedrock console.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include an architecture diagram and explain it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per PUBLISHING.md: 'You do not need to create the architecture diagram image that appears above each serverless pattern on ServerlessLand.com. The team that manages the website is responsible for creating the image.'

- Copyright year: 2025 -> 2026
- example-pattern.json title: 'Lambda' -> 'AWS Lambda'
- Default Bedrock model: claude-sonnet-4-5 -> claude-sonnet-5 (template.yaml,
  application.properties, README)
- README: add 'cd ..' after 'mvn test' so the next step runs from the project root
- README: note that list-durable-executions-by-function requires the unqualified
  function name (no :live suffix) when --durable-execution-name is also passed
- README: replace deleted System.out.printf references with sam logs and
  get-durable-execution instructions
- README: add StepConfig / RetryStrategies.fixedDelay example to the
  waitForCallback section to show retry configuration
- template.yaml: tighten Bedrock IAM resources from wildcard to
  anthropic.* foundation models and us.anthropic.* inference profiles;
  pin inference-profile to ${AWS::AccountId}
- DocumentReviewWorkflow: remove System.out.printf / announceReviewRequest
  entirely; callback ID is retrieved from execution history per the README

Replied on PR for three comments that required explanation rather than code
changes: GetAuthorizationToken cannot be scoped (AWS constraint), the Serverless
Land URL is assigned post-merge, and the architecture diagram is the website
team's responsibility per PUBLISHING.md.

Verified: mvn test 4/4, sam validate --lint passes, draft query tested against
live stack.
Makes it immediately clear which parameter to pass to sam deploy,
without needing to read the deploy example below it.
@parasjain01

Copy link
Copy Markdown
Contributor Author

All feedback addressed — thank you for the thorough review.

Code changes (pushed):

  • Copyright year updated to 2026
  • Title updated to "AWS Lambda durable functions" in example-pattern.json
  • Default Bedrock model updated to us.anthropic.claude-sonnet-5 in template.yaml, application.properties, and README
  • Bedrock IAM resources tightened to anthropic.* foundation models and us.anthropic.* inference profiles; inference-profile ARN now pins ${AWS::AccountId}
  • System.out.printf / announceReviewRequest removed entirely; callback ID is retrieved from execution history as documented in the README
  • Retry logic added to the waitForCallback section in the README with a StepConfig / RetryStrategies.fixedDelay example
  • cd .. added after mvn test so the reader stays in the project root
  • Note added to the list-durable-executions-by-function command that the unqualified function name must be used when --durable-execution-name is also passed
  • Build script output label changed from Image: to AgentImageUri: to match the parameter name

Replied on the following (no code change needed):

  • ecr:GetAuthorizationToken with Resource: "*" — this action does not support resource-level permissions per AWS IAM documentation; it must remain *
  • Serverless Land URL — this is an intentional placeholder; the URL is assigned by the website team after the PR merges
  • Architecture diagram — per PUBLISHING.md, the website team is responsible for creating the diagram

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants