# Memory Management and Context Persistence Across Agent Interactions in ChatDev: A Deep Dive into the Architecture

> Explore ChatDev's layered memory system for AI agents. Discover how it stores, retrieves, and persists context across interactions using abstracted stores and graph orchestration.

- Repository: [OpenBMB/ChatDev](https://github.com/OpenBMB/ChatDev)
- Tags: deep-dive
- Published: 2026-04-01

---

**ChatDev implements a layered memory system that allows AI agents to store, retrieve, and persist context across workflow runs through abstracted memory stores, configurable attachments, and automatic graph-level orchestration.**

The OpenBMB/ChatDev framework treats **memory** as a first-class component, enabling agents to reason over past information both within a single execution and across successive sessions. This article examines the concrete implementation of the memory subsystem, from the abstract `MemoryBase` API to the `GraphExecutor` orchestration logic that binds persistent stores to individual agent nodes.

## Memory Architecture Overview

ChatDev’s memory system is built on a three-tier architecture that separates storage abstractions from concrete implementations and runtime management.

### The MemoryBase Abstraction Layer

At the core of the system lies the `MemoryBase` abstract class defined in [`runtime/node/agent/memory/memory_base.py`](https://github.com/OpenBMB/ChatDev/blob/main/runtime/node/agent/memory/memory_base.py). This interface declares four fundamental operations that every memory store must implement:

- **`load()`** – Restores persisted data from disk or external storage.
- **`save()`** – Persists the current memory state to durable storage.
- **`retrieve(query, top_k)`** – Fetches relevant memory items based on a query snapshot.
- **`update(payload)`** – Writes new memory content and immediately triggers persistence.

The `MemoryManager` class (lines 33-44 in the same file) acts as the runtime intermediary between agent nodes and their underlying stores. It maintains a registry of **memory attachments**—configurable bindings that define read/write permissions, retrieval stages, and similarity thresholds for each store.

### Concrete Store Implementations

The framework provides three built-in persistence strategies, each optimized for different scalability and sharing requirements:

**`SimpleMemory`** ([`runtime/node/agent/memory/simple_memory.py`](https://github.com/OpenBMB/ChatDev/blob/main/runtime/node/agent/memory/simple_memory.py)) maintains an in-memory dictionary suitable for transient sessions and testing scenarios where durability is not required.

**`FileMemory`** ([`runtime/node/agent/memory/file_memory.py`](https://github.com/OpenBMB/ChatDev/blob/main/runtime/node/agent/memory/file_memory.py)) extends the base functionality with JSON-based persistence and optional embedding-based similarity search, automatically resolving `auto` paths to workflow-specific directories.

**`BlackboardMemory`** ([`runtime/node/agent/memory/blackboard_memory.py`](https://github.com/OpenBMB/ChatDev/blob/main/runtime/node/agent/memory/blackboard_memory.py)) implements a shared knowledge pool that multiple agents can concurrently read and write, enabling collective reasoning across distributed nodes.

## Configuration and Factory Pattern

ChatDev decouples memory store instantiation from workflow logic through a factory pattern and YAML-based configuration.

### Defining Stores in Workflow YAML

Workflow authors declare memory stores in the top-level `memory` section and bind them to specific agents via the `memories` attachment list. The `MemoryAttachmentConfig` model (defined in [`entity/configs/node/memory.py`](https://github.com/OpenBMB/ChatDev/blob/main/entity/configs/node/memory.py)) validates these bindings at runtime.

```yaml
memory:
  - name: session_mem
    type: simple
    memory_path: auto

nodes:
  - id: agent1
    type: agent
    config:
      memories:
        - name: session_mem
          read: true
          write: true
          top_k: 5
          retrieve_stage: [input]

```

When the executor encounters `memory_path: auto`, it generates a JSON file under the workflow directory (e.g., [`memory_session_mem.json`](https://github.com/OpenBMB/ChatDev/blob/main/memory_session_mem.json)), ensuring automatic persistence without manual path management.

### The MemoryFactory Registry

The `MemoryFactory` class in [`runtime/node/agent/memory/builtin_stores.py`](https://github.com/OpenBMB/ChatDev/blob/main/runtime/node/agent/memory/builtin_stores.py) (lines 37-42) registers concrete store types and instantiates the appropriate implementation based on the `type` field. This registry pattern allows developers to extend ChatDev with custom storage backends by registering new `MemoryBase` subclasses.

```python
from runtime.node.agent.memory.builtin_stores import MemoryFactory
from entity.configs.node.memory import SimpleMemoryConfig

simple_cfg = SimpleMemoryConfig(name="test_mem", type="simple", memory_path="tmp_mem.json")
store = MemoryFactory.create_memory(simple_cfg)  # Returns SimpleMemory instance

store.load()  # Hydrates from existing JSON or initializes empty

```

## Runtime Orchestration and Persistence

The `GraphExecutor` class in [`workflow/graph.py`](https://github.com/OpenBMB/ChatDev/blob/main/workflow/graph.py) orchestrates memory lifecycle events, ensuring that persistence occurs automatically without explicit agent intervention.

### GraphExecutor Memory Lifecycle

During initialization, `GraphExecutor.__init__` prepares two critical dictionaries: `global_memories` (shared stores) and `agent_memory_managers` (per-node managers). The `_build_global_memories` method (lines 50-81) iterates over the workflow configuration, resolves file paths, invokes `MemoryFactory.create_memory`, and calls `load()` to restore previous session data.

Subsequently, `_build_agent_memories` constructs a `MemoryManager` instance for every agent node, mapping declared attachments to the already-initialized global stores. This two-phase construction ensures that shared stores exist before any agent attempts to reference them.

### Per-Agent MemoryManager Binding

When a node executes, it receives its dedicated `MemoryManager` via the `ExecutionContext`. The manager exposes two primary methods used by node executors:

- **`retrieve(agent_role, query, current_stage)`** (lines 49-88) – Scores items across all readable attachments and returns a `MemoryRetrievalResult` containing formatted text ready for injection into the agent’s prompt.
- **`update(payload)`** – Accepts a `MemoryWritePayload` containing input/output snapshots, persists the data via the underlying store’s `save()` method, and updates the retrieval index.

```python
from runtime.node.agent.memory.memory_base import MemoryContentSnapshot

# Inside a node executor

mem_mgr = node.execution_context.memory_managers[node.id]
query = MemoryContentSnapshot.from_message(user_message)

result = mem_mgr.retrieve(
    agent_role="assistant",
    query=query,
    current_stage=AgentExecFlowStage.INPUT
)

```

### Automatic Save and Load Mechanics

At workflow completion, `GraphExecutor._save_memories` (lines 55-59) iterates through all global stores and invokes `save()`, flushing in-memory state to disk. On the next execution, the initialization sequence calls `load()` on each store, restoring the exact knowledge state from the previous run and providing true **context persistence across sessions**.

## Retrieval Scoring and Context Traces

Beyond simple storage, ChatDev implements intelligent retrieval algorithms and cross-turn context management.

### Time-Decay Scoring Algorithm

The `_score_memory` method in [`memory_base.py`](https://github.com/OpenBMB/ChatDev/blob/main/memory_base.py) (lines 90-104) implements a multi-factor relevance calculation that combines:

- **Time-decay factor** – Exponentially reduces scores for older memories.
- **Length bias** – Prefers concise memories to minimize token consumption.
- **Lexical relevance** – Measures similarity between the query and memory content.

This composite scoring ensures that recent, relevant, and compact memories surface first, while older information remains accessible with reduced priority.

### Cross-Turn Context Persistence

For agents requiring conversation continuity within a single workflow, ChatDev utilizes the **context trace** mechanism. When a node produces output containing a `"context_trace"` metadata field (a serialized list of previous messages), the `GraphExecutor._restore_context_trace` method (lines 92-116) automatically deserializes this payload and prepends the messages to the node’s input queue on the next execution cycle.

This feature is activated when a node configuration specifies `context_window != 0`, allowing agents to "remember" their previous actions and responses across distinct turns without requiring external memory stores.

## Implementation Examples

### Programmatic Memory Management

For testing or custom integrations, developers can manually instantiate stores and managers:

```python
from runtime.node.agent.memory.memory_base import MemoryManager, MemoryWritePayload, MemoryContentSnapshot
from runtime.node.agent.memory.builtin_stores import MemoryFactory
from entity.configs.node.memory import MemoryAttachmentConfig

# Initialize store

store = MemoryFactory.create_memory(simple_cfg)
store.load()

# Configure attachment

attachment = MemoryAttachmentConfig(
    name="test_mem",
    read=True,
    write=True,
    top_k=3,
    retrieve_stage=None,
    similarity_threshold=0.5
)

# Create manager and write data

mgr = MemoryManager([attachment], {"test_mem": store})

payload = MemoryWritePayload(
    agent_role="assistant",
    inputs_text="User asked about weather",
    input_snapshot=MemoryContentSnapshot.from_message("User asked about weather"),
    output_snapshot=MemoryContentSnapshot.from_message("It will be sunny tomorrow.")
)

mgr.update(payload)  # Persists instantly via store.save()

```

### Context Trace Serialization

Nodes can preserve conversation state for subsequent turns:

```python

# Creating a context trace

output_msg = Message(
    role=MessageRole.ASSISTANT,
    content="Processing complete.",
    metadata={"context_trace": [msg.to_dict() for msg in node.input]}
)

# On next execution, GraphExecutor._restore_context_trace automatically

# deserializes the trace and injects messages into node.input,

# maintaining continuity across the context window.

```

## Summary

- **ChatDev** implements a robust memory management and context persistence system through the `MemoryBase` abstraction and `GraphExecutor` orchestration.
- Three concrete store types—**`SimpleMemory`**, **`FileMemory`**, and **`BlackboardMemory`**—provide flexibility for transient testing, durable persistence, and multi-agent sharing respectively.
- The **`MemoryManager`** mediates all agent interactions with stores, handling retrieval scoring and automatic updates via `retrieve()` and `update()` methods.
- **`GraphExecutor`** manages the complete lifecycle, constructing global stores at startup, binding per-node managers, and ensuring persistence via `_save_memories()` at workflow completion.
- **Time-decay scoring** and **context traces** provide intelligent retrieval and cross-turn continuity without manual state management.

## Frequently Asked Questions

### How does ChatDev persist memory between workflow runs?

ChatDev persists memory through the `FileMemory` implementation and the `GraphExecutor._save_memories` method. At the end of each workflow execution (lines 55-59 in [`workflow/graph.py`](https://github.com/OpenBMB/ChatDev/blob/main/workflow/graph.py)), the executor calls `save()` on every global memory store, serializing the state to JSON files. On the next run, `_build_global_memories` invokes `load()` on each store, restoring the previous session's data and enabling agents to resume with full historical context.

### What is the difference between SimpleMemory and FileMemory in ChatDev?

**`SimpleMemory`** ([`runtime/node/agent/memory/simple_memory.py`](https://github.com/OpenBMB/ChatDev/blob/main/runtime/node/agent/memory/simple_memory.py)) stores data only in Python dictionaries, making it ideal for unit tests or ephemeral sessions where disk I/O is unnecessary. **`FileMemory`** ([`runtime/node/agent/memory/file_memory.py`](https://github.com/OpenBMB/ChatDev/blob/main/runtime/node/agent/memory/file_memory.py)) extends this with JSON serialization to disk and optional embedding-based retrieval, automatically managing file paths and ensuring data survives process termination. Production workflows typically use `FileMemory` or `BlackboardMemory` for durability.

### How does the scoring algorithm prioritize recent memories?

The `_score_memory` method in [`runtime/node/agent/memory/memory_base.py`](https://github.com/OpenBMB/ChatDev/blob/main/runtime/node/agent/memory/memory_base.py) (lines 90-104) applies a composite algorithm that weights recent items with an exponential time-decay factor, penalizes excessively long memories with a length bias, and rewards lexical similarity to the query. This ensures that the `retrieve()` method returns memories that are recent, concise, and semantically relevant to the current agent context.

### Can multiple agents share the same memory store in ChatDev?

Yes, through **`BlackboardMemory`** ([`runtime/node/agent/memory/blackboard_memory.py`](https://github.com/OpenBMB/ChatDev/blob/main/runtime/node/agent/memory/blackboard_memory.py)). This implementation acts as a shared knowledge pool where multiple nodes can read and write to the same underlying storage. When configuring the workflow YAML, attach the same blackboard store name to multiple agent nodes, and the `GraphExecutor` will inject the same store instance into each agent's `MemoryManager`, enabling collective memory and cross-agent communication.