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

# Resolve Conflicting Facts in Memory

> Feed cognee two documents that disagree and watch it flag the conflict, resist a five-star rating, and change its answer only when a correction is remembered

Two people file reports about the same project and the numbers do not match — one says the budget is 2 million euros, the other says 5 million. Your memory layer has to hold both, tell you they conflict, and be honest about what it takes to settle the disagreement.

## What You'll Build

Two one-line documents about Project Falcon go into an isolated dataset, and they contradict each other on both the lead and the budget. With contradiction detection on, the second ingestion records a `contradicts` edge carrying both fact texts, the reason, and a confidence score — nothing is overwritten or deleted — and the session layer tracks which graph elements each answer used, so a rating can be folded into retrieval weights. What comes out is an answer that reports the conflict, keeps reporting it even after a 5/5 rating, and flips to the corrected budget only when an explicit correction is remembered.

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

## Features in Play

* [Remember](/core-concepts/main-operations/remember) — ingests each document into the demo dataset, and it is the `cognify()` stage inside it that the contradiction check hangs off
* [Contradiction Detection](/python-api/cognify#contradiction-detection) — the opt-in check that compares the newly touched facts against the ones already stored and writes the `contradicts` edge
* [Sessions](/guides/sessions) — records each answered question as a QA entry, including which graph nodes and edges the answer used, so feedback has something to attach to
* [Feedback System](/guides/feedback-system) — `add_feedback()` puts a 5/5 rating and a comment on that QA entry, and a feedback-weights pipeline pushes it into the graph
* [Feedback-Weighted Ranking](/guides/truth-subspace-reranking#relationship-to-the-feedback-loop) — `DEFAULT_FEEDBACK_INFLUENCE` is what makes those weights count during retrieval scoring

## What to Expect

Every box is read back from the graph or the session store rather than echoed from the inputs — you see the actual edges and weights, not a narration of them. The boxes below are from a real run, trimmed. Because ingestion, the contradiction judgement, and every answer are live LLM calls, the exact wording, the confidence scores, and even the number of contradictions flagged vary from run to run.

**STEP 1** — the first report goes in, and the box lists the facts extracted from it: Anna as the lead, the 2 million euro budget, and their types. With only one document stored there is nothing to disagree with yet.

```text theme={null}
+--------- STEP 1  REMEMBER: 'Anna leads Falcon. Budget is 2M EUR.' ---------+
| facts now in the knowledge graph:                                          |
|                                                                            |
|    (anna) --leads--> (project falcon)                                      |
|    (anna) --is_a--> (person)                                               |
|    (project falcon) --has_budget--> (2 million euros)                      |
|    (project falcon) --is_a--> (project)                                    |
|    (2 million euros) --is_a--> (monetaryamount)                            |
|                                                                            |
| contradictions flagged: 0                                                  |
+----------------------------------------------------------------------------+
```

**STEP 2** — the conflicting report lands. Both budget facts now sit in the graph side by side, and the detection pass flags the disagreements — in this run both the budget and the lead — each as a `FACT A`/`FACT B` pair with the model's reason and confidence. The flagged line says it explicitly: nothing was deleted.

```text theme={null}
+-------- STEP 2  REMEMBER: 'Marko leads Falcon. Budget is 5M EUR.' ---------+
| facts now in the knowledge graph:                                          |
|                                                                            |
|    (anna) --leads--> (project falcon)                                      |
|    (anna) --is_a--> (person)                                               |
|    (project falcon) --has_budget--> (2 million euros)                      |
|    (project falcon) --is_a--> (project)                                    |
|    (project falcon) --has_budget--> (5 million euros)                      |
|    (2 million euros) --is_a--> (monetaryamount)                            |
|    (marko) --leads--> (project falcon)                                     |
|    (marko) --is_a--> (person)                                              |
|    (5 million euros) --is_a--> (monetaryamount)                            |
|                                                                            |
| contradictions flagged: 2 (nothing was deleted)                            |
...
|    FACT A : project falcon has budget 2 million euros                      |
|    FACT B : project falcon has budget 5 million euros                      |
|    reason : Both facts state different budgets for the same subject (proje |
| ct falcon); the project cannot simultaneously have two different monetary  |
| amounts as its budget.                                                     |
|    confidence: 1.0                                                         |
+----------------------------------------------------------------------------+
```

**STEP 3** — the question is asked for the first time. Retrieval sees both budget facts, so the answer reports the conflict instead of picking a side. The box also shows the session bookkeeping the next step depends on: which graph elements the answer used, all still at the neutral weight `0.5`, and the `qa_id` the exchange was recorded under.

```text theme={null}
+----------- STEP 3  ASK: 'What is the budget of Project Falcon?' -----------+
| answer:                                                                    |
|    The sources conflict: Project Falcon's budget is listed as either 2 mil |
| lion euros or 5 million euros.                                             |
|                                                                            |
| graph elements used by this answer: 27                                     |
|    node b7c10547-0775: weight 0.5                                          |
...
| recorded in session 'board_demo_session' as qa_id 389844ab...              |
+----------------------------------------------------------------------------+
```

**STEP 4** — the 5/5 rating is attached to that `qa_id` and the weights pipeline pushes it into the graph: every element the rated answer used moves from `0.5` to `0.55`, uniformly. That uniformity is the setup for the next box.

```text theme={null}
+--------------- STEP 4  FEEDBACK: rated 5/5 -> weights shift ---------------+
| feedback weight per element (before -> after):                             |
|                                                                            |
|    node b7c10547-0775: 0.5 -> 0.55                                         |
|    node 1ee4a665-25e7: 0.5 -> 0.55                                         |
...
| high-rated elements now rank higher in future searches                     |
| (DEFAULT_FEEDBACK_INFLUENCE=0.2 blends weight into retrieval scoring);     |
| both original facts and the contradicts edge remain stored.                |
+----------------------------------------------------------------------------+
```

**STEP 5** — the same question in a fresh session, so only the graph and its new weights are in play. Both budget facts were raised by the same amount, so their relative ranking is unchanged and the answer still reports the conflict: a rating steers attention, it does not decide what is true.

```text theme={null}
+---------- STEP 5  ASK AGAIN: a rating alone cannot pick a winner ----------+
| answer (fresh session, graph + weights only):                              |
|    The sources conflict: one chunk states Project Falcon's budget is 2 mil |
| lion euros, another states 5 million euros.                                |
...
+----------------------------------------------------------------------------+
```

**STEP 6** — the correction is remembered as a document, and retrieval finally has a statement that explicitly supersedes the 2 million figure. The answer in a third fresh session flips to 5 million euros, while the old fact stays stored and auditable through its `contradicts` edge.

```text theme={null}
+----------- STEP 6  REMEMBER THE CORRECTION -> the answer flips ------------+
| new document: 'The approved budget is 5M EUR; the 2M figure is             |
| outdated.' (this is what textual feedback becomes when persisted)          |
|                                                                            |
| answer (fresh session):                                                    |
|    The approved budget for Project Falcon is 5 million euros. (The earlier |
|  2 million euro figure is outdated.)                                       |
|                                                                            |
| contradictions now flagged in the graph: 2                                 |
| the 2M fact is still stored (auditable), but retrieval now has a           |
| correction that explicitly supersedes it -- new knowledge, not the         |
| rating, is what changed the answer.                                        |
+----------------------------------------------------------------------------+
```

## Before You Start

* Complete [Quickstart](/getting-started/quickstart) to understand basic operations
* Ensure you have [LLM Providers](/setup-configuration/llm-providers) configured — ingestion, the contradiction judgement, and every answer are live calls, so the wording and the confidence score vary by model
* Run it from a checkout of the cognee repo with dependencies installed and a configured `.env`
* Expect the script to set its own environment before importing cognee: it points cognee's data and system roots at `/tmp/conflict_demo` (deleted at startup), turns on `CONTRADICTION_DETECTION`, keeps `CACHING` on, and raises `DEFAULT_FEEDBACK_INFLUENCE` to `0.2` from its default of `0.0` — see [Contradiction detection](/python-api/cognify#contradiction-detection) for the contradiction tuning knobs
* The run starts with `prune_data()` and `prune_system(metadata=True)`, which is safe here only because those roots are the isolated demo directory rather than your real storage

## How It Works

### Stage 1: Isolate Storage and Enable Detection

```python theme={null}
os.environ.update(
    {
        "DATA_ROOT_DIRECTORY": str(DEMO_ROOT / "data"),
        "SYSTEM_ROOT_DIRECTORY": str(DEMO_ROOT / "system"),
        "CONTRADICTION_DETECTION": "true",
        "CACHING": "true",
        "DEFAULT_FEEDBACK_INFLUENCE": "0.2",
    }
)
```

These five settings run before `import cognee`, which is what makes them take effect. Two of them are the demo's subject: `CONTRADICTION_DETECTION` appends the conflict check to the end of the ingestion pipeline (it is off by default), and `DEFAULT_FEEDBACK_INFLUENCE` lifts feedback weights from ignored to a fifth of the retrieval score, so a rating can actually move ranking. The storage roots keep the whole run inside `/tmp/conflict_demo`.

### Stage 2: Remember the Second, Conflicting Document

```python theme={null}
    await cognee.remember(
        "Marko leads Project Falcon. The budget of Project Falcon is 5 million euros.",
        dataset_name=DATASET,
        self_improvement=False,
    )
    facts, conflicts = await read_facts()
```

The first `remember()` call stores Anna and the 2 million euro budget; this second one disagrees on both counts. Because entity ids are derived from entity names, "Project Falcon" lands on the same node, which puts the new budget fact one hop from the stored one — the neighbourhood the contradiction check compares. `self_improvement=False` skips the enrichment pass so nothing but ingestion is in play.

### Stage 3: Read the Conflict Back From the Graph

```python theme={null}
async def read_facts():
    """Read every semantic fact and every contradicts edge back from the graph."""
    graph = await get_graph_engine()
    nodes, edges = await graph.get_graph_data()
    names = {str(node_id): props.get("name", str(node_id)[:8]) for node_id, props in nodes}

    facts, conflicts = [], []
    for source, target, relationship, props in edges:
        if relationship == "contradicts":
            conflicts.append(props)
        elif relationship not in STRUCTURAL:
            facts.append(
                f"({names.get(str(source))}) --{relationship}--> ({names.get(str(target))})"
            )
    return facts, conflicts
```

Every step re-reads the graph through this helper rather than trusting what the previous call returned. It splits the edges into two piles: `contradicts` edges, whose properties carry `first_fact`, `second_fact`, `reason`, and `confidence` for the printout, and ordinary semantic facts. Structural edges such as `contains` and `is_part_of` are filtered out so the printed list is only human-meaningful statements — and both budget facts stay in that list, because flagging a conflict never removes either side.

### Stage 4: Ask, and Capture What the Answer Used

```python theme={null}
    results = await cognee.recall(
        QUESTION,
        query_type=SearchType.GRAPH_COMPLETION,
        datasets=[DATASET],
        session_id=SESSION,
    )
    answer = first_answer(results)

    user = await get_default_user()
    qa_entries = await get_session_manager().get_session(user_id=str(user.id), session_id=SESSION)
    assert isinstance(qa_entries, list) and qa_entries, "session recorded no QA entry"
    qa = qa_entries[-1]
    weights_before = await element_weights(qa.used_graph_element_ids)
```

Asking for the budget with a `session_id` writes a QA entry into the session store, and that entry's `used_graph_element_ids` is the link between an answer and the graph elements behind it. The demo reads the current feedback weight of each of those elements now, before any rating exists, so the next step has a baseline to compare against. Retrieval sees both budget facts, so the answer reports the conflict rather than choosing.

### Stage 5: Rate the Answer and Push the Rating Into the Graph

```python theme={null}
    await cognee.session.add_feedback(
        session_id=SESSION,
        qa_id=qa.qa_id,
        feedback_score=5,
        feedback_text="Correct — 5 million is the approved budget; Marko took over in June.",
    )
    await apply_feedback_weights_pipeline(
        user=user, session_ids=[SESSION], dataset=DATASET, alpha=0.1
    )
    weights_after = await element_weights(qa.used_graph_element_ids)
```

`add_feedback()` attaches the score and comment to that one QA entry — at this point the feedback lives in the session and nothing in the graph has moved. The weights pipeline is the second half of the loop: it walks the elements the rated answer used and updates their `feedback_weight`, with `alpha` controlling how hard one rating pulls. Printing the before and after side by side shows the shift. In an ordinary application this is what [`improve()`](/core-concepts/main-operations/improve#with-session-ids) does for you.

### Stage 6: Ask Again — a Rating Cannot Break a Tie

```python theme={null}
    # The 5/5 rating up-weighted every element the answer used, INCLUDING both
    # budget facts (the answer needed both to report the conflict). A symmetric
    # signal cannot break the tie, so the answer still reports the conflict.
    results = await cognee.recall(
        QUESTION,
        query_type=SearchType.GRAPH_COMPLETION,
        datasets=[DATASET],
        session_id="fresh_session_1",
    )
```

The same question runs in a fresh session, so nothing but the graph and its new weights can influence the answer — no conversation history carries the rating's text forward. The rating raised both budget facts equally, because the answer it praised had used both, and equal raises leave their relative order unchanged. The answer still reports the conflict, which is the point of the step: feedback steers which memories get attention, not which ones are true.

### Stage 7: Remember the Correction and Watch the Answer Flip

```python theme={null}
    await cognee.remember(
        "The approved budget of Project Falcon is 5 million euros. "
        "The earlier 2 million euro figure is outdated.",
        dataset_name=DATASET,
        self_improvement=False,
    )
    _, conflicts = await read_facts()
    results = await cognee.recall(
        QUESTION,
        query_type=SearchType.GRAPH_COMPLETION,
        datasets=[DATASET],
        session_id="fresh_session_2",
    )
```

The same claim the feedback comment made in prose is now remembered as a document — this is what textual feedback becomes when you persist it. Retrieval finally has a statement that explicitly supersedes the 2 million figure, and the answer in a third fresh session flips to 5 million. The 2 million fact is still stored and still auditable through its `contradicts` edge; what changed the answer was new knowledge, not the rating.

## Run It

```bash theme={null}
uv run python examples/demos/feedback/contradiction_feedback_demo.py
```

<Columns cols={2}>
  <Card title="Cognify" icon="brain" href="/python-api/cognify#contradiction-detection">
    What the contradiction check compares, what it skips, and how to tune it.
  </Card>

  <Card title="Feedback System" icon="brain-circuit" href="/guides/feedback-system">
    Rating a session answer and pushing that rating into graph weights.
  </Card>

  <Card title="Truth Subspace Reranking" icon="compass" href="/guides/truth-subspace-reranking">
    How feedback weights and truth weighting reach retrieval scoring.
  </Card>

  <Card title="Improve" icon="sparkles" href="/core-concepts/main-operations/improve">
    The operation that applies feedback weights for you outside a demo.
  </Card>
</Columns>
