{"slug": "how-much-memory-does-your-agent-need-a-practical-memory-store-selection-guide", "title": "How Much Memory Does Your Agent Need? — A Practical Memory Store Selection Guide", "summary": "An engineer's practical guide to selecting memory stores for AI agents argues that vector databases are often overkill, citing that 95% of agent time in a university admissions scraper was spent on simple state reads and that vector retrieval accounted for only 3.7% of memory requests at ByteDance's internal agent platform. The article proposes a three-layer memory architecture using built-in key-value storage, Markdown files, and error logs to cover session state, domain knowledge, and error history without adding dependencies.", "body_md": "The Pain: You search GitHub and find everyone using different memory stores — ChromaDB, PostgreSQL, plain Markdown files, \"SQLite is good enough for a decade.\" Which is right?\n\nThe Answer: All of them. And none of them.Choosing without understanding your scenario is like buying a car without checking the road.\n\nWhen I was building a university admissions data scraper (91 universities), I made the classic mistake: I equipped the agent with a full ChromaDB + vector retrieval stack, spent three days tuning it, and then discovered — **95% of the agent's time was just reading \"which page did I get to last time.\"**\n\nA boolean would have sufficed. I built a vector database capable of semantic search.\n\nAn engineer friend at ByteDance told me their internal agent platform found, after six months, that **vector retrieval accounted for only 3.7% of all Memory Store requests.** The remaining 96.3%? Key-value lookups, state reads/writes, error dedup. The vector search you spent two weeks integrating might serve less than 4% of your queries.\n\nSo before discussing \"which storage,\" we must answer a more fundamental question: **what does your agent actually need to remember?**\n\nI categorize memory into four types:\n\n| Memory Type | Typical Content | Size | Access Frequency | Consistency |\n|---|---|---|---|---|\n| Session state | \"Processing university 37/91\" | ~100 bytes | Every call | Strong |\n| Domain knowledge | \"A-University rate limit is 10 req/s\" | ~1KB | On demand | Eventual |\n| Error history | \"B-University returns 403 because UA blocked\" | ~MB | Before new task | Append-only |\n| Semantic memory | \"Map 'that red button' to settings page\" | varies | Occasional | Eventual |\n\nSee the pattern? If your agent mainly does multi-step automation (data scraping, report generation, CI/CD pipelines), **the first three types are the real needs — and none of them require a vector database.**\n\n**Core thesis: memory selection is about finding the layer that is \"just enough.\" One layer too many is waste; one layer too few is disaster.**\n\nI've used each of these across three projects of different scales. The \"crash moments\" are recorded in the table:\n\n| Dimension | memory()/STATE.md | SQLite | ChromaDB | PostgreSQL |\n|---|---|---|---|---|\n| Type | KV (memory+JSON) | Markdown file | Embedded relational | Vector DB |\n| Capacity | ~4,000 chars | No hard limit | TB-scale | TB-scale |\n| Query | Exact key match | grep/regex/fulltext | SQL (JOIN/aggregate) | Semantic top-K |\n| Install | 0 (built-in) | 0 (OS) | pip install | pip + ONNX/API |\n| Ops | None | None | Low (single file) | Medium (index) |\n| Concurrency | Single process | Lock-free | WAL mode | Single writer |\n| Latency | <1ms | <1ms | <5ms | 10-100ms |\n| Best for | Preferences/contracts | Project progress/state | Structured task data | Long-tail semantic search |\n\n**Crash moments (real experience):**\n\n*Three layers complement each other: L1 fast but small / L2 structured / L3 dedupable.*\n\nHere's an architecture validated across three projects. No new dependencies — just a combination that covers \"session state + domain knowledge + error history.\"\n\n```\n┌──────────────────────────────────────────┐\n│        Three-Layer Memory Architecture    │\n│                                           │\n│  L1: memory()      ← 4KB KV, preferences │\n│  L2: STATE.md      ← file, progress      │\n│  L3: ERROR_LOG.md  ← file, pitfalls      │\n│                                           │\n│  L1 fast but small / L2 structured /      │\n│  L3 dedupable                              │\n└──────────────────────────────────────────┘\n```\n\nBest for: user preferences (\"reply in Chinese\"), project conventions (\"outputs go to /outputs/\"), environment facts (\"Python 3.11\").\n\n**Fatal trap**: the 4,000-char hard limit. The solution isn't to avoid it — treat L1 as a cache and push critical data down to L2.\n\nThe backbone of three-layer memory. A single Markdown file, read at session start, updated after task completion. Core code is under 80 lines:\n\n```\n# state_manager.py — automatic STATE.md reader/writer\n\"\"\"Agent's workbench — read each session, update after each task\"\"\"\nfrom pathlib import Path\nfrom datetime import datetime\nimport re\n\nSTATE_FILE = Path(\"./STATE.md\")\n\ndef load_state() -> str:\n    \"\"\"Call at session start: read STATE.md, inject into agent context.\"\"\"\n    if STATE_FILE.exists():\n        content = STATE_FILE.read_text()\n        now = datetime.now().strftime(\"%Y-%m-%d %H:%M\")\n        return (\n            f\"> Project current state (last updated: agent-maintained)\\n\"\n            f\"> Time: {now}\\n\\n{content}\\n\\n\"\n            f\"---\\nAbove is project state. Continue based on this.\\n\"\n        )\n    # First use: initialize template\n    return \"\"\"# Project State File\n> Maintained by agent. Read at each session start.\n\n## Active Tasks\n(none)\n\n## Blockers\n(none)\n\n## Attempted & Failed\n(none)\n\n## Project Overview\n| Metric | Value | Note |\n|--------|-------|------|\n| Status | OK | - |\n\"\"\"\n\ndef update_state(section: str, content: str):\n    \"\"\"Call when agent completes a task or finds a blocker.\"\"\"\n    current = STATE_FILE.read_text() if STATE_FILE.exists() else load_state()\n    marker = f\"## {section}\"\n\n    if marker in current:\n        parts = current.split(marker, 1)\n        before = parts[0]\n        after_parts = parts[1].split(\"\\n## \", 1)\n        rest = \"\\n## \" + after_parts[1] if len(after_parts) > 1 else \"\"\n        new_content = before + f\"{marker}\\n{content}\\n\" + rest\n    else:\n        new_content = current.rstrip() + f\"\\n\\n{marker}\\n{content}\\n\"\n\n    STATE_FILE.write_text(new_content)\n    print(f\"[state_manager] Updated section '{section}'\")\n```\n\nAppend-only log of errors with dedup. The key mechanism: each error gets a fingerprint (e.g., hash of the error message). If the same fingerprint appears, skip it — no duplicate entries.\n\n``` python\n# error_log.py — deduplicated error logging\nfrom pathlib import Path\nimport hashlib\nfrom datetime import datetime\n\nERROR_FILE = Path(\"./ERROR_LOG.md\")\n\ndef log_error(scene: str, message: str, solution: str = \"\"):\n    \"\"\"Log an error, dedup by message hash.\"\"\"\n    fp = hashlib.md5(message.encode()).hexdigest()[:8]\n    current = ERROR_FILE.read_text() if ERROR_FILE.exists() else \"\"\n    if f\"[{fp}]\" in current:\n        return  # already logged — skip\n    entry = (\n        f\"\\n## [{fp}] {scene} @ {datetime.now().strftime('%Y-%m-%d %H:%M')}\\n\"\n        f\"- Error: {message}\\n\"\n        f\"- Solution: {solution or 'TBD'}\\n\"\n    )\n    with ERROR_FILE.open(\"a\") as f:\n        f.write(entry)\n```\n\n**Three layers complement each other: L1 fast but small / L2 structured & readable / L3 dedupable & queryable.**\n\n```\nNeed to store...\n├── User preference / project convention?  → L1 memory()\n├── Task progress / state?                 → L2 STATE.md\n├── Error / pitfall?                       → L3 ERROR_LOG.md\n├── Structured task data (queries needed)? → SQLite\n├── Semantic search?                       → ChromaDB (only if 4% case)\n└── Multi-tenant production?               → PostgreSQL\n```\n\n**Rule of thumb: start with the simplest layer that works. Add complexity only when you hit a concrete limit.**\n\nYou're no longer the developer who installs a vector database because \"it's what the cool kids use.\" You're becoming an architect who **matches storage to the actual memory pattern** of your agent.\n\nMost agents don't need vector search. They need a sticky note, a navigation map, and a pitfall record. The three-layer architecture gives you all three with zero new dependencies.\n\n**Next: Quality isn't accidental — Maker/Checker separation and automated validation.**\n\n*About the author: Wu Ji (无记) — AI & digitalization practitioner focused on Agent engineering, Loop Engineering, and digital transformation. Practical, hands-on tutorials — follow along and it just works.*", "url": "https://wpnews.pro/news/how-much-memory-does-your-agent-need-a-practical-memory-store-selection-guide", "canonical_source": "https://dev.to/weiwuji/how-much-memory-does-your-agent-need-a-practical-memory-store-selection-guide-56h2", "published_at": "2026-08-01 03:41:32+00:00", "updated_at": "2026-08-01 04:07:40.083737+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "developer-tools"], "entities": ["ChromaDB", "PostgreSQL", "SQLite", "ByteDance"], "alternates": {"html": "https://wpnews.pro/news/how-much-memory-does-your-agent-need-a-practical-memory-store-selection-guide", "markdown": "https://wpnews.pro/news/how-much-memory-does-your-agent-need-a-practical-memory-store-selection-guide.md", "text": "https://wpnews.pro/news/how-much-memory-does-your-agent-need-a-practical-memory-store-selection-guide.txt", "jsonld": "https://wpnews.pro/news/how-much-memory-does-your-agent-need-a-practical-memory-store-selection-guide.jsonld"}}