Most AI agents have a memory problem disguised as a prompting problem.
We keep adding conversation history to the context window, summarizing old messages, or placing documents in a vector database. These techniques are useful, but they do not answer several important questions:
- What should be remembered for hours, weeks, or months?
- What should decay when it stops being useful?
- Which source supports a stored claim?
- What happens when two memories contradict each other?
- Why was a particular memory returned?
- How do we prevent one user's memory from leaking into another user's context?
I built Aura Memory to explore a different approach: memory as a governed cognitive layer that runs beside the model.
The model can remain stateless. Aura owns persistence, retrieval, lifecycle, provenance, and bounded adaptation.
A transcript is not a memory system
A chat transcript records what happened. A memory system decides what remains useful.
That distinction matters once an agent runs longer than a single conversation. Raw history grows without bound. Summaries lose detail. Vector search can find semantically similar text, but similarity alone does not express durability, trust, contradiction, or whether a record has been superseded.
For an agent to develop useful continuity, memory needs its own lifecycle:
interaction
↓
store an observation, decision, outcome, or preference
↓
retrieve bounded context for the next task
↓
inspect provenance and uncertainty
↓
decay, promote, consolidate, correct, or archive
This is the role Aura is designed to fill.
What Aura Memory is
Aura is an open-source cognitive memory runtime with a Rust core and Python bindings. Core memory operations run locally and do not require an LLM call, an embedding API, or a cloud database.
The basic interface is intentionally small:
pip install aura-memory
from aura import Aura, Level
brain = Aura("./agent_memory")
brain.store(
"The user always deploys to staging before production",
level=Level.Domain,
tags=["deployment", "preference"],
)
brain.store(
"A staging deploy caught a migration error",
level=Level.Decisions,
tags=["deployment", "outcome"],
)
context = brain.recall(
"How should I deploy this release?",
token_budget=1200,
)
print(context)
The returned value is bounded context that can be inserted into a model prompt or used by an agent tool. Storage and retrieval are separate from model inference, so the same memory can be used with Claude, Gemini, OpenAI models, Ollama, CrewAI, LangChain, or an MCP client.
Memory has different timescales
Not every record deserves permanent storage. Aura organizes records into four levels:
| Level | Typical role | Expected timescale |
|---|---|---|
| Working | Temporary task context | Hours |
| Decisions | Choices, actions, and active work | Days |
| Domain | Project knowledge and stable preferences | Weeks |
| Identity | Durable rules and identity-level facts | Months or longer |
Maintenance cycles apply decay, promotion, consolidation, and archival. Frequently useful or important records can persist; low-value working context can fade.
report = brain.run_maintenance()
This is deliberately different from appending every message forever. Forgetting is part of the design, not an error condition.
From records to cognitive structure
Raw records are only the first layer. Aura can build bounded cognitive overlays:
Records → Beliefs → Concepts → Causal Patterns → Policy Hints
- Records preserve observations, decisions, preferences, and outcomes.
- Beliefs group supporting and conflicting evidence.
- Concepts capture recurring abstractions across stable beliefs.
- Causal patterns represent repeated cause-and-effect relationships.
- Policy hints surface advisory guidance such as “prefer staging first.”
The important word is advisory. A policy hint does not execute an action. Cognitive reranking is bounded, inspectable, and can be disabled. The application remains responsible for deciding what the agent is allowed to do.
brain.enable_full_cognitive_stack()
hints = brain.get_surfaced_policy_hints()
This separation helps avoid a dangerous design pattern: allowing an automatically generated memory to silently become an instruction with unlimited authority.
Retrieval should explain itself
When memory influences an agent answer, “the vector database returned it” is not enough of an explanation.
Aura exposes inspection surfaces such as:
brain.explain_recall("deployment decision", top_k=5)
brain.explain_record(record_id)
brain.provenance_chain(record_id)
The goal is to make memory behavior operator-visible. Applications can inspect why a record surfaced, how it was derived, whether it conflicts with other information, and whether a correction is pending.
Aura also supports governed correction and bounded adaptation. Experiences can enter a reviewable pipeline rather than directly rewriting model weights or silently mutating high-trust knowledge.
What changed in version 1.5.6
The 1.5.6 release focuses on a problem that becomes critical for research and production agents: a plausible claim is not the same as a verifiable claim.
Immutable evidence lineage
Evidence lineage binds a claim to:
- a specific source document revision;
- the exact byte span used as evidence;
- a content hash;
- verification status;
- an independent answer-permission decision.
A high confidence score cannot override a changed source hash, a superseded claim, or blocked citation admission. Evidence-aware reports only compose admitted findings, preventing blocked source material from being reintroduced indirectly through free-form synthesis.
Deterministic context capsules
An agent often needs a stable “working set” for a task. Re-running broad recall can produce unnecessary context churn, while maintaining a second wiki creates another source of truth.
Context capsules provide a read-only, token-bounded projection over existing memory:
capsule = brain.build_context_capsule(
purpose="continue the current reliability investigation",
token_budget=2000,
namespace="reliability-team",
)
A capsule reports why each record was selected, how many records were omitted, its estimated token count, and a stable content hash. Blocked or superseded records are excluded.
Observable empty recall
“The search completed successfully but found nothing” is an operational event, not merely an empty list.
Version 1.5.6 adds counters for total and empty recall/search outcomes across formatted recall, structured recall, tier recall, and exact search. This makes it possible to monitor empty-recall rates and distinguish missing knowledge from transport or model failures.
One user, one isolated memory boundary
Local development is simple: one agent process can use one Aura directory.
A hosted multi-user system needs an explicit isolation strategy. The safest model is to treat each user's memory as a separate data boundary:
from pathlib import Path
from aura import Aura
def brain_for(user_id: str) -> Aura:
safe_id = validate_user_id(user_id)
return Aura(Path("./memory") / safe_id)
Namespaces can provide additional logical isolation, but authentication and authorization still belong to the application. Never accept an arbitrary storage path or namespace directly from a browser request.
For a hosted TypeScript frontend, Aura should run behind a trusted backend or dedicated memory service. The frontend calls an authenticated API; the service selects the correct user-owned store. The Python package is not meant to run inside a static frontend bundle.
This deployment detail is easy to miss. A library can make local installation simple, but persistent memory still has to run somewhere and its data has to be backed up, encrypted, observed, and isolated.
What Aura does not do
Aura is not a language model and does not generate the final answer. It does not make unverified input true. It does not replace application authorization, tool permission checks, or a backup strategy.
It is also not a hosted database-as-a-service. If you deploy it on a server, you own the runtime and storage lifecycle. That tradeoff is intentional: local control and offline operation come with operational responsibility.
Sparse Distributed Representation indexing is the default local retrieval path, while optional embeddings can be supplied when an application needs them. Performance depends on workload and hardware; the project includes benchmarks, but production systems should measure their own corpus and access patterns.
Why build memory outside the model?
Models change. Providers change. Context-window pricing changes. Agent memory should not have to disappear every time the inference layer is replaced.
Keeping memory outside the model creates a stable boundary:
Agent runtime
├── model: reasoning and generation
├── tools: actions in the world
└── Aura: persistence, retrieval, evidence, lifecycle, correction
That boundary makes memory portable across models and easier to inspect independently. More importantly, it gives operators somewhere concrete to enforce retention, provenance, isolation, and correction policies.
The project is MIT licensed. You can explore the source code and examples on GitHub, install the package from PyPI, or read the overview at aurasdk.dev.
I am especially interested in how other developers handle three questions:
- Where do you draw the boundary between conversation history and durable memory?
- How do you isolate memory in multi-user agents?
- What evidence should an agent be required to carry before using a stored claim in an answer?
Top comments (7)
The four fixed timescales (Working/Decisions/Domain/Identity) are the part I'd stress-test first, because promotion is where these systems quietly rot. "The user always deploys to staging before production" is a stable preference right up until they don't — a reorg, a new CI pipeline, a policy change. Once that record gets promoted to Domain or Identity, decay no longer protects you, and the very durability you wanted becomes the mechanism that keeps serving a stale rule. The contradiction-handling in your Beliefs layer is the real load-bearing wall here, not decay.
Which raises the question your maintenance model has to answer explicitly: when a fresh Decisions-level record contradicts a long-lived Identity-level fact, who wins? Recency and trust-by-tenure point in opposite directions, and "higher level = higher trust" is exactly the bias that lets an outdated rule outvote current evidence. I'd want
explain_recallto show me not just why a record surfaced but why the contradicting one didn't — the suppressed evidence is usually where the bug lives.That is a fair stress test, and after tracing the current implementation, I agree that promotion is the dangerous boundary.
Aura does not directly treat a higher memory level as stronger evidence inside belief resolution. Hypotheses are scored from confidence, supporting and conflicting evidence, recency, and consistency. The main recall score is also based on retrieval relevance, record strength, and effective trust—not the memory level itself.
However, level still creates significant structural advantages. The current promotion gate is primarily activation- and strength-based; Identity records are protected from route-state decay; level contributes to the generic importance score; and formatted context reserves a separate budget for Identity records and emits them first. So an outdated Identity record can become entrenched even without explicitly “winning” the evidence comparison.
For a fresh Decisions record contradicting an old Identity record, Aura currently has no blanket rule that either level wins. Explicit temporal supersession, expiration, or world refutation can retire the old rule. Otherwise, the Beliefs layer may resolve the competing hypotheses by evidence score or mark them unresolved. There is also a subtle failure mode here: hypothesis recency currently uses last_activated, so repeatedly recalling an old rule can make it appear fresh.
I agree about suppressed evidence. explain_recall now reports candidates rejected because they are expired, not yet valid, below the strength threshold, or outside top_k, but it does not yet expose a losing contradictory hypothesis or explain why the formatter omitted it.
The next safeguard should therefore be:
Block Domain/Identity promotion while a belief is unresolved, volatile, or has significant conflict.
Treat business-time validity and explicit supersession as stronger than level or tenure.
Require stronger and preferably diverse evidence for promotion, not repeated retrieval alone.
Extend the decision trace with the winning and suppressed hypotheses, their scores, evidence, temporal validity, and the exact suppression reason.
So I agree with your underlying point: level should control retention policy, not truth. Evidence and temporal validity must control which version is allowed to guide an answer.
The useful shift here is treating memory as an operational subsystem with decay, provenance, contradiction handling, and tenant boundaries instead of just a bigger retrieval bucket. The questions you list are the right ones because they force memory design to answer "why did this state reappear?" rather than only "can the model recall it?" I also think governed memory gets much easier to trust when retrieval decisions are inspectable alongside the rest of the tool flow, since debugging a bad answer usually means debugging a bad memory read. That is where something like agent-inspect fits naturally for me: seeing the retrieved memory, supporting evidence, and downstream tool use in one local trace. Curious whether you think contradiction resolution should happen at write-time, read-time, or as a separate reconciliation pass.
Thanks — I agree that a bad answer is often a bad memory read before it becomes a bad model response.
I don’t think contradiction resolution should live in only one place. My preferred design is layered:
at write-time, detect and link contradictions without silently deleting either claim;
at read-time, apply validity, provenance, tenant, and admission rules while exposing why one version was selected;
in a separate reconciliation pass, evaluate accumulated evidence and propose a governed correction or supersession with an audit trail.
Aura already follows much of this direction through contradiction records, correction review, provenance, temporal validity, namespace isolation, and explainable recall. The next useful step is a unified retrieval trace showing candidates, exclusions, selected memories, supporting evidence, and the downstream tool actions influenced by them.
AgentInspect looks complementary here: Aura can govern the memory decision, while the execution trace connects that decision to the rest of the agent run. An OpenTelemetry/OpenInference-compatible bridge carrying record IDs, scores, validity boundaries, evidence hashes, and selection reasons would make the integration especially useful.
I think the biggest shift is realizing that memory and context solve different problems. Context answers what the model needs right now; memory answers what should survive the session. We've seen the same distinction become important at IT Path Solutions when building long-running AI agents once memory starts persisting across days or weeks, provenance, TTL, conflict resolution, and tenant isolation matter just as much as retrieval quality. One thing I'd add is temporal versioning: business rules evolve, so agents need to know when a memory was valid, not just whether it's semantically relevant. That's usually where "it worked in staging" starts diverging from production.
Exactly — semantic relevance and temporal validity are two different dimensions.
Aura already handles part of this through created timestamps, supersede(), full version_chain() history, snapshots, and filtering superseded records from context capsules. But I agree that explicit validity intervals would make the model much stronger.
The next step is essentially bitemporal memory: valid_from / valid_until for when a business rule applies, alongside recorded_at / superseded_at for when the system learned or changed it. That would also enable “recall as of time T” and prevent an outdated rule from resurfacing simply because it is semantically relevant.
Your staging-versus-production example captures the problem perfectly. Thanks for adding this — temporal validity deserves to be a first-class part of governed memory.
@teolex2020 Good — glad the trace backed it up, because "higher level ≠ stronger evidence" is the part that's easy to say and hard to actually enforce once promotion is running. The failure I'd watch for isn't the explicit tie-break; it's the implicit one. Even if your resolver weighs recency correctly, a promoted record usually carries more corroboration, more retrieval hits, denser links — so it wins on aggregate signal without ever winning on the rule you wrote down. The bias sneaks in through the features, not the comparator.
That's why I keep pushing on explain_recall showing the suppressed side. If the contradicting Decisions-level record lost, I want the trace to name whether it lost on recency, on trust, or just on "the Identity fact had ten more edges pointing at it." Those are three different bugs and only one of them is the behavior you intended.
Since you've got the implementation open — how does Aura handle the promotion event itself when the record being promoted already contradicts something at the target level? Does it resolve the conflict at write time, or does it promote both and let recall sort it out later? That choice is where I'd expect the stale-rule problem to actually live.