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

# Teach an Agent From Its Own Tool Traces

> Turn an agent's tool-call successes and failures into agent-profile session guidance, distill it into the knowledge graph, and read it back before the next run

Your coding agent has already hit the errors that matter — a test run that failed on a missing dependency, a lint pass that flagged an unused import — and the next run will hit them again unless something remembers what happened. This demo turns that raw tool-call history into guidance the agent can be handed before it starts.

## What You'll Build

Five tool-call traces from one agent working a task — a failing `pytest` run, a `uv sync` that fixed it, a passing test run, a file read, and a lint failure — are stored one by one into a session as `TraceEntry` records. Cognee extracts agent-profile lessons from them as they accumulate, then distillation rewrites the accepted lessons into markdown documents and cognifies them into the dataset, so they outlive the session. The payoff is the last act: `recall()` returns those lessons as a read-only context block for the agent-profile question "what should I know before running tests in this repo?", while the same query under the QA profile returns nothing and the raw traces are still retrievable as evidence.

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

## Features in Play

* [Agent Session Traces](/guides/agent-session-traces) — each tool call becomes a `TraceEntry` in the session, carrying its parameters, return value, or error message
* [Sessions and Caching](/core-concepts/sessions-and-caching) — the session cache holds both the raw traces and the agent-profile guidance extracted from them
* [Session Distillation](/guides/session-distillation) — rewrites the accepted guidance into markdown documents and cognifies them into the dataset
* [Recall](/core-concepts/main-operations/recall) — reads the guidance back with `scope` and `context_profile`, and the raw traces alongside it as evidence

## What to Expect

The excerpts below are from a real run, trimmed. The narration streams to stderr in three acts, every session-memory entry is labeled with its section and its source, and the run finishes by writing `agentic_session_context_demo_output.json` and printing its path; exact lesson wording varies by model.

**Act 1 — a failing trace becomes a lesson instantly.** Storing the failed `pytest` run writes a deterministic `failure_lessons` entry, no LLM involved: `source=live trace`.

```text theme={null}
Session memory content after trace 1
Trace
  tool: run_tests
  status: error
  input:
    command: pytest -q
  error:
    ModuleNotFoundError: No module named 'dotenv'

Session memory
  entries (1):
    - [failure_lessons] run_tests failed: ModuleNotFoundError: No module named 'dotenv' (confidence=0.85, source=live trace)
```

**Act 1 — the batch pass builds typed guidance around it.** After trace 2, the first extraction pass reads the failure-plus-fix pair and adds `success_patterns`, `workflow_state`, and a generalized failure lesson, all marked `source=batch LLM`.

```text theme={null}
Session memory content after trace 2 (first batch context extraction)
Trace
  tool: run_command
  status: success
  input:
    command: uv sync
  output:
    Installed 42 packages in 1.2s

Session memory
  entries (4):
    - [failure_lessons] run_tests failed: ModuleNotFoundError: No module named 'dotenv' (confidence=0.85, source=live trace)
    - [success_patterns] When tests fail with a ModuleNotFoundError, install the missing Python packages via run_command (e.g., pip) and then re-run the tests. (confidence=0.9, source=batch LLM)
    - [workflow_state] After a successful package installation step, the immediate next action should be to re-run the test suite to verify the dependency issue is resolved. (confidence=0.9, source=batch LLM)
    - [failure_lessons] Avoid running tests before installing project dependencies; proactively run dependency installation (for example pip install -r requirements.txt) to prevent ModuleNotFoundError failures. (confidence=0.85, source=batch LLM)
```

**Act 1 — later traces widen the picture.** The second batch pass, after the file read, adds `environment_facts` and `tool_rules` on top; memory is now eight entries drawn from five kinds of section.

```text theme={null}
Session memory content after trace 4 (second batch context extraction)
...
Session memory
  entries (8):
...
    - [environment_facts] The project's config file contains a comment indicating it is managed with the 'uv' tool. (confidence=0.9, source=batch LLM)
    - [environment_facts] The project's declared name in the config is 'demo'. (confidence=0.9, source=batch LLM)
    - [tool_rules] When a project configuration names a specific package manager, prefer invoking that manager (via run_command) to install dependencies instead of an ad-hoc tool. (confidence=0.8, source=batch LLM)
    - [success_patterns] Treat a run_command output that reports 'Installed N packages' as a successful dependency installation signal and proceed to re-run the test suite. (confidence=0.85, source=batch LLM)
```

