5.7 KiB
description, icon
| description | icon |
|---|---|
| Durable, tool-scoped rules for safety-critical guidance and learnings. | shield-check |
Tool-Scoped Memory
The tool-scoped memory layer captures actionable guidance about how the agent should use specific tools - separate from the Memory Tools general-purpose recall and from the tool_effectiveness statistics namespace. It is the surface that turns "never email Sarah" into a hard constraint the agent has to obey on every subsequent turn.
It implements issue #1400 - a first-class storage and retrieval system for durable learnings and high-priority rules.
What it stores
Every tool gets its own namespace, tool-{tool_name}, distinct from global, skill-{id}, and the statistics-only tool_effectiveness namespace. Inside it, each entry is a ToolMemoryRule:
| Field | Purpose |
|---|---|
id |
Stable per-rule UUID. Upserts replay the same id. |
tool_name |
The tool the rule applies to (e.g. send_email, shell). |
rule |
Natural-language guidance the agent must follow. |
priority |
critical, high, or normal. Drives retrieval + compression. |
source |
user_explicit, post_turn, or programmatic - provenance. |
tags |
Free-form labels (safety, permission, ...). |
created_at / updated_at |
RFC3339 timestamps. |
Statistics (tool_effectiveness/tool/{name}) and rules (tool-{name}/rule/{id}) live in different namespaces by design - one tracks "what happened", the other tracks "what to do about it".
Priority levels
| Priority | Where it lives | Compression-resistant? |
|---|---|---|
critical |
Pinned into the system prompt via ToolMemoryRulesSection. |
Yes - the system prompt is frozen per-session and never rewritten by the mid-session compactor. |
high |
Same system prompt block, ranked below critical. | Yes - same mechanism. |
normal |
Stored in the namespace; retrieved on demand via memory_recall. |
No - eligible for compression like any other namespaced memory. |
The compression-resistance property is structural: critical and high rules ride in the system prompt, which the inference backend's prefix cache keeps frozen for the entire session. There is no way for token compression to silently drop a critical rule.
Capture pipeline
Two automatic capture paths fire after every turn (via ToolMemoryCaptureHook):
- User edicts - sentences like
never <verb> <noun>,don't <verb> ...,do not <verb> ..., orstop <verb>ing ...in the user message are promoted to a Critical rule on the matching tool. Common-noun aliases map"email"to a tool namedsend_email,"shell"tobash/exec, etc.; when no alias matches, the rule lands on the first tool that ran in the turn so it stays adjacent to the relevant call site. - Repeated tool failures - a tool that fails twice or more in a single turn earns a Normal-priority observation, with the failure class summarized inline so the agent has context next time it considers that tool.
The hook is enabled by default whenever the learning subsystem is on. Disable selectively with OPENHUMAN_LEARNING_TOOL_MEMORY_CAPTURE_ENABLED=0.
Retrieval at tool-selection time
At session start the harness pre-fetches every Critical and High rule via ToolMemoryStore::rules_for_prompt, renders them into the ## Tool-scoped rules block, and pins the block into the system prompt. Because the prompt is frozen for the lifetime of the session, the rules are visible on every turn at tool-selection time and before any actual tool execution.
Lower-priority guidance stays out of the prompt budget; the agent reaches it on demand by calling memory_recall against the tool-{name} namespace.
RPC surface
Six methods are exposed under the memory namespace:
| Method | Purpose |
|---|---|
memory.tool_rule_put |
Upsert a rule. Use priority='critical' for safety-critical entries. |
memory.tool_rule_get |
Fetch a rule by (tool_name, id). |
memory.tool_rule_list |
List all rules for a tool, sorted by priority + freshness. |
memory.tool_rule_delete |
Delete a rule. |
memory.tool_rules_for_prompt |
Return the rendered Markdown block + structured snapshot - what the session builder pins. |
memory.tool_rules_json |
Raw JSON list (for envelope consumers). |
JSON payloads use snake_case (priority: "critical", source: "user_explicit"). Every method goes through the same active_memory_client plumbing as the rest of the memory RPCs.
End-to-end safety case
The "never email Sarah" path is covered as a regression test:
- User says "Never email Sarah at sarah@example.com." during a turn that called
send_email. ToolMemoryCaptureHookextracts the edict, maps theemailalias to thesend_emailtool, and writes a Critical rule undertool-send_email/rule/{uuid}.- On the next session,
prefetch_tool_memory_rules_blockingpulls every Critical and High rule and the session builder appends aToolMemoryRulesSectionto the system prompt. - The agent sees
### \send_email`followed by- [critical] Never email Sarah at sarah@example.com.` before ever choosing a tool, and the rule survives any mid-session token compression.
Coverage and the integration test live in src/openhuman/memory/tool_memory/.
See also
- Memory Tools - general-purpose
recall,store,forget. - Smart Token Compression - what the system prompt is protected against.