> ## 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.

# Sessions and Caching

> Learn how Cognee handles short-term memory with sessions and caching.

In Cognee, a session defines the scope for a single conversation or agent run. It maintains a cache of short-term information, including recent queries, responses, and the context used to answer them.

## What Is a Session?

A session is Cognee's short-term memory for a specific user. It is identified by `(user_id, session_id)` and stores an ordered list of recent interactions.

In the v1.0 API, you interact with sessions through [`remember()`](/core-concepts/main-operations/remember) and [`recall()`](/core-concepts/main-operations/recall):

* `cognee.remember(data, session_id="my_session")` — writes content directly into the session cache for fast retrieval.
* `cognee.recall(query_text, session_id="my_session")` — searches session cache entries first, then falls through to the permanent graph if nothing matches.

The lower-level [`cognee.search()`](/core-concepts/main-operations/legacy-operations/search) also accepts `session_id`. Session-aware retrieval is used across the main completion-oriented search paths, including graph-completion variants, RAG, triplet, summaries, temporal, cypher, and natural-language retrieval.

For session-aware retrieval, omitting `session_id` makes `recall()` or `search()` use `default_session` and still store the turn when caching is enabled. This is different from `remember()`, where omitting `session_id` writes directly to permanent memory instead of creating a session.

Cognee reads from session memory at the start of a retrieval to recover earlier turns. When the retrieval finishes, it writes a new interaction to the session so the history grows over time.

Using the same `session_id` across calls allows Cognee to include previous interactions as conversational history in the LLM prompt, enabling follow-up questions and contextual awareness.

To inspect stored history, use `cognee.session.get_session(session_id=..., last_n=...)`. To annotate a stored entry, use `cognee.session.add_feedback(...)` and `cognee.session.delete_feedback(...)`.

<Note>
  Sessions require caching to be enabled. See the next sections and Configuration Details below. If caching is disabled or unavailable, searches still work but without access to previous interactions.
</Note>

## Session Cache vs Permanent Memory

Cognee keeps two distinct kinds of memory. `remember()` writes to one or the other depending on whether you pass `session_id`:

|                    | Session cache (short-term)                                                                                   | Permanent memory (knowledge graph)                                                                                                                                                                                                                                                 |
| ------------------ | ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **How to write**   | `remember(data, session_id="...")`                                                                           | `remember(data)` (no `session_id`)                                                                                                                                                                                                                                                 |
| **What happens**   | Raw text is written straight to the cache as a Q\&A entry — no chunking, no entity extraction, no embeddings | Runs the full [Add](/core-concepts/main-operations/legacy-operations/add) + [Cognify](/core-concepts/main-operations/legacy-operations/cognify) pipeline: chunking, entity/relationship extraction, and embeddings, plus an [Improve](/core-concepts/main-operations/improve) pass |
| **Latency / cost** | Near-instant, no LLM calls                                                                                   | Heavier — LLM and embedding calls scale with input size                                                                                                                                                                                                                            |
| **Scope**          | One conversation, keyed by `(user_id, session_id)`                                                           | A named dataset, shared across all sessions                                                                                                                                                                                                                                        |
| **Lifetime**       | Expires after `SESSION_TTL_SECONDS` (default 7 days)                                                         | Durable until you [Forget](/core-concepts/main-operations/forget) it                                                                                                                                                                                                               |
| **Best for**       | Conversation turns, scratch context, recent interactions                                                     | Documents, facts, anything you want to query later as a graph                                                                                                                                                                                                                      |

Passing `session_id` does **not** run graph extraction on that content — the write is raw and fast by design. This is why `remember(data, session_id=...)` does not, on its own, place data in the permanent graph. When `self_improvement=True` (the default), it additionally kicks off a background [Improve](/core-concepts/main-operations/improve) pass that bridges cached turns, agent traces, and accepted distilled session guidance into the permanent graph; with `self_improvement=False`, the content stays in the cache only until you explicitly call `cognee.improve(dataset=..., session_ids=[...])`. To write straight to permanent memory, call `remember()` without a `session_id`.

## How Sessions Work

Sessions integrate with both the v1.0 operations and the lower-level search pipeline.

**v1.0 session flow (via `recall`):**

When you call `cognee.recall(query_text, session_id="my_session")`:

1. **Check session cache** – Cognee searches the session cache for matching entries using keyword matching
2. **Fall through to graph** – If no session entries match, retrieval continues against the permanent knowledge graph
3. **Return tagged results** – Results include a `_source` field indicating whether they came from `"session"` or `"graph"`

**Lower-level session flow (via `search`):**

When you call `cognee.search()` with a `session_id`:

