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

# HR Resume Screening

> Turn a pile of CVs into a queryable candidate graph, then ask who has a given skill — rebuilding the graph only when the corpus changes

A recruiter has five CVs on disk and one question per role: who here actually has the skill we are hiring for? The CVs never change while the questions keep coming, so the expensive part — reading them into memory — should happen once and the asking should be cheap to repeat.

## What You'll Build

Five plain-text CVs from a sibling data folder — three data scientists, a graphic designer, and a sales manager — are ingested into one candidate knowledge graph, and a single graph-grounded query asks which of them has experience with design tools. The run is split into four phases (prune data, prune system, remember, retrieve), each one gated behind its own boolean, so the first run builds the graph and every later run can skip straight to the query against the graph that is already there.

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

## Features in Play

* [Remember](/core-concepts/main-operations/remember) — turns the five CV texts into a candidate graph in one call, with `self_improvement=False` to keep ingestion to the plain add-and-cognify path
* [Recall](/core-concepts/main-operations/recall) — asks the screening question against the finished graph with a pinned `query_type`
* [Inspecting Graph Completion Context](/guides/graph-completion) — `SearchType.GRAPH_COMPLETION` is what grounds the answer in graph triplets extracted from the CVs rather than raw resume text
* [Delete](/core-concepts/main-operations/legacy-operations/delete) — `prune_data()` and `prune_system(metadata=True)` wipe the previous run's storage and metadata when the rebuild phases are switched on

## What to Expect

The excerpts below come from one real run with both phases switched on, trimmed of most log lines. Ingestion and the closing query are live LLM calls, so the answer's wording varies from run to run; the shape of the output does not.

**The two resets confirm the run starts from nothing.** `Data pruned.` follows the file-storage wipe, and `System pruned.` follows the graph, vector, relational, and cache deletions — the relational database is dropped because the script passes `metadata=True`, which is also why the next phase begins by rebuilding the schema from scratch.

```text theme={null}
Data pruned.
...
System pruned.
```

**One `Remembering text:` line per CV, then a single confirmation.** Each line prints the first 35 characters of the resume, which is why the candidate's name wraps onto a second line, and the `Relevant` / `Not Relevant` labels are the first line of the sample files themselves, not something cognee added. Between the fifth line and `Knowledge graph created.` the remember call runs the add and cognify pipelines over all five resumes — about a minute and a half in this run, most of it in the `extract_graph_and_summarize` tasks.

```text theme={null}
Remembering text: CV 1: Relevant
Name: Dr. Emily Cart...
Remembering text: CV 2: Relevant
Name: Michael Rodrig...
Remembering text: CV 3: Relevant
Name: Sarah Nguyen
C...
Remembering text: CV 4: Not Relevant
Name: David Thom...
Remembering text: CV 5: Not Relevant
Name: Jessica Mi...
...
Knowledge graph created.
```

**The router log shows why `query_type` is pinned.** Left to itself, recall would have routed this question to `HYBRID_COMPLETION`; the script's explicit `SearchType.GRAPH_COMPLETION` overrides that, and the retrieval lines that follow show the graph path at work — a subgraph of 142 nodes and 358 edges is projected and narrowed to the 13 nodes and 15 connections that become the answer's context. The final line is the script's own print: a one-element list naming the graphic designer and the tools he is connected to, even though the question never named any of them.

```text theme={null}
2026-09-10T10:49:20.947808 [info     ] query_router: no patterns matched, default=HYBRID_COMPLETION query='Who has experience in design tools?' [query_router]
2026-09-10T10:49:20.947891 [info     ] Router override recorded: routed=HYBRID_COMPLETION, user_chose=GRAPH_COMPLETION (total=1) [query_router]
...
2026-09-10T10:49:21.612145 [info     ] Graph projection completed: 142 nodes, 358 edges in 0.00s [CogneeGraph]
2026-09-10T10:49:21.613129 [info     ] Completed resolving edges to text [cognee.shared.logging_utils] extra={'node_count': 13, 'connection_count': 15}
2026-09-10T10:49:31.367201 [info     ] recall: 1 results across sources=['graph'] (session=-) [recall]
['David Thompson — experienced with design tools including Adobe Photoshop, Illustrator, and InDesign.']
```

## Before You Start

