DEV Community

Cover image for Trust, Discovery, Context, Consistency: What AWS DevOps Agent Looks For in a Telemetry Layer
Patrick Londa for Bronto

Posted on • Originally published at bronto.io

Trust, Discovery, Context, Consistency: What AWS DevOps Agent Looks For in a Telemetry Layer

Authored by Stephen Crowley

AWS DevOps Agent was built for modern software environments: engineering teams running heterogeneous infrastructure, juggling multiple observability tools, and without enough bandwidth to manually investigate every service incident. Its GA announcement highlighted reductions in mean time to resolution from two hours to thirty minutes — but that kind of improvement only happens when the underlying telemetry data is clean, structured, and fast to query.

This is where Bronto comes in. Not as a point integration built specifically for AWS DevOps Agent, but as a telemetry layer designed from the ground up around the principles that make any AI-driven investigation successful: trust in every result, discovery before every query, full context in every response, and consistent behaviour across every dataset.

We put this to the test directly. In a side-by-side evaluation, the same system — the OpenTelemetry demo application — sent data simultaneously to Bronto, Elasticsearch/Lucene (via OpenSearch), and Grafana Loki, all exposed via their respective MCP integrations. AWS DevOps Agent ran the same incident investigation against all three, then we asked it to judge.

We gave it a deliberately neutral prompt, designed to let the agent reach its own conclusions based on what it had just experienced:

Compare Bronto, Elasticsearch/OpenSearch, and Grafana Loki for use by AI agents (like AWS DevOps Agent) performing incident investigation. Focus the comparison on what matters for AI agents, NOT human usability. Include Recommendations for AWS DevOps Agent. Do NOT focus on: Query syntax complexity (AI handles nested JSON fine), Human readability of responses, Nesting depth.

The prompt explicitly told the agent to ignore the things Bronto is often praised for by humans — simple query syntax, flat response structure, readable output. We wanted to know what mattered to the agent itself, working from its own experience querying all three systems during a live investigation.

Its verdict was unambiguous. For incident investigation, Bronto is the clear winner. The evaluation identified four factors that actually determine whether an AI agent can investigate reliably:

  • Silent failure risk — Does the system fail quietly with wrong results?
  • Data discovery — Can the agent explore before querying?
  • Response completeness — Does one query return enough context?
  • Deterministic behavior — Does the same query always produce the same behavior?
System AI Agent Suitability Primary Issue
Bronto High (9.4/10) None significant
Elasticsearch/Lucene Medium (5.3/10) Silent failures from case/mapping issues
Grafana Loki Low (4.6/10) Limited discovery, minimal context

1. Silent Failure Risk

The most critical factor for AI agents is not whether they can express a query — it's whether they can trust the result. When a query returns empty results, the agent must decide: is there genuinely no matching data, or did the query fail silently for some other reason? Unlike a human analyst who might notice something feels off and try a different approach, an AI agent treats an empty result as ground truth and continues its investigation on that basis.

Elasticsearch/Lucene uses case-sensitive keyword field matching by default. A filter written as { "term": { "service.name": "Checkout" } } returns zero results if the indexed value is checkout — and it does it silently, with no error. The same silent failure applies to wrong field types (.keyword vs analyzed), fields that exist in the data but weren't mapped at index setup time, and any variation in field naming between indices. The agent concludes "no data" when the reality is "query did not match."

Loki has fewer mapping complexities, but labels are case-sensitive and log content grep is case-sensitive by default. An agent querying {service_name="Checkout"} when the label value is checkout gets zero results with no signal that the label exists with a different casing.

Bronto returns a clear error for unknown field names. Its ILIKE operator handles any casing — $service.name = 'Checkout' matches checkout, CHECKOUT, and any other variant. When Bronto returns empty results, the agent can treat that as a trustworthy signal that no matching data exists.

Failure Type Bronto Elasticsearch/Lucene Loki
Wrong case in query value Still works (ILIKE) Silent empty result Silent empty result
Wrong case in field name Clear error Silent empty result Silent empty result
Field doesn't exist Clear error Silent empty result Silent empty result
Wrong field type (.keyword vs analyzed) N/A (consistent) Silent wrong results N/A
Typo in field name Clear error Silent empty result Silent empty result

