DEV Community

Taylor Wang
Taylor Wang

Posted on

The Alert Storm Got Quieter After I Added a Free Model. Then the Silence Nearly Cost Me the Server.

If you've ever silenced a noisy alert channel because it interrupted your sleep for a false alarm, you know the real problem isn't the notification itself — it's the trust you lose in every subsequent ping. I wanted to see whether a free model could restore some of that trust by acting as a first-pass triage layer for the alert stream coming off a free server. For 48 hours, I pointed every incident webhook at a small Python service that asked MonkeyCode's free model to label each alert as critical, watch, or ignore. Disclosure: This article was prepared as part of MonkeyCode's product outreach.

The Setup

I used MonkeyCode's free model tier for the classifications and its free server option to host the receiving endpoint, which kept the experiment cost at zero while still forcing me to deal with real network latency and cold starts. The receiver was a tiny Python HTTP server that parsed the alert JSON, stored the raw payload in SQLite, called the model with a strict prompt, and stored the model's answer next to the raw one. Only alerts labeled critical were forwarded to a separate notification webhook; watch went into a daily digest, and ignore stayed in the database for later inspection.

Why not just write deterministic rules? Because my existing rules were a patchwork of thresholds and substring matches that still produced about three false alarms per night. I wanted to see if a model could handle the ambiguous 20% that rules get wrong.

The Prompt and the Parse

The entire experiment hinged on the prompt, so I kept it explicit and gave the model a clear escape hatch. Here's the prompt template I used:

ALERT_PROMPT = """You are triaging alerts from a small web service.
Return ONLY JSON: {"label": "critical|watch|ignore", "reason": "short phrase"}.

Rules:
- critical means the service is down, data is at risk, or storage is full.
- watch means possible problem, needs a human in the next few hours.
- ignore means false alarm, routine event, or known noise.
- When uncertain, choose watch, never ignore.

Alert:
{alert}
"""
Enter fullscreen mode Exit fullscreen mode

I also wrote a parser that assumed the worst about the model's response, because text wrapping or a stray sentence can silently break any JSON pipeline. The parser falls back to watch on any parsing failure, which matches the rule that uncertain alerts should be visible.

import json

def parse_label(response_text: str) -> dict:
    start = response_text.find("{")
    end = response_text.rfind("}") + 1
    if start == -1 or end == 0:
        return {"label": "watch", "reason": "unparseable response"}
    try:
        return json.loads(response_text[start:end])
    except json.JSONDecodeError:
        return {"label": "watch", "reason": "invalid JSON"}
Enter fullscreen mode Exit fullscreen mode

The fallback to watch turned out to be the most important line in the whole service, because it converted every model hiccup into a visible alert instead of a silent one.

48 Hours in Three Scenes

Hour 14: The Cleanup Trap

During the first fourteen hours, the model's labels agreed with my own spot checks on 37 out of 41 alerts, which felt like a win. Then a disk usage alert arrived that mentioned a cleanup job, and the model labeled it ignore with the reason "scheduled housekeeping, known noise." The cleanup job had actually hung for forty minutes, and the disk crossed 94% before anything else woke me up. That was the first sign that the model was reading description words as if they were system state.

Hour 27: The Clock Drift Incident

The second day introduced a failure I hadn't predicted: my free server's clock drifted, and a maintenance window check I'd hardcoded into the prompt began matching nearly every alert. For six hours the model returned ignore for everything, including a 503 spike and an out-of-memory kill, because the prompt text said "inside maintenance window." The notification channel went completely quiet, and it took an hour of manual poking before I realized the silence itself was the alarm.

Hour 46: The Unparseable Response

Late on the second night the model returned a perfectly formatted JSON object wrapped in a fenced code block, and my parser extracted it correctly, but the label was watch for an alert that had already self-resolved. I checked the raw data and saw that the model had added a newline inside the JSON string, which my json.loads handled fine. The lesson there was less about parsing and more about trust: I needed to review the watch bucket too, not just critical.

The Evaluation Script

To make the experiment reproducible, I kept every raw alert and every model label in a SQLite table named labels. After the 48 hours, I manually reviewed all 214 alerts and marked a human_label column, then ran a small script that printed agreement and a confusion matrix:

import sqlite3
from collections import Counter

conn = sqlite3.connect("triage.db")
rows = conn.execute("SELECT model_label, human_label FROM labels").fetchall()

total = len(rows)
agree = Counter((m, h) for m, h in rows if m == h)
acc = sum(agree.values()) / total
print(f"accuracy: {acc:.2%}, n={total}")

paid = [((m, h), c) for (m, h), c in Counter((m, h) for m, h in rows if m != h).items()]
print("confusion:", dict(paid))
Enter fullscreen mode Exit fullscreen mode

The final numbers were an 81% accuracy, which sounds decent until you look at where the errors landed. Two of the three critical alerts were marked ignore, and both of those would have let the server run out of disk or memory.

What I'd Repeat and What I'd Change

I would absolutely repeat the practice of logging everything before deleting anything, because the SQLite table let me answer questions hours later without replaying the server's memory. I would also repeat the strict prompt rule that forces the model to choose watch instead of ignore when uncertain, because that rule probably prevented a dozen silent false negatives.

I would change two things: first, I'd add a second, purely deterministic guard that alerts immediately when the notification stream goes silent for more than ninety minutes. Second, I'd feed the model a cleaned numeric summary instead of the raw alert text, so it can't be fooled by words like "cleanup" or "scheduled." The experiment proved the model can filter noise, but it also proved that filters create new failure modes.

Limitations and Who Shouldn't Use This

This approach is not production-ready for anything that generates real revenue, safety, or compliance obligations. Free model endpoints come with latency, rate limits, and non-deterministic output, and my 48-hour window is far too short to measure how often the model drifts over weeks. You also need a human who is willing to audit labels regularly; without that audit, the classifier will quietly learn your team's worst assumptions.

I would not recommend this pattern for systems where a missed critical alert has a direct financial or legal cost, or for teams that lack the discipline to review confusion matrices. If all you need is to get a usable signal out of a noisy alert channel for an internal tool, then a free model plus a free server is honestly a thrilling way to spend a weekend. Just remember that the silence you're buying is only as trustworthy as the labels you actually check.

Top comments (0)