Memory
Record and recall agent memory through a log, managed views, or semantic ranking
Memory records facts and changes on an auditable log. Recall can select recent items, exact terms, or semantic matches. Add a vector backend or reranker for ranking by meaning.
Built for
Use Memory for assistants that retain user facts and agents that learn from recorded feedback.
How it works
laser.memory(namespace) opens durable memory. Remember, improve, and forget operations each append a message under a conversation ID. Connected processes can recall those records and their versioned history.
Choose how recall reads and ranks data:
- Default recall reads the managed key-value view and ranks by recency.
.folded()rebuilds memory from the topic within the process. All three clients support it, including with standalone Iggy.- Semantic ranking uses a backend. Rust selects
memory_with(namespace, MemoryBackend::Vector).embedder(..), Python usesMemory.vector(embedder), and TypeScript usesMemoryHandle.vector(embedder). Rust and TypeScript also support rerankers. Plain log memory treats.semantic(..)as recent recall.
.limit(n) limits returned items. A token budget can trim results for a later model call, as Context does for conversation history.
Recall provides four strategies:
.recent()returns the newest items without query text..semantic(text)ranks embedding similarity. Without an embedding backend, it returns recent items..keyword(text)matches exact terms, useful for names and identifiers..hybrid(text)combines semantic and keyword rankings by reciprocal rank. Each item retains attribution insignals.
remember(..) defaults to kind Fact. Other kinds are Message, Summary, Entity, Feedback, and Procedure. They distinguish events, summaries, entities, ranking feedback, and reusable workflows. Select a kind with .kind(..).
.dedup() derives a record ID from owner, kind, and body rather than generating a random ID. Repeating a fact then stores it once. Graph uses the same content-addressing approach for node IDs.
For directly addressed facts, use set_named(key, body), fetch_named(key), update_named(key, patch), and forget_named(key) instead of a recall search.
Rust's memory.context(conversation, token_budget) recalls items and formats a prompt block. It estimates one token per roughly 4 bytes without a tokenizer dependency. At the budget, it drops remaining items and adds an omission marker.
consolidate(scope, max_items) keeps the newest items, removes older ones, and reports the result. DefaultConsolidator can turn removed items into a Summary. prune_summarized() removes items covered by that summary.
Quick example
const memory = laser.memory("customer:42")
const conversation = ConversationId.new()
const id = await memory
.remember(utf8("Prefers aisle seats, travels monthly"))
.conversation(conversation)
.send()
const hits = await memory
.recall()
.conversation(conversation)
.recent()
.limit(5)
.folded()
.fetch()
for (const hit of hits) {
console.log(decodeUtf8(hit.payload))
}
await memory.improve({ conversation }, { target: id, weight: 1 })
await memory.forget({ conversation }, id)let memory = laser.memory("customer:42");
let scope = MemoryScope::builder().conversation(conversation).build();
let fact = memory
.remember("Prefers aisle seats, travels monthly".as_bytes())
.scope(conversation)
.send()
.await?;
let hits = memory
.recall(conversation)
.recent()
.limit(5)
.folded()
.fetch()
.await?;
for hit in &hits {
println!("{}", String::from_utf8_lossy(&hit.payload));
}
memory.improve(&scope, Feedback::new(fact, 1.0)).await?;
memory.forget(&scope, fact).await?;memory = laser.memory("customer:42")
fact_id = await memory.remember(
"Prefers aisle seats, travels monthly",
conversation=conversation,
)
hits = await memory.recall(
limit=5,
conversation=conversation,
strategy="recent",
folded=True,
)
print([hit.text for hit in hits])
await memory.improve(fact_id, 1.0, conversation=conversation)
await memory.forget(fact_id, conversation=conversation)Complete examples: Rust, Python, and TypeScript.
The memory example combines durable memory and a knowledge graph.
Key operations
| Verb | What it does |
|---|---|
laser.memory(namespace) | Scope memory to a namespace, log-backed and versioned |
remember(payload) | Store a fact under a conversation scope |
.kind(k) / .dedup() / .agent(id) | Memory kind, content-addressed dedup, per-agent scoping on a remember |
recall(..) + .recent() / .semantic(t) / .keyword(t) / .hybrid(t) | The four recall strategies |
.folded() | Read the memory topic in process, no managed capability needed |
.embedder(..) / .reranker(..) | Attach an embedding function or a reranking pass |
limit(n) | Cap how many hits come back |
improve(id, weight, ..) | Adjust a remembered item's standing with feedback |
forget(id, ..) | Remove a remembered item |
set_named(key, body) / fetch_named(key) | Keyed facts you address directly instead of searching for |
context(conversation, budget) | Recall and render one prompt-ready block (Rust) |
consolidate(scope, max_items) | Prune a scope with the default keep-newest policy |
All clients provide remember, recall, improve, forget, managed recency reads, folded reads, and four recall strategies. TypeScript also exposes kind, dedup, hybrid, and consolidate through camelCase APIs.
Python uses Memory.vector(embedder) for similarity ranking. Named facts use set, fetch, update, and remove. Python does not support kind= or dedup on remember, or the combined context and consolidate calls.
Running it
Memory records use the agent audit topic. Run bootstrap(partitions) once per stream before writing. The example uses .folded() and works with Iggy alone. Default recall requires the managed key-value view in laser-plane.