Detect contradictions between retrieved documents in RAG pipelines
Project description
contrachecker
Catch contradictions between documents before your LLM blindly trusts them.
contrachecker is a lightweight Python library that detects contradictions between retrieved document chunks in RAG (Retrieval-Augmented Generation) pipelines. It sits between your retriever and your LLM, ensuring the model knows when its sources disagree.
from contrachecker import check_contradictions
from contrachecker.models import Chunk
report = check_contradictions([
Chunk(id="2019", text="Metformin is the first-line treatment for type 2 diabetes."),
Chunk(id="2023", text="GLP-1 agonists should be preferred over metformin for cardiovascular risk patients."),
])
print(report.summary())
# Analyzed 2 chunks, extracted 4 claims, found 1 contradiction.
print(report.as_prompt_context())
# [CONTRADICTION REPORT]
# CONTRADICTION 1 (direct, confidence=70%): 'metformin is first-line treatment' CONFLICTS WITH ...
Why?
RAG pipelines retrieve the top-K most relevant chunks and feed them to an LLM. But relevant doesn't mean consistent. When your retriever pulls in a 2019 guideline and a 2023 update that contradicts it, the LLM has no way to know -- it treats both as equally true.
contrachecker solves this by detecting three types of contradictions:
| Type | Example | How it's found |
|---|---|---|
| Direct | Doc A says "Drug X is safe", Doc B says "Drug X is unsafe" | Same subject + relation, different conclusion |
| Indirect | "Coffee contains caffeine" + "Caffeine disrupts sleep" contradicts "Coffee promotes sleep" | BFS chain traversal finds transitive conflicts |
| Bridge | Adding "Coffee contains caffeine" reveals a hidden conflict between existing "Coffee is healthy" and "Caffeine is harmful" | New claim connects previously unrelated contradictions |
Install
pip install contrachecker # Core (pattern-based extraction)
pip install contrachecker[llm] # + OpenAI-based extraction
pip install contrachecker[langchain] # + LangChain integration
pip install contrachecker[all] # Everything
Quick Start
Standalone
from contrachecker import check_contradictions
from contrachecker.models import Chunk
chunks = [
Chunk(id="doc1", text="Remote work increases productivity by 13%."),
Chunk(id="doc2", text="Remote work decreases team productivity."),
]
report = check_contradictions(chunks)
if report.has_contradictions:
for c in report.contradictions:
print(f"[{c.type}] {c.explanation}")
With LangChain
from langchain_core.documents import Document
from contrachecker.integrations.langchain import ContradictionCheckerTransformer
docs = [...] # your retrieved documents
checker = ContradictionCheckerTransformer()
enriched_docs = checker.transform_documents(docs)
# Pass enriched_docs to your LLM -- contradiction report is appended automatically
With Pre-Extracted Claims
If you already have structured claims (from your own NLP pipeline, a knowledge graph, etc.):
from contrachecker.models import Claim
from contrachecker.detector import ContradictionDetector
claims = [
Claim(subject="metformin", relation="status", object="first_line", source_chunk_id="doc1"),
Claim(subject="metformin", relation="status", object="second_line", source_chunk_id="doc2"),
]
detector = ContradictionDetector(max_chain_depth=4)
contradictions = detector.detect(claims)
LLM-Powered Extraction
For higher accuracy claim extraction (requires OpenAI API key):
from contrachecker import check_contradictions
from contrachecker.extractors import LLMExtractor
extractor = LLMExtractor(api_key="sk-...")
report = check_contradictions(chunks, extractor=extractor)
How It Works
Retrieved Chunks -----------------------------------------------+
|
+-----------------------------------------------------------+ |
| contrachecker | |
| | |
| 1. EXTRACT CLAIMS | |
| Chunk text -> (subject, relation, object) triples | |
| Extractors: PatternExtractor | LLMExtractor | Custom | |
| | |
| 2. BUILD CLAIM GRAPH | |
| Index claims by subject, object, relation | |
| | |
| 3. DETECT CONTRADICTIONS | |
| +-- Direct: same topic, different conclusion | |
| +-- Indirect: BFS finds transitive conflicts | |
| +-- Bridge: new claim reveals hidden conflicts | |
| | |
| 4. GENERATE REPORT | |
| ContradictionReport with confidence scores | |
| .as_prompt_context() for LLM injection | |
+-----------------------------------------------------------+ |
|
Enriched Chunks + Contradiction Report ----------------> LLM |
Claim Extractors
| Extractor | Accuracy | Speed | Cost | Dependencies |
|---|---|---|---|---|
PatternExtractor |
Medium | Fast | Free | None |
LLMExtractor |
High | Slow | ~$0.001/chunk | openai |
| Custom | Your choice | Your choice | Your choice | Implement ClaimExtractor protocol |
Custom Extractor
from contrachecker.models import Chunk, Claim
from contrachecker.extractors.base import ClaimExtractor
class MyExtractor:
def extract(self, chunk: Chunk) -> list[Claim]:
# Your extraction logic here
...
def extract_many(self, chunks: list[Chunk]) -> list[Claim]:
return [claim for chunk in chunks for claim in self.extract(chunk)]
Use Cases
- Medical/Pharma: Detect when clinical guidelines, drug databases, and research papers contradict each other
- Legal: Find conflicting precedents or regulatory interpretations across retrieved case law
- Finance: Catch inconsistent analyst recommendations or conflicting market data
- Enterprise Knowledge: Surface contradictions in internal documentation, policies, and SOPs
- Journalism/Research: Fact-check across multiple sources before generating summaries
API Reference
check_contradictions(chunks, *, extractor=None, max_chain_depth=4, min_confidence=0.0)
Main entry point. Extracts claims from chunks and detects contradictions.
Parameters:
chunks: List ofChunkobjectsextractor:ClaimExtractorinstance (default:PatternExtractor)max_chain_depth: Max BFS depth for indirect detection (default: 4)min_confidence: Ignore claims below this threshold (default: 0.0)
Returns: ContradictionReport
ContradictionReport
.has_contradictions->bool.contradiction_count->int.contradictions->list[Contradiction].summary()-> human-readable string.as_prompt_context()-> text for LLM prompt injection
Contradiction
.type->"direct" | "indirect" | "bridge".claim_a,.claim_b-> the conflictingClaimobjects.confidence->float(0-1).explanation-> human-readable description.chain->list[str](for indirect/bridge: the connecting entities)
Origin
contrachecker evolved from ChainOfMeaning, a symbolic reasoning engine I built exploring how to represent and reason about knowledge outside of neural networks. After iterating through 4 engine versions -- from basic LSTM-inspired gates to a 1M-rule SQLite-backed inference system -- the most valuable discovery wasn't the engine itself, but the contradiction detection algorithms: direct conflict detection, transitive chain analysis, and bridge contradiction discovery.
This library extracts that core value and puts it where it's most useful: between your RAG retriever and your LLM, catching the conflicts that language models can't see.
Author
Baris Genc -- CS MSc, NLP researcher since 2013 (pre-word2vec era).
- Web: gencbaris.com
- Email: info@gencbaris.com
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file contrachecker-0.1.0.tar.gz.
File metadata
- Download URL: contrachecker-0.1.0.tar.gz
- Upload date:
- Size: 173.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0baa31297d69d12607aeafc58ac3cbd1ce800a4593294239897a17e2a2fbd055
|
|
| MD5 |
2ea21a62ad904b1f7892dc99234595fa
|
|
| BLAKE2b-256 |
a4875c4a7e00ce3ba49d175219e74e686a00d57b7a9570c4344475e4d8e51580
|
Provenance
The following attestation bundles were made for contrachecker-0.1.0.tar.gz:
Publisher:
publish.yml on cervantes79/contrachecker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
contrachecker-0.1.0.tar.gz -
Subject digest:
0baa31297d69d12607aeafc58ac3cbd1ce800a4593294239897a17e2a2fbd055 - Sigstore transparency entry: 1247207275
- Sigstore integration time:
-
Permalink:
cervantes79/contrachecker@9f096b02be9c9607dc7330159172ea1fc397732f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/cervantes79
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9f096b02be9c9607dc7330159172ea1fc397732f -
Trigger Event:
release
-
Statement type:
File details
Details for the file contrachecker-0.1.0-py3-none-any.whl.
File metadata
- Download URL: contrachecker-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4233b8395f5215cceba73e5afab37ce792b75d208300027a3715ff6da791b3aa
|
|
| MD5 |
d25d5b06b607eb3b2fca31f1bc681142
|
|
| BLAKE2b-256 |
4c5016ed0a280fa4e2703e0a0d32eb4de88b99f476273f69faeea19f6ebc6928
|
Provenance
The following attestation bundles were made for contrachecker-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on cervantes79/contrachecker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
contrachecker-0.1.0-py3-none-any.whl -
Subject digest:
4233b8395f5215cceba73e5afab37ce792b75d208300027a3715ff6da791b3aa - Sigstore transparency entry: 1247207314
- Sigstore integration time:
-
Permalink:
cervantes79/contrachecker@9f096b02be9c9607dc7330159172ea1fc397732f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/cervantes79
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9f096b02be9c9607dc7330159172ea1fc397732f -
Trigger Event:
release
-
Statement type: