Contradiction detection
Surface conflicts across a contract corpus — the query that breaks similarity search. Ingest contracts and policies, follow the two-hop trace, and flag conflicts in code.
Contradiction is the query type that similarity search can't do: the conflicting clause doesn't look like your query — it looks like the clause it contradicts. Rekall follows the chain: hop 1 finds the contract, hop 2 finds the policy that conflicts with it given hop 1.
Where reasoning wins
On contradiction detection, SKIM scores 1.00 where BM25, an 8B embedding model (Qwen3-Embedding-8B), and a 9B agentic loop (Qwen3.5-9B) all score 0.50 — measured on the synthetic reasoning benchmark. It's the thesis in one number: retrieval that reasons finds things similarity cannot.
1 · Ingest the corpus
The demo corpus mixes supplier contracts with company policies. Supplier A's contract sets net-90 payment terms; the 2026 policy caps supplier terms at net-60 — a conflict no keyword overlaps.
rekall ingest ./contracts --store contractsconst store = await rekall.store("contracts");
await store.ingest([
{ id: "supplier_a", text: "…Supplier A payment terms are net-90 as amended in §4.2…", metadata: { source: "supplier_a.pdf", team: "procurement" } },
{ id: "policy_2026", text: "…all supplier payment terms must not exceed net-60…", metadata: { source: "policy_2026.md" } },
]);store = rekall.store("contracts")
store.ingest([
{"id": "supplier_a", "text": "…Supplier A payment terms are net-90 as amended in §4.2…", "metadata": {"source": "supplier_a.pdf", "team": "procurement"}},
{"id": "policy_2026", "text": "…all supplier payment terms must not exceed net-60…", "metadata": {"source": "policy_2026.md"}},
])2 · Ask for the conflict
rekall search "which supplier contract conflicts with the 2026 policy?" \
--store contracts --trace● 2 passages · 2 hops · confidence 0.87 · 4.2 ms
hop 1 → supplier_a.pdf (matches query)
hop 2 → policy_2026.md (conflict found, given hop 1)
✓ sufficient — stopped after 2 of 4 max hopsconst r = await store.search(
"which supplier contract conflicts with the 2026 policy?",
{ trace: true },
);r = store.search(
"which supplier contract conflicts with the 2026 policy?",
trace=True,
)The trace shows the reasoning: supplier_a at hop 1 (matches query), then policy_2026
at hop 2 (conflict found, given hop 1).
3 · Flag conflicts programmatically
A conflict is a high-confidence result whose trace spans two different documents with a
conflict-shaped why on the later hop. Turn that into a boolean:
def is_conflict(r) -> bool:
if r.confidence < 0.6 or r.hops_used < 2 or not r.trace:
return False
docs = {step.doc_id for step in r.trace}
conflict_words = ("conflict", "contradict", "exceed", "violat")
later_why = r.trace[-1].why.lower()
return len(docs) >= 2 and any(w in later_why for w in conflict_words)
if is_conflict(r):
a, b = r.trace[0].doc_id, r.trace[-1].doc_id
print(f"⚠ conflict: {a} vs {b} (confidence {r.confidence:.2f})")function isConflict(r): boolean {
if (r.confidence < 0.6 || r.hopsUsed < 2 || !r.trace) return false;
const docs = new Set(r.trace.map((s) => s.docId));
const conflictWords = ["conflict", "contradict", "exceed", "violat"];
const laterWhy = r.trace[r.trace.length - 1].why.toLowerCase();
return docs.size >= 2 && conflictWords.some((w) => laterWhy.includes(w));
}
if (isConflict(r)) {
const a = r.trace[0].docId, b = r.trace[r.trace.length - 1].docId;
console.log(`⚠ conflict: ${a} vs ${b} (confidence ${r.confidence})`);
}Sweep the whole corpus
Pair this with the RAG-eval harness: run one
contradiction-shaped query per policy through search_many, and flag every result where
is_conflict is true — an auditable conflict report over the entire contract set, in
seconds, on a CPU.
RAG evaluation harness
At ~4 ms per query, evaluating 1,000 queries takes seconds. Use search_many to run support-recall evals and regression suites offline.
LangChain & LlamaIndex retriever swap
Swap your vector retriever for Rekall in one line. Whole-document ingestion kills the chunking pipeline; the reasoning loop handles the queries similarity search fails on.