Skip to main content

memory-core

English | 中文

A local-first, framework-agnostic long-term memory core for AI companions.

Status: pre-alpha (M2). Storage, hybrid recall, the bundle format, LLM consolidation, an MCP server, an AstrBot adapter, the daily diary and self-image, memory metabolism (fading, merging, hard forgetting), seeing pictures, a sense of time and inner state, what the being can do, and a read-only dashboard work and are tested. APIs may change.

Why

An AI companion should still be itself after you switch chat framework, LLM, or embedding model. Most memory plugins keep their data in a plugin-specific format, and many treat vectors as the memory itself — but vectors belong to one embedding model and become useless when the model changes.

memory-core is built on a few rules:

  1. Text is the memory; vectors are only an index. Raw experiences and readable memories are kept forever; vectors can be recomputed with any model at any time (memory-core reindex).
  2. Raw experiences are append-only. Everything else (memories, diaries, profiles, indexes) is derived from them and records its sources, so it can be rebuilt, audited, or deleted along the provenance chain.
  3. Framework-agnostic. A Python library (and soon a local MCP server); chat frameworks connect through thin adapters.
  4. Model-agnostic. Any OpenAI-compatible endpoint for embeddings (Ollama, vLLM, LM Studio, OpenAI...).
  5. Local-first. One SQLite file plus a folder. No telemetry, no network calls except to the model endpoints you configure.
  6. Portable. A versioned, plain-text, git-friendly export format — the soul bundle (SPEC.md).
  7. Privacy by scope. Every memory has a visibility scope, enforced in code: what happened in one group chat is not brought up in another.

Concepts

What Rebuildable?
Episode A raw experience: a message received or sent, something observed (a web page, a video), an action No — the source of truth
Memory A derived, one-sentence memory: fact, event, relation, reflection, diary, preference, lexicon (slang), style Yes, from episodes
Entity A person, group, place, work... with aliases (account IDs, nicknames) —
Identity Markdown "core memory" documents: self.md, owner.md, now.md Hand-edited
Index Full-text (SQLite FTS5) and vectors, keyed by model Yes, always

Scopes: public (anywhere), self (its own feelings), owner (only in private with the owner), space:<space> (only in that place).

Quick start

pip install "memory-core-lib[zh]"  # zh = jieba for better Chinese full-text search (optional); import memory_core
memory-core init ./soul
memory-core record ./soul --space web --source browser --kind observation --content "Watched a video about Genshin"
memory-core remember ./soul --scope public --kind fact --text "Genshin Impact is an open-world game"
memory-core recall ./soul "Genshin" --space web
memory-core export ./soul ./soul-bundle      # portable plain-text backup

Semantic recall: run an embedding model locally, e.g. ollama pull bge-m3, then in soul/config.toml:

[embedding]
provider = "openai-compatible"
base_url = "http://127.0.0.1:11434/v1"
model = "bge-m3"

and memory-core reindex ./soul.

Python

from memory_core import MemoryCore, Episode, Memory, Audience

with MemoryCore.open("./soul") as core:
    ep = core.record(Episode(kind="message_in", content="I don't eat coriander", space="chat:group:1",
                             source="qq", actor="qq:1001"))
    core.add_memory(Memory(kind="preference", text="Ming doesn't eat coriander",
                           scope="space:chat:group:1", subjects=["qq:1001"], sources=[ep.id]))
    for hit in core.recall("coriander", Audience(space="chat:group:1")):
        print(hit.score, hit.memory.text)

Consolidation

