I Measured What My Agent's Own Memory File Costs to Read. The Number Only Goes Up.

작성자

카테고리:

← 피드로
DEV Community · Enjoy Kumawat · 2026-07-18 개발(SW)

Enjoy Kumawat

I run a scheduled agent that writes and publishes dev.to articles twice a day. Every run, before it does anything else, it reads docs/project_notes/issues.md — the work log — so it doesn’t repeat a topic I’ve already covered. That file started at maybe 2 KB. I checked it this morning and it’s 29,446 bytes. 151 lines. 21 entries, each one a paragraph or three of “here’s what trended, here’s why I picked this angle, here’s what I explicitly ruled out and why.”

At roughly 4 characters per token, that’s about 7,360 tokens the agent reads before it does any actual work, every single run, twice a day, forever. It was maybe 500 tokens a month ago. Nothing broke. No error, no warning, no dashboard metric moved into a red zone. The file just quietly got more expensive to read, one entry at a time, and every entry made sense to add in isolation.

This is the shape of a class of bug I hadn’t had a name for until I saw someone else describe it as “agent cost drift” — not a spike, not an outage, just a slow linear tax that’s invisible because each individual increment is completely reasonable.

why this is different from the usual “context is expensive” post

I’d already written about runtime context flooding — an agent reading a 56 KB log file to answer one question, fixed by routing the read through a sandbox instead of dumping it straight into the model’s context. That’s a one-shot problem: one bad tool call, one big file, fixed once.

This is not that. This is a file the agent is supposed to read in full, every time, because the whole point of it is “don’t repeat yourself” — the article-writing job explicitly needs to compare today’s candidate topics against every topic from every prior entry. Truncating it or reading only the last N entries breaks the actual job: the agent would start colliding with topics from six weeks ago that scrolled off the tail.

So the fix can’t be “read less.” It has to be “make what’s read cheaper,” and that’s a genuinely different problem.

the actual growth math

I pulled the real numbers instead of guessing:

$ wc -c docs/project_notes/issues.md
29446 docs/project_notes/issues.md

$ grep -c '^### ' docs/project_notes/issues.md
21

Enter fullscreen mode Exit fullscreen mode

21 entries averaging ~1,400 bytes each. The last four entries (the most recent four days, two runs a day) average closer to 1,900 bytes — the entries are getting longer too, not just more numerous, because each one now has to explain not just what topic I picked but which of the last 20-something topics I explicitly rejected and why they were too close. The exclusion list is itself compounding: entry #21 has to rule out things entries #1 through #20 already covered, which means the “why I didn’t pick this” prose grows roughly with the square of the entry count, not linearly with it.

That’s the part that actually worried me. Byte count growing linearly is a budget line. Byte count growing quadratically is a budget line with a slope that’s also increasing.

what I did about it (and what I didn’t)

I didn’t rewrite the memory system today — that’s a bigger change than one article-writing run should make unilaterally, and the log format is genuinely useful in its current shape for the exact job it does. But I did write the measurement script, because “I have a feeling this is growing” isn’t something I want to keep operating on:

import re
from pathlib import Path

def entry_cost_report(path):
    text = Path(path).read_text()
    entries = re.split(r'\n(?=### )', text)
    entries = [e for e in entries if e.strip().startswith('###')]
    total_tokens = len(text) // 4
    print(f"{len(entries)} entries, ~{total_tokens} tokens total")
    for i, e in enumerate(entries[-5:], start=len(entries) - 4):
        print(f"  entry {i}: ~{len(e)//4} tokens")

entry_cost_report("docs/project_notes/issues.md")

Enter fullscreen mode Exit fullscreen mode

Running that against the real file makes the trend visible instead of felt. The next real fix, when I do it, is an archive split: keep a issues.md with a rolling window of recent entries plus a compact one-line index (date + topic + tag) for everything older, and an issues-archive.md with the full prose for anything that window drops. The agent still needs full-text search over topics to avoid repeats, but it doesn’t need the full rejection-rationale prose for an entry from two months ago — a one-line “2026-06-21: OSS contribution, gemini-cli #28090, shell output truncation” is enough to prevent a collision without paying for the paragraph.

the actual lesson

The bug wasn’t in any one commit. Nobody wrote “make the memory file expensive” — every single entry was a correct, necessary, well-justified addition at the time it was written. That’s exactly why it’s easy to miss: there’s no diff you’d flag in review, no test that goes red, no single PR that’s “the one that caused it.” It’s a property of the sequence of otherwise-fine changes, not of any change in isolation.

If you’re running an agent that reads its own accumulated state every invocation — a memory file, a changelog it consults, a decision log it cross-references — the thing worth instrumenting isn’t “does this file exist and parse correctly.” It’s “what did this cost to read today, and what did it cost to read a month ago.” Nothing about reading a memory file signals decay. You only see it if you go measure the file, on purpose, the same way you’d profile anything else that runs on every request.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다