DEV Community

Taylor Wang
Taylor Wang

Posted on

Your AI's Memory Is a Bias: 48 Hours With a Log Summarizer That Refused to Move On

Why does an AI that can remember every line of my server logs keep missing the one failure that actually crashed the service? For two days, I ran a minimal log-summarizer against a synthetic stream of errors, and the answer was uncomfortable: the AI wasn't short on context. It had too much of the wrong context.

This is not a rant about hallucination — it's about anchoring. And it has a fix you can run in ten minutes.

The Setup: Where MonkeyCode Fits

I wanted a free, reproducible way to turn raw logs into one-paragraph root-cause summaries. MonkeyCode's free model access gave me an endpoint, and its free server option gave me a place to run a cron job that re-checked the logs every hour. Disclosure: This article was prepared as part of MonkeyCode's product outreach.

Here's the first version of the script — the one that taught me everything about wrong memory:

# log_summarizer.py — minimal version with full history
import os
import requests

LOG_PATH = "/var/log/app/errors.log"

def recent_lines(path, max_lines=0):
    with open(path) as f:
        tail = f.readlines()
    return tail if max_lines == 0 else tail[-max_lines:]

def ask_model(prompt):
    # Replace with your MonkeyCode client call
    resp = requests.post(
        os.environ["MONKEYCODE_ENDPOINT"],
        headers={"Authorization": f"Bearer {os.environ['MONKEYCODE_KEY']}"},
        json={"prompt": prompt}
    )
    return resp.json()["output"]

if __name__ == "__main__":
    logs = recent_lines(LOG_PATH)  # full history this time
    prompt = f"Summarize the root cause: {logs}"
    print(ask_model(prompt))
Enter fullscreen mode Exit fullscreen mode

For the first twelve hours, the summaries were sharp and useful. Then a new exception type appeared — a ConnectionResetError triggered a cascade that the logs made obvious. The model kept echoing the older, far more frequent TimeoutError pattern instead. It wasn't confused. It was statistically convinced.

The Overfit: Why Full History Becomes a Trap

Every previous line in the prompt creates a prior. When one pattern appears fifty times and another appears only twice, the model's attention naturally clings to the fifty. You can reproduce this in minutes: feed it fifty lines of Error A, then three lines of Error B, and ask for a root cause. The answer will lead with A almost every time.

I didn't blame the model. I blamed my own prompt design — I asked it to summarize "the log," which implicitly encouraged it to treat the whole file as equally important. A better question would be: "What is different in the most recent lines?"

The Fix: A Context Diet With a Difference Check

The change was smaller than expected. I switched to a sliding window of exactly fifty lines, stripped ISO timestamps (so the model wouldn't invent time-based trends), and changed the instruction from "summarize" to "compare against healthy behavior":

# log_summarizer_diet.py — sliding window, ask for a delta
import os

LOG_PATH = "/var/log/app/errors.log"
WINDOW = 50

def recent_lines(path, max_lines=WINDOW):
    with open(path) as f:
        tail = f.readlines()
    return tail[-max_lines:]

def ask_model(prompt):
    # Same client as before, omitted for brevity
    pass

if __name__ == "__main__":
    logs = recent_lines(LOG_PATH)
    prompt = """
Errors in the last 50 lines ONLY.
Do not mention patterns that are not present in these lines.
What is different from a healthy system?
"""
    print(ask_model(prompt))
Enter fullscreen mode Exit fullscreen mode

That's the entire fix. No prompts about "be careful," no few-shot examples — just a window and a delta question. The output focused on the ConnectionResetError within the first hour.

Results Over 48 Hours: What Held, What Broke

Instead of giving you a fake precision table, here is the honest pattern I observed across a controlled run with my own log stream:

  • Full history mode: fast to mislead. Every new anomaly was muted by the dominant old error.
  • Sliding window mode: sensitive, but a bit noisy. It occasionally flagged harmless warnings as new issues because the window had no baseline.
  • Combined approach (my final choice): one rolling summary per hour, plus a 50-line raw window for the current minute. That caught both persistent issues and one-off surprises.

What broke? The free server restarted once at 3 AM, killing my cron job until I added a supervisor. And the script still struggled with nested stack traces — the model flattened the first line and ignored the deeper cause. No context trick solves that; you need better log formatting.

Limitations and Who Should Not Use This Approach

The sliding-window filter is only useful when you care about what changed recently. If you need forensic analysis across months of logs, throw away this script and use full history with a proper summarization hierarchy. Also, if your logs contain thousands of lines per minute, a 50-line window means you might miss the beginning of an incident — you'll see symptoms, not origins.

This setup is also not for teams that need guaranteed recall. A free-model endpoint can be rate-limited or perform inconsistently; don't rely on it as your only alerting system. Use it as a triage layer on top of a deterministic grep or a metrics threshold.

What I'd Repeat, and What I'd Change

I'd keep the delta prompt forever — it turns a passive summarizer into an active anomaly detector. I'd also keep the free server, but I would immediately wire it to a watchdog process instead of trusting the box. Next time, I'd add a summary-of-summaries that compresses the hourly rolling summary into a daily one, solving the long-history problem without reintroducing context bias.

The takeaway is simple: an AI's memory is not a gift. If you give it all your logs, you're handing it a bias on a silver platter. Control the context, and you control the model.

If you're building similar tooling, start with a 50-line window and a question about differences. Your future debugging self will thank you.

Top comments (0)