delayed memory recall

frdel committed Aug 8, 2025 at 16:58 UTC aa648f0f4973cb5f905d34dd575eed3a3daf7e26
8 files changed +93 -27
README.md
+1
@@ -176,6 +176,7 @@ docker run -p 50001:80 agent0ai/agent-zero
176 - New notifications system
177 - New local terminal interface for stability
178 - Rate limiter integration to models
179 +- Delayed memory recall
180 - Smarter autoscrolling in UI
181 - Action buttons in messages
182 - Multiple API keys support
prompts/memory.memories_query.sys.md
+7
@@ -7,11 +7,18 @@
7 - The response format is a plain text string containing the query
8 - No other text, no formatting
9
10 +# No query
11 +- If the conversation is not relevant for memory search, return a single dash (-)
12 +
13 # Rules
14 - Only focus on facts and events, ignore common conversation patterns, greeting etc.
15 - Ignore AI thoughts and behavior
16 - Focus on USER MESSAGE if provided, use HISTORY for context
17
18 +# Ignored:
19 +For the following topics, no query is needed and return a single dash (-):
20 +- Greeting
21 +
22 # Example
23 ```json
24 USER: "Write a song about my dog"
prompts/memory.memories_sum.sys.md
+10 -2
@@ -36,10 +36,18 @@
36 > AsyncRaceError in primary_modules.py was fixed by adding a thread lock on line 123 (important event with details for context)
37 > Local SQL database was created, server is running on port 3306 (important event with details for context)
38
39 -# Wrong examples with (explanation of error), never output memories like these
39 +# WRONG examples with (explanation of error), never output memories like these
40 > Dog Information (no useful facts)
41 > User greeted with 'hi' (just conversation, not useful in the future )
42 > Respond with a warm greeting and invite further conversation (do not memorize AI's instructions or thoughts)
43 > User's name (details missing, not useful)
44 > Today is Monday (just date, no value in this information)
45 -> Market inquiry (just a topic without detail)
\ No newline at end of file
45 +> Market inquiry (just a topic without detail)
46 +> RAM Status (just a topic without detail)
47 +> The user requested current RAM and CPU status. (No exact facts to memorize)
48 +
49 +
50 +# Further WRONG examples
51 +- Hello
52 +- The user requested current RAM and CPU status.
53 +-
\ No newline at end of file
prompts/memory.recall_delay_msg.md new
+1
@@ -0,0 +1 @@
1 +Info: auto memory recall set to delayed mode. auto memories will be available after next message. if manual memory check is required use memory tools.
\ No newline at end of file
python/extensions/message_loop_prompts_after/_50_recall_memories.py
+25 -14
@@ -3,9 +3,11 @@ from python.helpers.extension import Extension
3 from python.helpers.memory import Memory
4 from agent import LoopData
5 from python.tools.memory_load import DEFAULT_THRESHOLD as DEFAULT_MEMORY_THRESHOLD
6 -from python.helpers import dirty_json, errors, settings
6 +from python.helpers import dirty_json, errors, settings, log
7 +
8
9 DATA_NAME_TASK = "_recall_memories_task"
10 +DATA_NAME_ITER = "_recall_memories_iter"
11
12
13 class RecallMemories(Extension):
@@ -22,18 +24,30 @@ class RecallMemories(Extension):
24
25 set = settings.get_settings()
26
25 - # every 3 iterations (or the first one) recall memories
27 + # turned off in settings?
28 + if not set["memory_recall_enabled"]:
29 + return
30 +
31 + # every X iterations (or the first one) recall memories
32 if loop_data.iteration % set["memory_recall_interval"] == 0:
33 +
34 + # show util message right away
35 + log_item = self.agent.context.log.log(
36 + type="util",
37 + heading="Searching memories...",
38 + )
39 +
40 task = asyncio.create_task(
28 - self.search_memories(loop_data=loop_data, **kwargs)
41 + self.search_memories(loop_data=loop_data, log_item=log_item, **kwargs)
42 )
43 else:
44 task = None
45
46 # set to agent to be able to wait for it
47 self.agent.set_data(DATA_NAME_TASK, task)
48 + self.agent.set_data(DATA_NAME_ITER, loop_data.iteration)
49
36 - async def search_memories(self, loop_data: LoopData, **kwargs):
50 + async def search_memories(self, log_item: log.LogItem, loop_data: LoopData, **kwargs):
51
52 # cleanup
53 extras = loop_data.extras_persistent
@@ -46,16 +60,6 @@ class RecallMemories(Extension):
60 set = settings.get_settings()
61 # try:
62
49 - # if recall is disabled, return
50 - if not set["memory_recall_enabled"]:
51 - return
52 -
53 - # show full util message
54 - log_item = self.agent.context.log.log(
55 - type="util",
56 - heading="Searching memories...",
57 - )
58 -
63 # get system message and chat history for util llm
64 system = self.agent.read_prompt("memory.memories_query.sys.md")
65
@@ -100,6 +104,13 @@ class RecallMemories(Extension):
104 else:
105 query = user_instruction + "\n\n" + history
106
107 + # if there is no query (or just dash by the LLM), do not continue
108 + if not query or len(query) <= 3:
109 + log_item.update(
110 + query="No relevant memory query generated, skipping search",
111 + )
112 + return
113 +
114 # get memory database
115 db = await Memory.get(self.agent)
116
python/extensions/message_loop_prompts_after/_91_recall_wait.py
+23 -10
@@ -1,19 +1,32 @@
1 from python.helpers.extension import Extension
2 from agent import LoopData
3 -from python.extensions.message_loop_prompts_after._50_recall_memories import DATA_NAME_TASK as DATA_NAME_TASK_MEMORIES
3 +from python.extensions.message_loop_prompts_after._50_recall_memories import DATA_NAME_TASK as DATA_NAME_TASK_MEMORIES, DATA_NAME_ITER as DATA_NAME_ITER_MEMORIES
4 # from python.extensions.message_loop_prompts_after._51_recall_solutions import DATA_NAME_TASK as DATA_NAME_TASK_SOLUTIONS
5 -
5 +from python.helpers import settings
6
7 class RecallWait(Extension):
8 async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
9
10 - task = self.agent.get_data(DATA_NAME_TASK_MEMORIES)
11 - if task and not task.done():
12 - # self.agent.context.log.set_progress("Recalling memories...")
13 - await task
10 + set = settings.get_settings()
11 +
12 + task = self.agent.get_data(DATA_NAME_TASK_MEMORIES)
13 + iter = self.agent.get_data(DATA_NAME_ITER_MEMORIES) or 0
14 +
15 + if task and not task.done():
16 +
17 + # if memory recall is set to delayed mode, do not await on the iteration it was called
18 + if set["memory_recall_delayed"]:
19 + if iter == loop_data.iteration:
20 + # insert info about delayed memory to extras
21 + delay_text = self.agent.read_prompt("memory.recall_delay_msg.md")
22 + loop_data.extras_temporary["memory_recall_delayed"] = delay_text
23 + return
24 +
25 + # otherwise await the task
26 + await task
27
15 - # task = self.agent.get_data(DATA_NAME_TASK_SOLUTIONS)
16 - # if task and not task.done():
17 - # # self.agent.context.log.set_progress("Recalling solutions...")
18 - # await task
28 + # task = self.agent.get_data(DATA_NAME_TASK_SOLUTIONS)
29 + # if task and not task.done():
30 + # # self.agent.context.log.set_progress("Recalling solutions...")
31 + # await task
32
python/extensions/monologue_start/_10_memory_init.py new
+13
@@ -0,0 +1,13 @@
1 +from python.helpers.extension import Extension
2 +from agent import LoopData
3 +from python.helpers import memory
4 +import asyncio
5 +
6 +
7 +class MemoryInit(Extension):
8 +
9 + async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
10 + db = await memory.Memory.get(self.agent)
11 +
12 +
13 +
\ No newline at end of file
python/helpers/settings.py
+13 -1
@@ -58,6 +58,7 @@ class Settings(TypedDict):
58 agent_knowledge_subdir: str
59
60 memory_recall_enabled: bool
61 + memory_recall_delayed: bool
62 memory_recall_interval: int
63 memory_recall_history_len: int
64 memory_recall_memories_max_search: int
@@ -643,6 +644,16 @@ def convert_out(settings: Settings) -> SettingsOutput:
644 }
645 )
646
647 + memory_fields.append(
648 + {
649 + "id": "memory_recall_delayed",
650 + "title": "Memory auto-recall delayed",
651 + "description": "The agent will not wait for auto memory recall. Memories will be delivered one message later. This speeds up agent's response time but may result in less relevant first step.",
652 + "type": "switch",
653 + "value": settings["memory_recall_delayed"],
654 + }
655 + )
656 +
657 memory_fields.append(
658 {
659 "id": "memory_recall_query_prep",
@@ -1319,7 +1330,7 @@ def get_default_settings() -> Settings:
1330 chat_model_rl_input=0,
1331 chat_model_rl_output=0,
1332 util_model_provider="openrouter",
1322 - util_model_name="openai/gpt-5-mini",
1333 + util_model_name="google/gemini-2.5-flash-lite",
1334 util_model_api_base="",
1335 util_model_ctx_length=100000,
1336 util_model_ctx_input=0.7,
@@ -1342,6 +1353,7 @@ def get_default_settings() -> Settings:
1353 browser_model_rl_output=0,
1354 browser_model_kwargs={"temperature": "0"},
1355 memory_recall_enabled=True,
1356 + memory_recall_delayed=False,
1357 memory_recall_interval=3,
1358 memory_recall_history_len=10000,
1359 memory_recall_memories_max_search=12,