---
title: "Agent memory"
description: "How flows remember things across executions: sessions, episodic conversation history, consolidated semantic memories, and the axiom memory CLI."
category: concept
surfaces: [sdk, cli, canvas]
related: [guides/inspect-agent-memory, guides/debug-a-flow, concepts/sandboxing-and-tenancy, concepts/execution-model, reference/sdk/python, reference/glossary]
last_reviewed: 2026-06-06
---

# Agent memory

## What agent memory is

Agent memory is Axiom's built-in durable store that lets a flow remember things across executions. Each execution of a flow is otherwise stateless: a node receives its input message, returns its output message, and the execution context is gone. Memory fills that gap — a customer-support flow can recall what a user said yesterday, and accumulate facts about its task over many executions.

Node code reaches memory through one entry point: `ax.agent.memory` on `AxiomContext`. There is nothing to provision and no connection string to manage. Memory is scoped to the flow: every execution of the same flow shares one memory store, and different flows have isolated stores. Tenant isolation is enforced by the platform, not by your code — the sidecar stamps your tenant and flow identity onto every memory call, and whatever a node puts in those fields is ignored (see [Sandboxing and tenancy](../concepts/sandboxing-and-tenancy.md)).

Outside node code, the `axiom memory` CLI lists, inspects, searches, and deletes memory, and the flow debugger shows memory reads and writes live as a flow runs.

## The three memory tiers

Axiom separates "memory" into three tiers with different lifetimes:

### Working memory: the typed payload

The message flowing through the flow *is* the working memory. If a node needs conversation context within a single execution, that context belongs in its input and output message types. This tier is explicit, typed by Protocol Buffers, and needs no memory API at all. See [Type system](../concepts/type-system.md).

### Episodic memory: conversation history per session

Episodic memory is the time-ordered record of conversation turns within a session. Each turn has a `role` (`user`, `assistant`, `tool`, or `system`), a `content` string, and a timestamp. Node code appends turns and reads back the last N turns through the session API (below). Episodic memory is scoped to `(tenant, flow, session)` and is retained for a platform-configured period (90 days by default).

### Semantic memory: long-term facts per flow

Semantic memory is the flow's long-term knowledge: concise factual statements distilled from episodic history by a background consolidation process (below). Each entry carries an `importance` score between 0 and 1 and a `confidence` score. Semantic memory is scoped to `(tenant, flow)` — it persists across sessions and executions — and is retrieved by semantic search. Node code can also write facts directly with `ax.agent.memory.write(...)`.

## Sessions

A session groups related conversation turns — typically one user's conversation thread. Your node code chooses the session ID (a string, usually taken from the input message) and addresses it with `ax.agent.memory.session(session_id)`. The session object gives you:

- `history.append(role=..., content=...)` — record a turn.
- `history.last(n)` — read the most recent `n` turns.
- `search(query, limit=5)` / `write(content, importance=0.5)` — session-scoped memory entries.
- `end()` — formally close the session and trigger consolidation.

Sessions are not opened or declared anywhere; appending the first turn to a new session ID creates it. A session can also be closed from outside node code with `axiom memory end <session-id>`.

## How consolidation works

Consolidation turns a session's episodic history into permanent semantic memory. It runs when a session is ended — either node code calls `session.end()` or you run `axiom memory end <session-id>`. When a session ends, the platform reads its conversation history, extracts distinct facts and preferences as concise statements, and stores them as flow-scoped semantic memories. Near-duplicate facts are merged rather than duplicated, so the same piece of information does not accumulate as redundant entries over time.

Consolidation is non-destructive — the conversation history remains readable after it runs. Extracted facts typically appear within a minute of ending a session. Until a session has been ended and consolidated, `axiom memory show` reports no semantic memories for it (facts written directly with `write(...)` are the exception — they appear immediately).

## How memory search ranks results

`ax.agent.memory.search(query)` (and `axiom memory search`) searches the flow's memories and returns ranked results. The search combines keyword matching with semantic similarity, so relevant entries surface whether the query uses the exact same words or a different phrasing. Every returned entry includes its relevance `score`. The CLI also reports the retrieval method and the query latency in milliseconds.

## Read and write memory from node code

Memory is available in every SDK language through `AxiomContext`. The example below is Python (see the per-language guides for the others); it assumes a package whose messages define `ChatRequest` with `session_id` and `text` fields and `ChatReply` with a `text` field. Declare the handler `async def` to `await` the memory APIs.

```python
# nodes/chat_agent.py — node file in a Python package (axiom create node ChatAgent)
from gen.messages_pb2 import ChatRequest, ChatReply
from gen.axiom_context import AxiomContext


async def chat_agent(ax: AxiomContext, input: ChatRequest) -> ChatReply:
    """Replies using recent conversation turns and long-term facts."""
    session = ax.agent.memory.session(input.session_id)

    # Episodic: record this turn, then load recent history.
    await session.history.append(role="user", content=input.text)
    recent = await session.history.last(20)

    # Semantic: retrieve facts consolidated from earlier sessions of this flow.
    facts = await ax.agent.memory.search(input.text, limit=5)

    reply = f"I see {len(recent)} recent turn(s) and {len(facts)} relevant memorie(s)."
    await session.history.append(role="assistant", content=reply)
    return ChatReply(text=reply)
```

`ax.agent.memory.write(content, importance=0.5)` stores a flow-scoped fact directly and returns the new memory's ID; `session.write(...)` does the same at session scope. You never pass tenant or flow identifiers — the platform injects them.

## Inspect memory with the CLI

The `axiom memory` command group inspects and manages agent memory from your terminal. All commands require an active login (`axiom login`); memory data lives in the Axiom platform, with no local state.

```bash
# Requires: axiom CLI installed and logged in (axiom login)
axiom memory ls                                    # flows that have memory
axiom memory ls --flow <flow-id>                   # sessions for one flow
axiom memory show <session-id>                     # conversation + semantic memories
axiom memory search --flow <flow-id> "the query"   # semantic search over a flow's memories
axiom memory end <session-id>                      # close a session, trigger consolidation
axiom memory rm <session-id>                       # delete one session
axiom memory rm --flow <flow-id> --all             # delete all memory for a flow
```

`rm` deletes permanently and `end` closes a session; both ask for confirmation unless you pass `--yes`. For a worked walkthrough, see [Inspect agent memory](../guides/inspect-agent-memory.md); for every flag, see the [`axiom memory` CLI reference](../reference/cli/axiom-memory.md). While debugging a run, the flow debugger also displays each memory read and write as it happens — see [Debug a flow](../guides/debug-a-flow.md).