1. **Retrieve context** – Cognee finds relevant graph elements for your query
2. **Load conversation history** – If caching is enabled, previous interactions for `(user_id, session_id)` are loaded
3. **Generate answer** – The LLM receives the query, graph context, and retrieved history
4. **Save interaction** – A new Q\&A entry is stored in the session cache

## Cache Adapters

Cognee supports two cache adapters for storing sessions. Redis is recommended for distributed or multi-process setups, while Filesystem can be used when you need a simple local cache without network dependencies. Both provide the same functionality; only the storage backend differs. Below are the configuration options for each adapter with additional details.

<Tabs>
  <Tab title="Redis">
    Add to your `.env` file:

    ```dotenv theme={null}
    CACHING=true
    CACHE_BACKEND=redis
    CACHE_HOST=localhost
    CACHE_PORT=6379
    ```

    **Start Redis:**

    ```bash theme={null}
    # Using Docker
    docker run -d -p 6379:6379 redis:latest

    # Or using local installation
    redis-server
    ```

    * Fast in-memory storage
    * Supports shared locks for Kuzu (multi-process coordination)
    * Requires a running Redis instance and network connectivity
  </Tab>

  <Tab title="Filesystem">
    **Configuration:**

    Add to your `.env` file:

    ```dotenv theme={null}
    CACHING=true
    CACHE_BACKEND=fs
    ```

    * Sessions are stored in `{DATA_ROOT_DIRECTORY}/.cognee_fs_cache/sessions_db`.
    * Stores session data on the local filesystem using `diskcache`
    * No network dependency
    * Does not provide shared locks for Kuzu
    * Not designed for multi-node coordination
  </Tab>
</Tabs>