* Complete [Quickstart](/getting-started/quickstart) to understand basic operations
* Ensure you have [LLM Providers](/setup-configuration/llm-providers) configured — both the ingestion phase and the closing query make live LLM calls
* Run it from a checkout of the cognee repo: the script reads `cv_1.txt` through `cv_5.txt` from the sibling `dynamic_steps_resume_analysis_hr_example_data/` folder
* Point it at a scratch instance on the first run: with the rebuild phases on, it calls `prune_data()` and `prune_system(metadata=True)`, which wipe stored data and drop the relational database rather than deleting one dataset — see [Delete](/core-concepts/main-operations/legacy-operations/delete)

## How It Works

### Stage 1: Reset the Previous Run's State

```python theme={null}
    if enable_steps.get("prune_data"):
        await cognee.prune.prune_data()
        print("Data pruned.")

    if enable_steps.get("prune_system"):
        await cognee.prune.prune_system(metadata=True)
        print("System pruned.")
```

Every phase in `main()` is wrapped in an `enable_steps.get(...)` check, and the two resets are the first of them. They exist so a rebuild starts from an empty store instead of layering a second copy of the same five candidates onto the graph — which is also why they are the phases you turn off once the graph is built.

### Stage 2: Remember the CV Corpus

```python theme={null}
    if enable_steps.get("remember"):
        text_list = [job_1, job_2, job_3, job_4, job_5]
        for text in text_list:
            print(f"Remembering text: {text[:35]}...")
        await cognee.remember(text_list, self_improvement=False)
        print("Knowledge graph created.")
```

One `remember()` call takes the list of five CV strings and does the whole ingestion: the resumes are chunked, entities like people, skills, tools, and employers are extracted, and the result is stored as a graph plus embeddings. `self_improvement=False` stops the follow-up [improve](/core-concepts/main-operations/improve) pass, so this is the plain permanent-ingestion path and nothing enriches the graph behind the scenes.

### Stage 3: Ask the Screening Question

```python theme={null}
    if enable_steps.get("retriever"):
        results = await cognee.recall(
            query_type=SearchType.GRAPH_COMPLETION, query_text="Who has experience in design tools?"
        )
        print([result.text for result in results])
```

The screening question goes to `recall()` with `query_type` pinned to `SearchType.GRAPH_COMPLETION`, so the answer is generated from graph triplets rather than from whichever resume chunk happens to look similar to the question. That matters for a question like this one: "design tools" appears in none of the CVs verbatim, and the answer has to come from the tools a candidate is connected to.

### Stage 4: Choose Which Phases Run

```python theme={null}
    rebuild_kg = True
    retrieve = True
    steps_to_enable = {
        "prune_data": rebuild_kg,
        "prune_system": rebuild_kg,
        "remember": rebuild_kg,
        "retriever": retrieve,
    }
```

Two booleans at the entry point drive the four phases: `rebuild_kg` groups both prunes and the ingestion, `retrieve` covers the query. Both start out `True`, which is what a first run needs — and editing them is how the same script becomes either a rebuild or a query-only run.

## Run It

```bash theme={null}
uv run python examples/demos/custom_pipelines/dynamic_steps_resume_analysis_hr_example.py
```

A full run takes about two minutes, nearly all of it in the remember phase. Its output is walked through in [What to Expect](#what-to-expect) above.

## Re-Running Only the Query

Once the graph exists, set `rebuild_kg = False` and leave `retrieve = True`. Both prunes and the `remember()` call are skipped, the script goes straight to the recall against the stored graph, and the question is answered in about ten seconds instead of waiting on the rebuild. Edit `query_text` and re-run to screen the same corpus for a different skill; flip `rebuild_kg` back to `True` only when the CVs in the data folder change.

<Columns cols={2}>
  <Card title="Remember" icon="brain" href="/core-concepts/main-operations/remember">
    The ingestion call behind the candidate graph, and what `self_improvement` changes.
  </Card>

  <Card title="Recall" icon="search" href="/core-concepts/main-operations/recall">
    Query routing, pinning a `query_type`, and the other retrieval parameters.
  </Card>

  <Card title="Inspecting Graph Completion Context" icon="route" href="/guides/graph-completion">
    What `GRAPH_COMPLETION` retrieves from the graph before an answer is generated.
  </Card>

  <Card title="Human Resources" icon="link" href="/examples/human-resources">
    The wider HR use case: aligning CVs with job posts through entity resolution.
  </Card>
</Columns>
