Skip to main content

Find exactly which parts of your LLM output are hallucinated.

Project description

๐Ÿ” FactEval

Find exactly which parts of your LLM output are hallucinated.

Debug hallucinations in LLM outputs like you debug code.

Input:  "Paris is the capital of Germany."
Output:  Paris is the capital of Germany โŒ
         โ†’ Contradicts evidence: "Paris is the capital of France."

PyPI version Hugging Face Spaces License: MIT Python 3.10+


What is FactEval?

FactEval verifies claims against provided reference context (e.g., retrieved documents in a RAG system). It helps developers debug LLM outputs by:

  • Breaking answers into atomic claims
  • Checking each claim against reference evidence
  • Highlighting hallucinated parts with explanations and diagnostics

โšก Try It Instantly

Live Demo on Hugging Face Spaces โ†’

No setup needed โ€” paste your text and see hallucinations highlighted in seconds.


๐Ÿ“ฆ Install

pip install facteval

For development:

git clone https://github.com/sahilaf/FactEval.git
cd FactEval && pip install -e ".[dev]"

๐Ÿš€ Quick Start

โšก Note: First run downloads and loads models (~15s). After that: ~0.3s per query.

from facteval import fast_check

result = fast_check(
    claims=["Paris is the capital of Germany.", "Paris has 5 million people."],
    contexts=["Paris is the capital of France. Population: 2.2M."],
)

for claim in result["claims"]:
    print(f'{claim["label"]:15s} {claim["claim"]}')
    print(f'                โ†’ {claim["reason"]}')
contradicted    Paris is the capital of Germany.
                โ†’ Contradicted by: "Paris is the capital of France."
contradicted    Paris has 5 million people.
                โ†’ Contradicted by: "Population: 2.2M."

๐Ÿงช Drop-in RAG Evaluator

Paste this into your RAG app to instantly catch hallucinations:

from facteval import fast_check

# 1. Your existing pipeline
response = llm(query)
docs = retriever(query)

# 2. Drop-in evaluation
result = fast_check(
    claims=response.split("."),  # Simple claim splitting
    contexts=docs
)

if result["summary"]["contradicted"] > 0:
    print("๐Ÿšจ Hallucination detected! Halting response.")

Full Pipeline (Automated Extraction)

If you don't want to split claims yourself, analyze() uses a lightweight LLM (Qwen 1.5B) to automatically decompose complex answers into atomic claims:

from facteval import analyze

result = analyze(
    answer="Paris is the capital of Germany and has 5 million people.",
    contexts=["Paris is the capital of France. Paris has approximately 2.2 million inhabitants."],
)

CLI (Command Line Interface)

# Quick check
facteval check --answer "The earth is flat." --context "The earth is an oblate spheroid."

# From file
facteval check input.json --output results.json

# With calibrator
facteval check input.json --calibrator calibrator.pkl

โœจ Features

  • Claim-level verdicts โ€” each sentence gets โœ… supported, โŒ contradicted, or โ“ unverifiable
  • Highlighted output โ€” color-coded HTML showing exactly which parts are wrong
  • Human-readable reasons โ€” every verdict explains why
  • Pipeline diagnostics โ€” hallucination vs. retrieval gap vs. missing context
  • Calibrated confidence โ€” isotonic regression for trustworthy probability scores
  • Lightweight mode โ€” fast_check() runs instantly (~0.3s) without heavy models
  • Drop-in API โ€” works seamlessly with LangChain, LlamaIndex, or custom pipelines
  • Batch NLI โ€” all claims in a single forward pass
  • CLI โ€” facteval check for scripting and CI/CD

๐Ÿ“Š Comparison

Feature FactEval FacTool QAFactEval
Claim-level granularity โœ… โœ… โŒ
Calibrated confidence scores โœ… โŒ โŒ
Pipeline Diagnostics (why it failed) โœ… โŒ โŒ
Sub-second mode (fast_check) โœ… โŒ โŒ
HTML highlighting output โœ… โŒ โŒ

๐Ÿ“‹ Output Format

Short version:

{
  "claims": [
    {
      "claim": "Paris is the capital of Germany.",
      "label": "contradicted",
      "confidence": 0.9971,
      "reason": "Contradicts evidence: \"Paris is the capital of France.\"",
      "diagnostics": {
        "failure_type": "hallucination",
        "retrieval_quality": "strong",
        "suggestion": "Claim directly contradicts the evidence."
      }
    }
  ],
  "summary": {
    "total_claims": 2, "supported": 0, "contradicted": 2,
    "unverifiable": 0, "hallucination_rate": 1.0
  },
  "highlighted_answer": "<mark>Paris is the capital of Germany โŒ</mark>..."
}
Full output includes

Each claim also contains:

  • evidence โ€” the matched reference sentence
  • evidence_score โ€” retrieval similarity (0โ€“1)
  • raw_nli_scores โ€” per-label NLI probabilities (entailment, neutral, contradiction)
  • calibrated_confidence โ€” post-calibration confidence (if calibrator provided)
  • calibration_error โ€” estimated calibration error

Top-level fields:

  • calibrated โ€” whether a fitted calibrator was used
  • pipeline_time_seconds โ€” total processing time

Labels

Label Meaning
โœ… supported Claim is entailed by the evidence
โŒ contradicted Claim contradicts the evidence
โ“ unverifiable No relevant evidence, or evidence is neutral

Diagnostics

