Feature: added memory maintenance memory as part of onboarding

Rephrased onboarding prompt to adjust to the recommended memory management
This commit is contained in:
Michael Panchenko committed 2026-05-13 23:31:31 +02:00
1 parent 18874913da
commit bc9d4e050c
5 files changed
+134 -51

No files matched your search

@@ -14,39 +14,31 @@ class PromptFactory(PromptFactoryBase):
"""
def get_info_jet_brains_debug_repl_template_string(self) -> str:
return self.get_prompt_template_string("info_jet_brains_debug_repl")
return self.get_prompt_template_string('info_jet_brains_debug_repl')
def create_info_jet_brains_debug_repl(self) -> str:
return self._render_prompt("info_jet_brains_debug_repl", locals())
return self._render_prompt('info_jet_brains_debug_repl', locals())
def get_onboarding_prompt_template_string(self) -> str:
return self.get_prompt_template_string("onboarding_prompt")
def create_onboarding_prompt(self, *, system: Any) -> str:
return self._render_prompt("onboarding_prompt", locals())
return self.get_prompt_template_string('onboarding_prompt')
def create_onboarding_prompt(self, *, memory_maintenance_name: Any, system: Any) -> str:
return self._render_prompt('onboarding_prompt', locals())
def get_connection_prompt_template_string(self) -> str:
return self.get_prompt_template_string("connection_prompt")
return self.get_prompt_template_string('connection_prompt')
def create_connection_prompt(self) -> str:
return self._render_prompt("connection_prompt", locals())
return self._render_prompt('connection_prompt', locals())
def get_system_prompt_template_string(self) -> str:
return self.get_prompt_template_string("system_prompt")
def create_system_prompt(
self,
*,
available_markers: Any,
available_tools: Any,
context_system_prompt: Any,
global_memories_list: Any,
mode_system_prompts: Any,
) -> str:
return self._render_prompt("system_prompt", locals())
return self.get_prompt_template_string('system_prompt')
def create_system_prompt(self, *, available_markers: Any, available_tools: Any, context_system_prompt: Any, global_memories_list: Any, mode_system_prompts: Any) -> str:
return self._render_prompt('system_prompt', locals())
def get_cc_system_prompt_override_template_string(self) -> str:
return self.get_prompt_template_string("cc_system_prompt_override")
return self.get_prompt_template_string('cc_system_prompt_override')
def create_cc_system_prompt_override(self) -> str:
return self._render_prompt("cc_system_prompt_override", locals())
return self._render_prompt('cc_system_prompt_override', locals())
+37
View File
@@ -93,6 +93,43 @@ class MemoriesManager:
name = cls._prepare_name(name)
return cls._MEMORY_REF_PREFIX + name
MEMORY_MAINTENANCE_NAME: str = "memory_maintenance"
_MEMORY_MAINTENANCE_TEMPLATE_PATH: Path = Path(__file__).parent / "resources" / "memory_maintenance.md"
def ensure_memory_maintenance_memory(self) -> str:
"""
Ensures a memory describing how memories should be maintained exists for this project,
and returns the name to reference it by.
Precedence:
1. If a global copy exists at ``global/memory_maintenance``, return that name; no
project copy is created (the global version takes precedence).
2. Else if a project copy already exists, return its name unchanged.
3. Else seed a project copy from the package-shipped template and return that name.
Existing memory files are never overwritten; users may have customized them. To
refresh from the shipped template, delete the existing memory first.
:return: the bare name to reference the maintenance memory by (without the ``mem:``
prefix); either ``"global/memory_maintenance"`` or ``"memory_maintenance"``.
:raises FileNotFoundError: if the shipped template is missing on disk.
:raises AssertionError: if this manager has no associated project directory.
"""
global_name = f"{self.GLOBAL_TOPIC}/{self.MEMORY_MAINTENANCE_NAME}"
if self.get_memory_file_path(global_name).exists():
return global_name
if self.get_memory_file_path(self.MEMORY_MAINTENANCE_NAME).exists():
return self.MEMORY_MAINTENANCE_NAME
# seed a project copy from the shipped template
template_path = self._MEMORY_MAINTENANCE_TEMPLATE_PATH
if not template_path.exists():
raise FileNotFoundError(f"Memory maintenance template not found at {template_path}")
content = template_path.read_text(encoding=self._encoding)
self.save_memory(self.MEMORY_MAINTENANCE_NAME, content, is_tool_context=False)
return self.MEMORY_MAINTENANCE_NAME
def rename_references_to_memory(self, content: str, old_name: str, new_name: str) -> tuple[str, int]:
r"""
Replaces all occurrences of a memory reference (e.g. ``mem:foo``) in ``content`` with
@@ -4,33 +4,44 @@
prompts:
onboarding_prompt: |
You are viewing the project for the first time.
Your task is to assemble relevant high-level information about the project which
will be saved to memory files in the following steps.
The information should be sufficient to understand what the project is about,
and the most important commands for developing code.
Your task is to assemble durable, non-obvious information about the project and write it
to memory files that future agents will consult.
The project is being developed on the system: {{ system }}.
You need to identify at least the following information:
* the project's purpose
* the tech stack used
* the code style and conventions used (including naming, type hints, docstrings, etc.)
* which commands to run when a task is completed (linting, formatting, testing, etc.)
* the rough structure of the codebase
* the commands for testing, formatting, and linting
* the commands for running the entrypoints of the project
* the util commands for the system, like `git`, `ls`, `cd`, `grep`, `find`, etc. Keep in mind that the system is {{ system }},
so the commands might be different than on a regular unix system.
* whether there are particular guidelines, styles, design patterns, etc. that one should know about
**Before writing anything, read `mem:{{ memory_maintenance_name }}`** using the `read_memory` tool.
It defines the style, naming, reference, and add/update threshold conventions that every
memory in this project must follow. Do not skip this step.
This list is not exhaustive, you can add more information if you think it is relevant.
Target memory layout — use the `write_memory` tool, one call per memory:
For doing that, you will need to acquire information about the project with the corresponding tools.
Read only the necessary files and directories to avoid loading too much data into memory.
If you cannot find everything you need from the project itself, you should ask the user for more information.
* `mem:core` — top-level routing/source map: a terse index pointing at the focused memories
below, plus only those project-wide invariants that don't belong in a focused memory.
* `mem:tech_stack` — language(s), framework(s), build tools, package manager, version
pins where they matter.
* `mem:suggested_commands` — project commands the user will actually run (dev, test, lint,
format, run entrypoints) and any system util commands (`git`, `ls`, `grep`, …) whose
form differs on {{ system }} from a standard unix shell. Skip generic commands that
behave identically across systems.
* `mem:conventions` — code style, naming, type hints, docstring conventions, design
patterns specific to this codebase.
* `mem:task_completion` — exact commands to run when a coding task is considered done
(linter, formatter, test runner, type checker, etc.).
After collecting all the information, you will use the `write_memory` tool (in multiple calls) to save it to various memory files.
A particularly important memory file will be the `suggested_commands.md` file, which should contain
a list of commands that the user should know about to develop code in this project.
Moreover, you should create memory files for the style and conventions and a dedicated memory file for
what should be done when a task is completed.
**Important**: after done with the onboarding task, remember to call the `write_memory` to save the collected information!
If the project has clearly distinct modules (e.g. frontend/backend), create per-module
`mem:<module>/core` memories for module-specific routing instead of pushing everything
into `mem:core`.
Acquire information using the available tools. Read only the files needed; do not load
entire directory trees. If essential information is missing, ask the user.
Memory body style (full rules in `mem:{{ memory_maintenance_name }}`): dense agent notes —
terse bullets, invariants, paths, commands. No self-referential cues ("Use when…",
"Start here…", "Read before…"); routing belongs in `mem:core` upstream, not inside the
body it points to. References to other memories use `` `mem:NAME` `` (backticked, with
the `mem:` prefix).
When all memories are written, sanity-check the references by mentioning that the user can run
`serena memories check` from the project root.
**Important**: the onboarding is only complete once you have actually called `write_memory`
for each memory above — do not summarize the content in chat and skip writing.
@@ -0,0 +1,40 @@
# Memory Maintenance
## Discovery Model
- Agents start with the full memory name list and should read `mem:core` first as the routing entry point.
- All other memory discovery comes from memory names plus routing already read.
- Routing is useful only before the target memory is selected. Once a memory is open, target-selection text inside it can no longer help decide to read it.
- Therefore, never put self-referential selection cues in a memory body: no "Start here", "Use when", "Use before", "Use after", or "read this when..." openers.
Such self-referential cues are an error mode: they are unreachable for selection, duplicate routing that belongs upstream, and drift independently from the actual routing graph.
- Add routing only when names are insufficient, ambiguous, or a non-obvious dependency/order matters. Do not add tautological routing such as "frontend debugging -> frontend/debugging".
- Routing can live outside core memories when needed, but core memories own most module-level routing because they are read before focused memories in that subfolder.
- Top-level/startup memory is `mem:core`; top-level memory in each memory subfolder is `<folder>/core` (for example `mem:frontend/core`).
- Memory references must use a mem: prefix inside backticks, e.g. `mem:frontend/core`.
## Style
Dense agent notes, not prose docs. Prefer invariants, paths, commands, terse bullets. Avoid obvious context, rationale, and examples unless they prevent likely mistakes. Keep guidance durable and generalizable, not task-local.
## Add/update threshold
Add or update memory only for stable, non-obvious project conventions that avoid complex rediscovery.
Do not add: quick-read/rg facts; generic language/framework knowledge; one-off task notes; volatile line-level details; behavior likely to change soon.
Operational constants (commands, paths, version pins): phrase as current behavior; before changing the value, update it in the owning memory and run `serena memories check` to catch references that need updating.
## Graph Ownership
- Project-level routing: `mem:core`.
- Module-level routing/source maps: module core memories.
- Focused memories: topic details only; assume relevant routing was read before selection.
## Maintenance Actions
- Renamed/removed/split/merged/superseded memory: update all references. Done automatically if handled via Serena's memory rename tool.
- Stale paths/namespaces/commands/behavior: update or remove in owning memory. If working with Serena, call `serena memories check` for a report.
- Split catch-all memory when sections are commonly needed independently or agents must read unrelated domains to reach needed topic.
- Retire/delete memory when owning module/subsystem is removed or replaced; remove all references.
- Added/renamed/split/moved/deleted memory: update affected routing only where names are insufficient.
- Before finishing: list memories for old names and key new names to catch stale/missing references.
+4 -1
View File
@@ -54,7 +54,10 @@ class OnboardingTool(Tool):
if not write_memory_tool_available:
return "Memory writing tool not activated, skipping onboarding."
system = platform.system()
return self.prompt_factory.create_onboarding_prompt(system=system)
# seed the project-local memory-maintenance memory (or detect a global override) so
# the prompt can point the agent at the conventions before it writes anything
memory_maintenance_name = self.memories_manager.ensure_memory_maintenance_memory()
return self.prompt_factory.create_onboarding_prompt(system=system, memory_maintenance_name=memory_maintenance_name)
class InitialInstructionsTool(Tool, ToolMarkerDoesNotRequireActiveProject):