**Act 2 — distillation makes the lessons permanent.** The pending tail is flushed, then the accepted guidance is rewritten into markdown documents and cognified into the dataset, where it outlives the session.

```text theme={null}
final batch context extraction: pending 1 -> 0
context entries touched: 3
...
Graph documents written
status: completed
document 1:
  # Session learning — 2026-09-02 (session agentic_demo_session)
  
  Install a project's Python dependencies before running its test suite (for example, pip install -r requirements.txt); if tests fail with a ModuleNotFoundError, install the missing package(s) (for example, pip install python-dotenv) and immediately re-run the test suite to verify the issue is resolved. (Learned after run_tests failed with ModuleNotFoundError: No module named 'dotenv' during a test run.)
...
```

**Act 3 — recall hands the guidance back, read-only.** The agent-profile query renders the distilled lessons as an Active session guidance block; the QA profile returns nothing, the raw traces stay retrievable as evidence, and recall performs no writes.

```text theme={null}
Agent-profile session memory recall
## Active session guidance
Items are listed oldest to newest within each section. When guidance conflicts, prefer the later item.
### Tool rules
- [09:33:44] When a project configuration names a specific package manager, prefer invoking that manager (via run_command) to install dependencies instead of an ad-hoc tool.
...
### Failure lessons
- [09:33:30] Avoid running tests before installing project dependencies; proactively run dependency installation (for example pip install -r requirements.txt) to prevent ModuleNotFoundError failures.
...
QA-profile session memory results: 0
raw trace recall results: 1
writes during recall: False
```

## Before You Start

