> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Lab Walkthrough Add Persistent Memory

> Guide to adding file-backed persistent memory to a Python agent, saving and loading user preferences, injecting them into the system prompt, and safely calling tools.

Time for a lab.

In this lesson you'll add long-term persistence to an agent so preferences survive after the terminal closes. The pattern is simple:

* Save user preferences to a JSON file on disk.
* Load that JSON at startup.
* Inject the loaded preferences into the system prompt so the model has awareness of previously saved state.

Environment & assumptions

* Python and a virtual environment are available.
* The OpenAI Python SDK is installed.
* Your working directory is `/root/code`.
* This walkthrough creates a single file: `persistent_agent.py`.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/z7NmHsFQN9LCEiD0/images/AI-Agents-for-Beginners-OpenClaw-Case-Study/Production-OpenClaw/Lab-Walkthrough-Add-Persistent-Memory/retro-pixel-ui-add-persistent-memory.jpg?fit=max&auto=format&n=z7NmHsFQN9LCEiD0&q=85&s=3d189e4afa294936d63c9c5e2994c900" alt="A retro, pixel-art UI screen that says &#x22;ADD PERSISTENT MEMORY&#x22; with the subtitle &#x22;Give your agent long-term memory&#x22; and a small &#x22;HANDS‑ON LAB&#x22; badge. It shows neon buttons labeled SAVE, LOAD, and INJECT on a dark grid background." width="1920" height="1080" data-path="images/AI-Agents-for-Beginners-OpenClaw-Case-Study/Production-OpenClaw/Lab-Walkthrough-Add-Persistent-Memory/retro-pixel-ui-add-persistent-memory.jpg" />
</Frame>

What you'll build

* A safe-agent pattern with a simple `check_calendar` tool and a `try_call_tool` wrapper.
* A persistent memory layer backed by `agent_memory.json`.
* `save_preference` and `forget_preference` handlers.
* System prompt injection of loaded preferences at startup.
* A small main loop demonstrating the flow.

<Callout icon="lightbulb" color="#1CB2FE">
  This lab focuses on the memory pattern and safe tool execution. In production, replace the local JSON file with a secure database and ensure sensitive information is encrypted.
</Callout>

Step-by-step implementation

1. File and client setup
   Create `persistent_agent.py` and start with imports, client initialization, and constants:

```python theme={null}
# persistent_agent.py
import os
import json
from openai import OpenAI
from typing import Dict, Any

# Client setup (reads environment variables)
client = OpenAI(
    api_key=os.getenv("OPENAI_API_KEY"),
    base_url=os.getenv("OPENAI_API_BASE"),
)

MAX_ITERATIONS = 10
MEMORY_FILE = "agent_memory.json"
```

2. Safe tool pattern
   Define a simple calendar "tool" and a `try_call_tool` helper that catches exceptions and returns a safe string. This keeps the agent robust when tools fail.

```python theme={null}
def check_calendar(date: str) -> str:
    """
    A baseline 'tool' that simulates checking the calendar.
    In production this would call a real calendar API.
    """
    # Simulated response
    return f"Checked calendar for {date}: 1pm available."

def try_call_tool(func, *args, **kwargs):
    """
    Call a tool handling exceptions and returning a safe string result.
    """
    try:
        return func(*args, **kwargs)
    except Exception as e:
        return f"Tool error: {e}"
```

3. Persistent memory layer
   Add the functions to load/save JSON and the two preference handlers: `save_preference` and `forget_preference`. These read/write `agent_memory.json` and return human-readable status strings for the agent.

```python theme={null}
def load_memory() -> Dict[str, Any]:
    """
    Loads memory from MEMORY_FILE and returns a dict.
    Returns an empty dict if the file does not exist or is invalid.
    """
    if not os.path.exists(MEMORY_FILE):
        return {}
    try:
        with open(MEMORY_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        # In case of parse error or other IO issue, return empty memory
        return {}

def save_memory(memory: Dict[str, Any]) -> None:
    """
    Saves memory dict to MEMORY_FILE as formatted JSON.
    """
    with open(MEMORY_FILE, "w", encoding="utf-8") as f:
        json.dump(memory, f, indent=2, ensure_ascii=False)

def save_preference(key: str, value: Any) -> str:
    """
    Adds or updates a preference in memory and persists it.
    """
    memory = load_memory()
    memory[key] = value
    save_memory(memory)
    return f"Saved: {key} = {value}"

def forget_preference(key: str) -> str:
    """
    Removes a key from memory if present and persists the change.
    """
    memory = load_memory()
    if key in memory:
        del memory[key]
        save_memory(memory)
        return f"{key} removed from memory"
    return f"{key} not found in memory"
```

4. Optional: expose tool metadata
   If your agent framework expects a tool registry, provide metadata for `save_preference` and `forget_preference`. This is optional but helpful for structured agent orchestrators.

```python theme={null}
tools = [
    {
        "type": "function",
        "function": {
            "name": "save_preference",
            "parameters": {
                "required": ["key", "value"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "forget_preference",
            "parameters": {
                "required": ["key"]
            }
        }
    },
    # The calendar check could also be exposed here if your agent framework uses this list.
]
```

5. Inject memory into the system prompt
   Load the memory at startup and append known preferences to the system prompt. This ensures the model is aware of persistent state without requiring the user to repeat preferences.

