engram/
├── src/
│ ├── server.py # MCP Server (FastMCP entry point)
│ ├── maintenance.py # Maintenance script (daily/weekly/report)
│ ├── migrate.py # Data migration from markdown files
│ └── engines/ # Cognitive engines
│ ├── __init__.py # Package exports
│ ├── decay.py # Importance decay (Bjork-enhanced)
│ ├── classifier.py # Two-layer cascade classifier
│ ├── consolidation.py # Memory consolidation
│ └── conflict.py # Contradiction detection & resolution
├── scripts/
│ ├── run-maintenance.sh # Maintenance wrapper for cron
│ └── mem0-backup.sh # Qdrant snapshot backup
├── docs/ # Documentation
├── tests/ # Test suite
├── config.example.yaml # Configuration template
├── requirements.txt # Python dependencies
└── README.md # Project overview
We use Mem0's custom metadata fields (stored as top-level Qdrant payload) rather than separate collections per scope. This keeps the architecture simple while enabling filtering.
使用 Mem0 的自定义 metadata 字段(存为 Qdrant payload 顶层字段)而非每个 scope 一个集合。
Known limitation / 已知限制: Mem0 1.0.5 + Qdrant's AND/OR composite filters are broken (GitHub #3791). We use simple key-value filters + Python post-filtering as a workaround.
Day-time mem0_add() uses infer=False — only embedding, no LLM entity extraction. This keeps write latency at ~1.3s. Entity extraction is deferred to nightly Opus maintenance.
白天写入用 infer=False,只做 embedding 不调 LLM(~1.3s)。Entity extraction 交给凌晨 Opus 维护。
Mem0's update() only accepts text content, not metadata. For updating access_count, archived, superseded_by, we use QdrantClient.set_payload() directly. This is a partial update — other fields are preserved.
Mem0 的 update() 只接受文本,不支持 metadata 更新。我们用 QdrantClient.set_payload() 直接更新。
Every memory stores embedding_model in metadata. When switching models, old vectors can be identified and re-embedded without data loss.
每条记忆的 metadata 里存了 embedding_model,换模型时可识别旧向量重新嵌入。
- Create
src/engines/your_engine.py - Export from
src/engines/__init__.py - Call from
src/maintenance.pyin the appropriate step - Add configuration to
config.example.yaml - Write tests in
tests/test_your_engine.py
- Add to
config.yaml→memory_typessection with decay and conflict rules - Add keyword patterns to
src/engines/classifier.py→KEYWORD_RULES - Update documentation in
docs/usage.md
- Define the scope prefix convention (e.g.,
project:xxx) - Update search logic in
src/server.py→mem0_search()if special handling needed - Add to
config.yaml→ agent read/write permissions - Update documentation
-
NO_PROXY=localhost,127.0.0.1— Required! macOS system proxy intercepts localhost HTTP requests to Qdrant. Set in MCP server env config and all scripts. -
GOOGLE_API_KEY— Forgemini-embedding-001. Set in MCP server env or shell. -
LLM_API_KEY— For maintenance LLM calls (OpenAI-compatible). Falls back toOPENAI_API_KEY. -
EMBEDDING_API_KEY— Optional override for embedding API key (defaults toGOOGLE_API_KEY).
# Run all unit tests / 运行全部单元测试
pytest tests/ -v
# Run a specific test file / 运行指定测试文件
pytest tests/test_engines.py -v- Fork the repo
- Create a feature branch
- Write tests for new functionality
- Ensure all existing tests pass
- Submit a PR with clear description
Please follow the existing code style: bilingual docstrings, type hints, and structured error handling.