How to Implement Persistent Agent Memory Using ContextGraph in Semantica

Persistent agent memory in Semantica is achieved by combining AgentMemory for conversational fragments with ContextGraph for entity relationships and decisions, using JSON serialization methods save()/load() and save_to_file()/load_from_file() to restore complete agent state across restarts.

Semantica's architecture deliberately separates short-term RAG memory from persistent knowledge storage. This design lets agents retain learned entities, relationship patterns, and decision history even after process termination. The ContextGraph class in semantica/context/context_graph.py serves as the backbone for this persistence layer, working alongside AgentMemory to create a complete, reloadable cognitive state.


Core Components of Persistent Memory

Three integrated classes handle persistence in Semantica:

Component Source File Persistence Role
AgentMemory semantica/context/agent_memory.py Stores conversational fragments, vector embeddings, and entity links with save()/load() methods
ContextGraph semantica/context/context_graph.py Maintains entity/relationship graph and decision nodes with save_to_file()/load_from_file() methods
AgentContext semantica/context/agent_context.py High-level façade that wires memory and graph together; persistence flows through its composed components

Both AgentMemory and ContextGraph use JSON serialization exclusively—no pickles for security. This makes persisted states version-controllable, portable across machines, and cloud-cacheable.


Initializing ContextGraph for Persistent Memory

To enable persistence, instantiate ContextGraph with analytics capabilities and pass it to AgentContext:

from semantica.context import AgentContext, ContextGraph
from semantica.vector_store import SimpleInMemoryStore

# Create the vector store backend

vector_store = SimpleInMemoryStore()

# Initialize ContextGraph with advanced analytics

kg = ContextGraph(advanced_analytics=True)  # Enables centrality, community detection

# Create the agent context with decision tracking

ctx = AgentContext(
    vector_store=vector_store,
    knowledge_graph=kg,
    decision_tracking=True,      # Required to use ContextGraph decision APIs

    retention_days=60,           # Automatic memory expiration

)

Setting advanced_analytics=True in ContextGraph.__init__() enables graph algorithms (degree centrality, community detection) that help the agent rank entity importance and identify knowledge clusters.


Storing Memories with Entity Extraction

The AgentContext.store() method forwards to AgentMemory.store(), which performs three operations: buffering in short-term memory, indexing in the vector store, and updating the ContextGraph with extracted entities:


# Store a conversational memory with linked entities

mem_id = ctx.store(
    content="User asked how to encrypt data with AES-256",
    metadata={"type": "conversation", "conversation_id": "conv-42"},
    entities=[
        {"id": "aes_256", "type": "algorithm", "text": "AES-256"},
        {"id": "encryption", "type": "concept", "text": "encryption"},
    ],
)

The internal _update_knowledge_graph() call in AgentMemory creates nodes for each entity and links them to the memory item. These entities become addressable in the persistent graph for future retrieval and decision-making.


Recording Decisions in ContextGraph

When decision_tracking=True, AgentContext exposes record_decision() to create causal links between memories and agent actions:

dec_id = ctx.record_decision(
    category="security_policy",
    scenario="data_at_rest",
    reasoning="User wants strong encryption",
    outcome="enable_aes_256",
    confidence=0.97,
    entities=["aes_256"],        # Links decision to existing graph node

)

This creates a decision node in ContextGraph connected to the same entities that appear in memory items. The resulting structure forms a causal view of why specific agent actions were taken, queryable via trace_decision_causality().


Persisting Agent State to Disk

To implement true persistence, serialize both components before shutdown:


# Persist AgentMemory (includes items, buffer, vector-ID mappings, statistics)

ctx.memory.save("./persisted_memory")   # Creates agent_memory.json

# Persist ContextGraph (includes nodes, edges, sub-graphs, decision nodes)

ctx.knowledge_graph.save_to_file("./graph.json")

Both calls produce self-contained JSON files:


Restoring Persistent Memory After Restart