<AccordionGroup>
  <Accordion title="Session Lifecycle Persistence (Relational DB)">
    In addition to the cache layer, Cognee persists session lifecycle metadata to the relational database (SQLite or Postgres). Running database migrations — either via `await cognee.run_startup_migrations()` or `alembic upgrade head` — creates the required relational tables for this metadata.

    Two tables are created:

    <Tabs>
      <Tab title="session_records">
        One row per `(user_id, session_id)`:

        | Column             | Type                 | Description                                                                 |
        | ------------------ | -------------------- | --------------------------------------------------------------------------- |
        | `session_id`       | String (PK)          | The caller-supplied session identifier.                                     |
        | `user_id`          | UUID (PK)            | The owning user. Same `session_id` from two users is two separate sessions. |
        | `dataset_id`       | UUID (nullable)      | Associated dataset, if any.                                                 |
        | `status`           | String               | Stored status: `running`, `completed`, or `failed`.                         |
        | `started_at`       | Timestamp            | When the session started.                                                   |
        | `last_activity_at` | Timestamp            | When the session last received an LLM call.                                 |
        | `ended_at`         | Timestamp (nullable) | When the session was marked completed or failed.                            |
        | `tokens_in`        | Integer              | Cumulative input tokens across all LLM calls in this session.               |
        | `tokens_out`       | Integer              | Cumulative output tokens.                                                   |
        | `cost_usd`         | Float                | Estimated cumulative cost in USD.                                           |
        | `error_count`      | Integer              | Number of errors recorded in this session.                                  |
        | `last_model`       | Text (nullable)      | Most recently used LLM model name.                                          |
      </Tab>

      <Tab title="session_model_usage">
        One row per `(session_id, user_id, model)`:

        | Column       | Type        | Description                                 |
        | ------------ | ----------- | ------------------------------------------- |
        | `session_id` | String (PK) | The session.                                |
        | `user_id`    | UUID (PK)   | The owning user.                            |
        | `model`      | Text (PK)   | The model name (e.g. `openai/gpt-4o-mini`). |
        | `tokens_in`  | Integer     | Input tokens attributed to this model.      |
        | `tokens_out` | Integer     | Output tokens attributed to this model.     |
        | `cost_usd`   | Float       | Cost attributed to this model.              |
        | `updated_at` | Timestamp   | When this row was last updated.             |
      </Tab>
    </Tabs>

    Splitting per-model usage out of `session_records` allows mixed-model sessions (e.g. a completion model plus an embedding model) to attribute cost correctly.

    **Session visibility rules**

    Each `session_records` row remains keyed by the `user_id` that actually created the session, but the HTTP read paths can surface any rows visible to the requesting user at read time. That includes the requesting user's own sessions, sessions created by child-agent users whose `parent_user_id` matches the requesting user's `id`, and sessions visible through dataset read permissions. This affects `GET /api/v1/sessions`, `GET /api/v1/sessions/{session_id}`, `GET /api/v1/sessions/stats`, and `GET /api/v1/sessions/cost-by-model`.

    **Session status lifecycle:**

    Sessions move through: `running` → `completed` or `failed`. The `abandoned` status is never written to the database — it is computed at read time: a session whose `last_activity_at` is older than the abandonment threshold and is still in `running` state is reported as `abandoned`. The threshold defaults to 30 minutes and is configurable:

    ```dotenv theme={null}
    SESSION_ABANDON_AFTER_SECONDS=1800  # default: 30 minutes
    ```

    This means no background sweeper is needed to mark stale sessions. Reads include the effective status automatically.

    <Note>
      Token counts use a character-based estimate (`len(text) // 4`) when the LLM client does not return exact usage counts. These are approximate and suitable for dashboard aggregates rather than precise billing.
    </Note>
  </Accordion>

  <Accordion title="Session Data Structure">
    Sessions store interactions as JSON entries in a list. Each item returned by `cognee.session.get_session()` is a `SessionQAEntry` model with the following fields:

    | Field                    | Type                             | Description                                                                                                                              |
    | ------------------------ | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
    | `time`                   | `str`                            | ISO 8601 timestamp when the entry was created.                                                                                           |
    | `qa_id`                  | `Optional[str]`                  | Unique identifier for the Q\&A turn. Cognee assigns a UUID when the turn is stored; use this ID with feedback and per-entry update APIs. |
    | `question`               | `str`                            | The user's original query text.                                                                                                          |
    | `context`                | `str`                            | Retrieved context used to answer the question. May be empty if summarization is not enabled.                                             |
    | `answer`                 | `str`                            | The generated answer.                                                                                                                    |
    | `feedback_text`          | `Optional[str]`                  | Free-form feedback text, or `None` if not set.                                                                                           |
    | `feedback_score`         | `Optional[int]`                  | Integer rating from `1` to `5`, or `None` if not set.                                                                                    |
    | `used_graph_element_ids` | `Optional[Dict[str, List[str]]]` | Graph node and edge IDs used during retrieval. Keys are `node_ids` and `edge_ids`.                                                       |
    | `memify_metadata`        | `Optional[Dict[str, bool]]`      | Session persistence and memify status flags, such as `feedback_weights_applied`.                                                         |

    Sessions are keyed by `agent_sessions:{user_id}:{session_id}`.

    Each user can have multiple sessions, each maintaining its own cache of short-term information.
  </Accordion>

  <Accordion title="Configuration Details">
    **Environment Variables:**

    * `CACHING` (bool): Enable/disable caching (default: `true`). Set to `false` to disable session storage and conversational memory.
    * `AUTO_FEEDBACK` (bool): Enable automatic session-context guidance and feedback detection on each answered turn (default: `true`). Requires `CACHING` to be on and uses the resolved session (`default_session` when `session_id` is omitted). When enabled, each session-capable completion search turn runs one extra structured-output LLM call to analyze the turn against the previous one (see [Session-context guidance](#session-context-guidance-auto-feedback) below). Set to `false` to disable the extra call and restore plain history-only sessions.
    * `CACHE_BACKEND` (str): `"redis"`, `"fs"`, or `"tapes"` (default: `"fs"`). When set to `"fs"`, sessions are stored on local disk; when set to `"redis"`, sessions are stored in Redis and shared across processes; when set to `"tapes"`, sessions are stored locally and new Q\&A turns are mirrored to a running Tapes ingest service.
    * `CACHE_HOST` (str): Redis hostname (default: `"localhost"`)
    * `CACHE_PORT` (int): Redis port (default: `6379`)
    * `CACHE_USERNAME` (str, optional): Redis username
    * `CACHE_PASSWORD` (str, optional): Redis password
    * `SESSION_TTL_SECONDS` (int, optional): Time-to-live for cached session entries in seconds (default: `604800` — 7 days). Set to `0` to disable expiry.

    **Conversation history window:**

    * Cognee includes up to the last 10 session entries when building LLM conversation history.
    * Each entry is a full question/answer turn — a single `SessionQAEntry` holding both the user's `question` and the generated `answer` (see [Session Data Structure](#session-data-structure) above). So the window covers up to 10 prior exchanges, not 10 individual messages.
    * This 10-entry window is **fixed and not configurable** — there is no environment variable for it. The configurable session settings are the cache toggle and backend (`CACHING`, `CACHE_BACKEND`, `CACHE_HOST`, `CACHE_PORT`, `CACHE_USERNAME`, `CACHE_PASSWORD`), the entry expiry (`SESSION_TTL_SECONDS`), and the abandonment threshold (`SESSION_ABANDON_AFTER_SECONDS`).

    Sessions expire automatically after `SESSION_TTL_SECONDS` when that value is greater than `0`. If you set `SESSION_TTL_SECONDS=0`, sessions persist until the cache is cleared (e.g. via prune or by wiping the cache backend).

    **Graceful fallback behavior:**

    * If no cache backend is configured or the cache is unavailable, `cognee.session.get_session()` returns `[]`.
    * In the same situation, `cognee.session.add_feedback()` and `cognee.session.delete_feedback()` return `False`.
  </Accordion>

  <Accordion title="Session-context guidance (AUTO_FEEDBACK)">
    When `AUTO_FEEDBACK` is enabled (the default) and `CACHING` is on, session-capable completion searches run a lightweight analysis step before retrieval under the resolved session (`default_session` when `session_id` is omitted). This step is on by default — existing session usage now performs one additional structured-output LLM call per answered turn.

    For each turn, Cognee:

    * Compares the current query against the previous turn's question, answer, and the context that was served for it.
    * May derive an **effective query** used for retrieval and answer generation instead of the raw query (for example, resolving a terse follow-up into a self-contained question).
    * Accumulates durable, per-session **guidance** grouped into `goals`, `rules`, `preferences`, and `lessons_learned`, which can be injected into later answers in the same session.
    * May **gate** a follow-up turn: when the analysis determines the turn does not require retrieval, the search returns a short acknowledgement (the analysis-provided reply, or `"Got it."`) instead of running retrieval and completion.

    The step **fails open** — if analysis errors or no session is available, the original query is answered normally. Because guidance and the effective query can change retrieval inputs, answers (and turn gating) may differ from history-only sessions. To disable this behavior and keep only conversation-history replay, set `AUTO_FEEDBACK=false`.

    <Note>
      This adds one structured-output LLM call per answered turn, with a corresponding increase in latency and token usage. Sessions still work without it; set `AUTO_FEEDBACK=false` to opt out.
    </Note>
  </Accordion>

  <Accordion title="Session distillation into long-term memory">
    Session-context guidance is short-term until it is bridged into the graph. When you run `cognee.improve(dataset=..., session_ids=[...])`, Cognee can distill gated guidance from those sessions into permanent lesson documents.

    Distillation:

    * Loads session Q\&A and active session-context entries.
    * Keeps only guidance that was never rated harmful and has enough confidence.
    * Curates proposed durable lessons, checks them against previously distilled lessons and graph entities, and rejects lessons that are already known, unsupported, or not durable.
    * Writes accepted lessons back into the dataset through `add()` + `cognify()`.
    * Tags distilled lessons with `session_learnings` and a session-specific node set.

    You can run this directly for one finished session:

    ```python theme={null}
    result = await cognee.session.distill_session(
        "my_session",
        dataset="my_dataset",
    )
    ```

    `result.documents` contains the rendered lesson documents when the status is `completed`. Empty output can be normal when the session has no gated entries or no accepted lessons.
  </Accordion>

  <Accordion title="Adapter Comparison">
    | Feature          | Redis                   | Filesystem             | Tapes                                    |
    | ---------------- | ----------------------- | ---------------------- | ---------------------------------------- |
    | Storage          | In-memory (Redis)       | Local disk (diskcache) | Local disk + mirrored ingest             |
    | Performance      | Very fast               | Fast (local I/O)       | Fast local writes + network mirror       |
    | Multi-process    | ✅ Supported             | ❌ Not supported        | ❌ Not supported                          |
    | Shared locks     | ✅ Yes                   | ❌ No                   | ❌ No                                     |
    | Network required | ✅ Yes                   | ❌ No                   | ⚠️ Only for mirroring to Tapes           |
    | Setup complexity | Medium                  | Low                    | Medium                                   |
    | Best for         | Production, distributed | Development, local     | Local session cache with Tapes ingestion |
  </Accordion>
</AccordionGroup>

<Note>
  Cached sessions can be persisted into the knowledge graph for long-term retrieval using [`improve()`](/core-concepts/main-operations/improve). The older [session persistence memify pipeline](/guides/memify-session-persistence) documents the legacy Q\&A persistence path.
</Note>

<Columns cols={3}>
  <Card title="Search" icon="search" href="/core-concepts/main-operations/legacy-operations/search">
    Learn how sessions integrate with search
  </Card>

  <Card title="Sessions Guide" icon="code" href="/guides/sessions">
    Practical examples with Redis and filesystem
  </Card>

  <Card title="Setup Configuration" icon="settings" href="/setup-configuration/overview">
    Configure cache adapters
  </Card>
</Columns>
