Memory Blocks and Sleep-Time Compute (Letta)
MemGPT became Letta in 2024. The 2026 evolution adds two ideas: discrete functional memory blocks the model can edit directly, and a sleep-time agent that consolidates memory while the primary agent is idle. Together they let memory scale beyond a single conversation.
Learning Objectives
- Name the three memory tiers Letta uses (core, recall, archival) and the role of each.
- Explain the memory-block pattern: Human block, Persona block, and user-defined blocks as first-class typed objects.
- Describe what sleep-time compute is, why it sits off the critical path, and why it can run a stronger model than the primary agent.
- Implement a scripted two-agent loop where a primary agent serves responses and a sleep-time agent consolidates blocks between turns.
The Problem
MemGPT (Lesson 07) gave us the virtual-memory control flow. Running it in production surfaced three problems:
- Latency. Every memory operation sits on the critical path. If the agent has to prune, summarize, or reconcile while the user waits, tail latency blows up.
- Memory rot. Writes accumulate and nothing cleans up. Contradicted facts stay put, and retrieval drowns in stale content.
- Structure loss. A flat archival store can't express "the Human block is always in the prompt, the Persona block is always in the prompt, and the Task block swaps per session."
Letta (letta.com), the project MemGPT became in 2024, answers these. Memory blocks make the structure explicit. Sleep-time compute moves consolidation off the critical path.
The Concept
A memory block is a typed, persistent, editable section of an agent's always-visible memory. Sleep-time compute is a second agent that rewrites that memory in the background while the primary agent is idle. The rest of this section covers both, starting with where blocks live.
Three tiers
| Tier | Scope | Where it lives | Written by |
|---|---|---|---|
| Core | Always visible | Inside the main prompt | Agent tool call + sleep-time rewrites |
| Recall | Conversation history | Retrievable | Automatic turn logging |
| Archival | Arbitrary facts | Vector + KV + graph | Agent tool call + sleep-time ingest |
Core is the MemGPT core. Recall is the conversation buffer with its evicted tail. Archival is the external store. Splitting them out fixes the overloading MemGPT had with only two tiers.
Memory blocks
A block is a typed, persistent, editable section of the core tier. The original MemGPT paper defined two:
- Human block: facts about the user (name, role, preferences, goals).
- Persona block: the agent's self-concept (identity, tone, constraints).
Letta generalizes this to arbitrary user-defined blocks: a Task block for the current goal, a Project block for codebase facts, a Safety block for hard constraints. Each block has an id, a label, a value, a limit (character cap), and a description so the model knows when to edit it.
The model edits blocks through the tool surface:
block_append(label, text)block_replace(label, old, new)block_read(label)block_summarize(label): condense a block that is near its limit.
Sleep-time compute
Sleep-time compute, a 2025 Letta addition, runs a second agent in the background, off the critical path. Sleep-time agents process conversation transcripts and codebase context, write learned_context into shared blocks, and consolidate or invalidate archival records.
Three properties fall out of that:
- No latency cost. Primary responses never wait on memory operations.
- Stronger model allowed. Nothing is waiting on the sleep-time agent, so it can be a slower, more expensive model.
- A natural consolidation window. Dedup, summarize, and invalidate contradicted facts while the user isn't waiting.
It matches how people work: you do the task, you sleep on it, and long-term memory settles overnight.
Letta V1 and native reasoning
Letta V1 (letta_v1_agent, 2026) drops send_message/heartbeat and inline Thought: tokens in favor of native reasoning. The Responses API (OpenAI) and the Messages API with extended thinking (Anthropic) emit reasoning on a separate channel, passed through turns (encrypted across providers in production). The control loop is still ReAct. The thought trace is just structural now instead of prompt-shaped.
Where this pattern goes wrong
- Block bloat. Unbounded
block_appendhits the limit fast. Wire in a block summarizer that runs before any write that would push the block over the cap. - Silent drift. The sleep-time agent rewrites a block and the primary agent never notices. Version your blocks and surface the diffs in the trace.
- Poisoned consolidation. If the sleep-time agent processes attacker-reachable content into core, the attacker gets a persistent foothold. Lesson 27 applies to the sleep-time surface too.
Build It
code/main.py implements:
Block: id, label, value, limit, description, plus anear_limit()check.BlockStore: creates blocks and looks them up by label.- Two scripted agents:
PrimaryAgentserves a turn,SleepTimeAgentconsolidates between turns. - A trace of a four-turn conversation with block writes, followed by a sleep-time pass that invalidates a stale fact. The pass also summarizes any block at 80 percent of its limit, but no block gets that full in this trace.
Run it:
python3 code/main.py
The example does not expose block_summarize as a tool. Summarization runs inside the sleep pass, and Exercise 1 asks you to add it as a tool the primary agent can call.
The transcript shows the split. Primary turns are fast and leave raw writes behind. The sleep pass cleans up after them.
Use It
- Letta (letta.com) is the reference implementation. Self-host it or use the managed cloud.
- Claude Agent SDK skills work as block-shaped knowledge. A skill is a named, versioned, retrievable block of instructions the agent loads on demand.
- Custom builds make sense when you need control over the storage backend. Follow the Letta API contract so you can migrate later.
Ship It
outputs/skill-memory-blocks.md generates a Letta-shaped block system with sleep-time hooks for any runtime, including safety rules and citation wiring.
The design choice I'd defend hardest is that the sleep-time agent is a separate writer, and you should treat it like one. Consolidation feels like housekeeping, harmless because nobody is waiting on it. But it's the only process that rewrites core memory while nobody is watching, which makes a bad rewrite the hardest kind to catch. Version your blocks from day one and put the diffs in the trace. When an agent "forgets" something, the first question is always which write removed it, and without history you're guessing.
Exercises
- Add the
block_summarizetool from the Letta surface (the example only summarizes in the sleep pass). It replaces the block value with a model-generated summary whennear_limitreturns true. Which trigger threshold minimizes both summarization calls and block overflow? - Implement sleep-time dedup over archival: two records whose text has >90% token overlap collapse to one. Do it only in the sleep pass, never on the critical path.
- Version blocks. On every write, record the old value and a diff. Expose
block_history(label)so operators can debug "why did the agent forget X." - Treat sleep-time agents as untrusted writers. When one touches the Persona or Safety block, require a second-agent review before committing.
- Port the example to the Letta API (
letta_v1_agent). What changes in the block schema, and how does native reasoning alter the trace shape?
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| Memory block | "Editable prompt section" | Typed, persistent, LLM-editable segment of core memory |
| Human block | "User memory" | Facts about the user, pinned in core |
| Persona block | "Agent identity" | Self-concept, tone, constraints, pinned in core |
| Sleep-time compute | "Async memory work" | Second agent doing consolidation off the critical path |
| Core / Recall / Archival | "Tiers" | Three-layer memory split: always-visible / conversation / external |
| Block limit | "Cap" | Character limit per block; forces summarization |
| Native reasoning | "Thinking channel" | Provider-level reasoning output, not prompt-level Thought: |
| Learned context | "Sleep output" | Facts the sleep-time agent writes into shared blocks |
Further Reading
- Letta, Memory Blocks blog: the block pattern
- Letta, Sleep-time Compute blog: async consolidation
- Letta, Rearchitecting the Agent Loop: native reasoning rewrite
- Packer et al., MemGPT (arXiv:2310.08560): the origin
Code
- main.py (python, 245 lines)