An AI agent consists of a model (which you can call via Amazon Bedrock), a prompt, a set of tools that you can make available via AgentCore Gateway, and a loop that manages the model and tool calls, which can be implemented with Strands SDK and that can run in AgentCore Runtime. You probably want to add some auth with AgentCore Identity, memory with AgentCore Memory, and set up proper observability with AgentCore Observability sending logs and traces to Amazon CloudWatch. I can throw 10 more keywords at you, but I think you get the idea: Agents have a lot of moving parts, and AWS offers a bunch of services and features that you need to combine in the right way. Or you can read Simple AWS and learn about AgentCore harness 😁.

Amazon Bedrock AgentCore harness is a capability of AgentCore, released on June 17, 2026, which acts as an opinionated way to create AI agents in the easiest way possible. You create a Harness resource and describe the agent through configuration: its model, instructions, tools, skills, memory, environment, and execution limits. AgentCore runs the loop inside an isolated AgentCore Runtime session and connects it to the configured capabilities, just like you would if you set everything up manually.

This article is sponsored by Inngest

Don't let agent failures blow the budget.

Agents, like all of us, often need to recover from failure. But they shouldn't start from scratch.

Inngest's durable execution checkpoints your agents every step of the way.

So your agents can pick up where they left off, and avoid doubling up on token & compute spend.

Inngest offers a generous free tier and a sleek local dev server for testing.

Creating an Agent With AgentCore Harness

Let's start by creating an agent. We’ll build an AWS release researcher: You can ask it about an AWS service or feature, and it will use AgentCore Browser to check official AWS pages before returning the launch status, a short explanation, and its sources.

Open the Amazon Bedrock AgentCore console, navigate to Harnesses, click the Quick create harness dropdown, and click Advanced create harness. You can also click Quick create harness and that will give you a harness in 3 clicks, like I promised in the title, but let's take the scenic route.

Give the Harness a name such as aws_release_researcher, pick a model such as Sonnet 4.6, and add this system prompt:

You are an AWS release research assistant.

Use the browser to consult official AWS documentation and AWS blogs.

For every answer:
1. State the current launch status.
2. Explain what changed.
3. Include the source URLs you used.
4. Say when the available evidence does not support a claim.

Do not use third-party sources.

Inside Tools, toggle Browser tool and select AgentCore Browser Tool. Keep everything else as is, you can play with it later (I know you will!).

Security FYI: AgentCore needs an execution role for the Harness. That role is assumed inside the Runtime session and needs permission to invoke your selected model and use the capabilities attached to the Harness. The default configuration is to create a new role for this. You can change it under Permissions if you're mindful about security, or keep it as it is for this test.

Click Create Harness and wait for it to be ready. Once it's created, click the yellow Test Harness button on the top right, and you'll get redirected to the Harness playground interface. You can interact with your agent here. Try asking something that requires current information, such as:

When did Amazon Bedrock AgentCore Harness become generally available,
and what was added at GA?

AgentCore starts an isolated Runtime session, sends the prompt and Browser tool definition to the model, runs the Browser when the model requests it, returns the results to the model, and streams the final answer back to the console. Notice you did not write the orchestration loop or deploy an agent container, all of that is handled for you.

The console is the quickest way to understand the experience, but the same agent can be created and invoked through the SDK. The following example uses an existing IAM execution role and an Amazon Bedrock model ID supplied through environment variables, and sets up everything else using Python and boto3:

import os
import time
import uuid
import boto3

REGION = os.getenv("AWS_REGION", "us-west-2")
ROLE_ARN = os.environ["HARNESS_EXECUTION_ROLE_ARN"]
MODEL_ID = os.environ["BEDROCK_MODEL_ID"]

SYSTEM_PROMPT = """
You are an AWS release research assistant.

Use the browser to consult official AWS documentation and AWS blogs.

For every answer:
1. State the current launch status.
2. Explain what changed.
3. Include the source URLs you used.
4. Say when the available evidence does not support a claim.

Do not use third-party sources.
""".strip()

control = boto3.client(
    "bedrock-agentcore-control",
    region_name=REGION,
)

runtime = boto3.client(
    "bedrock-agentcore",
    region_name=REGION,
)

created = control.create_harness(
    harnessName="aws_release_researcher",
    executionRoleArn=ROLE_ARN,
    model={
        "bedrockModelConfig": {
            "modelId": MODEL_ID,
        }
    },
    systemPrompt=[
        {
            "text": SYSTEM_PROMPT,
        }
    ],
    tools=[
        {
            "type": "agentcore_browser",
            "name": "browser",
        }
    ],
    maxIterations=12,
    timeoutSeconds=300,
)

