Amazon Bedrock AgentCore Harness: Build a Serverless AI Image Editing Agent

If you want to build an AI image editor that takes plain-English instructions and returns edited images without managing your own orchestration loop, Amazon Bedrock AgentCore harness is designed for that job. It can handle tool routing, memory, and isolated runtime execution through configuration instead of custom agent code. For teams already evaluating Bedrock agent patterns, this is the same general direction behind work on AgentCore memory with Kiro CLI, where state handling is pushed into managed components instead of app-side glue code.

A reference implementation shows a full stack that lets users upload an image, request edits like changing an object or extending a canvas, and get the result back after the system selects the right tool and applies a watermark. The stack uses a React frontend, AWS Lambda, Amazon S3, Amazon Cognito, and Amazon Bedrock models.

The practical takeaway is simple: if your agent mostly needs to receive a prompt, choose the right tool, perform the edit, and return a result, AgentCore harness can remove a large amount of orchestration work. If you need deeper control over the execution flow, you may need AgentCore Runtime instead.

Quick answer: what AgentCore harness does in this image editing setup

In this design, AgentCore harness runs the agent in an isolated microVM and manages several parts of the stack that developers often wire up by hand. That includes conversation memory, model invocation, tool selection, and observability.

The image editor accepts natural-language requests such as changing an object in a photo or extending the image to one side. The agent uses Claude Sonnet 4.6 for image editing turns, can use Claude Haiku 4.5 for lighter chat interactions, and calls three editing tools backed by Stability AI models through Amazon Bedrock. After the edit completes, a command runs directly on the runtime to add a watermark without spending model tokens. In practice, that keeps the expensive part of the loop focused on reasoning and tool choice instead of simple post-processing.

The result is a serverless architecture where the agent logic is mostly declarative. Instead of writing a custom loop for reasoning, tool calls, retries, and state management, you define the harness and the tools it is allowed to use.

How the architecture is organized

Hands editing architectural photo on tablet using Lightroom app.

The reference application is split into four main layers.

  • Frontend: A React application hosted on AWS Amplify where users upload images, enter edit prompts, and draw masks for region-specific changes.
  • Security boundary: An AWS Lambda proxy sits between browser-side credentials and the harness API, helping control what prompts and parameters are passed through.
  • Agent layer: An Amazon Bedrock AgentCore harness agent uses AgentCore Memory to preserve conversation context across turns.
  • Editing tools: Three AWS Lambda functions call Stability AI foundation models through Amazon Bedrock to perform different image editing tasks.

That split matters because it keeps the browser from talking directly to the harness in an unrestricted way, while also separating image generation tools from the conversational agent itself. The harness handles reasoning and selection. The tools do the actual image processing.

What kinds of image edits the agent can handle

The sample is built around three editing patterns, each mapped to a specific tool. A prompt like changing a car color can be treated as an object-level replacement. A prompt like extending the image to the right is a canvas expansion task. Region-specific edits can be done by drawing a mask and then describing what should appear in that area.

This is a useful pattern if you are building your own editing agent, because users usually do not think in tool names. They think in outcomes. They want to say what should change, not choose between inpainting, outpainting, or search-and-replace. The model interprets that intent and selects the tool based on the schema and descriptions exposed through the gateway. That same intent-routing pattern also shows up in adjacent AWS tooling, including Amazon Lex assisted NLU work, where better structure around inputs improves downstream decision quality.

Why configuration is the main selling point

A woman uses a stylus to edit photos on a tablet, showcasing freelance creativity.

The strongest claim in this setup is not that it edits images. Plenty of systems can do that. The key point is that the core agent is created through API parameters instead of a handwritten orchestration layer.

The harness definition includes the model, the system prompt, the tool gateway, the list of allowed tools, maximum iterations, timeout settings, and memory configuration. That means the agent behavior can be changed by updating configuration rather than rebuilding and redeploying a custom orchestration service.

In practical terms, that reduces several common chores:

  • Writing the model call loop
  • Parsing and routing tool calls manually
  • Managing conversation history and replaying it to the model
  • Building streaming handlers and retry logic just to get a baseline agent working

This does not eliminate all code. The sample still includes a Lambda proxy, three tool Lambdas, and deployment logic. But it removes a meaningful amount of glue code that usually grows fast in agent projects.

How tool routing works with AgentCore Gateway and MCP

In many agent systems, tool use becomes its own mini-framework. You have to define schemas, catch tool calls, validate arguments, invoke the target service, and send results back into the model loop.

Here, the tools are exposed through AgentCore Gateway using Model Context Protocol, or MCP. Each tool is described with a schema and pointed at a Lambda function. The harness then discovers those tools and presents them to the model during reasoning. When the model decides to use one, the harness invokes it through the gateway and feeds the result back into the ongoing interaction.

