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

# Session Distillation

> Turn guidance stated in a session into permanent lessons in the knowledge graph so future recalls respect it

Guidance a user states during a session — preferences, corrections, durable instructions — is short-term by default: it lives in the session cache and expires with it. **Session distillation** bridges that guidance into long-term memory by turning a session's accepted, gated guidance into permanent lesson documents in the knowledge graph, tagged with the `session_learnings` node set. A brand-new session with no memory of the original conversation will then recall and respect what was learned.

**Before you start:**

* Complete [Quickstart](/getting-started/quickstart) or have Cognee installed and configured
* Ensure you have [LLM Providers](/setup-configuration/llm-providers) configured
* Be familiar with [Sessions](/guides/sessions) and the `remember()` / `recall()` workflow
* [Caching must be enabled](/core-concepts/sessions-and-caching#cache-adapters) so session Q\&A history is retained

## How to Distill a Session

Sessions can be bridged into the knowledge graph with `improve()`, which can persist cached Q\&A, persist agent traces, and distill accepted session guidance into `session_learnings`:

```python theme={null}
await cognee.improve(
    dataset="project_memory",
    session_ids=["conversation_1"],
)
```

If you only want to distill one finished session's gated guidance, call `cognee.session.distill_session()` directly:

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

The direct result includes `status` and `documents`. A completed run writes accepted lesson documents back into the dataset; statuses such as `no_gated_entries` or `no_accepted_lessons` mean there was nothing durable enough to persist.

## Example: Learning a Snack Preference

A minimal end-to-end demo of session distillation: state a preference in one session, distill that session into the knowledge graph, then confirm that a fresh session's recall now reflects the learned preference. Setting `AUTO_FEEDBACK=true` lets the session capture the user's stated preference as learned guidance during distillation.

### How It Works

**1. Remember two neutral snack facts.** The graph starts with one sweet and one savory snack — and no preference:

```python theme={null}
await cognee.remember(
    [
        "Oreos are a sweet snack: chocolate cookies with a sugary cream filling.",
        "Doritos are a savory snack: salty, cheesy, seasoned tortilla chips.",
    ],
    dataset_name="snack_preference_demo",
)
```

**2. Ask before distillation.** With no known preference, `recall()` (`RAG_COMPLETION`) makes an arbitrary pick:

```python theme={null}
before = await cognee.recall(
    query_text="I want a snack. Should I get Oreos or Doritos? Recommend one.",
    query_type=SearchType.RAG_COMPLETION,
    datasets=["snack_preference_demo"],
    session_id="snack_session",
)
```

**3. State the opposite preference in the same session** so the answer has to flip. Here the model picked Oreos first, so the user states a savory preference — the [full example](#full-example) below detects the first pick and states the opposite preference for either case:

```python theme={null}
await cognee.recall(
    query_text="Just so you know, I always prefer savory snacks over sweet ones.",
    query_type=SearchType.RAG_COMPLETION,
    datasets=["snack_preference_demo"],
    session_id="snack_session",
)
```

**4. Distill the session into long-term memory.** The curated preference lesson becomes a retrievable chunk in the graph:

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

**5. Ask again in a fresh session.** A brand-new session with no memory of the conversation still recalls the preference, so the recommendation now respects it:

```python theme={null}
after = await cognee.recall(
    query_text="I want a snack. Should I get Oreos or Doritos? Recommend one.",
    query_type=SearchType.RAG_COMPLETION,
    datasets=["snack_preference_demo"],
    session_id="snack_verification_session",
)
```

### Full Example

The complete runnable script is on GitHub: [`examples/guides/session_distillation.py`](https://github.com/topoteretes/cognee/blob/dev/examples/guides/session_distillation.py). It picks the *opposite* of the model's first pick as the stated preference and then asserts the recommendation flips — a self-checking way to prove distillation changed the outcome.

<Accordion title="Full example script">
  ```python theme={null}
  import asyncio
  import os
  import sys

  # Let the session capture the user's stated preference as learned guidance.
  os.environ["AUTO_FEEDBACK"] = "true"
  os.environ.setdefault("LOG_LEVEL", "ERROR")

  import cognee
  from cognee import SearchType
  from cognee.infrastructure.session.get_session_manager import get_session_manager
  from cognee.modules.users.methods import get_default_user

  SESSION_ID = "snack_session"

  # flavor -> (snack that has it, statement of the preference)
  SNACK_FOR_FLAVOR = {"savory": "Doritos", "sweet": "Oreos"}


  def progress(message: str):
      print(f"[snack-demo] {message}", file=sys.stderr, flush=True)


  def answer_text(result) -> str:
      """recall() returns a list of response entries; join their text for parsing/printing."""
      if isinstance(result, str):
          return result
      parts = []
      for entry in result or []:
          parts.append(getattr(entry, "text", None) or str(entry))
      return " ".join(parts)


  def recommended_snack(text: str) -> str:
      """Whichever snack the model recommends first in its answer."""
      lowered = text.lower()
      oreo_at = lowered.find("oreo")
      dorito_at = lowered.find("dorito")
      if oreo_at == -1 and dorito_at == -1:
          return "Oreos"  # fallback; shouldn't happen with the snack facts in context
      if dorito_at == -1:
          return "Oreos"
      if oreo_at == -1:
          return "Doritos"
      return "Oreos" if oreo_at < dorito_at else "Doritos"


  async def ask(message: str, user, session_id: str):
      # RAG_COMPLETION answers from retrieved chunks. Before distillation only the two snack
      # facts exist, so the model has no basis to prefer one. After distillation the curated
      # preference lesson is a retrievable chunk, so it steers the pick.
      return await cognee.recall(
          query_text=message,
          query_type=SearchType.RAG_COMPLETION,
          datasets=["snack_preference_demo"],
          session_id=session_id,
          user=user,
      )


  async def main():
      progress("Clearing previous demo state.")
      await cognee.prune.prune_data()
      await cognee.prune.prune_system(metadata=True)

      progress("Ingesting the two snack facts.")
      await cognee.remember(
          [
              "Oreos are a sweet snack: chocolate cookies with a sugary cream filling.",
              "Doritos are a savory snack: salty, cheesy, seasoned tortilla chips.",
          ],
          dataset_name="snack_preference_demo",
      )

      user = await get_default_user()
      await get_session_manager().delete_session(user_id=str(user.id), session_id=SESSION_ID)

      question = "I want a snack. Should I get Oreos or Doritos? Recommend one."

      # 1) Before distillation: no preference known -> arbitrary pick.
      progress("Asking BEFORE distillation (no preference known).")
      before = answer_text(await ask(question, user, SESSION_ID))
      first_pick = recommended_snack(before)
      print("\n----- BEFORE distillation -----\n", file=sys.stderr)
      print(f"picked: {first_pick}\n{before}", file=sys.stderr)

      # 2) State the OPPOSITE preference so the answer has to flip to the other snack.
      if first_pick == "Doritos":
          preferred_flavor, opposite_flavor = "sweet", "savory"
      else:
          preferred_flavor, opposite_flavor = "savory", "sweet"
      expected_after = SNACK_FOR_FLAVOR[preferred_flavor]
      progress(
          f"Model picked {first_pick}; telling it the user prefers {preferred_flavor} "
          f"(expect it to flip to {expected_after})."
      )
      await ask(
          f"Just so you know, I always prefer {preferred_flavor} snacks over {opposite_flavor} ones.",
          user,
          SESSION_ID,
      )

      # 3) Distill the session into long-term memory.
      progress("Distilling the session into the graph.")
      result = await cognee.session.distill_session(
          SESSION_ID, dataset="snack_preference_demo", user=user
      )
      progress(f"Distillation status={result.status} documents={len(result.documents)}")
      for doc in result.documents:
          print("\n----- distilled lesson -----\n", file=sys.stderr)
          print(doc, file=sys.stderr)

      # 4) After distillation, in a FRESH session, ask the same question again.
      progress("Asking AFTER distillation in a fresh session.")
      after = answer_text(await ask(question, user, "snack_verification_session"))
      second_pick = recommended_snack(after)
      print(f"\n----- AFTER distillation (expected {expected_after}) -----\n", file=sys.stderr)
      print(f"picked: {second_pick}\n{after}", file=sys.stderr)

      flipped = second_pick == expected_after and second_pick != first_pick
      progress(
          f"RESULT: {first_pick} -> {second_pick} "
          f"({'flipped as expected ✅' if flipped else 'did NOT flip ❌'})"
      )


  if __name__ == "__main__":
      asyncio.run(main())
  ```
</Accordion>

## Going Further

Distilled `session_learnings` are the foundation for other self-improvement features: [`improve()`](/core-concepts/main-operations/improve) bridges whole sessions (Q\&A, traces, and guidance) in one pass, and [truth-subspace reranking](/guides/truth-subspace-reranking) builds retrieval anchors from distilled lessons.

<Columns cols={2}>
  <Card title="Sessions Guide" icon="message-circle" href="/guides/sessions">
    Learn how sessions and caching work in Cognee
  </Card>

  <Card title="Improve" icon="sparkles" href="/core-concepts/main-operations/improve">
    Bridge sessions into the permanent graph with the improve pass
  </Card>
</Columns>