failure_type What happened What to do
verified Supported by strong evidence Nothing โ€” it's correct
hallucination Contradicts strong evidence Factual error in LLM output
possible_hallucination Contradicts weak evidence Add better context to confirm
no_evidence No context for this topic Add reference passages
retrieval_gap Evidence too dissimilar Context may not cover this claim
inconclusive Evidence is neutral Cannot confirm or deny

๐Ÿค” When Should You Use FactEval?

Use FactEval if you are:

  • Building a RAG system and need to verify answers against retrieved documents
  • Debugging hallucinations in LLM outputs
  • Evaluating whether generated answers are grounded in context
  • Building CI/CD checks for LLM-powered features

Not intended for:

  • General fact-checking without reference context (FactEval needs ground truth documents)
  • Real-time inference on user-facing APIs (model loading adds latency)

โšก Performance

Mode First run Subsequent runs VRAM
check() (full) ~60s (loads 3 models) ~1.3s ~3.4 GB
verify() (lightweight) ~15s (loads 2 models) ~0.3s ~0.5 GB

First run downloads and loads models from Hugging Face. After that, models are cached in memory. Use verify() when you already have claims and need low-latency evaluation.


๐Ÿงช Built For

  • RAG pipelines โ€” verify that generated answers are grounded in retrieved documents
  • LLM evaluation workflows โ€” measure hallucination rates across test sets
  • AI product debugging โ€” find exactly where your model is making things up

๐Ÿ—๏ธ Architecture

Answer Text โ”€โ”€โ†’ Claim Extractor โ”€โ”€โ†’ Evidence Retriever โ”€โ”€โ†’ NLI Verifier โ”€โ”€โ†’ Calibrator โ”€โ”€โ†’ Output
                (Qwen 1.5B)         (MiniLM + FAISS)      (DeBERTa)        (Isotonic)
                                          โ”‚                                      โ”‚
                                          โ””โ”€โ”€ Semantic Highlighting โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                               (reuses MiniLM embeddings)
Stage Model Size Latency
Claim Extraction Qwen/Qwen2.5-1.5B-Instruct ~3 GB ~1s
Evidence Retrieval all-MiniLM-L6-v2 + FAISS ~90 MB <10ms
NLI Verification DeBERTa-v3-base-mnli-fever-anli ~370 MB <10ms/batch
Calibration Isotonic Regression (sklearn) ~1 KB <1ms

๐Ÿ–ฅ๏ธ Try It Locally

pip install -e ".[demo]"
python demo/app.py

Features:

  • Highlighted answer text with โœ…โŒโ“ annotations
  • Per-claim cards with reasons, diagnostic badges, and suggestions
  • Summary dashboard with hallucination rate
  • 4 built-in examples

Deploy to Hugging Face Spaces

git remote add hf https://huggingface.co/spaces/YOUR_USERNAME/facteval
git push hf main

๐Ÿ“ Project Structure

FactEval/
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ ANALYSIS.md                # Development analysis & lessons learned
โ”œโ”€โ”€ LICENSE                    # MIT
โ”œโ”€โ”€ pyproject.toml             # Package config + CLI entry point
โ”œโ”€โ”€ app.py                     # HF Spaces entry point
โ”œโ”€โ”€ requirements.txt           # HF Spaces dependencies
โ”œโ”€โ”€ examples/
โ”‚   โ”œโ”€โ”€ basic.py               # Minimal usage example
โ”‚   โ””โ”€โ”€ rag_debug.py           # RAG pipeline debugging example
โ”œโ”€โ”€ demo/
โ”‚   โ””โ”€โ”€ app.py                 # Gradio interactive demo
โ””โ”€โ”€ facteval/
    โ”œโ”€โ”€ __init__.py            # Public API: check(), verify()
    โ”œโ”€โ”€ config.py              # Model names, prompts, defaults
    โ”œโ”€โ”€ models.py              # Claim, Evidence, ClaimWithEvidence
    โ”œโ”€โ”€ claim_extractor.py     # Qwen2.5-1.5B claim decomposition
    โ”œโ”€โ”€ retriever.py           # FAISS + MiniLM evidence retrieval
    โ”œโ”€โ”€ verifier.py            # DeBERTa batch NLI + reasons
    โ”œโ”€โ”€ calibrator.py          # Isotonic regression calibration
    โ”œโ”€โ”€ core.py                # Pipeline orchestration + diagnostics
    โ””โ”€โ”€ cli.py                 # facteval check CLI

๐Ÿ“„ License

MIT โ€” see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

facteval-0.1.1.tar.gz (23.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

facteval-0.1.1-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file facteval-0.1.1.tar.gz.

File metadata

  • Download URL: facteval-0.1.1.tar.gz
  • Upload date:
  • Size: 23.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for facteval-0.1.1.tar.gz
Algorithm Hash digest
SHA256 6313f5827ce1d7b575aeb2dd15c60e3233ac3dcdee63266446785e111a005233
MD5 1fa45167fe033f9f27e9168d14496a0a
BLAKE2b-256 aafc9eb0fbfc2107dd263f19ea3b9a86bad10bc9f81bb642085d1da3af57279e

See more details on using hashes here.

File details

Details for the file facteval-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: facteval-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 22.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for facteval-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 676b34b8fc7a2799df32a7489d111f90a8f57a35c18b03ddeae54b919e73a650
MD5 0055f4a43187f9547158180da7ca8672
BLAKE2b-256 37c7888cb0e1d7378ddb97c3f0fa7d58e58643deb3b9c7c30125b2c7e2a81206

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page