harness = created["harness"]

while harness["status"] == "CREATING":
    time.sleep(3)
    harness = control.get_harness(
        harnessId=harness["harnessId"],
    )["harness"]

if harness["status"] != "READY":
    raise RuntimeError(
        f"Harness creation failed: {harness.get('failureReason', 'unknown error')}"
    )

response = runtime.invoke_harness(
    harnessArn=harness["arn"],
    runtimeSessionId=str(uuid.uuid4()),
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "text": (
                        "When did Amazon Bedrock AgentCore Harness become "
                        "generally available, and what was added at GA?"
                    )
                }
            ],
        }
    ],
)

for event in response["stream"]:
    if "contentBlockDelta" in event:
        delta = event["contentBlockDelta"].get("delta", {})
        if "text" in delta:
            print(delta["text"], end="", flush=True)

    elif "runtimeClientError" in event:
        raise RuntimeError(event["runtimeClientError"]["message"])

This code snippet creates the resource, waits for it, and prints the response stream. Notice that there is no Strands Agent, no tool dispatcher, no loop that reads toolUse blocks, and no container entrypoint. Those things are entirely defined by the harness, though you can override them if you want.

Agent as Configuration

The whole point of harness is not really the defaults and the three clicks, but rather the ability to define an agent through configuration. The model, prompt, tools, memory, environment, and limits are properties of the Harness, passed as parameters to a simple create_harness function call, instead of constants buried inside an agent application.

That separates the agent definition from the mechanism used to create it. Here is a CloudFormation version of the same release researcher:

AWSTemplateFormatVersion: "2010-09-09"
Description: AWS release research agent built with AgentCore Harness

Parameters:
  HarnessExecutionRoleArn:
    Type: String

  BedrockModelId:
    Type: String

Resources:
  ReleaseResearcherHarness:
    Type: AWS::BedrockAgentCore::Harness
    Properties:
      HarnessName: aws_release_researcher
      ExecutionRoleArn: !Ref HarnessExecutionRoleArn

      Model:
        BedrockModelConfig:
          ModelId: !Ref BedrockModelId
          Temperature: 0.2

      SystemPrompt:
        - Text: |
            You are an AWS release research assistant.

            Use the browser to consult official AWS documentation
            and AWS blogs.

            For every answer:
            1. State the current launch status.
            2. Explain what changed.
            3. Include the source URLs you used.
            4. Say when the available evidence does not support a claim.

            Do not use third-party sources.

      Tools:
        - Type: agentcore_browser
          Name: browser

      Memory:
        ManagedMemoryConfiguration:
          Strategies:
            - SEMANTIC
            - SUMMARIZATION
          EventExpiryDuration: 30

      MaxIterations: 12
      TimeoutSeconds: 300

      Tags:
        - Key: application
          Value: aws-release-researcher
        - Key: managed-by
          Value: cloudformation

This template defines the model, its instructions, the tool it can use, how memory is managed, and how long the agent can keep working on one request. CloudFormation creates the Harness, and AgentCore turns those properties into a Runtime, a managed loop, an isolated session environment, and the connections needed by the configured capabilities.

AgentCore Browser, Code Interpreter, Gateway, and remote MCP tools can be connected to the managed loop. Skills can be added with reusable instructions, scripts, references, and files. Memory can be managed by the Harness, supplied through an existing AgentCore Memory resource, or disabled. A custom environment can add dependencies and command-line tools while the orchestration remains managed. All of that is done as configuration, and Harness takes care of translating it into implementation details.

You can also change the configuration for a single invocation. Suppose we want a shorter answer from the release researcher without changing the deployed Harness:

response = runtime.invoke_harness(
    harnessArn=HARNESS_ARN,
    runtimeSessionId=str(uuid.uuid4()),
    systemPrompt=[
        {
            "text": (
                "You are an AWS release research assistant. "
                "Use only official AWS sources. "
                "Return at most three paragraphs and include the URLs."
            )
        }
    ],
    maxIterations=6,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "text": "Explain what AgentCore Harness manages."
                }
            ],
        }
    ],
)

The override applies to that invocation. The Harness resource uses its existing configuration for every other call. You can use this to test another supported model or tool set without creating a second harness.

