Part 19. Agent Memory on SQLite
SDD stores the project intent in the repository: QWEN.md, AGENTS.md, specs/, CHANGELOG.md. But agent development has another memory layer: what happened in sessions, which errors repeated, which preferences the user confirmed, which commands worked, which decisions the agent had to infer from context.
Qwen Code already has built-in memory: QWEN.md, automatic memory, /remember, /forget, /dream. For most projects, this is sufficient. But if you want to own the memory yourself, see an audit log, transfer it between runtime environments, and use your own cleanup rules, you can build a local memory layer on SQLite.
The idea of modern approaches to agent memory is simple:
flowchart TD
A["Qwen Code events<br/>requests, tools, outcomes"] --> B["Hooks<br/>log_event.py"]
B --> C[("SQLite<br/>events table")]
C --> D["Background summarization<br/>/dream or dream_sqlite.py"]
D --> E[("SQLite<br/>memories table")]
E --> F["Context injection<br/>inject_memory.py"]
F --> G["New session<br/>or new request"]
E --> H{"Did memory become a rule?"}
H -- "yes" --> I["Move to specs/<br/>QWEN.md or skill"]
H -- "no" --> EImportant limitation: this memory does not replace specifications. It complements them. Decisions that the product depends on must go into specs/ or QWEN.md, not live only in the database.
What exactly to remember
You don't need to save everything in the request context. You can store a lot, but you should add little to the context.
Useful categories:
- persistent user preferences;
- confirmed project commands;
- repeated agent errors;
- findings after validation;
- references to external documents;
- notes on active branches and processes;
- decisions that need to be moved to specifications.
Bad categories:
- secrets;
- full large session transcripts without necessity;
- random intermediate thoughts;
- outdated workarounds without expiration date;
- things the agent can easily read from the code.
SQLite schema
Create a directory:
mkdir -p .qwen/hooks .qwen/memory
The schema is in a separate file: examples/sqlite-memory/schema.sql.
Where to get example files. Going forward, it is assumed that the tutorial repository sdd-qwen-code-ru/ sits next to your project, and the TUTORIAL_DIR variable points to it. If the tutorial is a separate clone, set export TUTORIAL_DIR=/path/to/sdd-qwen-code-ru before the commands below. If you obtained the example files differently (downloaded a zip, copied manually), simply replace the path prefix.
Initialization:
cp "$TUTORIAL_DIR/examples/sqlite-memory/schema.sql" .qwen/memory/schema.sql
sqlite3 .qwen/memory/agent-memory.db < .qwen/memory/schema.sql
Hook 1: event logging
The script is in a separate file: examples/sqlite-memory/hooks/log_event.py.
Copy it into your project and make it executable:
cp "$TUTORIAL_DIR/examples/sqlite-memory/hooks/log_event.py" .qwen/hooks/log_event.py
chmod +x .qwen/hooks/log_event.py
Hook 2: adding relevant memory
The script is in a separate file: examples/sqlite-memory/hooks/inject_memory.py.
cp "$TUTORIAL_DIR/examples/sqlite-memory/hooks/inject_memory.py" .qwen/hooks/inject_memory.py
chmod +x .qwen/hooks/inject_memory.py
Connecting hooks in Qwen Code
A configuration example is in examples/sqlite-memory/settings-hooks.example.json.
If your project already has settings, merge the JSON manually. Do not overwrite model and authentication settings.
Background memory summarization
Background summarization should not run on every hook. It reads accumulated events and creates compact memory records. You can run it manually, via cron, or after completing a major phase.
The simplest option without an API: ask Qwen Code to do consolidation and write the result via SQL.
/clear
Read the last rows from .qwen/memory/agent-memory.db via sqlite3.
Compress durable findings into these memory paths:
- profile/preferences.md
- project/agentclinic.md
- workflow/sdd-validation.md
- tools/qwen-code.md
Do not include secrets or raw transcripts.
Before writing, show the proposed memory notes.
You can automate further: dream_sqlite.py takes recent events, calls a model through your API client, and updates or inserts records into memories.
Two ready-made files:
- examples/sqlite-memory/dream_sqlite_skeleton.py — skeleton with an empty
summarize_with_llmadapter. Suitable as a reference when connecting any provider. - examples/sqlite-memory/dream_sqlite_qwen_example.py — working example that calls the same OpenAI-compatible DashScope endpoint as Qwen Code in part 4 (variable
BAILIAN_API_KEY, modelqwen3-coder-plus). This is enough to run summarization without a separate integration.
Installation:
cp "$TUTORIAL_DIR/examples/sqlite-memory/dream_sqlite_qwen_example.py" .qwen/memory/dream_sqlite.py
Run:
python .qwen/memory/dream_sqlite.py --since 24h --dry-run
python .qwen/memory/dream_sqlite.py --since 24h
How this relates to Anthropic's background summarization
A VentureBeat article describes the principle: the agent does not change model weights, but periodically reviews past sessions, identifies repeating patterns, errors, and successful working practices, then makes these notes available to future sessions. For SDD, this is especially useful after validation:
- the agent twice forgot to update
roadmap.md;
- the user always demands to show diffs first;
- the test command in the project differs from the standard one;
- a certain type of migrations often breaks;
- a successful validation checklist is worth turning into a skill.
In the SQLite version, this becomes a fully local and auditable process: raw events stay in events, compressed notes in memories, and a human can open both tables.
Where memory must yield to specifications
If background summarization found a new persistent rule:
workflow/sdd-validation.md
Always update CHANGELOG.md before merging feature branches.
it must be moved to QWEN.md or a changelog skill.
If background summarization found a product agreement:
project/agentclinic.md
AgentClinic feedback notes must be public and satirical,
not private support tickets.
it must be moved to a feature specification or mission.md.
Memory helps discover rules. Specifications make rules mandatory.
Why less context often works better
Agent memory can easily become a "everything that was ever discussed" storage. This is a bad idea. Research and recent practice document a phenomenon called "context rot": on large inputs, the model is worse at selecting relevant pieces. A short, precise context of 300 tokens often gives a better result than a large irrelevant one of tens of thousands.
From this follows a simple rule for connecting memory:
- in
QWEN.md, inject not "everything that was remembered", but only records relevant to the current task (e.g., by tag/category); - limit the injection length — a few short bullet points are better than a long list;
- if a record lost relevance, delete it, don't "clarify";
- if the same record is needed every time — it's no longer "memory", but a rule, and its place is in
QWEN.mdor a constitution.
The same principle explains why /clear between roles works: you consciously narrow the context to what matters for the next role, rather than accumulating everything in one session. On context hygiene in skills and sessions, see part 14.
Inspection and review
Event list:
sqlite3 .qwen/memory/agent-memory.db \
"select event_name, tool_name, substr(prompt,1,80), timestamp from events order by id desc limit 20;"
Memory records list:
sqlite3 .qwen/memory/agent-memory.db \
"select path, updated_at from memories order by path;"
Search:
sqlite3 .qwen/memory/agent-memory.db \
"select path, snippet(memory_fts, 1, '[', ']', '...', 12) from memory_fts where memory_fts match 'validation OR changelog';"
Manual entry:
sqlite3 .qwen/memory/agent-memory.db < "$TUTORIAL_DIR/examples/sqlite-memory/manual-memory-example.sql"
Privacy and security
Agent memory easily becomes a storage of unnecessary things. Establish rules:
- do not log environment variables;
- truncate tool responses;
- do not save secrets and personal data;
- do not add more than 3–5 memory blocks to context;
- regularly delete outdated records;
- commit only the schema and scripts, not
.qwen/memory/agent-memory.db.
An example .gitignore rules are in examples/sqlite-memory/gitignore.example.
Practice
- Create the SQLite schema.
- Connect
log_event.pytoUserPromptSubmit,PostToolUse, andStop.
- Connect
inject_memory.pytoSessionStartandUserPromptSubmit. - Run one SDD feature.
- Review
events. - Create 2–3 manual memory notes.
- Start a new Qwen Code session and verify that relevant memory is added to context.
- After validation, manually or via script, do a brief memory summarization.
- Move mandatory rules from memory to
QWEN.md, specifications, or a skill.
Review questions
- Why should raw events and persistent memory notes be separate tables?
- What information must not be saved in agent memory?
- Why is background memory summarization better run offline, not in every hook?
- When should memory be moved to
specs/? - How do you limit the injected context so that memory doesn't pollute the request?