AWS Bedrock AgentCore
Amazon Bedrock AgentCore is AWS’s managed platform for deploying and operating agents built with any framework — Strands Agents, LangGraph, CrewAI, Google ADK, the OpenAI Agents SDK, or your own code. AgentCore Runtime wraps your agent in the AWS Distro for OpenTelemetry (ADOT) and sends its spans to CloudWatch by default; you point those same spans at Neens with a few environment variables and zero Neens-specific code.
This page covers two AWS products:
- AgentCore Runtime — you write the agent (most often with Strands) and AgentCore hosts it.
- Amazon Bedrock Agents — the fully managed agents you configure in the Bedrock console and call with
invoke_agent.
At a glance
| Language | Python |
| Instrumentation | Your framework’s OpenTelemetry instrumentation (for Strands: built-in StrandsTelemetry + openinference-instrumentation-strands-agents); for Bedrock Agents: openinference-instrumentation-bedrock |
| Endpoint | POST /v1/traces (OTLP/HTTP) |
| Auth | Authorization: Bearer nk_live_… agent key |
| AgentCore setting | Turn off AgentCore’s built-in ADOT tracer (DISABLE_ADOT_OBSERVABILITY=true) |
AgentCore Runtime
The example uses Strands Agents, AgentCore’s default framework. For any other framework, keep steps 2 and 4 and swap step 1 for that framework’s quickstart (LangGraph, CrewAI, Google ADK, OpenAI Agents SDK, …).
Add the instrumentation to your agent’s dependencies
bedrock-agentcore
strands-agents[otel]
openinference-instrumentation-strands-agentsInstall your own tracer in the entrypoint
Set up telemetry at module load, before the app starts serving. AgentCore passes each invocation’s runtime session id on the request context — stamp it on the agent’s trace as session.id so every turn of one AgentCore session lands in one Neens conversation.
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from openinference.instrumentation.strands_agents import StrandsAgentsToOpenInferenceProcessor
from strands import Agent
from strands.telemetry import StrandsTelemetry
# Reads OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_EXPORTER_OTLP_HEADERS (set in step 4).
telemetry = StrandsTelemetry()
telemetry.setup_otlp_exporter()
telemetry.tracer_provider.add_span_processor(StrandsAgentsToOpenInferenceProcessor())
app = BedrockAgentCoreApp()
@app.entrypoint
def invoke(payload, context):
agent = Agent(trace_attributes={"session.id": context.session_id})
result = agent(payload.get("prompt", ""))
return {"result": str(result)}
if __name__ == "__main__":
app.run()Turn off AgentCore’s built-in tracer
By default AgentCore Runtime launches your agent under ADOT, which registers its own tracer provider and exports to CloudWatch. Turn that off so your tracer from step 2 is the one that runs:
In agentcore.json, on your agent’s entry in runtimes:
"instrumentation": { "enableOtel": false }Point the exporter at Neens and deploy
Set these environment variables on the runtime. DISABLE_ADOT_OBSERVABILITY=true unsets the ADOT defaults AgentCore would otherwise inject; the OTEL_* variables aim the exporter at Neens. Neens accepts OTLP over HTTP (protobuf or JSON), not gRPC, so keep the protocol at http/protobuf.
DISABLE_ADOT_OBSERVABILITY=true
OTEL_EXPORTER_OTLP_ENDPOINT=https://<your-neens-host>
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer nk_live_your_key_hereAdd them to the agent’s envVars in agentcore.json ([{ "name": "DISABLE_ADOT_OBSERVABILITY", "value": "true" }, …]), then run agentcore deploy.
agentcore.json is usually committed to source control. Don’t commit your nk_live_… key there — inject OTEL_EXPORTER_OTLP_HEADERS from your deployment pipeline’s secret store instead.
Invoke your agent
Invoke the runtime as usual (agentcore invoke, or invoke_agent_runtime from boto3). Reuse the same runtimeSessionId across the turns of a conversation, and Neens groups those turns into one session.
Neens or CloudWatch, not both. DISABLE_ADOT_OBSERVABILITY=true sends your agent’s spans to Neens instead of CloudWatch GenAI Observability. AgentCore’s own service metrics (invocations, latency, errors) still land in CloudWatch. To keep both trace destinations, run an OpenTelemetry Collector that fans out to each.
Amazon Bedrock Agents
For agents you build in the Bedrock console and call through the bedrock-agent-runtime client, instrument the boto3 client. The OpenInference Bedrock instrumentor traces invoke_agent, plus invoke_model and converse calls.
Install the instrumentation
pip install openinference-instrumentation-bedrock opentelemetry-sdk opentelemetry-exporter-otlpPoint the exporter at Neens
from openinference.instrumentation.bedrock import BedrockInstrumentor
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
exporter = OTLPSpanExporter(
endpoint="https://<your-neens-host>/v1/traces",
headers={"Authorization": "Bearer nk_live_your_key_here"},
)
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
# Instrument once, BEFORE you create the boto3 client.
BedrockInstrumentor().instrument(tracer_provider=provider)The OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_EXPORTER_OTLP_HEADERS env vars work here too, exactly as in the other quickstarts.
Invoke your agent with tracing on
Pass enableTrace=True so Bedrock returns the agent’s orchestration trace — the reasoning steps, model calls and action-group (tool) calls — which the instrumentor turns into child spans:
import boto3
client = boto3.client("bedrock-agent-runtime")
response = client.invoke_agent(
agentId="<AgentId>",
agentAliasId="<AgentAliasId>",
sessionId="conv-42",
inputText="What's my order status?",
enableTrace=True,
)
for event in response["completion"]:
if "chunk" in event:
print(event["chunk"]["bytes"].decode())Read the whole completion stream — the trace events arrive on it, so the instrumentor can only record the steps you consume.
What Neens captures
On AgentCore Runtime, Neens receives whatever your framework’s instrumentation emits — for Strands, every agent invocation, event-loop cycle, model call (model, token counts, prompt/response messages) and tool call. For Bedrock Agents, the instrumentor emits the invoke_agent call plus a span per orchestration step, model invocation and action-group call. See Traces & sessions for how turns group into conversations.
Neens speaks standard OTLP — this is not a Neens SDK fork. See Send traces for the full attribute reference, size limits, and response codes.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Traces show up in CloudWatch but not Neens | AgentCore’s ADOT tracer is still active | Check DISABLE_ADOT_OBSERVABILITY=true is set and the built-in instrumentation is turned off (step 3), then redeploy. |
| Exporter errors about the connection or protocol | The exporter is using gRPC (some frameworks default to it) | Set OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf. |
401 in the agent’s logs | Header value malformed | OTEL_EXPORTER_OTLP_HEADERS must be exactly Authorization=Bearer nk_live_… — no quotes inside the value. |
| Each turn is a separate conversation | No session id on the trace | Set session.id from context.session_id (step 2) and reuse runtimeSessionId across turns. |
Verify
Open Traces in Neens; your agent invocations appear within a few seconds. Continue to Traces & sessions.