The reload sequence recreates the identical cognitive state:

from semantica.context import AgentContext, ContextGraph
from semantica.vector_store import SimpleInMemoryStore

# Reinitialize with the same vector store backend

vector_store = SimpleInMemoryStore()

# Restore the ContextGraph first

kg = ContextGraph()
kg.load_from_file("./graph.json")   # Rebuilds entity/decision graph

# Create AgentContext with restored graph

ctx = AgentContext(
    vector_store=vector_store,
    knowledge_graph=kg,
    decision_tracking=True,
)

# Restore AgentMemory (must happen after AgentContext creation)

ctx.memory.load("./persisted_memory")   # Restores all memories and mappings

# Agent now retains full prior state: conversations, entities, and decisions

Critical restoration order: ContextGraph must be loaded before AgentContext construction, but AgentMemory.load() must occur after AgentContext exists since AgentContext creates and holds the AgentMemory instance.


Querying Persisted Knowledge Graph Data

Once restored, combine semantic retrieval with graph analytics:


# Hybrid RAG + graph context retrieval

results = ctx.retrieve(
    "What encryption method should I use?",
    max_results=5,
    min_score=0.2,
    type="conversation",
)

# Rank results by entity centrality in the knowledge graph

for r in results:
    for entity in r.get("entities", []):
        centrality = ctx.knowledge_graph.get_node_centrality(entity["id"])
        print(f"{entity['text']}: degree={centrality['degree']}")

The get_node_centrality() method—available when advanced_analytics=True—returns degree, betweenness, and eigenvector centrality scores that help the agent prioritize more "important" entities in reasoning.


Tracing Decision Causality

Leverage ContextGraph to explain why specific decisions were made:


# Trace the causal chain for a recorded decision

causal_chain = ctx.knowledge_graph.trace_decision_causality(dec_id)

for hop in causal_chain:
    print(f"Node: {hop['node_id']}, Reason: {hop['reason']}")

This traversal follows edges from decision nodes through supporting entities and prerequisite memories, enabling explainable agent behavior audits.


Summary

  • Persistent agent memory requires both AgentMemory (conversational data) and ContextGraph (entity/decision graph) working together
  • JSON serialization via save()/load() and save_to_file()/load_from_file() provides secure, portable, version-controllable persistence
  • Entity linking during store() operations creates the bridge between raw memory and structured graph knowledge
  • Decision tracking with record_decision() builds causal, explainable agent action histories
  • Restoration order matters: load ContextGraph before AgentContext, then AgentMemory after

Frequently Asked Questions

What file formats does Semantica use for persistent memory?

Semantica uses JSON exclusively for all persistence. Both AgentMemory.save() and ContextGraph.save_to_file() produce JSON outputs. This design avoids pickle serialization for security (preventing arbitrary code execution) and enables human inspection, version control, and cross-platform portability.

Can I use a different vector store with persisted ContextGraph?

Yes. The vector store and ContextGraph are loosely coupled. You can persist AgentMemory and ContextGraph independently, then restore them with any vector store implementation that matches your original schema. However, AgentMemory maintains an internal vector_id_map that must remain consistent with your vector store's indexing—use the same backend type (e.g., SimpleInMemoryStore, Chroma, Pinecone) for reliable retrieval.

How do I migrate persisted memory between Semantica versions?

Since persistence uses self-contained JSON, migrate by: (1) loading with the original version, (2) optionally reprocessing with AgentContext methods to update schemas, and (3) saving with the new version. The ContextGraph structure in semantica/context/context_graph.py uses standard node/edge dictionaries that remain backward-compatible across minor versions.

Does ContextGraph persistence include vector embeddings?

No—embeddings are not stored in ContextGraph. Vector embeddings reside in the configured vector_store (passed to AgentContext). Persist the vector store separately using its own mechanism (e.g., Chroma's persist() or Pinecone's native storage). AgentMemory saves only the mapping between memory items and vector IDs, not the embeddings themselves.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →