Improve auto-memory extraction quality
Filter passive fragment memories before consolidation so action history, temporary artifacts, local runtime coordinates, and personal absolute paths do not get saved automatically. Tighten memory utility prompts toward durable preferences, stable project facts, recurring constraints, and reusable solutions while keeping explicit memory_save behavior unchanged. Add focused tests for fragment quality gating.
Alessandro committed
Jun 2, 2026 at 09:42 UTC
a0a815943a3bb3b6fb1aed719214943dce91dc64
7 files changed
+189
-17
plugins/_memory/README.md
+2
@@ -15,6 +15,8 @@ This plugin stores memories and knowledge embeddings in a FAISS-backed vector da
15
- Loads configured knowledge directories into memory when a database is initialized.
16
- **Memory tools**
17
- Includes tools for saving, loading, deleting, forgetting, and behavior adjustment workflows.
18
+- **Automatic conversation memory**
19
+ - Stores durable preferences, project facts, and recurring constraints while filtering transient action-history fragments before insertion.
20
- **Dashboard APIs**
21
- Exposes search, delete, bulk delete, update, and subdirectory listing endpoints for the memory dashboard.
22
- **Scoped storage**
plugins/_memory/extensions/python/monologue_end/_50_memorize_fragments.py
+19
-3
@@ -8,6 +8,7 @@ from helpers.defer import DeferredTask, THREAD_BACKGROUND
8
9
# Direct import - this extension lives inside the memory plugin
10
from plugins._memory.helpers.memory import Memory
11
+from plugins._memory.helpers.memory_quality import filter_auto_memory_fragments
12
from plugins._memory.tools.memory_load import DEFAULT_THRESHOLD as DEFAULT_MEMORY_THRESHOLD
13
14
@@ -104,9 +105,24 @@ class MemorizeMemories(Extension):
105
if not isinstance(memories, list) or len(memories) == 0:
106
log_item.update(heading="No useful information to memorize.")
107
return
107
- else:
108
- memories_txt = "\n\n".join([str(memory) for memory in memories]).strip()
109
- log_item.update(heading=f"{len(memories)} entries to memorize.", memories=memories_txt)
108
+
109
+ raw_memories_count = len(memories)
110
+ memories = filter_auto_memory_fragments(memories)
111
+ filtered_memories_count = raw_memories_count - len(memories)
112
+
113
+ if not memories:
114
+ log_item.update(
115
+ heading="No durable information to memorize.",
116
+ filtered_memories_count=filtered_memories_count,
117
+ )
118
+ return
119
+
120
+ memories_txt = "\n\n".join(memories).strip()
121
+ log_item.update(
122
+ heading=f"{len(memories)} durable entries to memorize.",
123
+ memories=memories_txt,
124
+ filtered_memories_count=filtered_memories_count,
125
+ )
126
127
# Process memories with intelligent consolidation
128
total_processed = 0
plugins/_memory/helpers/memory_quality.py
new
+88
@@ -0,0 +1,88 @@
1
+from __future__ import annotations
2
+
3
+import re
4
+from typing import Iterable
5
+
6
+
7
+_DURABLE_SUBJECT_RE = re.compile(
8
+ r"\b(user|project|repo|repository|workspace|runtime|service|server|"
9
+ r"plugin|agent zero|a0|profile|team|organization|company)\b",
10
+ re.IGNORECASE,
11
+)
12
+_DURABLE_RELATION_RE = re.compile(
13
+ r"\b(prefers?|requires?|uses?|runs?|lives?|located|configured|default|"
14
+ r"current(?:ly)?|must|should|always|never|constraint|path|endpoint|port)\b",
15
+ re.IGNORECASE,
16
+)
17
+_TRANSIENT_RE = re.compile(
18
+ r"(/tmp\b|\btmp/|\btemporary\b|\btemp\b|\bworkdir\b|"
19
+ r"\bcontainer-local\b|\bmachine-local\b|\bpersonal absolute path\b|"
20
+ r"\blocal endpoint\b|\blocal port\b|"
21
+ r"\btest marker\b|\bmemory test\b|\bcleanup token\b|\bthrowaway\b|"
22
+ r"\bsample file\b|\bdemo file\b|\bvalidation directory\b|\blive-test\b|"
23
+ r"\bone-off\b|\bsession-only\b)",
24
+ re.IGNORECASE,
25
+)
26
+_LOCAL_COORDINATE_RE = re.compile(
27
+ r"\b(localhost|127\.0\.0\.1|0\.0\.0\.0|::1)\b|"
28
+ r"https?://(?:localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(?::\d+)?|"
29
+ r"/(?:home|Users)/[^/\s]+/|"
30
+ r"\b[A-Za-z]:\\Users\\",
31
+ re.IGNORECASE,
32
+)
33
+_AGENT_ACTION_RE = re.compile(
34
+ r"\b(agent|assistant|codex|a0)\b.{0,80}\b("
35
+ r"created|implemented|built|ran|tested|verified|fixed|wrote|generated|"
36
+ r"saved|cleaned|inspected|synced|restarted|committed|staged|opened|"
37
+ r"reported|showed)\b",
38
+ re.IGNORECASE,
39
+)
40
+_USER_REQUEST_RE = re.compile(
41
+ r"\b(user|the user)\s+(asked|requested|wanted|told|prompted|instructed)\b",
42
+ re.IGNORECASE,
43
+)
44
+_COMMAND_HISTORY_RE = re.compile(
45
+ r"\b(ran|executed)\s+[`'\"]?[\w./-]+|\bterminal command\b|"
46
+ r"\bcommand output\b|\bexit code\b|\bstdout\b|\bstderr\b",
47
+ re.IGNORECASE,
48
+)
49
+_LOW_VALUE_RE = re.compile(
50
+ r"^(hello|hi|greeting|conversation start|task completed|file created|"
51
+ r"implementation completed|test passed|memory search)$",
52
+ re.IGNORECASE,
53
+)
54
+
55
+
56
+def normalize_memory_candidate(value: object) -> str:
57
+ return str(value).strip()
58
+
59
+
60
+def is_auto_fragment_worth_saving(text: str) -> bool:
61
+ """Return True only for durable facts suitable for automatic fragments."""
62
+
63
+ text = normalize_memory_candidate(text)
64
+ has_durable_relation = bool(_DURABLE_RELATION_RE.search(text))
65
+ if len(text) < 24:
66
+ return False
67
+ if _LOW_VALUE_RE.match(text):
68
+ return False
69
+ if _TRANSIENT_RE.search(text):
70
+ return False
71
+ if _LOCAL_COORDINATE_RE.search(text):
72
+ return False
73
+ if _COMMAND_HISTORY_RE.search(text):
74
+ return False
75
+ if _AGENT_ACTION_RE.search(text) and not has_durable_relation:
76
+ return False
77
+ if _USER_REQUEST_RE.search(text) and not has_durable_relation:
78
+ return False
79
+
80
+ return bool(_DURABLE_SUBJECT_RE.search(text) and has_durable_relation)
81
+
82
+
83
+def filter_auto_memory_fragments(memories: Iterable[object]) -> list[str]:
84
+ return [
85
+ text
86
+ for text in (normalize_memory_candidate(memory) for memory in memories)
87
+ if is_auto_fragment_worth_saving(text)
88
+ ]
plugins/_memory/prompts/fw.memory.hist_suc.sys.md
+4
-1
@@ -20,4 +20,7 @@
20
21
# Rules
22
- Focus on important details like libraries used, code, encountered issues, error fixing etc.
23
-- Do not include simple solutions that don't require instructions to reproduce like file handling, web search etc.
\ No newline at end of file
23
+- Do not include simple solutions that don't require instructions to reproduce like file handling, web search etc.
24
+- Do not save one-off completed work, temporary file paths, exact session artifacts, benchmark/task IDs, or command transcripts
25
+- Save a solution only when it is a reusable technical procedure likely to help with future similar work
26
+- Prefer concise reproduction steps over a diary of what the agent did
plugins/_memory/prompts/memory.keyword_extraction.sys.md
+6
-6
@@ -31,23 +31,23 @@ Return ONLY a JSON array of strings containing the extracted keywords/phrases:
31
32
## Examples
33
34
-**Memory Content**: "Successfully implemented OAuth authentication using JWT tokens for the user login system. The solution handles token refresh and validation properly."
34
+**Memory Content**: "User prefers Linux shell examples and ./.venv/bin/python paths instead of Windows-only virtualenv commands."
35
36
**Output**:
37
```json
38
-["OAuth authentication", "JWT tokens", "user login", "token refresh", "authentication implementation"]
38
+["Linux shell examples", "virtualenv paths", "user preference", "Python commands"]
39
```
40
41
-**Memory Content**: "Fixed the database connection timeout issue by increasing the connection pool size and optimizing slow queries with proper indexing."
41
+**Memory Content**: "Project uses a configured Dockerized live runtime for smoke checks."
42
43
**Output**:
44
```json
45
-["database connection", "timeout issue", "connection pool", "query optimization", "indexing"]
45
+["Dockerized runtime", "live runtime", "smoke checks", "project runtime"]
46
```
47
48
-**Memory Content**: "Learned that Alpine.js x-data components should use camelCase for method names and snake_case for data properties to follow best practices."
48
+**Memory Content**: "Agent Zero WebUI stores use Alpine.js createStore from /js/AlpineStore.js."
49
50
**Output**:
51
```json
52
-["Alpine.js", "x-data components", "camelCase methods", "naming conventions"]
52
+["Agent Zero WebUI", "Alpine.js", "createStore", "AlpineStore.js"]
53
```
plugins/_memory/prompts/memory.memories_sum.sys.md
+17
-7
@@ -1,10 +1,10 @@
1
# Assistant's job
2
1. The assistant receives a HISTORY of conversation between USER and AGENT
3
-2. Assistant searches for relevant information from the HISTORY worth memorizing
4
-3. Assistant writes notes about information worth memorizing for further use
3
+2. Assistant searches for durable information from the HISTORY worth memorizing
4
+3. Assistant writes notes about stable information worth recalling in future work
5
6
# Format
7
-- The response format is a JSON array of text notes containing facts to memorize
7
+- The response format is a JSON array of text notes containing durable facts to memorize
8
- If the history does not contain any useful information, the response will be an empty JSON array.
9
10
# Output example
@@ -16,14 +16,18 @@
16
~~~
17
18
# Rules
19
-- Only memorize complete information that is helpful in the future
19
+- Only memorize complete information that is likely to remain helpful across future conversations
20
- Never memorize vague or incomplete information
21
- Never memorize keywords or titles only
22
-- Focus only on relevant details and facts like names, IDs, events, opinions etc.
22
+- Focus on durable user preferences, stable project facts, recurring collaboration constraints, important identities, configured services, and long-lived requirements
23
+- Do not memorize machine-specific local endpoints, personal absolute paths, container work directories, or ephemeral runtime coordinates as fragment memories; convert to a generic durable fact only if the stable project relationship matters
24
- Do not include irrelevant details that are of no use in the future
25
- Do not memorize facts that change like time, date etc.
26
- Do not add your own details that are not specifically mentioned in the history
27
- Do not memorize AI's instructions or thoughts
28
+- Do not memorize what the agent did in this session, commands it ran, files it created, test output, temporary paths, implementation minutiae, or cleanup-only facts
29
+- Do not memorize a user's one-off request unless it states a durable preference, stable project fact, or recurring constraint
30
+- If the only notable information is task progress or a completed implementation, return an empty array; reusable procedures belong to successful-solution memory, not fragments
31
32
# Merging and cleaning
33
- The goal is to keep the number of new memories low while making memories more complete and detailed
@@ -36,8 +40,9 @@
40
41
# Correct examples of data worth memorizing with (explanation)
42
> User's name is John Doe (name is important)
39
-> AsyncRaceError in primary_modules.py was fixed by adding a thread lock on line 123 (important event with details for context)
40
-> Local SQL database was created, server is running on port 3306 (important event with details for context)
43
+> User prefers Linux shell commands and relative virtualenv paths over Windows-only examples (stable user preference)
44
+> Project currently uses a configured live runtime for smoke checks (stable project fact without local coordinates)
45
+> Runtime-impacting plugin changes must be synced into the configured live environment before testing (recurring project constraint)
46
47
# WRONG examples with (explanation of error), never output memories like these
48
> Dog Information (no useful facts)
@@ -50,6 +55,11 @@
55
> RAM Status (just a topic without detail)
56
> User used to prefer X before changing to Y (historical preference is usually not useful; memorize the current preference only)
57
> Temporary marker ABC123 was used in a memory test (test residue, not useful)
58
+> Agent created a temporary CLI demo file and ran a shell test (agent action history, not a durable fact)
59
+> The live UI was reachable at a machine-local endpoint during this session (local runtime detail, not a durable memory)
60
+> User asked to build a tiny CLI todo app (one-off request, not a recurring preference)
61
+> The markdown-to-HTML script generated sample.html with 181 bytes (task output, not useful later)
62
+> AsyncRaceError in primary_modules.py was fixed by adding a thread lock on line 123 (belongs in successful solutions if reusable, not fragments)
63
64
65
# Further WRONG examples
tests/test_memory_quality.py
new
+53
@@ -0,0 +1,53 @@
1
+from __future__ import annotations
2
+
3
+import sys
4
+from pathlib import Path
5
+
6
+PROJECT_ROOT = Path(__file__).resolve().parents[1]
7
+if str(PROJECT_ROOT) not in sys.path:
8
+ sys.path.insert(0, str(PROJECT_ROOT))
9
+
10
+from plugins._memory.helpers.memory_quality import (
11
+ filter_auto_memory_fragments,
12
+ is_auto_fragment_worth_saving,
13
+)
14
+
15
+
16
+def test_auto_fragment_quality_keeps_durable_preferences_and_project_facts():
17
+ assert is_auto_fragment_worth_saving(
18
+ "User currently prefers concise technical answers with verification."
19
+ )
20
+ assert is_auto_fragment_worth_saving(
21
+ "Project currently uses a configured live runtime for smoke checks."
22
+ )
23
+ assert is_auto_fragment_worth_saving(
24
+ "Runtime-impacting plugin changes must be synced into the configured live environment before testing."
25
+ )
26
+
27
+
28
+def test_auto_fragment_quality_rejects_action_history_and_transient_artifacts():
29
+ rejected = [
30
+ "Agent created a temporary CLI demo file and ran a shell test.",
31
+ "The user asked to build a tiny CLI todo app in Python.",
32
+ "Temporary marker ABC123 was used in a memory test.",
33
+ "The markdown-to-HTML script generated sample.html with 181 bytes.",
34
+ "Fixed AsyncRaceError in primary_modules.py by adding a thread lock on line 123.",
35
+ "The live UI was reachable at a machine-local endpoint during this session.",
36
+ "Project repository path is a personal absolute path on this machine.",
37
+ ]
38
+
39
+ for memory in rejected:
40
+ assert not is_auto_fragment_worth_saving(memory)
41
+
42
+
43
+def test_auto_fragment_filter_normalizes_and_preserves_kept_order():
44
+ memories = [
45
+ "User currently prefers Linux paths in examples.",
46
+ "Agent created a demo file and reported success.",
47
+ "Project repository uses a configured source workspace.",
48
+ ]
49
+
50
+ assert filter_auto_memory_fragments(memories) == [
51
+ "User currently prefers Linux paths in examples.",
52
+ "Project repository uses a configured source workspace.",
53
+ ]