> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cognee.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Watch a Session Become Permanent Memory

> Follow a narrated run in which conversation rules and lessons are absorbed turn by turn, distilled into the knowledge graph, and answered back from a brand-new session

You have been told that session memory becomes permanent memory, and you would like to see it happen rather than take it on faith — which turn absorbed which rule, when the distillation fired, and whether a session that never heard the conversation can still answer from it.

## What You'll Build

Five sentences about a fictional robotics company are remembered into a permanent graph. A six-turn session then runs on top of it, mixing rules, lessons, and preferences that appear nowhere in those seed documents with ordinary questions — and every turn prints the guidance the session absorbed from it. Every third turn, an `improve()` checkpoint distills that accumulated guidance into the graph. The payoff is the last two steps: the same lesson is asked for from a brand-new session that has no conversation history to lean on, and the graph is rendered to HTML with the distilled nodes ringed in gold.

The complete runnable script is
[`examples/demos/sessions/session_flow_stepwise_demo.py`](https://github.com/topoteretes/cognee/blob/main/examples/demos/sessions/session_flow_stepwise_demo.py) —
this page walks through its key moments rather than reproducing it.

## Features in Play

* [Remember](/core-concepts/main-operations/remember) — seeds the permanent graph with the five robotics documents in one call
* [Sessions](/guides/sessions) — the shared `session_id` that makes six separate `recall()` calls one conversation
* [Sessions and Caching](/core-concepts/sessions-and-caching) — `AUTO_FEEDBACK` is what turns a stated rule into a gated guidance entry on the turn that states it
* [Session Distillation](/guides/session-distillation) — curates those gated entries into permanent `session_learnings` lessons
* [Improve](/core-concepts/main-operations/improve) — the call the demo fires every third turn to trigger that distillation
* [Graph Visualization](/guides/graph-visualization) — renders the finished graph so the distilled nodes can be found by eye

## Before You Start

* Complete [Quickstart](/getting-started/quickstart) to understand basic operations
* Ensure you have [LLM Providers](/setup-configuration/llm-providers) configured — every turn is a live recall, and the distillation checkpoints add more calls on top
* Keep [caching enabled](/core-concepts/sessions-and-caching#cache-adapters) so the session cache is available; `CACHE_BACKEND=sqlite` is the default, and the script warns on stderr and degrades if the cache is unavailable
* Run it from a checkout of the cognee repo: it loads the repo-root `.env` with `override=True` before importing cognee, and writes its narrated log and the graph HTML into a sibling `logs/` folder
* Expect it to set some environment for you — it forces `AUTO_FEEDBACK=true`, defaults `LLM_MODEL` to `openai/gpt-4o-mini` on the OpenAI path when unset, mirrors `LLM_API_KEY` into `OPENAI_API_KEY`, and skips cognee's LLM preflight. Set `DEMO_USE_OLLAMA=1` instead to run it fully locally against `ollama serve` (see [Local Ollama](/guides/local-ollama))
* The run starts by pruning data and system metadata, so point it at a scratch instance rather than memory you want to keep

## How It Works

### Stage 1: Seed Permanent Memory

```python theme={null}
    banner("(A) remember(documents)  ->  PERMANENT memory  (runs add -> cognify -> improve)")
    await cognee.prune.prune_data()
    await cognee.prune.prune_system(metadata=True)
    step(f"remember() {len(DOCUMENTS)} documents into dataset '{DATASET_NAME}'")
    result = await cognee.remember(DOCUMENTS, dataset_name=DATASET_NAME)
```

A single `remember()` with a `dataset_name` and no `session_id` takes the permanent path: add, cognify, then improve. The five documents describe products, firmware, and people at Aurora Robotics — the baseline the session will later be measured against, because nothing in them says what a technician must do after flashing firmware.

### Stage 2: Script a Session of Rules and Questions

```python theme={null}
SESSION_TURNS = [
    ("rule", "Always run the HALT test suite before a VoltaArm firmware release."),
    (
        "lesson",
        "Flashing VoltaArm firmware wipes calibration data, so calibration must be re-run afterwards.",
    ),
    ("question", "How does the TerraScout rover navigate warehouses?"),
    ("preference", "Keep firmware answers to a few short bullet points."),
    (
        "lesson",
        "After re-running VoltaArm calibration, verify it against the battery-backed memory bank.",
    ),
    ("question", "Who leads the VoltaArm firmware team?"),
]

# Demo cadence: automatically extract learnings every N turns (option #1). Kept small and
# in-demo on purpose — real cadence/triggering would live in the session lifecycle, not here.
AUTO_DISTILL_EVERY = 3
```

The turns interleave two kinds of message: durable statements (a release rule, two firmware lessons, a formatting preference) and plain questions the seed documents can already answer. The labels are narration only — the session has no idea which is which, and `AUTO_FEEDBACK` decides on its own what is worth keeping.

### Stage 3: Absorb Guidance Turn by Turn

```python theme={null}
async def run_multi_turn_session(user) -> None:
    banner("(B+C) multi-turn session  ->  recall() absorbs guidance; auto-distill every N turns")
    for turn_no, (label, message) in enumerate(SESSION_TURNS, start=1):
        step(f"turn {turn_no} [{label}]", message)
        await cognee.recall(
            query_text=message,
            query_type=SearchType.GRAPH_COMPLETION,
            datasets=[DATASET_NAME],
            session_id=SESSION_ID,
            user=user,
        )
        if turn_no % AUTO_DISTILL_EVERY == 0:
            await auto_distill_checkpoint(user, turn_no)
```

Every turn is an ordinary `recall()`; the only thing making them a conversation is the shared `SESSION_ID`. Because `AUTO_FEEDBACK` is on, each answered turn is also analyzed, and any rule or lesson it states is written into the session's active-guidance layer as a gated entry — the raw material distillation will later curate.

### Stage 4: Distill the Session into the Graph

```python theme={null}
    step(
        f"auto-extraction checkpoint (after turn {turn_no}, every {AUTO_DISTILL_EVERY} turns)",
        "calling improve(session_ids=[...]) -> distills accumulated learnings into the graph",
    )
    await cognee.improve(dataset=DATASET_NAME, session_ids=[SESSION_ID], user=user)
    await show_gated_guidance(user)
```

`improve(session_ids=[...])` is what promotes the session's gated guidance into permanent lessons, so no explicit distillation call is needed. The demo fires it on a simple every-third-turn cadence and then prints the guidance entries with their sections and confidences, so you can see exactly what went in.

### Stage 5: Verify from a Fresh Session

```python theme={null}
    result = await cognee.recall(
        query_text=LESSON_QUESTION,
        query_type=SearchType.GRAPH_COMPLETION,
        datasets=[DATASET_NAME],
        session_id="verification_session",
        user=user,
    )
```

The question — what a technician must do after flashing VoltaArm firmware, and why — is asked under a different `session_id`, so there is no conversation history to answer from. If the answer still carries the firmware-and-calibration lesson, it can only have come from the graph, which is the whole point of the run.

### Stage 6: Find the Distilled Nodes in the Graph

```python theme={null}
    html = await cognee.visualize_graph(
        destination_file_path=str(VIZ_PATH),
        user=user,
    )
    has_ring = "#FFC53D" in html
```

The final step renders the graph to an HTML file next to the run's log. Distilled session-learning nodes are drawn with a dashed gold ring, and the script checks for that color in the markup so the console tells you whether any distilled nodes exist before you open the file.

## Run It

```bash theme={null}
uv run python examples/demos/sessions/session_flow_stepwise_demo.py
```

The run narrates itself in five stages, `(A)` through `(E)`. `(A)` prints the `RememberResult` — status, dataset name, and item count — for the five seeded documents. `(B+C)` prints each of the six turns with its label and message, and after turns 3 and 6 an auto-extraction checkpoint followed by the absorbed guidance entries, each with its section, confidence, and truncated text. `(D)` prints the fresh-session question and the answer the long-term graph alone produced. `(E)` prints the path of the rendered HTML and whether the gold memory ring was found in it. A closing `DONE` banner repeats the log and visualization paths; the wording of answers and distilled lessons varies by model.

<Columns cols={2}>
  <Card title="Session Distillation" icon="graduation-cap" href="/guides/session-distillation">
    How gated session guidance becomes permanent `session_learnings` lessons.
  </Card>

  <Card title="Improve" icon="sparkles" href="/core-concepts/main-operations/improve">
    The operation behind the distillation checkpoints, and what else it bridges.
  </Card>

  <Card title="Sessions" icon="message-square" href="/guides/sessions">
    Working with `session_id` for conversational memory.
  </Card>

  <Card title="Graph Visualization" icon="network" href="/guides/graph-visualization">
    Rendering a graph to HTML and controlling what it shows.
  </Card>
</Columns>