In our evaluation, Elasticsearch/Lucene returned zero events for every error and warning query across cart, payment, and ad services — even though the events existed in the index. An agent relying on those results would have proceeded under the conclusion that no active errors existed, while multiple services were actively failing. Bronto returned all of them.

System When Query Returns Empty Agent Concludes Actually Correct?
Bronto No matching data exists "No errors found" Likely correct
Elasticsearch/Lucene Unknown reason "No errors found" May be wrong
Loki Unknown reason "No errors found" May be wrong

2. Data Discovery

Can the agent explore what data exists and verify a query will work before it runs it? This determines whether the agent enters an investigation with confidence or proceeds on assumptions that may silently be wrong.

Bronto treats discovery as a first-class workflow — and critically, it works across all data in Bronto, not just OpenTelemetry. The agent calls get_datasets() to get a named catalogue of every dataset. It calls get_keys(log_id="checkout") to list all searchable fields. It calls get_key_values(log_id="checkout", key="$severity_text") to enumerate the actual values present — ["INFO", "ERROR", "WARN"]. By the time the agent issues its first search query, it knows the field exists, knows what values are in it, and can form that query with confidence.

Elasticsearch/Lucene's discovery path is complex and unreliable. GET _cat/indices returns a raw index list. GET logs-checkout-*/_mapping returns hundreds of lines of nested JSON that describe field types but not values. To discover what values actually exist in a field, the agent must run a separate aggregation query — and must already know whether the field is .keyword or analyzed to construct it correctly.

Loki's discovery is limited to labels. list_loki_label_names() typically returns three or four labels: pod, service_name, stream. There is no way to discover the fields inside log content.

Capability Bronto Elasticsearch/Lucene Loki
List available datasets get_datasets (all sources) _cat/indices (raw list) Via labels only
List searchable fields get_keys (simple list) _mapping (complex nested JSON) Labels only (3–5 fields)
List field values get_key_values Terms aggregation query Label values only
Verify field exists before query Easy, explicit Must parse mapping Cannot for log content
Works across non-OTEL data sources Yes Varies by index config Not applicable
Zero-knowledge start Yes No No

3. Response Completeness

Does a single query return enough context for the agent to understand what happened, identify impact, and decide what to investigate next?

Bronto returns row-based dense JSON where every event is a self-describing object. A single error event reliably includes service name, service version, pod name, node name, namespace, trace ID, and user ID:

{
  "@raw": "exporter export timeout: rpc error: code = Unavailable",
  "@time": "2026-05-04 11:36:56.845 UTC",
  "@status": "error",
  "attributes": {
    "$service.name": "checkout",
    "$service.version": "2.2.0",
    "$k8s.pod.name": "checkout-f86478f-xgclj",
    "$k8s.node.name": "ip-192-168-46-190.ec2.internal",
    "$trace_id": "367379916186c4759c7c226a0350408f",
    "$user_id": "dea7494c-4867-11f1-ab4e-6edbf8c21e43"
  }
}
Enter fullscreen mode Exit fullscreen mode

In our live test, Bronto found 15 real error events in 3 queries, with root cause visible immediately: high memory usage causing exporter timeouts. Trace IDs were present on every event, allowing the agent to pivot to trace correlation without an additional query.

Elasticsearch/Lucene's response completeness depends entirely on what was mapped when the index was created. The response also wraps every hit in metadata (_index, _id, _score, _shards) that the agent must parse past before reaching the actual log data.

Loki returned zero application errors in the same test — only synthetic canary logs. Where it does return results, all structured context is embedded inside the raw log line string.

Context Needed Bronto Elasticsearch/Lucene Loki
Error message @raw field message field Log line
Service name $service.name If mapped If label exists
Service version $service.version If mapped Rarely available
Pod / node name Indexed If mapped Rarely available
Trace ID $trace_id If mapped Must parse log line
User ID $user_id If mapped Must parse log line
Timestamp Human-readable ISO format Nanoseconds
Investigation Task Bronto Elasticsearch/Lucene Loki
Find error + full context 1 query 1–3 queries 3–5+ queries
Correlate with trace 0 additional (trace_id included) 0–1 additional 2+ additional
Identify user impact 0 additional (user_id included) 0–1 additional 2+ additional
Find related logs by trace 1 query 1 query 5+ queries (per service)

4. Deterministic Behavior

Does the agent's investigative workflow work consistently across different services and datasets, or must it learn a different approach for each index it encounters?

What is consistent in Bronto is the workflow the agent uses to deal with variation. Discovery is the deterministic part: get_datasets() always returns the catalogue, get_keys(log_id) always returns the actual fields present in that dataset, and get_key_values(log_id, key) always returns the actual values. That same three-call pattern works identically for an OTEL dataset, a syslog dataset, or a custom JSON ingest:

# OTEL service
get_keys("checkout") → [$service.name, $severity_text, $trace_id, ...]
search: "$severity_text ILIKE 'error'"

# Custom JSON ingest with a different schema
get_keys("legacy-app") → [app_name, log_level, request_id, ...]
search: "log_level ILIKE 'error'"

# Syslog
get_keys("network-devices") → [host, facility, severity, message, ...]
search: "severity ILIKE 'err' OR severity ILIKE 'crit'"
Enter fullscreen mode Exit fullscreen mode

The fields differ but the workflow does not — so the agent never has to guess.

Elasticsearch/Lucene cannot make this guarantee. There is no enforced standard for field naming — one index might store service name as service.name.keyword, another as serviceName (analyzed), another as svc. Two indices with the same conceptual data can require materially different agent logic.

Aspect Bronto Elasticsearch/Lucene Loki
Discovery workflow Same 3 calls for any dataset _mapping shape varies Labels-only
Field types Consistent within a dataset Varies by mapping N/A
Query behavior Predictable Depends on analyzers Depends on labels
Cross-dataset patterns Discover then query — works for any source Per-index logic likely needed Per-stream logic likely needed

What Each Platform Was Built For

Each of these platforms reflects the use case it was designed for — and none of them, except Bronto, were designed with AI agents in mind.

Elasticsearch/Lucene was built around a human at a dashboard. The Query DSL is JSON because it was meant to be assembled by Kibana on behalf of an analyst clicking through visualisations. Hand the same API to an agent, and the assumptions invert: silent case-sensitivity, mapping-dependent matching, and relevance ranking that buries rare events become failure modes.

Grafana Loki was built around a single bet on cost — index only labels, store everything else as opaque log lines, charge less than indexed alternatives. The same minimalism that keeps Loki cheap keeps it opaque to an agent that needs to discover fields, correlate by trace ID, or filter on high-cardinality attributes. The cost argument has weakened independently: Loki Cloud lists $0.50/GB ingested versus Bronto's $0.10/GB.

Bronto was built for AI agent investigation from the ground up. Discovery as a first-class workflow, case-insensitive matching by default, dense self-describing JSON responses, deterministic discovery patterns across heterogeneous data sources.

Factor Weight Bronto Elasticsearch/Lucene Loki
Silent failure risk 30% 9/10 4/10 5/10
Data discovery 25% 10/10 5/10 4/10
Response completeness 20% 10/10 7/10 3/10
Deterministic behavior 15% 9/10 5/10 5/10
Query simplicity 10% 9/10 6/10 7/10
Weighted total 9.4/10 5.3/10 4.6/10

Connecting Bronto to AWS DevOps Agent is straightforward: deploy Bronto's hosted MCP server, route your telemetry to it, and authorise the agent. Both are built on the same open standards, so it works the first time, with no custom skills or pre-configuration.


See It in Action

We recorded AWS DevOps Agent running a live incident investigation against Bronto — no pre-configuration, no custom skills, just the hosted MCP server connected and pointed at the OpenTelemetry demo.

The investigation completed in under a minute. Once started, the agent generated its investigation plan within eight seconds. Within sixty seconds, it had queried Bronto, identified that the ad service was experiencing a high error rate, followed the statement IDs to the specific log lines causing failures, and surfaced the symptom: ads were failing to load. After further analysis — scanning the full codebase and correlating what Bronto's logs revealed — the agent returned its root cause: the adFailure feature flag was controlled entirely by an external flagd service.

From alert to root cause, with the data layer doing exactly what it was designed to do.

Set Up AWS DevOps Agent with Bronto

Top comments (0)