That design makes tool descriptions especially important. If your tool names, descriptions, or input schemas are vague, the model has less guidance for picking the right one. If they are clear, you can often avoid writing separate routing logic altogether. The same principle carries over to safety controls, where explicit checks before or around tool execution matter more than vague policy text, a theme that also appears in InvokeGuardrailChecks in Bedrock.

Model switching and personas without losing context

A notable part of the sample is per-invocation model switching. The frontend can route lightweight prompts such as greetings or capability questions to Claude Haiku 4.5 for lower latency, while sending image-editing requests to Claude Sonnet 4.6 for better tool selection and reasoning quality.

AgentCore Memory preserves the conversation history across those switches. So if a user first requests one color change and then follows up with a short prompt like “make it blue instead,” the active model still gets the prior context for that session.

The same per-invocation pattern is used for persona changes. Users can choose domain-specific personas such as Real Estate, Retail, or Automotive. Those system prompts are injected on the next turn without redeploying the agent.

This is a strong fit for applications where tone, vocabulary, or domain framing matters, but where you do not want to maintain separate agent deployments for each industry use case.

How post-processing works without using model tokens

After an edit is generated, the sample runs a Python script directly on the harness microVM using InvokeAgentRuntimeCommand. In this implementation, that script applies a tiled watermark to the image.

The bigger idea is more interesting than the watermark itself. Some tasks are deterministic and do not need model reasoning at all. If you can run them directly in the runtime environment, you avoid extra token usage and keep those steps outside the agent’s reasoning loop.

Examples mentioned for this pattern include resizing images before model input, validating outputs, extracting structured data, or applying business rules. The microVM includes Python and bash by default, and additional packages can be installed at runtime.

If you are designing a production workflow, this separation is useful. Let the model decide what should happen. Let direct commands handle mechanical steps that do not benefit from inference.

Deployment requirements and what gets created

The deployment uses AWS Cloud Development Kit and a single deployment script to provision the full application. The prerequisites listed for the sample include an AWS account with permissions to create IAM roles, Lambda functions, S3 buckets, Cognito pools, and AgentCore resources, along with Node.js 20.x or later, Python 3.13 or later, and AWS CLI 2.x configured with credentials.

The setup also requires access in Amazon Bedrock to Anthropic Claude models, specifically Sonnet and Haiku, plus access to Stability AI models. Estimated deployment time is listed as 3 to 5 minutes. In real environments, the bigger variable is often model access approval and regional availability, not infrastructure provisioning time.

Once deployed, the stack creates authentication through Amazon Cognito, encrypted image storage in Amazon S3 protected by AWS KMS, Lambda-based image tools exposed through the gateway, the harness agent with memory, and an AWS Amplify-hosted React frontend.

What the user experience looks like

After sign-in, the interface presents an image canvas and a chat panel. The flow is straightforward.

  1. Upload an image to S3 under the signed-in user’s scoped path.
  2. Describe the desired edit in natural language.
  3. Draw a mask if the edit should target a specific region.
  4. Review the generated result, which appears in the chat thread with a watermark applied.
  5. Optionally switch the model or persona for the next turn.

The interface also exposes operational details in a behind-the-scenes view, including model choice, tool usage, token counts, latency, and whether watermarking was applied. That kind of transparency is useful during development because it makes routing behavior visible instead of opaque.

When to use AgentCore Runtime instead

The sample also draws a boundary around the harness approach. If your agent needs custom orchestration, such as multi-step pre-processing between turns, arbitrary Python before and after each model call, or a more complex state machine like LangGraph, AgentCore Runtime offers fuller control.

That distinction matters because configuration-first systems are fastest when your workflow matches the abstraction. Once your workflow starts fighting that abstraction, direct runtime control becomes more valuable than the convenience of declarative setup.

A reasonable approach is to start with the harness for simpler tool-calling agents, then move to AgentCore Runtime if your logic grows beyond prompt, tool, result patterns.

Common implementation lessons from this sample

A few patterns in the reference design are worth carrying into your own projects.

First, keep the browser separated from sensitive backend operations through a proxy or similar control point. That lets you constrain prompts, manage allowed parameters, and reduce the exposure of backend capabilities.

Second, scope tool access tightly. The harness configuration explicitly limits the agent to the named tools the gateway exposes, instead of allowing broad wildcard access. That principle matters for both safety and predictability.

Third, use memory intentionally. A persistent session can improve follow-up edits and reduce repeated input, but you should still decide how long conversation history should remain useful for your application. For deeper memory-heavy patterns, the tradeoffs become clearer in examples like AgentCore memory orchestration with Kiro CLI, where longer-lived context can help but also needs tighter operational boundaries.

Fourth, reserve the model for reasoning tasks. Deterministic image processing, validation, and formatting often belong in direct runtime commands or separate functions.