Security FYI: This flexibility also creates a permission boundary. A caller who can override model fields, tools, skills, or instructions can materially change what the agent accesses and how it behaves. The API you expose to invoke the harness should remove fields the caller should never control and allowlist the values that remain.

AgentCore Harness has no separate service charge. You're billed the regular price for model inference, Runtime consumption, managed Memory, Browser, Gateway, Code Interpreter, storage, data transfer, CloudWatch, and anything else you use. The configuration is also the right place to add tags and limits so those costs can be attributed and bounded. For Bedrock calls, instead of a base model ID use an inference profile with the tags you want. For all the other resources, you set the tags on the Harness directly:

# Inside your AWS::BedrockAgentCore::Harness resource
MaxIterations: 12
MaxTokens: 8000
TimeoutSeconds: 300

Tags:
  - Key: application
    Value: aws-release-researcher
  - Key: environment
    Value: production
  - Key: owner
    Value: platform-team
  - Key: cost-center
    Value: genai

To use these tags in Cost Explorer and billing reports, you still need to activate their keys as user-defined cost allocation tags in AWS Billing and Cost Management.

Release Lifecycle of a Harness

I showed you how easy it is to create the first Harness. But after five prompt changes, two model experiments, and a new tool, you'll probably have a hard time keeping track of what it does. That's where versions and endpoints become useful.

Suppose version 1 of our release researcher reports the launch date and source links, but users also want to know about regional availability, and a warning when an AWS page still contains stale preview language. We can test those new instructions through an invocation override, and iterate on them until we get the right prompt. Then we update the Harness configuration.

That update creates a new immutable Harness version. Version 1 still contains the original model, prompt, tools, memory, environment, and limits. Version 2 contains the updated definition, with our changes.

Harness endpoints decide which version receives requests. The automatically created DEFAULT endpoint always follows the latest version, and you can create named endpoints where you control which version they point to.

For production, I recommend you create a named endpoint instead of sending traffic through DEFAULT:

aws bedrock-agentcore-control create-harness-endpoint \
  --harness-id "$HARNESS_ID" \
  --endpoint-name "production" \
  --target-version "1" \
  --description "Production release researcher"

You can update the Harness several times while production remains on version 1. After version 2 passes your tests, move the endpoint to point to it:

aws bedrock-agentcore-control update-harness-endpoint \
  --harness-id "$HARNESS_ID" \
  --endpoint-name "production" \
  --target-version "2" \
  --description "Promote version 2"

If the new version behaves badly, you can point the endpoint back to version 1, thus rolling back the change. You don't need to keep track of the old prompt or remember which model settings you used back then, version 1 contains all of that.

This is how you should be iterating on a Harness:

Override one setting → inspect the session → evaluate the result → update the Harness → create a new version → move the endpoint

Do this for every change. Remember that every single configuration item can significantly alter how the agent behaves. A simple prompt change can lead the model to select a different tool. A new skill can change the procedure it follows. Picking a different model can affect output quality, latency and cost. It's great that none of those changes require an application rebuild, but all of them can change what users see and what downstream systems receive, so they must be treated as new application versions.

Observability and Evaluations for Harness

Harness sends traces, logs, and metrics for model calls, tool calls, memory operations, and shell commands to AgentCore Observability and CloudWatch. You need to enable CloudWatch Transaction Search before those traces are visible, then decide which dashboards, alerts, retention settings, and cost reports your team will use.

Harness also gives you an observability dashboard where you can monitor your agents, pulling the same data that CloudWatch shows you, and with links to easily access more detailed information in CloudWatch.

AgentCore Evaluations can score sessions and compare behavior across changes. Batch evaluations can run the same dataset against multiple versions, while the broader optimization capabilities can generate prompt or tool-description recommendations and test variants. The service can run the evaluation, but you still need to provide representative cases and a threshold that means something for the application.

For the release researcher, that dataset could contain questions about launches with clean documentation, launches whose status changed from preview to GA, and services with conflicting or stale pages. An evaluation that checks only whether the final answer is fluent and well written will miss the failures this agent was created to prevent: stale information confidently presented as current.

The Limits of AgentCore Harness

Harness is easy to use because it makes several decisions for you. It runs an AWS-managed, Strands-powered agent loop with the configuration points AgentCore exposes. You can select models, provide instructions, attach tools and skills, configure memory and the environment, and set execution limits. The main limitation is that you cannot replace the loop with any control flow you want.

