Last Updated: December 1, 2025
Version: 1.0
Purpose: Comprehensive guide for AI assistants working with the A-MEM codebase
A-MEM (Agentic Memory) is an MCP-integrated memory system for LLM agents based on the Zettelkasten principle. It provides persistent, graph-based memory with semantic retrieval capabilities for AI assistants in IDE environments (Cursor, VSCode).
Based on the paper: "A-Mem: Agentic Memory for LLM Agents"
Authors: Wujiang Xu, Zujie Liang, Kai Mei, Hang Gao, Juntao Tan, Yongfeng Zhang
This implementation focuses on:
-
MCP Protocol Integration for IDE environments
-
Explicit Graph-Based Memory Linking with typed edges, reasoning, and weights
-
Dual Storage Architecture: ChromaDB (vector similarity) + Graph Backend (NetworkX/RustworkX/FalkorDB)
-
Autonomous Maintenance: Memory Enzymes for graph health
-
Research Integration: Web research agent for JIT context optimization
┌─────────────────────────────────────────────────────────────┐
│ MCP Server Layer │
│ (src/a_mem/main.py - stdio_server) │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Memory Controller Layer │
│ (src/a_mem/core/logic.py) │
│ • Async I/O via run_in_executor │
│ • Background evolution tasks │
│ • Enzyme scheduler orchestration │
└───────────┬─────────────────────────┬───────────────────────┘
│ │
▼ ▼
┌───────────────────────┐ ┌──────────────────────────────────┐
│ Storage Layer │ │ LLM Service Layer │
│ (storage/engine.py) │ │ (utils/llm.py) │
│ │ │ │
│ • GraphStore │ │ • Metadata extraction │
│ • VectorStore │ │ • Embedding generation │
│ • Cross-platform │ │ • Multi-provider support │
│ file locking │ │ (Ollama/OpenRouter) │
└───────────────────────┘ └──────────────────────────────────┘
-
Async Non-Blocking I/O
-
All blocking operations use
asyncio.run_in_executor -
Background tasks for evolution and maintenance
-
Parallel HTTP server for external tool access
-
-
Dual Storage Architecture
-
Vector Store (ChromaDB): Semantic similarity search
-
Graph Store: Explicit typed relationships with reasoning
-
Enables hybrid retrieval: similarity + graph traversal
-
-
Graph Backend Flexibility
-
NetworkX (default): Cross-platform, no extra dependencies
-
RustworkX (performance): 3x-100x faster, Windows-compatible
-
FalkorDB (experimental): Persistent storage, Redis-based
-
-
Type Safety with Pydantic
-
All data models use Pydantic BaseModel
-
Automatic validation and serialization
-
Type hints throughout codebase
-
-
Event-Driven Architecture
-
JSONL event log for audit trail (
data/events.jsonl) -
All critical operations logged
-
Enables debugging and analytics
-
a-mem-mcp-server/
├── src/
│ └── a_mem/
│ ├── __init__.py
│ ├── main.py # MCP server entry point
│ ├── config.py # Configuration & environment
│ ├── models/
│ │ ├── __init__.py
│ │ └── note.py # Data models (AtomicNote, NoteInput, etc.)
│ ├── core/
│ │ ├── __init__.py
│ │ └── logic.py # MemoryController (core business logic)
│ ├── storage/
│ │ ├── __init__.py
│ │ ├── engine.py # StorageManager, GraphStore, VectorStore
│ │ ├── rustworkx_store.py # RustworkX graph backend
│ │ ├── falkordb_store.py # FalkorDB graph backend (Linux/macOS)
│ │ ├── falkordb_store_windows.py # FalkorDB Windows adapter
│ │ └── safe_graph_wrapper.py # Edge case handling wrapper
│ └── utils/
│ ├── __init__.py
│ ├── llm.py # LLM service (metadata, embeddings)
│ ├── priority.py # Priority scoring & event logging
│ ├── enzymes.py # Memory maintenance (14+ operations)
│ ├── researcher.py # Web research agent
│ ├── researcher_tools.py # HTTP-based research tools
│ ├── validation.py # MCP parameter validation
│ └── serializers.py # Data serialization helpers
├── tests/ # Test suite (24+ tests)
│ ├── test_a_mem.py # Core functionality tests
│ ├── test_code_structure.py # Structure validation
│ ├── test_new_features.py # Type classification, priority, events
│ ├── test_enzymes.py # Memory enzymes tests
│ ├── test_scheduler.py # Scheduler tests
│ ├── test_mcp_integration.py # MCP server integration tests
│ └── test_researcher*.py # Researcher agent tests
├── tools/ # Standalone utilities
│ ├── amem_stats.py # CLI status tool (like git status)
│ ├── visualize_memory.py # Web-based graph dashboard
│ ├── extract_graph.py # Graph data extractor
│ └── a_mem_cli.py # Command-line interface
├── docs/ # Extended documentation
│ ├── MEMORY_ENZYMES_DETAILED.md # Enzyme documentation
│ ├── RESEARCHER_AGENT_DETAILED.md # Research agent guide
│ ├── TEST_REPORT.md # Test results
│ ├── MCP_SERVER_TEST_REPORT.md # MCP integration tests
│ └── *.svg # Architecture diagrams
├── data/ # Runtime data (auto-created)
│ ├── chroma/ # ChromaDB vector store
│ ├── graph/
│ │ ├── knowledge_graph.json # Graph snapshot (NetworkX)
│ │ ├── knowledge_graph.graphml # Graph snapshot (RustworkX)
│ │ └── graph.lock # Cross-platform file lock
│ └── events.jsonl # Event log (append-only)
├── .env.example # Configuration template
├── requirements.txt # Python dependencies
├── mcp_server.py # MCP server launcher
├── README.md # User documentation
├── MCP_SERVER_SETUP.md # MCP setup guide
└── CLAUDE.md # This file (AI assistant guide)
| File | Purpose | Key Classes/Functions |
|------|---------|----------------------|
| src/a_mem/main.py | MCP server implementation | list_tools(), call_tool(), main() |
| src/a_mem/core/logic.py | Core business logic | MemoryController, create_note(), retrieve() |
| src/a_mem/storage/engine.py | Storage layer | StorageManager, GraphStore, VectorStore |
| src/a_mem/utils/llm.py | LLM integration | LLMService, extract_metadata(), get_embedding() |
| src/a_mem/utils/enzymes.py | Memory maintenance | run_memory_enzymes(), 14+ enzyme functions |
| src/a_mem/models/note.py | Data models | AtomicNote, NoteInput, NoteRelation |
| src/a_mem/config.py | Configuration | Config class, environment variables |
Purpose: JSON-RPC server implementing Model Context Protocol (MCP)
Key Functions:
-
list_tools()- Returns 15 available MCP tools -
call_tool(name, arguments)- Routes tool calls to controller -
main()- Server initialization, enzyme scheduler, HTTP server
15 MCP Tools:
-
create_atomic_note- Store new memory -
retrieve_memories- Semantic search with priority -
get_memory_stats- System statistics -
add_file- Import file with chunking -
reset_memory- Clear all data -
list_notes- List all notes -
get_note- Get single note by ID -
update_note- Update note metadata -
delete_atomic_note- Delete note -
list_relations- List graph edges -
add_relation- Manual edge creation -
remove_relation- Delete edge -
get_graph- Full graph snapshot -
run_memory_enzymes- Manual maintenance -
research_and_store- Web research + storage
Communication:
-
Uses
stdio_serverfor IDE integration -
Logs to stderr (not stdout) to avoid breaking JSON-RPC
-
Optional HTTP server on port 42424 for external tools
Purpose: Orchestrates memory operations with async I/O
Key Methods:
class MemoryController:
async def create_note(input_data: NoteInput) -> str:
"""
1. Extract metadata via LLM (or use pre-provided)
2. Generate embedding (concat: content + summary + keywords + tags)
3. Store in vector DB + graph
4. Log event
5. Background evolution task
"""
async def retrieve(query: str) -> List[SearchResult]:
"""
1. Search vector DB for similar notes
2. Compute priority scores (type + age + usage + edges)
3. Traverse graph for connected notes
4. Sort by combined score (similarity × priority)
5. Return with context
"""
async def _evolve_memory(note, embedding):
"""
Background task:
1. Find similar notes (cosine similarity)
2. Create typed edges with reasoning
3. Check for existing notes to merge/update
4. Log evolution events
"""Async Pattern:
# All blocking I/O uses run_in_executor
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(None, blocking_function, args)GraphStore:
class GraphStore:
def __init__(self):
self.graph = nx.DiGraph() # Or RustworkX/FalkorDB
self.load()
def add_node(self, note: AtomicNote):
"""Store node with all metadata"""
def add_edge(self, source, target, relation_type, reasoning, weight):
"""Create typed edge with reasoning"""
def save_snapshot(self):
"""Atomic save with temp file + rename"""VectorStore:
class VectorStore:
def __init__(self):
self.client = chromadb.PersistentClient(path=settings.CHROMA_DIR)
self.collection = self.client.get_or_create_collection("a_mem_notes")
def add(self, note: AtomicNote, embedding: List[float]):
"""Store with metadata for filtering"""
def search(self, query_embedding, max_results=10):
"""Cosine similarity search"""Cross-Platform File Locking:
-
Uses
fcntlon Linux/macOS -
Falls back to
portalockeron Windows -
Prevents concurrent write conflicts
Multi-Provider Support:
class LLMService:
def __init__(self):
self.provider = settings.LLM_PROVIDER # "ollama" or "openrouter"
def extract_metadata(self, content: str) -> dict:
"""
Extracts:
- contextual_summary
- keywords (max 7)
- tags
- type (rule/procedure/concept/tool/reference/integration)
"""
def get_embedding(self, text: str) -> List[float]:
"""Generate embedding vector"""
def refine_summary(self, content: str, old_summary: str) -> str:
"""Make similar summaries more specific"""Provider Configuration:
-
Ollama (local): HTTP requests to localhost:11434
-
OpenRouter (cloud): API key-based, OpenAI-compatible
14+ Autonomous Maintenance Operations:
def run_memory_enzymes(graph, llm, prune_config, suggest_config, refine_config):
"""
1. Link Pruner: Remove old/weak edges (age > 90 days, weight < 0.3)
2. Zombie Node Remover: Delete empty nodes
3. Duplicate Merger: Find and merge exact/semantic duplicates
4. Edge Validator: Fix edges (add reasoning, standardize types)
5. Self-Loop Remover: Remove self-referential edges
6. Isolated Node Finder: Identify unconnected nodes
7. Isolated Node Linker: Auto-link isolated nodes (similarity ≥ 0.70)
8. Keyword Normalizer: Clean and limit keywords (max 7)
9. Quality Score Calculator: Score notes by content/metadata/connections
10. Note Validator: Validate and correct missing fields
11. Low Quality Note Remover: Remove CAPTCHA/error/spam pages
12. Summary Refiner: Make similar summaries more distinct
13. Corrupted Node Repairer: Fix nodes with invalid data
14. Relation Suggester: Find semantic connections (similarity ≥ 0.75)
15. Summary Digester: Compress nodes with >8 children
"""Scheduler:
-
Runs automatically every hour (configurable)
-
Auto-saves graph every 5 minutes
-
Graceful error handling
Purpose: JIT web research for low-confidence queries
Workflow:
class ResearcherAgent:
async def research(query: str, context: str) -> List[AtomicNote]:
"""
1. Search web (Google Search API or DuckDuckGo)
2. Extract top N URLs
3. Fetch content:
- Web pages: Jina Reader (local/cloud) or Readability
- PDFs: Unstructured (library/API)
4. Parse and clean content
5. Extract metadata via LLM
6. Create AtomicNote objects
7. Return for storage
"""Hybrid Tool Strategy:
-
Primary: MCP tools (if available via callback)
-
Fallback: HTTP-based tools (Google API, DuckDuckGo, Jina Reader)
Configuration:
RESEARCHER_ENABLED=true
RESEARCHER_CONFIDENCE_THRESHOLD=0.5 # Auto-trigger when score < 0.5
RESEARCHER_MAX_SOURCES=5
GOOGLE_SEARCH_ENABLED=true
JINA_READER_ENABLED=true
UNSTRUCTURED_ENABLED=true
Core Models:
class AtomicNote(BaseModel):
id: str # UUID
content: str # Original text
contextual_summary: str # LLM-generated summary
keywords: List[str] # Max 7 keywords
tags: List[str] # Categorical tags
created_at: datetime # Timestamp
type: Optional[str] # rule/procedure/concept/tool/reference/integration
metadata: Dict[str, Any] # Experimental fields
class NoteInput(BaseModel):
content: str
source: Optional[str] = "user_input"
# Pre-extracted metadata (optional, from ResearcherAgent)
contextual_summary: Optional[str] = None
keywords: Optional[List[str]] = None
tags: Optional[List[str]] = None
type: Optional[str] = None
metadata: Optional[Dict[str, Any]] = None
class NoteRelation(BaseModel):
source_id: str
target_id: str
relation_type: str # relates_to/contradicts/supports/etc.
reasoning: Optional[str] # Why this relation exists
weight: float = 1.0 # 0.0-1.0
created_at: datetime
class SearchResult(BaseModel):
note: AtomicNote
score: float # Combined similarity × priority
related_notes: List[AtomicNote] # Graph-connected notes- Define tool schema in
main.py:list_tools():
Tool(
name="my_new_tool",
description="Clear description for AI assistants",
inputSchema={
"type": "object",
"properties": {
"param1": {"type": "string", "description": "..."},
},
"required": ["param1"]
}
)- Add handler in
main.py:call_tool():
elif name == "my_new_tool":
param1 = arguments.get("param1", "")
result = await controller.my_method(param1)
return [TextContent(type="text", text=json.dumps(result, indent=2))]- Implement logic in
core/logic.py:
async def my_method(self, param1: str):
loop = asyncio.get_running_loop()
# Offload blocking I/O
result = await loop.run_in_executor(None, self._blocking_operation, param1)
return result- Add tests in
tests/test_mcp_server.py
GraphStore modifications:
-
Edit
storage/engine.pyfor NetworkX -
Edit
storage/rustworkx_store.pyfor RustworkX -
Edit
storage/falkordb_store.pyfor FalkorDB
Key methods to update:
def add_node(self, note: AtomicNote)
def add_edge(self, source, target, relation_type, reasoning, weight)
def get_neighbors(self, node_id) -> List[Tuple[str, dict]]
def save_snapshot()
def load()VectorStore modifications:
-
Edit
storage/engine.py:VectorStore -
ChromaDB API:
add(),query(),delete()
- Create enzyme function in
utils/enzymes.py:
def my_enzyme(graph: GraphStore, llm: LLMService, config: dict) -> dict:
"""
Args:
graph: GraphStore instance
llm: LLMService instance
config: Enzyme-specific configuration
Returns:
dict: Results with counts, lists, etc.
"""
# Your logic here
log_event("MY_ENZYME_RUN", {"count": 42})
return {"count": 42}- Add to enzyme runner in
utils/enzymes.py:run_memory_enzymes():
results["my_enzyme"] = my_enzyme(graph, llm, config)-
Add to MCP tool parameters (optional) in
main.py -
Add tests in
tests/test_enzymes.py
View recent events:
tail -n 50 data/events.jsonl | jq .
Filter by event type:
grep "NOTE_CREATED" data/events.jsonl | jq .
Event types:
-
NOTE_CREATED,RELATION_CREATED,MEMORY_EVOLVED -
LINKS_PRUNED,RELATION_PRUNED,NODE_PRUNED -
DUPLICATES_MERGED,SELF_LOOPS_REMOVED -
ISOLATED_NODES_FOUND,ISOLATED_NODES_LINKED -
KEYWORDS_NORMALIZED,QUALITY_SCORES_CALCULATED -
NOTES_VALIDATED,LOW_QUALITY_NOTES_REMOVED -
CORRUPTED_NODES_REPAIRED,RELATIONS_SUGGESTED -
ENZYME_SCHEDULER_RUN,RESEARCHER_MANUAL_RUN
LLM Provider:
LLM_PROVIDER=ollama # "ollama" or "openrouter"
# Ollama (local)
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_LLM_MODEL=qwen3:4b
OLLAMA_EMBEDDING_MODEL=nomic-embed-text:latest
# OpenRouter (cloud)
OPENROUTER_API_KEY=your_key_here
OPENROUTER_LLM_MODEL=openai/gpt-4o-mini
OPENROUTER_EMBEDDING_MODEL=openai/text-embedding-3-small
Graph Backend:
GRAPH_BACKEND=networkx # "networkx", "rustworkx", or "falkordb"
Retrieval:
MAX_NEIGHBORS=5 # Max connected notes per result
MIN_SIMILARITY_SCORE=0.4 # Minimum cosine similarity
Research Agent:
RESEARCHER_ENABLED=true
RESEARCHER_CONFIDENCE_THRESHOLD=0.5
RESEARCHER_MAX_SOURCES=5
GOOGLE_SEARCH_ENABLED=true
GOOGLE_API_KEY=your_key
GOOGLE_SEARCH_ENGINE_ID=your_id
JINA_READER_ENABLED=true
UNSTRUCTURED_ENABLED=true
HTTP Server (optional):
TCP_SERVER_ENABLED=false # Enable HTTP endpoint for tools
TCP_SERVER_HOST=127.0.0.1
TCP_SERVER_PORT=42424
-
Base:
.envfile (default values) -
Override: MCP
envblock inmcp.json -
Priority: MCP env > .env file
Example MCP config with overrides:
{
"mcpServers": {
"a-mem": {
"command": "python",
"args": ["-m", "src.a_mem.main"],
"cwd": "/path/to/a-mem-mcp-server",
"env": {
"LLM_PROVIDER": "ollama",
"OLLAMA_LLM_MODEL": "llama3.2:3b",
"RESEARCHER_ENABLED": "true"
}
}
}
}
Communication:
-
Uses stdio (stdin/stdout) for JSON-RPC messages
-
All logging must go to stderr (not stdout)
-
Server runs in background as subprocess
Helper function:
def log_debug(message: str):
"""Logs to stderr to avoid breaking MCP JSON-RPC"""
print(message, file=sys.stderr)Cursor IDE:
{
"mcpServers": {
"a-mem": {
"command": "python",
"args": ["-m", "src.a_mem.main"],
"cwd": "/absolute/path/to/a-mem-mcp-server"
}
}
}
Location:
-
Windows:
%USERPROFILE%\.cursor\mcp.json -
macOS/Linux:
~/.cursor/mcp.json
From AI assistant:
User: "Remember this: Python uses asyncio for concurrent I/O"
Assistant calls:
{
"tool": "create_atomic_note",
"arguments": {
"content": "Python uses asyncio for concurrent I/O",
"source": "user_input"
}
}
Response:
{
"status": "success",
"note_id": "732c8c3b-7c71-42a6-9534-a611b4ffe7bf",
"message": "Note created. Evolution started in background."
}
Retrieve:
User: "What do you know about Python async?"
Assistant calls:
{
"tool": "retrieve_memories",
"arguments": {
"query": "Python async programming",
"max_results": 5
}
}
Response:
{
"status": "success",
"results": [
{
"id": "...",
"content": "...",
"summary": "...",
"type": "concept",
"relevance_score": 0.87,
"connected_memories": 3,
"connected_context": "..."
}
]
}
When enabled (TCP_SERVER_ENABLED=true):
-
MCP server runs on stdio
-
HTTP server runs on port 42424
-
Same
MemoryControllerinstance shared
Endpoint:
curl http://127.0.0.1:42424/get_graph
Use case: External tools (visualizer, CLI) can access live graph without interfering with MCP protocol
tests/
├── test_a_mem.py # Core: create, retrieve, evolve
├── test_code_structure.py # Architecture validation
├── test_new_features.py # Type classification, priority, events
├── test_enzymes.py # All 14+ enzymes
├── test_scheduler.py # Automatic enzyme scheduling
├── test_mcp_integration.py # MCP server integration
├── test_researcher*.py # Research agent (live + mocked)
├── test_safe_graph_wrapper.py # Edge case handling
└── test_rustworkx*.py # RustworkX backend
All tests:
python tests/test_a_mem.py
python tests/test_code_structure.py
python tests/test_new_features.py
python tests/test_enzymes.py
python tests/test_scheduler.py
Single test:
python -m pytest tests/test_a_mem.py::test_create_note -v
With coverage:
pytest --cov=src/a_mem tests/
-
Use temp directories for test data
-
Clean up after each test (
tearDown) -
Mock external services (LLM, web requests)
-
Test both success and error paths
-
Verify event logs for critical operations
Example test:
def test_create_note(self):
note_input = NoteInput(
content="Test note",
source="test"
)
note_id = asyncio.run(self.controller.create_note(note_input))
self.assertIsNotNone(note_id)
# Verify storage
note = asyncio.run(self.controller.get_note_data(note_id))
self.assertEqual(note["content"], "Test note")
# Verify event log
events = self._read_events()
self.assertTrue(any(e["event_type"] == "NOTE_CREATED" for e in events))- Update
utils/llm.py:
def extract_metadata(self, content: str) -> dict:
if self.provider == "ollama":
# Existing Ollama logic
elif self.provider == "openrouter":
# Existing OpenRouter logic
elif self.provider == "new_provider":
# Your new provider logic- Add config in
config.py:
NEW_PROVIDER_API_KEY = os.getenv("NEW_PROVIDER_API_KEY", "")
NEW_PROVIDER_LLM_MODEL = os.getenv("NEW_PROVIDER_LLM_MODEL", "default-model")-
Update
.env.example -
Add tests in
tests/test_llm.py
Switch to RustworkX:
pip install rustworkx
# .env
GRAPH_BACKEND=rustworkx
Benchmark:
python tools/benchmark_enzymes.py
Expected speedup: 3x-100x for large graphs (>1000 nodes)
Start visualizer:
python tools/visualize_memory.py
Open browser: http://localhost:8050
Features:
-
Interactive network graph (priority-based sizing, type-based coloring)
-
Priority statistics by type
-
Relation type distribution
-
Event timeline
-
Node details table
Update data:
python tools/extract_graph.py # Requires TCP_SERVER_ENABLED=true
Quick status:
python tools/amem_stats.py
Output:
🧠 A-MEM Graph Status
==================================================
📝 Notes: 127
🔗 Relations: 342
📊 Notes by Type:
🔴 rule 23
🔵 procedure 45
🟢 concept 59
⚙️ Last Enzyme Run: 15min ago
==================================================
Watch mode:
python tools/amem_stats.py --watch
Diff mode:
python tools/amem_stats.py --diff
# +12 notes | +28 relations | -5 zombie nodes
Via MCP:
{
"tool": "run_memory_enzymes",
"arguments": {
"prune_max_age_days": 90,
"prune_min_weight": 0.3,
"suggest_threshold": 0.75,
"auto_add_suggestions": false
}
}
Via CLI:
python tools/a_mem_cli.py --enzyme-run
Schedule:
-
Automatic: Every hour (configurable in
main.py) -
Manual: Use tool or CLI
- Type hints everywhere:
def my_function(param: str) -> dict:
"""Clear docstring."""
return {}- Pydantic for data validation:
class MyModel(BaseModel):
field: str
optional_field: Optional[int] = None- Async I/O for blocking operations:
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(None, blocking_func, args)- Logging to stderr (not stdout):
print(message, file=sys.stderr)Graceful degradation:
try:
result = await risky_operation()
except Exception as e:
log_debug(f"[ERROR] Operation failed: {e}")
return {"error": str(e), "status": "partial_success"}Return structured errors:
return {
"status": "error",
"error_code": "INVALID_INPUT",
"message": "Parameter X is required",
"details": {...}
}Always log critical operations:
from .utils.priority import log_event
log_event("OPERATION_NAME", {
"key": "value",
"timestamp": datetime.now().isoformat()
})Atomic saves:
temp_file = path.with_suffix(".tmp")
with open(temp_file, 'w') as f:
json.dump(data, f)
temp_file.rename(path) # Atomic on POSIXBackup before destructive operations:
if path.exists():
backup = path.with_suffix(".backup")
shutil.copy(path, backup)-
Use RustworkX for graphs >1000 nodes
-
Batch operations when possible
-
Offload blocking I/O to executor
-
Cache embeddings (already in ChromaDB)
-
Limit graph traversal depth (currently 1 hop)
Symptoms:
-
IDE shows "Server failed to start"
-
No stderr output
Solutions:
-
Check Python path in
mcp.json -
Verify
cwdis absolute path -
Check
.envfile exists and is valid -
Test standalone:
python -m src.a_mem.main -
Check logs:
tail -f data/graph_save.log
Symptoms:
-
retrieve_memoriestakes >5 seconds -
Graph has >10,000 nodes
Solutions:
-
Switch to RustworkX:
GRAPH_BACKEND=rustworkx -
Run enzymes to prune weak edges
-
Increase
MIN_SIMILARITY_SCOREto filter results -
Reduce
MAX_NEIGHBORSfor less graph traversal
Symptoms:
-
Notes disappear after server restart
-
Graph snapshot file empty
Solutions:
-
Check file permissions on
data/graph/ -
Verify no concurrent writes (check
graph.lock) -
Enable FalkorDB for persistence:
GRAPH_BACKEND=falkordb -
Check logs:
grep "ERROR" data/graph_save.log
Symptoms:
-
research_and_storereturns no notes -
Web search fails
Solutions:
-
Check environment:
-
RESEARCHER_ENABLED=true -
GOOGLE_API_KEYset (or use DuckDuckGo fallback) -
JINA_READER_ENABLED=true
-
-
Check server logs for
[RESEARCHER]messages -
Test components:
python tests/test_researcher_live.py
-
Verify network access (firewalls, proxies)
Symptoms:
-
Enzyme scheduler crashes
-
Event log shows
ENZYME_ERROR
Solutions:
-
Check enzyme parameters (age, weight, threshold)
-
Run enzymes manually with logging:
python -c "from src.a_mem.utils.enzymes import *; run_memory_enzymes(...)" -
Check for corrupted nodes:
grep "CORRUPTED" data/events.jsonl -
Reset if necessary (backup first!)
Symptoms:
-
ChromaDB error: "Embedding dimension mismatch"
-
Different models produce different dimensions
Solutions:
-
Check model dimensions:
-
nomic-embed-text: 768 -
text-embedding-3-small: 1536
-
-
Clear ChromaDB when switching models:
rm -rf data/chroma/
-
See
docs/EMBEDDING_DIMENSIONS.md
-
README.md - User-facing documentation
-
MCP_SERVER_SETUP.md - MCP tool reference
-
docs/MEMORY_ENZYMES_DETAILED.md - Enzyme deep dive
-
docs/RESEARCHER_AGENT_DETAILED.md - Research agent guide
-
docs/TEST_REPORT.md - Test results
-
docs/ARCHITECTURE_DIAGRAM.md - Visual architecture (Mermaid)
Located in docs/*.svg:
-
a-mem-system-architecture.svg- Overall system -
a-mem-storage-architecture.svg- Storage layer -
a-mem-memory-enzymes.svg- Enzyme workflow -
a-mem-mcp-tools.svg- Tool overview -
a-mem-type-classification.svg- Note type system
-
Research Paper - Original A-Mem paper
-
Original Repo - Authors' implementation
-
MCP Documentation - Protocol spec
-
Read this file (CLAUDE.md)
-
Read README.md
-
Explore
src/a_mem/structure -
Run tests:
python tests/test_a_mem.py -
Read
src/a_mem/models/note.py
-
Study
src/a_mem/core/logic.py(MemoryController) -
Study
src/a_mem/storage/engine.py(Storage layer) -
Study
src/a_mem/utils/llm.py(LLM integration) -
Run:
python -m src.a_mem.mainand test via Cursor
-
Study
src/a_mem/utils/enzymes.py(Memory maintenance) -
Study
src/a_mem/utils/researcher.py(Web research) -
Study
src/a_mem/main.py(MCP server) -
Run visualizer:
python tools/visualize_memory.py
-
Pick an issue or feature
-
Write tests first
-
Implement feature
-
Run all tests
-
Submit PR
When asking for help, provide:
-
Error message (full traceback)
-
Configuration (
.envsettings) -
Steps to reproduce
-
Expected vs. actual behavior
-
Relevant logs (
data/events.jsonl,data/graph_save.log)
Debug checklist:
-
Check
.envfile exists and is valid -
Check Python version (3.9+)
-
Check dependencies:
pip install -r requirements.txt -
Check file permissions on
data/directory -
Check logs for errors
-
Test standalone:
python -m src.a_mem.main
v1.0 (December 1, 2025)
-
Initial CLAUDE.md creation
-
Comprehensive architecture documentation
-
Development workflows and best practices
-
Troubleshooting guide
-
Learning path for new contributors
End of CLAUDE.md
This document is maintained by the community. When making significant architectural changes, please update this file accordingly.