Build an AI Observability Alert Noise Reducer with Claude API
Alert fatigue is a data problem before it's a culture problem — most teams have never actually measured which alerts fire often and get dismissed. Build a tool that analyzes historical alert-to-resolution patterns with Claude API and recommends specific tuning, not generic advice.
"We have alert fatigue" is a common complaint with an uncommonly specific fix available: your alerting system already has the data to show exactly which alerts fire without leading to action, and by how much they should be adjusted. This tool turns Alertmanager's own history into concrete, per-alert tuning recommendations instead of a vague team retro about "being better about alerts."
Setup
pip install anthropic requestsAlert History Analyzer
import anthropic
import requests
from collections import defaultdict
from datetime import datetime, timedelta
client = anthropic.Anthropic()
def get_alert_history(days: int = 90) -> list[dict]:
"""Pull historical alert firing/resolution data from Alertmanager."""
resp = requests.get(
"http://alertmanager:9093/api/v2/alerts/history",
params={"since": (datetime.utcnow() - timedelta(days=days)).isoformat()}
)
return resp.json()
def compute_alert_stats(history: list[dict]) -> dict:
"""Aggregate per-alert-rule firing frequency, duration, and — critically —
whether it ever led to a PagerDuty acknowledgment vs auto-resolved unattended."""
stats = defaultdict(lambda: {"fire_count": 0, "durations": [], "was_acked": []})
for event in history:
rule = event["labels"]["alertname"]
stats[rule]["fire_count"] += 1
stats[rule]["durations"].append(event.get("duration_seconds", 0))
stats[rule]["was_acked"].append(event.get("acknowledged", False))
return dict(stats)Cross-Referencing With Actual Action Taken
def get_incident_correlation(alert_rule: str, days: int = 90) -> dict:
"""Check PagerDuty history — did this alert ever correlate with an
incident that got a real human response, or does it always
auto-resolve with nobody looking at it?"""
resp = requests.get(
"https://api.pagerduty.com/incidents",
headers={"Authorization": f"Token token={PAGERDUTY_TOKEN}"},
params={"since": (datetime.utcnow() - timedelta(days=days)).isoformat()}
)
incidents = resp.json()["incidents"]
matching = [i for i in incidents if alert_rule in i.get("title", "")]
return {
"total_incidents": len(matching),
"resolved_by_human_action": len([i for i in matching if i.get("resolve_reason") == "manual"]),
"auto_resolved": len([i for i in matching if i.get("resolve_reason") == "timeout"]),
}Tuning Recommendations With Claude
import json
ANALYZE_PROMPT = """Analyze this alert rule's firing history and recommend
specific tuning.
Alert rule: {rule_name}
Current threshold config: {current_config}
Firing statistics (last 90 days):
- Total fires: {fire_count}
- Average duration before auto-resolve: {avg_duration}s
- Percentage that were acknowledged by a human: {ack_rate}%
Incident correlation:
- Times this led to a PagerDuty incident: {total_incidents}
- Of those, resolved by actual human action (not auto-timeout): {resolved_by_human}
Determine:
1. Is this alert too sensitive (fires often, rarely acknowledged, rarely
leads to real action) — recommend a specific threshold/duration adjustment
2. Is this alert well-tuned (fires rarely, but when it does, leads to
real action) — leave alone, flag as healthy
3. Is this alert potentially UNDER-sensitive (rarely fires, but incident
history suggests real problems happened without it firing) — recommend
tightening
Respond with ONLY valid JSON:
{{"verdict": "too_noisy" | "well_tuned" | "under_sensitive",
"recommended_change": "specific config change or null",
"reasoning": "..."}}"""
def analyze_alert(rule_name: str, current_config: dict, stats: dict, correlation: dict) -> dict:
ack_rate = (sum(stats["was_acked"]) / len(stats["was_acked"]) * 100) if stats["was_acked"] else 0
avg_duration = sum(stats["durations"]) / len(stats["durations"]) if stats["durations"] else 0
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=500,
messages=[{
"role": "user",
"content": ANALYZE_PROMPT.format(
rule_name=rule_name, current_config=current_config,
fire_count=stats["fire_count"], avg_duration=round(avg_duration, 1), ack_rate=round(ack_rate, 1),
total_incidents=correlation["total_incidents"],
resolved_by_human=correlation["resolved_by_human_action"],
)
}]
)
text = response.content[0].text.strip()
if text.startswith("```"):
text = text.split("```")[1].replace("json", "", 1).strip()
return json.loads(text)Example Output
Alert Noise Report — Last 90 Days
TOO NOISY (6 rules):
- HighMemoryUsage: fired 340 times, 4% acknowledgment rate, 0 incidents
led to human action. Recommendation: raise threshold from 80% to 90%
memory utilization, and require sustained breach for 15min (currently
5min) — the current config is catching normal GC-related memory sawtooth
patterns, not real problems.
- SlowQueryWarning: fired 210 times, 8% acknowledgment rate. Recommendation:
this fires on the reporting service's known-slow analytical queries —
either exclude that service from this rule or raise the threshold
specifically for it.
WELL TUNED (12 rules): no changes recommended
UNDER-SENSITIVE (1 rule):
- DatabaseReplicationLag: fired only 2 times in 90 days, but incident
history shows 3 separate replication-lag-caused incidents where this
alert never fired. Recommendation: lower the lag threshold from 60s
to 20s based on the incident pattern — the current threshold is too
permissive to catch the lag levels that actually caused problems.
Why This Beats a Generic "Reduce Alert Fatigue" Initiative
The recurring failure of alert-fatigue cleanup efforts is that they're vague — "let's review our alerts" turns into an unstructured meeting nobody prepares real data for. This tool converts that into a ranked, evidence-backed list: exactly which rules to change, by how much, and why — grounded in your own system's actual firing and resolution history, not generic alerting best practices that may not fit your specific traffic patterns.
More AI observability tooling? Read our Build AI log pattern classifier with Claude API and Prometheus Grafana monitoring guide.
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Why Agentic AI Will Kill the Traditional On-Call Rotation by 2028
60% of enterprises now use AIOps self-healing. 83% of alerts auto-resolve without humans. The era of 2 AM PagerDuty wake-ups is ending. Here's what replaces it.
Build an AI Log Pattern Classifier with Claude API
Build a production-ready log pattern classifier using Claude API that automatically categorizes log lines into errors, warnings, anomalies, and noise — saving on-call engineers hours of manual log triage.
How to Set Up AIOps-Powered Alerting with Grafana Machine Learning in 2026
Step-by-step guide to setting up Grafana's machine learning features for anomaly detection, predictive alerting, and intelligent noise reduction. Stop alert fatigue with AI.