---
id: agentcore
title: AWS AgentCore
sidebar_label: AgentCore
---

<IntegrationTagsDisplayer otel={true} cicdEvals={true} traceability={true} />

[Amazon AgentCore](https://aws.amazon.com/bedrock/agentcore/) is AWS's managed runtime for deploying and scaling AI agents.

The `deepeval` integration auto-instruments AgentCore apps through OpenTelemetry. Every agent invocation, model call, and tool call becomes a span you can inspect, without wiring trace structure by hand.

<AgentTraceTerminal
  title="agentcore_agent · deepeval"
  ariaLabel="Example AgentCore agent trace with per-step metric scores"
  lines={[
    { kind: "cmd", name: "deepeval test run test_agentcore_agent.py" },
    { kind: "blank" },
    { kind: "root", prefix: "●", name: "test_agentcore_agent" },
    { kind: "blank", prefix: "│" },
    {
      kind: "agent",
      prefix: "└─",
      name: "refund_assistant",
      metric: "Task Completion",
      score: "0.95",
      duration: "240ms",
      pass: true,
    },
    {
      kind: "llm",
      prefix: "   ├─",
      name: "amazon.nova-lite-v1:0 · plan",
      metric: "G-Eval",
      score: "0.43",
      duration: "96ms",
      pass: false,
    },
    {
      kind: "tool",
      prefix: "   ├─",
      name: 'lookup_order(order_id="A-1001")',
      duration: "52ms",
    },
    {
      kind: "llm",
      prefix: "   └─",
      name: "amazon.nova-lite-v1:0 · respond",
      metric: "Faithfulness",
      score: "0.94",
      duration: "88ms",
      pass: true,
    },
    { kind: "blank" },
    {
      kind: "summary",
      name: "Trace score  0.77   ·   2/3 metrics passed",
      pass: false,
    },
  ]}
/>

`deepeval`'s AgentCore integration enables you to:

- **Auto-instrument every AgentCore invocation** — each app entrypoint call produces a trace, and each agent, LLM, and tool call becomes a component span.
- **Evaluate traces or model / agent components** with any `deepeval` metric.
- **Run evals from scripts or CI/CD** — same metrics, different surfaces.
- **Customize trace and span data at runtime** from tool bodies, wrappers, or staged span config.

## Getting Started

<Steps>

<Step>

### Installation

```bash
pip install -U deepeval bedrock-agentcore strands-agents opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
```

Under the hood the integration registers an OpenTelemetry span processor that translates AgentCore spans into `deepeval` traces.

</Step>

<Step>

### Instrument and evaluate

Call `instrument_agentcore(...)` before creating or invoking your AgentCore app. From that point on, AgentCore spans are available to `deepeval`.

```python title="agentcore_agent.py" showLineNumbers
from bedrock_agentcore import BedrockAgentCoreApp
from strands import Agent
from deepeval.integrations.agentcore import instrument_agentcore
from deepeval.dataset import EvaluationDataset, Golden
from deepeval.metrics import TaskCompletionMetric

instrument_agentcore()

app = BedrockAgentCoreApp()
agent = Agent(model="amazon.nova-lite-v1:0")

@app.entrypoint
def invoke(payload):
    result = agent(payload["prompt"])
    return {"result": result.message}

# Goldens are the inputs you want to evaluate.
dataset = EvaluationDataset(goldens=[Golden(input="Help me return my order.")])

# `evals_iterator` loops through goldens and applies metrics.
for golden in dataset.evals_iterator(metrics=[TaskCompletionMetric()]):
    invoke({"prompt": golden.input}) # Produces trace for evaluation
```

Done ✅. You've run your first eval with full traceability into AgentCore via `deepeval`.

:::tip
The examples in this doc use Strands as the agent framework running inside AgentCore. Strands is not required; it is just one framework you can deploy with AgentCore. `deepeval`'s integration works with any framework.
:::

</Step>

</Steps>

## What gets traced

Each AgentCore app invocation produces a **trace** — the end-to-end unit your user observes. Inside that trace are **component spans** for each step the agent took:

- **Agent spans** — Strands agent invocations and agent workflow steps.
- **LLM spans** — model calls emitted through AgentCore / Strands.
- **Tool spans** — tool calls and function executions.

```text
Trace                                    ← what the user observes
└── Agent: refund_assistant              ← one AgentCore app invocation
    ├── LLM: amazon.nova-lite-v1:0       ← component span: model plans
    ├── Tool: lookup_order               ← component span: tool input + output
    └── LLM: amazon.nova-lite-v1:0       ← component span: final answer
```

The trace and its component spans are independently evaluable.

## Running evals

There are two surfaces for running evals against an AgentCore app. Pick by where you want results to surface — your terminal during development, or your CI pipeline as a pass/fail gate.

### In CI/CD (pytest)

Use the `deepeval` pytest integration. Each parametrized test invocation becomes one AgentCore app invocation; failing metrics fail the test, which fails the build.

```python title="test_agentcore_agent.py" showLineNumbers
import pytest

from bedrock_agentcore import BedrockAgentCoreApp
from strands import Agent
from deepeval import assert_test
from deepeval.dataset import EvaluationDataset, Golden
from deepeval.integrations.agentcore import instrument_agentcore
from deepeval.metrics import TaskCompletionMetric

instrument_agentcore()

app = BedrockAgentCoreApp()
agent = Agent(model="amazon.nova-lite-v1:0")

@app.entrypoint
def invoke(payload):
    result = agent(payload["prompt"])
    return {"result": result.message}

dataset = EvaluationDataset(goldens=[
    Golden(input="Help me return my order."),
    Golden(input="Explain my refund options."),
])

@pytest.mark.parametrize("golden", dataset.goldens)
def test_agentcore_agent(golden: Golden):
    invoke({"prompt": golden.input})
    assert_test(golden=golden, metrics=[TaskCompletionMetric()])
```

Run it with:

```bash
deepeval test run test_agentcore_agent.py
```

### In a script

Use `EvaluationDataset` + `evals_iterator(...)`. Each `Golden` becomes one app invocation; metrics score the resulting trace.

```python title="agentcore_agent.py" showLineNumbers
dataset = EvaluationDataset(goldens=[
    Golden(input="Help me return my order."),
    Golden(input="Explain my refund options."),
])

for golden in dataset.evals_iterator(metrics=[TaskCompletionMetric()]):
    invoke({"prompt": golden.input})
```

## Applying metrics to components

The `metrics=[...]` you passed to `evals_iterator` evaluates the **trace**. To evaluate a **component** instead — a specific LLM call or agent span — stage the metric with the appropriate `next_*_span(...)` wrapper before invoking the app.

### Agent spans

```python title="agentcore_agent.py" showLineNumbers
from deepeval.tracing import next_agent_span
...

def run_agentcore(prompt: str):
    with next_agent_span(metrics=[TaskCompletionMetric()]):
        return invoke({"prompt": prompt})
```

### LLM calls

```python title="agentcore_agent.py" showLineNumbers
from deepeval.tracing import next_llm_span
...

def run_agentcore(prompt: str):
    with next_llm_span(metrics=[AnswerRelevancyMetric()]):
        return invoke({"prompt": prompt})
```

For deterministic tool calls, prefer `update_current_span(...)` to add metadata, inputs, and outputs instead of attaching metrics to the tool span.

## Customizing trace and span data at runtime

Trace-level fields you pass to `instrument_agentcore(...)` are defaults. For anything dynamic, the right API depends on where your code runs.

AgentCore creates most of the trace structure for you, which means the agent, LLM, and tool spans are mostly hidden behind the app invocation. Calls like `update_current_trace(...)` and `update_current_span(...)` only work while there is an active `deepeval` trace/span in context. In practice, tool bodies are the clearest mutation point, because AgentCore has already opened the trace and tool span before your function runs.

If you need to customize from outside a tool, use `instrument_agentcore(...)` for static defaults, `next_*_span(...)` to stage config for the next AgentCore-created span, or `@observe` / `with trace(...)` when you own the outer operation.

### Trace-level fields from inside a tool

```python title="agentcore_agent.py" showLineNumbers
from deepeval.tracing import update_current_trace
...

def lookup_order(order_id: str) -> dict:
    order = orders_db.get(order_id)
    update_current_trace(user_id=order["user_id"], metadata={"order_id": order_id})
    return order
```

### Span-level fields from inside a tool

```python title="agentcore_agent.py" showLineNumbers
from deepeval.tracing import update_current_span
...

def lookup_order(order_id: str) -> dict:
    order = orders_db.get(order_id)
    update_current_span(metadata={"order_id": order_id}, output=order)
    return order
```

## Advanced patterns

The primitives above — `instrument_agentcore(...)`, `@observe`, `with trace(...)`, `next_*_span(...)`, `update_current_*(...)` — compose around one boundary: AgentCore owns the auto-instrumented spans, and your code customizes them from the places it can actually see.

### Evaluate subagents with `next_*_span`

`next_*_span(metrics=[...])` stages a metric for the next matching AgentCore component span. Use this when you want to evaluate a subagent or model step instead of the full trace. Pick the helper that matches the span you want to score: `next_agent_span(...)` or `next_llm_span(...)`.

```python title="agentcore_agent.py" showLineNumbers
from deepeval.tracing import next_agent_span
...

def run_agent(prompt: str):
    with next_agent_span(metrics=[TaskCompletionMetric()]):
        return invoke({"prompt": prompt})
```

#### No trace-level metrics required

Trace-level metrics are end-to-end metrics: they score the whole trace. They are not strictly necessary here because the `TaskCompletionMetric` is attached to the next agent span, so CI/CD and scripts only need to run the subagent.

This is how you'd run it:

<Tabs items={["CI/CD", "Scripts"]}>
<Tab value="CI/CD">

```python title="test_agentcore_agent.py" showLineNumbers
import pytest
from deepeval import assert_test
...

@pytest.mark.parametrize("golden", dataset.goldens)
def test_agent_span(golden: Golden):
    run_agent(golden.input)
    assert_test(golden=golden)
```

Then finally:

```bash
deepeval test run test_agentcore_agent.py
```

</Tab>
<Tab value="Scripts">

```python title="agentcore_agent.py" showLineNumbers
...

for golden in dataset.evals_iterator():
    run_agent(golden.input)
```

</Tab>
</Tabs>

### Wrap an AgentCore invocation in `@observe`

When the AgentCore app is part of a larger operation, decorate the outer function with `@observe`. AgentCore spans nest under your observed span automatically.

```python title="agentcore_agent.py" showLineNumbers
from deepeval.tracing import observe
...

@observe(name="respond_to_user")
def respond_to_user(prompt: str) -> str:
    response = invoke({"prompt": prompt})
    return response["result"]
```

## API reference

`instrument_agentcore(...)` accepts the following trace-level kwargs. Each one is a default; runtime calls always win.

| Kwarg               | Type        | Description                                                                |
| ------------------- | ----------- | -------------------------------------------------------------------------- |
| `name`              | `str`       | Default trace name. Override at runtime via `update_current_trace`.        |
| `thread_id`         | `str`       | Default thread identifier. Useful for grouping conversational turns.       |
| `user_id`           | `str`       | Default actor identifier. Override per-request via `update_current_trace`. |
| `metadata`          | `dict`      | Default trace metadata. Merged with runtime overrides; runtime wins.       |
| `tags`              | `list[str]` | Default tags applied to every trace produced by this app.                  |
| `environment`       | `str`       | One of `"development"`, `"staging"`, `"production"`, `"testing"`.          |
| `metric_collection` | `str`       | Default metric collection applied at the trace level.                      |

For runtime helpers (`update_current_trace`, `update_current_span`, `next_agent_span`, `next_llm_span`) and the test-decorator surface (`@observe`, `@assert_test`, `with trace(...)`), see the [tracing reference](/docs/evaluation-llm-tracing).

## FAQs

<FAQs
  qas={[
    {
      question: "Can I score a specific sub-agent running inside AgentCore?",
      answer: (
        <>
          Yes. Stage a metric with{" "}
          <code>with next_agent_span(metrics=[...])</code> right before the
          invocation — it lands on the next AgentCore agent span, so a single
          sub-agent step is scored in isolation without touching the others. Use{" "}
          <code>next_llm_span(...)</code> to target a model step instead.
        </>
      ),
    },
    {
      question: "Can I monitor an AgentCore app once it's deployed to production?",
      answer: (
        <>
          Yes. Pass <code>environment="production"</code> to{" "}
          <code>instrument_agentcore(...)</code> and, when logged into Confident
          AI, your live AgentCore traces stream in for{" "}
          <a href="https://www.confident-ai.com/docs/llm-tracing/online-evals">
            online evals
          </a>{" "}
          on real traffic.
        </>
      ),
    },
    {
      question: "Can I wire AgentCore evals into a CI/CD pipeline?",
      answer: (
        <>
          Yes. Call <code>instrument_agentcore()</code> once, then run your app
          inside a parametrized <code>pytest</code> test with{" "}
          <code>assert_test(...)</code> and <code>deepeval test run</code> so a
          failing metric breaks the build.
        </>
      ),
    },
    {
      question: "Where do these traces and scores show up beyond my terminal?",
      answer: (
        <>
          Run <code>deepeval login</code> and{" "}
          <a href="https://www.confident-ai.com">Confident AI</a> renders the
          same AgentCore traces — agent spans, LLM spans, tool calls — and their
          scores in a shared cloud UI. It's optional; everything runs locally
          without it.
        </>
      ),
    },
  ]}
/>
