Get from zero to a working recall in 5 minutes.

Install

pip install pensyve

Initialize

import pensyve

p = pensyve.Pensyve(namespace="my-agent")

Pensyve() accepts an optional path (defaults to ~/.pensyve/) and namespace (defaults to "default").

Create Entities

Entities represent participants — agents, users, teams, or tools.

agent = p.entity("assistant", kind="agent")
user = p.entity("alice", kind="user")

kind defaults to "user" if omitted. Entity creation is idempotent: calling p.entity("alice") twice returns the same entity.

Record an Episode

Episodes are bounded interaction sequences. Use them as context managers.

with p.episode(agent, user) as ep:
    ep.message("user", "I prefer dark mode and use vim keybindings")
    ep.message("assistant", "Got it, noted for future sessions")
    ep.outcome("success")  # optional: "success", "failure", "partial"

When the with block exits, Pensyve automatically extracts memories from the conversation.

Remember a Fact

Store an explicit semantic memory without recording an episode:

mem = p.remember(entity=user, fact="Alice prefers Python over JavaScript", confidence=0.95)

print(mem.memory_type)  # "semantic"
print(mem.confidence)   # 0.95

confidence defaults to 0.8.

Recall Memories

results = p.recall("programming language preference", entity=user)

for m in results:
    print(f"[{m.memory_type}] {m.content} (score={m.score:.2f})")

Each Memory object has these properties:

PropertyTypeDescription
idstrUUID
contentstrMemory text
memory_typestr"episodic", "semantic", or "procedural"
confidencefloatConfidence in [0, 1]
stabilityfloatFSRS stability in [0, 1]
scorefloatRetrieval relevance score

Filtering

# Only semantic memories
results = p.recall("vim", entity=user, types=["semantic"])

# Limit results
results = p.recall("vim", entity=user, limit=3)

Recall Grouped (for LLM readers)

When you're feeding recalled memories to another LLM as context, use recall_grouped() instead. It returns memories already clustered by source session and ordered chronologically — ready to format as session blocks in a reader prompt with no client-side grouping logic.

groups = p.recall_grouped("How many projects have I led this year?", limit=50)

# Each SessionGroup is one conversation episode, ready to format as a prompt block.
for g in groups:
    print(f"### Session {g.session_id} ({g.session_time}):")
    for m in g.memories:
        print(f"  {m.content}")

No more OrderedDict clustering, no more reordering by date string, no more boilerplate every consumer has to reinvent.

Consolidation

Run consolidation to promote repeated episodic facts to semantic memories, apply FSRS decay, and archive low-stability memories:

stats = p.consolidate()
print(stats)  # {"promoted": 2, "decayed": 5, "archived": 1}

Forget

Archive (soft-delete) or permanently delete all memories for an entity:

# Soft delete (archive)
p.forget(entity=user)

# Hard delete
p.forget(entity=user, hard_delete=True)

Full Example

import pensyve

p = pensyve.Pensyve(namespace="my-agent")

agent = p.entity("assistant", kind="agent")
user = p.entity("alice", kind="user")

# Record a conversation
with p.episode(agent, user) as ep:
    ep.message("user", "I always use dark mode")
    ep.message("assistant", "Noted!")

# Store an explicit fact
p.remember(entity=user, fact="Alice is a backend engineer", confidence=0.9)

# Query memories
for m in p.recall("what does alice do?", entity=user):
    print(f"[{m.memory_type}] {m.content}")