Episodes become memories through a chat model (any OpenAI-compatible endpoint, or your framework's own model):

# soul/config.toml
[llm]
base_url = "https://api.deepseek.com/v1"
model = "deepseek-flash"
api_key_env = "DEEPSEEK_API_KEY"   # the key stays in an environment variable, not in the file

[owner]
spaces = ["qq:private:<owner id>"] # private chats with the owner: their memories get the "owner" scope
memory-core consolidate ./soul

For each place with new episodes, the model sees the new messages and the related memories it already has, and proposes to add a memory, update one (the old one is kept, marked superseded), or nothing. The model proposes, code decides: it can never widen visibility — memories about people stay in the place they were learned (or owner), only memories about no person can become public. In a public space ([spaces] public, default ["web"]: what the being read online on its own, e.g. with surfing-the-web) what it saw becomes public, while memories drawn from its own thoughts there stay with the owner. A place's cursor only advances after its batch was applied, so a failed call loses nothing.

Diary and self-image

A being grows a sense of itself from what it lived (memory_core.reflect):

from memory_core.reflect import day_window, update_self_image, write_diary

start, end = day_window(day, boundary_hour=4)        # late nights belong to the day before
write_diary(core, llm, day, start, end, persona=identity_text, owner_spaces=[...], owner_title="Dad")
update_self_image(core, llm, day, persona=identity_text)
  • Diary: one first-person entry per day from that day's episodes (a diary memory with the owner scope, also written to diary/<date>.md for reading). Idempotent; force=True rewrites it (the old one is superseded).
  • Self-image: identity/self-image.md, a short "how I see myself" rewritten from the recent diaries. It is the being's own layer: it must not contradict the core identity (identity/self.md, which only the owner edits) and carries no private details of anyone. Previous versions are kept in identity/history/.

The prompts are Chinese for now; the model answers in the language of its persona and experiences.

Memory metabolism

A memory that only grows gets worse. memory_core.metabolism and MemoryCore keep it healthy:

  • Fading: a memory's strength halves every 30 days it is not recalled (recalling it strengthens it again). fade(core) archives weak memories; important ones (importance ≥ 0.8) and diaries are never archived. Archived memories no longer come up by themselves, but a deliberate search (recall(..., include_archived=True)) finds them, and that makes them active again.
  • Merging: merge_duplicates(core, llm) finds similar memories in the same scope by vector similarity and lets the model decide whether they record the same thing; if so they become one memory keeping every detail (MemoryCore.merge; the originals stay, marked superseded). Groups kept apart are not asked about again.
  • Hard forgetting: core.forget(ids, with_sources=True) erases every version of a memory, the episodes it came from, and every other memory derived from those episodes (e.g. that day's diary, which can then be written again from what is left). The only exception to append-only episodes.
  • Write gate for the core identity: the being may only propose(...) a change to identity/self.md; resolve(..., approve=True) by the owner appends it under a dated section and keeps the previous version (memory_core.proposals).

MCP server

Any MCP client (Claude Code, DeepSeek Harness, Codex CLI, ...) can share the same memory:

memory-core mcp ./soul             # stdio; add --read-only for clients that should only read
{"mcpServers": {"memory": {"command": "memory-core", "args": ["mcp", "/path/to/soul"]}}}

Tools: recall, recent, profile, stats, record, remember, consolidate. Identity documents are exposed as resources (identity://self, ...). Every tool that returns memories takes the current space and only returns what is visible there.

Adapters

  • AstrBot — astrbot_plugin_memory_core (its own repository since 0.6.0, in AstrBot's plugin market as 记忆核心): records every visible message and the bot's own answers, uses the identity documents as the persona, injects recalled memories before each reply, consolidates periodically with AstrBot's own chat model, writes the daily diary and self-image, runs the daily memory metabolism, gives the model memory_recall / memory_remember / identity_propose tools, and the owner commands to forget and to approve proposals; a read-only dashboard (overview, memories with their sources and versions, recall explained, a graph of who and what the memories are about).

Roadmap

M0 ✅ Storage, append-only episodes, hybrid recall (full-text + vectors + entities), scopes, soul bundle, tests
M1 ✅ Consolidation (add / update / supersede, entity aliases), MCP server, AstrBot adapter
M2 ✅ Diary and self-image (0.2.0); fading and archiving (0.3.0), merging duplicates, hard forget along provenance, identity proposals
M3 Public benchmarks (LoCoMo, LongMemEval), docs
M4 🔄 Experience collectors: public spaces (0.4.0) and surfing-the-web (reading public sites on its own); next: a real browser, video sites
M5 Lexicon (slang/meme) learning, style learning, identity proposals with rollback, inner monologue

Acknowledgements

Design ideas from Generative Agents (memory stream, retrieval scoring, reflection), mem0 (add / update / delete consolidation), Graphiti (bi-temporal facts), Letta (core vs archival memory), dsh-memory (raw text as source of truth, layered write gates), and the AstrBot plugins LivingMemory and self_learning (ideas only; no code copied).

License

Apache-2.0

Metadata

Release files for memory-core-lib 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for memory-core-lib 0.6.0
File Size Uploaded Provenance Actions
memory_core_lib-0.6.0.tar.gz 88.4 kB With provenance Details for memory_core_lib-0.6.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for memory-core-lib 0.6.0
File Interpreter ABI Platform Actions
memory_core_lib-0.6.0-py3-none-any.whl Python 3 none any Details for memory_core_lib-0.6.0-py3-none-any.whl

Total release size: 162.1 kB

Release files / memory_core_lib-0.6.0.tar.gz

Download URL memory_core_lib-0.6.0.tar.gz
Size 88.4 kB
Tags Source
SHA-256 checksum
How to use checksums
99000bef7c51e9bb13b2600b2711ef434cb463ceba038a66a5857977f3570843
BLAKE2b-256 checksum
How to use checksums
42fa88ef988a70a206dcc83351392cd62d2033d891c38dc7f0160931a015baa5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Release files / memory_core_lib-0.6.0-py3-none-any.whl

Download URL memory_core_lib-0.6.0-py3-none-any.whl
Size 73.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a0dc0ddbfb568635a4c4c1dfdc4d6a02204dada13242952599a1122c6003d712
BLAKE2b-256 checksum
How to use checksums
e74ea1d711a6f9e1f73cca33d75a8ff7ac5f61c9e788661ff6934ab519ac8f0a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Security for memory-core-lib 0.6.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for memory_core_lib-0.6.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page