```python theme={null}
def build_system_prompt() -> str:
    memory = load_memory()
    base_prompt = "You are a helpful personal assistant."
    if memory:
        # Use json.dumps to produce valid JSON for clarity
        prefs_json = json.dumps(memory, ensure_ascii=False)
        return f"{base_prompt}\nKnown user preferences: {prefs_json}"
    return base_prompt
```

6. Main loop: demonstration of flow
   A minimal main loop shows the overall pattern: build the system prompt (with memory injected), process messages, call tools safely, and save/forget preferences as appropriate. This example simulates agent behavior and is intentionally straightforward to illustrate the pattern.

```python theme={null}
def main():
    system_prompt = build_system_prompt()
    print("System prompt:")
    print(system_prompt)

    # Simulated user messages for demonstration purposes.
    # In practice you'd call the model and parse tool calls from its response.
    user_messages = [
        "I prefer afternoon meetings and I am vegetarian.",
        "Schedule a lunch meeting for me this week.",
        "Forget that I am vegetarian."
    ]

    # Simulated "agent" behavior: detect simple keywords and call tools
    for i, msg in enumerate(user_messages, start=1):
        print(f"\nUser: {msg}")

        # Example: detect a preference statement and save it
        if "prefer afternoon" in msg or "afternoon meetings" in msg:
            print("Calling save_preference: meeting_time = afternoon")
            print(save_preference("meeting_time", "afternoon"))

        if "vegetarian" in msg and "forget" not in msg:
            print("Calling save_preference: diet = vegetarian")
            print(save_preference("diet", "vegetarian"))

        # Example: schedule: use calendar tool and honor stored preferences
        if "Schedule a lunch" in msg or "lunch meeting" in msg:
            mem = load_memory()
            # The agent sees the memory via the system prompt; simulate using it here.
            meeting_time = mem.get("meeting_time", "no preference found")
            diet = mem.get("diet", "no preference found")
            print(f"Loaded preferences: {json.dumps(mem)}")
            # Simulate calling the calendar tool (safe call)
            cal_res = try_call_tool(check_calendar, "this Thursday")
            print(f"Agent: I'll schedule your lunch meeting for this Thursday {meeting_time} at 1pm.")
            print(f"Calendar result: {cal_res}")

        # Example: forget preference
        if "Forget" in msg or "forget" in msg:
            # find key to forget; this is a simplified example
            if "vegetarian" in msg:
                print(forget_preference("diet"))

        if i >= MAX_ITERATIONS:
            break

if __name__ == "__main__":
    main()
```

Running the script

* Run with: `python3 persistent_agent.py`
* Observe the printed system prompt (memory is injected) and the simulated session.

Example session output:

```text theme={null}
System prompt:
You are a helpful personal assistant.

User: I prefer afternoon meetings and I am vegetarian.
Calling save_preference: meeting_time = afternoon
Saved: meeting_time = afternoon
Calling save_preference: diet = vegetarian
Saved: diet = vegetarian

User: Schedule a lunch meeting for me this week.
Loaded preferences: {"meeting_time": "afternoon", "diet": "vegetarian"}
Agent: I'll schedule your lunch meeting for this Thursday afternoon at 1pm.
Calendar result: Checked calendar for this Thursday: 1pm available.

User: Forget that I am vegetarian.
diet removed from memory
```

Persisted memory file
After the first run, check `agent_memory.json` to confirm persistence:

```bash theme={null}
root@controlplane ~/code (venv) $ cat agent_memory.json
{
  "meeting_time": "afternoon",
  "diet": "vegetarian"
}
```

Run the script again (or change the simulated messages) to see the system prompt print the loaded preferences:

```text theme={null}
System prompt:
You are a helpful personal assistant.
Known user preferences: {"meeting_time": "afternoon", "diet": "vegetarian"}
```

Final notes and production guidance

<Callout icon="warning" color="#FF6B6B">
  Storing sensitive user data or secrets in plaintext JSON on disk is not secure. For production systems, use an encrypted store or a database with proper access controls and encryption at rest.
</Callout>

* The pattern shown here is portable: swap `agent_memory.json` for a database or key-value store, and replace the simulated `check_calendar` tool with a real calendar API.
* Consider schema validation for preferences and limits on memory size to avoid letting the system prompt become too large.

Quick reference table

| Component | Purpose | Example / Notes |
| - | - | - |
| `MEMORY_FILE` | Path to JSON memory store | `agent_memory.json` |
| `load_memory()` | Read and return memory dict | Returns `{}` if missing or invalid |
| `save_memory()` | Persist memory dict to disk | Uses `json.dump(..., indent=2)` |
| `save_preference(key, value)` | Save or update a preference | `save_preference("diet", "vegetarian")` |
| `forget_preference(key)` | Remove a preference | `forget_preference("diet")` |
| `check_calendar(date)` | Simulated calendar tool | Replace with real API in production |
| `try_call_tool(func, ...)` | Safe tool invocation wrapper | Returns `Tool error: ...` on exceptions |
| `build_system_prompt()` | Injects loaded memory into system prompt | Uses `json.dumps(memory)` for readability |

Links and references

* [OpenAI Python client docs](https://platform.openai.com/docs/api-reference?lang=python)
* [Python json module](https://docs.python.org/3/library/json.html)

Save, load, forget — that's the persistent-memory layer. Adapt this pattern to your infrastructure and production-grade storage for robust, long-term agent memory.

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/ai-agents-for-beginner-openclaw-case-study/module/b8b38b25-c4eb-425f-a093-cec426365977/lesson/215c1af1-7e81-49b2-a0f3-02620fc858c6" />
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.