* Complete [Quickstart](/getting-started/quickstart) to understand basic operations
* Ensure you have [LLM Providers](/setup-configuration/llm-providers) configured — lesson extraction and distillation are LLM-backed, so the full run needs one; `--offline` skips both
* The script sets its own environment before importing cognee: `CACHING=true`, `CACHE_BACKEND=fs`, `AUTO_FEEDBACK=true`, `ENABLE_BACKEND_ACCESS_CONTROL=false`, and `LOG_LEVEL=ERROR` unless you already set it. The [filesystem cache adapter](/core-concepts/sessions-and-caching#cache-adapters) is what stores the session
* Run it from a checkout of the cognee repo; it writes `agentic_session_context_demo_output.json` into the working directory with every snapshot the run took
* The run starts by pruning data and system metadata and deleting any previous `agentic_demo_session`, so point it at a scratch instance rather than memory you want to keep

## How It Works

### Stage 1: Script the Agent's Tool Traces

```python theme={null}
TRACES = [
    {
        "origin_function": "run_tests",
        "status": "error",
        "method_params": {"command": "pytest -q"},
        "error_message": "ModuleNotFoundError: No module named 'dotenv'",
    },
    {
        "origin_function": "run_command",
        "status": "success",
        "method_params": {"command": "uv sync"},
        "method_return_value": "Installed 42 packages in 1.2s",
    },
    {
        "origin_function": "run_tests",
        "status": "success",
        "method_params": {"command": "uv run pytest -q"},
        "method_return_value": "188 passed in 4.1s",
    },
    {
        "origin_function": "read_file",
        "status": "success",
        "method_params": {"path": "pyproject.toml"},
        "method_return_value": "[project] name = 'demo'  # managed with uv",
    },
    {
        "origin_function": "run_lint",
        "status": "error",
        "method_params": {"command": "uv run ruff check ."},
        "error_message": "F401 'os' imported but unused",
    },
]
```

These five dictionaries stand in for what a real agent's tool layer would emit: the tool name, whether the call succeeded, its input, and either the return value or the error. The story they tell — bare `pytest` fails, `uv sync` fixes it, `uv run pytest` passes — is the raw material a lesson can be drawn from, and nothing in the run tells the extractor what that lesson is.

### Stage 2: Store Each Trace and Extract as You Go

```python theme={null}
    for index, trace in enumerate(TRACES, start=1):
        before = await snapshot(user)
        await cognee.remember(
            TraceEntry(**trace),
            dataset_name=DATASET_NAME,
            session_id=SESSION_ID,
            self_improvement=False,
            user=user,
        )
        if drive_periodic_extraction:
            await extract_pending_agent_context(
                session_manager=get_session_manager(),
                user_id=str(user.id),
                session_id=SESSION_ID,
                min_new_traces=DEMO_TRACE_EXTRACTION_INTERVAL,
                overlap=DEMO_TRACE_EXTRACTION_OVERLAP,
            )
```

`remember()` with a `session_id` writes each `TraceEntry` to the session cache rather than the graph. The trace-write path already runs the periodic extraction pass for you on its own interval; the demo calls it directly with `DEMO_TRACE_EXTRACTION_INTERVAL = 2` and `DEMO_TRACE_EXTRACTION_OVERLAP = 1` so a five-trace run actually shows the pass firing — after traces 2 and 4 — instead of only at the end. The snapshot taken before and after each trace is what lets the run print whether that trace advanced the processed-trace watermark.

### Stage 3: Flush the Tail and Distill Into the Graph

```python theme={null}
        before_flush = await snapshot(user)
        touched_ids = await extract_pending_agent_context(
            session_manager=get_session_manager(),
            user_id=str(user.id),
            session_id=SESSION_ID,
            min_new_traces=1,
        )
        distillation_input = await snapshot(user)
        result = await distill_session(SESSION_ID, dataset=DATASET_NAME, user=user)
```

Traces 2 and 4 triggered extraction, which leaves the fifth trace — the lint failure — pending. Dropping `min_new_traces` to 1 forces that tail through, so distillation sees the complete set of lessons. `distill_session()` then rewrites the accepted guidance into markdown documents and cognifies them into the demo dataset, which is what makes the lessons outlast this session. The script reaches it through its internal module path; in your own code call it as [`cognee.session.distill_session()`](/guides/session-distillation), or let [`improve(session_ids=[...])`](/core-concepts/main-operations/improve) run the same distillation as part of a wider pass.

### Stage 4: Recall Guidance by Profile

```python theme={null}
    agent_ctx = await cognee.recall(
        "what should I know before running tests in this repo?",
        scope=["session_context"],
        context_profile="agent",
        session_id=SESSION_ID,
        only_context=True,
        user=user,
    )
    qa_ctx = await cognee.recall(
        "what should I know before running tests in this repo?",
        scope=["session_context"],
        context_profile="qa",
        session_id=SESSION_ID,
        only_context=True,
        user=user,
    )
    raw_traces = await cognee.recall(
        "dotenv",
        scope=["trace"],
        session_id=SESSION_ID,
        only_context=True,
        user=user,
    )
```

Three recalls against the same session show what the profile does. `scope=["session_context"]` with `context_profile="agent"` returns the distilled guidance block, ready to prepend to the next agent run; the identical query under `context_profile="qa"` comes back empty, because this demo wrote no QA-profile entries. `scope=["trace"]` reaches past the lessons to the raw evidence they were drawn from, so a lesson about `dotenv` can be traced back to the call that failed. `only_context=True` keeps all three as context reads rather than completions.

### Stage 5: Prove the Reads Changed Nothing

```python theme={null}
async def served_state(user) -> dict:
    """Map of agent-lesson id -> last_served_at, to prove recall does not stamp anything."""
    rows = await get_session_manager().get_session_context_entries(
        user_id=str(user.id), session_id=SESSION_ID
    )
    return {
        row.get("id"): row.get("last_served_at")
        for row in rows
        if row.get("context_profile", "qa") == "agent"
    }
```

The recall act captures this map of agent-lesson id to `last_served_at` before and after the three queries and compares them. Serving guidance to an agent is a read: nothing is stamped, no entry is aged, and the run prints the comparison so you do not have to take that on faith.

## Run It

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

## Offline Mode

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

`--offline` runs the same trace capture and recall without any LLM calls: the periodic extraction passes are skipped, Act 2 is skipped entirely, and the only session-memory entries that appear are the deterministic ones written when a failing trace is stored. Use it to see the capture-and-recall shape of the demo without a configured provider — and, in the full run, as the baseline that shows which entries the LLM added.

<Columns cols={2}>
  <Card title="Agent Session Traces" icon="footprints" href="/guides/agent-session-traces">
    Recording tool calls as traces and recalling them later.
  </Card>

  <Card title="Session Distillation" icon="graduation-cap" href="/guides/session-distillation">
    How gated session guidance becomes permanent lessons in the graph.
  </Card>

  <Card title="Sessions and Caching" icon="message-square" href="/core-concepts/sessions-and-caching">
    The session cache behind traces, guidance, and the `fs` backend this demo uses.
  </Card>

  <Card title="Watch a Session Become Permanent Memory" icon="repeat" href="/examples/memory-loop-walkthrough">
    The same distillation loop, driven by a conversation instead of tool traces.
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.