Your agent needs memory. The obvious answer: spin up a database. Postgres, Redis, a vector store — pick your favorite.
We went the other direction. Our personal AI agent has run thousands of autonomous cycles over months, remembers everything it needs to, and uses zero databases. Here's how.
The Architecture: Four Tiers of Markdown
Hot (In-Memory) → Last 20 conversations (runtime array)
Warm (Daily File) → daily/YYYY-MM-DD.md
Cold (Long-term) → MEMORY.md + topic files
Topic (Scoped) → topics/*.md (loaded by keyword matching)
Every memory is a Markdown file. Human-readable. Git-versioned. Grep-searchable.
When the agent builds context for a new cycle, it doesn't query a database — it assembles context from files. Hot memories are always loaded. Topic memories are loaded only when relevant keywords appear in the conversation. Cold memories provide the persistent backdrop.
Search Without Embeddings
For search, we use FTS5 (SQLite's full-text search) with BM25 ranking. Better-sqlite3, in-process, no external service. Grep as fallback.
searchMemory(query)
→ FTS5 BM25 search → results? return them
→ no results? → fallback grep
No vector embeddings. No cosine similarity. No embedding model to maintain. For a personal agent with fewer than 1,000 memory entries, BM25 keyword search is fast enough and precise enough.
The Unexpected Win: Temporal Transparency
Here's what surprised us. The biggest advantage of file-based memory isn't simplicity — it's temporal transparency.
When our agent makes a wrong decision based on stale information, we run git blame on the memory file. We see exactly:
- When that belief was written
- What conversation created it
- What it replaced (git diff)
This is temporal memory for free. No special "when was this fact true?" queries. No event sourcing framework. Just git doing what git already does.
The agent's memory evolves like code: iteratively, with full history, reviewable by humans.
When This Breaks Down
File-based memory fails in specific scenarios:
- Multi-agent concurrency: Two agents writing the same file = merge conflicts. If your agents share state, you need a real database.
- Scale: Past ~10K entries, FTS5 on flat files gets slow. We're at ~800 and it's instant.
- Complex queries: "Find all memories about X that were updated after Y" is trivial in SQL, awkward in grep.
If you're building a platform where thousands of agents need shared, concurrent, queryable memory — yes, use Postgres. Ghost, Neon, Supabase — they exist for good reasons.
But if you're building a personal agent that runs on your machine, serves one user, and needs to be transparent about its reasoning? Files win.
The Real Insight
The storage engine matters less than the memory architecture. Four tiers with smart loading beats a flat database table regardless of what backs it.
The question isn't "Postgres or files?" It's: "Which memories should my agent load right now, and which should it ignore?"
Get that wrong, and your agent drowns in its own history — whether that history lives in Postgres or Markdown.
This is part of a series on building AI agents that perceive before they plan. Previously: Why Your AI Agent Needs a System 1 and Why I Replaced My AI Agent's Vector Database With grep.
Top comments (0)