If you need another agent framework, graph or workflow orchestration inside the agent, custom hooks or middleware, bidirectional streaming, or direct control over how each reasoning step proceeds, you'll need to define your agent in the old-fashioned way, and run it directly in AgentCore Runtime. In those cases, orchestration is part of your application rather than reusable plumbing around it, so Harness's sensible defaults to implement it no longer apply to your case.

You do have some room between a completely managed Harness and a completely custom agent. Inline functions let the client execute individual tool calls, or AWS Step Functions can place a Harness inside a larger deterministic workflow with branching, retries, approvals, and non-agent steps. This way the reasoning loop remains managed and easy to build, while the wider process stays explicit and hyper-configurable.

Security FYI: Harness can't advise you on whether the configured behavior is safe for your application. The agent's execution assumes an IAM role you provide, but you still need to scope that role to the models, tools, credentials, and resources the agent requires. Skill repositories and S3 locations are treated as trusted content, but you probably want to scope this down. Invocation overrides can change provider settings and available tools, and it's your job to prevent this from happening. The caller and the Harness may both be authenticated while the requested action is still something your application should reject. You can set very good controls with Layered Authorization for AI Agents using Amazon Bedrock AgentCore.

Memory needs the same level of attention. Managed memory is convenient and can be easily enabled through the Harness, but actor IDs, retention, retrieval, privacy, and deletion remain application decisions (i.e. your decisions). The service can remember that user-123 prefers short summaries, but your application must ensure that whomever initiated an agent invocation is actually allowed to read and write memory for user-123.

Exporting a Harness

AgentCore can turn an existing Harness into editable Python source code using Strands. This gives you a practical path from a configuration-managed agent to a code-based agent that can run in AgentCore Runtime, without recreating the whole thing by hand.

The export includes model settings, tools, skills, memory, execution limits, context truncation, filesystem mounts, and authorization configuration into the generated agent. Currently the only available export target is Python with Strands, with either a CodeZip or container build. Support for other frameworks and languages should be added soon.

Our release researcher was created in the console, so to export it via the AgentCore CLI we need to use its ARN:

agentcore export harness \
  --arn "$HARNESS_ARN" \
  --target-agent-name "AWSReleaseResearcherRuntime" \
  --build CodeZip

If the Harness was created inside the same AgentCore CLI project, you can use its name instead:

agentcore export harness \
  --name "aws_release_researcher" \
  --target-agent-name "AWSReleaseResearcherRuntime"

If you want to use a container-based Runtime, you can change the build type while exporting it:

agentcore export harness \
  --arn "$HARNESS_ARN" \
  --target-agent-name "AWSReleaseResearcherRuntime" \
  --build Container

The CLI writes the generated agent under app/ and adds it to the AgentCore project as a normal Runtime. Before touching the code, read the generated EXPORT_NOTES.md. That file lists configuration the exporter could not carry over automatically, explains why it needs attention, and tells you what to change. Once those items are resolved, deploy the generated agent to AgentCore Runtime by running agentcore deploy.

The generated agent is ordinary source code from this point forward. You can change the Strands loop, add middleware, integrate another Python library, implement custom tool behavior, or restructure how the agent reasons. You also own its tests, dependencies, packaging, security updates, deployment, and future maintenance.

Keep in mind that you don't need to export a Harness simply because it has reached production. Versions, named endpoints, observability, evaluations, managed memory, and infrastructure-as-code support are all available while the agent remains a Harness. Export should be used when you need to configure something that Harness doesn't support.

Conclusion

AgentCore Harness and the AWS console give you the fastest path to an agent, with just three clicks. Advanced create harness lets you set the values for every configuration item. The SDK and CloudFormation expose the same agent as a resource you can automate and review. Invocation overrides let you test changes without mutating it. Immutable versions and named endpoints let you release those changes deliberately. Export gives you the source code when you need to change something that is not supported in Harness's configuration.

You still need to build the parts that are specific to your application. The prompt needs a clear job. Tools need correct behavior and narrow permissions. Memory needs proper user scoping. Evaluations need representative cases. The application still needs an interface, access controls, budgets, and someone who owns the result. And you still need to do the security work.

Harness lets you separate the what from the how. You define via configuration every element of your agent, and Harness takes care of how to implement each element and how to tie everything together. Additionally, it gives you sufficient control that you can take an agent to production and keep managing it through Harness, with versions, endpoints and invocation overrides. You don't exactly outgrow Harness, but you might hit the limits of its configurations, and at that point you can export it and continue as code.

Did you like this issue?

Login or Subscribe to participate

Recommended for you

View all
caret-right