ragcheck-eval 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ragcheck_eval-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
- ragcheck_eval-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +16 -0
- ragcheck_eval-0.1.0/.github/workflows/ci.yaml +23 -0
- ragcheck_eval-0.1.0/.github/workflows/smoke-eval.yaml +51 -0
- ragcheck_eval-0.1.0/.gitignore +15 -0
- ragcheck_eval-0.1.0/CHANGELOG.md +19 -0
- ragcheck_eval-0.1.0/CODE_OF_CONDUCT.md +7 -0
- ragcheck_eval-0.1.0/CONTRIBUTING.md +36 -0
- ragcheck_eval-0.1.0/LICENSE +21 -0
- ragcheck_eval-0.1.0/PKG-INFO +190 -0
- ragcheck_eval-0.1.0/README.md +145 -0
- ragcheck_eval-0.1.0/benchmarks/README.md +70 -0
- ragcheck_eval-0.1.0/benchmarks/benchmark_dataset.jsonl +16 -0
- ragcheck_eval-0.1.0/benchmarks/corpus/fetch_corpus.py +101 -0
- ragcheck_eval-0.1.0/benchmarks/cost_quality_frontier.png +0 -0
- ragcheck_eval-0.1.0/benchmarks/generate_qa.py +87 -0
- ragcheck_eval-0.1.0/benchmarks/pipelines/agentic_rag.py +109 -0
- ragcheck_eval-0.1.0/benchmarks/pipelines/common.py +116 -0
- ragcheck_eval-0.1.0/benchmarks/pipelines/hybrid_rag.py +41 -0
- ragcheck_eval-0.1.0/benchmarks/pipelines/naive_rag.py +26 -0
- ragcheck_eval-0.1.0/benchmarks/pipelines/reranked_rag.py +36 -0
- ragcheck_eval-0.1.0/benchmarks/run_benchmark.py +295 -0
- ragcheck_eval-0.1.0/benchmarks/synthetic_dataset.jsonl +28 -0
- ragcheck_eval-0.1.0/docs/ci-integration.md +54 -0
- ragcheck_eval-0.1.0/docs/judge-validation.md +48 -0
- ragcheck_eval-0.1.0/docs/metrics.md +53 -0
- ragcheck_eval-0.1.0/docs/quickstart.md +73 -0
- ragcheck_eval-0.1.0/examples/judge_labels_sample.jsonl +10 -0
- ragcheck_eval-0.1.0/examples/quickstart.ipynb +197 -0
- ragcheck_eval-0.1.0/examples/toy_config.yaml +13 -0
- ragcheck_eval-0.1.0/examples/toy_config_groq.yaml +13 -0
- ragcheck_eval-0.1.0/examples/toy_dataset.jsonl +10 -0
- ragcheck_eval-0.1.0/examples/toy_pipeline.py +85 -0
- ragcheck_eval-0.1.0/pyproject.toml +68 -0
- ragcheck_eval-0.1.0/ragcheck/__init__.py +18 -0
- ragcheck_eval-0.1.0/ragcheck/adapters/__init__.py +7 -0
- ragcheck_eval-0.1.0/ragcheck/adapters/base.py +39 -0
- ragcheck_eval-0.1.0/ragcheck/adapters/function.py +19 -0
- ragcheck_eval-0.1.0/ragcheck/adapters/langchain.py +59 -0
- ragcheck_eval-0.1.0/ragcheck/cache.py +67 -0
- ragcheck_eval-0.1.0/ragcheck/cli.py +210 -0
- ragcheck_eval-0.1.0/ragcheck/config.py +54 -0
- ragcheck_eval-0.1.0/ragcheck/datasets/__init__.py +6 -0
- ragcheck_eval-0.1.0/ragcheck/datasets/loaders.py +35 -0
- ragcheck_eval-0.1.0/ragcheck/datasets/models.py +32 -0
- ragcheck_eval-0.1.0/ragcheck/datasets/synthetic.py +255 -0
- ragcheck_eval-0.1.0/ragcheck/demo.py +168 -0
- ragcheck_eval-0.1.0/ragcheck/judge/__init__.py +5 -0
- ragcheck_eval-0.1.0/ragcheck/judge/judge.py +93 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/answer_relevance.md +21 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/answers_equivalent.md +16 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/chunk_relevance.md +13 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/citation_support.md +13 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/dataset_paraphrase.md +10 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/dataset_qa_cross.md +18 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/dataset_qa_multi.md +15 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/dataset_qa_single.md +15 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/dataset_unanswerable.md +15 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/faithfulness_decompose.md +18 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/faithfulness_verify.md +13 -0
- ragcheck_eval-0.1.0/ragcheck/judge/prompts/refusal_detection.md +13 -0
- ragcheck_eval-0.1.0/ragcheck/judge/validation.py +119 -0
- ragcheck_eval-0.1.0/ragcheck/llm.py +186 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/__init__.py +59 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/base.py +56 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/generation/__init__.py +7 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/generation/citation_accuracy.py +87 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/generation/faithfulness.py +99 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/generation/relevance.py +55 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/retrieval/__init__.py +8 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/retrieval/context_precision.py +70 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/retrieval/context_recall.py +81 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/retrieval/hit_rate.py +42 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/retrieval/mrr.py +41 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/robustness/__init__.py +6 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/robustness/paraphrase_consistency.py +96 -0
- ragcheck_eval-0.1.0/ragcheck/metrics/robustness/refusal_calibration.py +81 -0
- ragcheck_eval-0.1.0/ragcheck/report/__init__.py +5 -0
- ragcheck_eval-0.1.0/ragcheck/report/cli_summary.py +45 -0
- ragcheck_eval-0.1.0/ragcheck/report/html.py +145 -0
- ragcheck_eval-0.1.0/ragcheck/report/models.py +55 -0
- ragcheck_eval-0.1.0/ragcheck/report/regression.py +94 -0
- ragcheck_eval-0.1.0/ragcheck/runner.py +166 -0
- ragcheck_eval-0.1.0/tests/__init__.py +0 -0
- ragcheck_eval-0.1.0/tests/conftest.py +71 -0
- ragcheck_eval-0.1.0/tests/test_adapters.py +24 -0
- ragcheck_eval-0.1.0/tests/test_cache.py +26 -0
- ragcheck_eval-0.1.0/tests/test_datasets.py +37 -0
- ragcheck_eval-0.1.0/tests/test_demo.py +48 -0
- ragcheck_eval-0.1.0/tests/test_html_report.py +82 -0
- ragcheck_eval-0.1.0/tests/test_judge.py +32 -0
- ragcheck_eval-0.1.0/tests/test_langchain_adapter.py +50 -0
- ragcheck_eval-0.1.0/tests/test_llm.py +70 -0
- ragcheck_eval-0.1.0/tests/test_metrics_generation.py +143 -0
- ragcheck_eval-0.1.0/tests/test_metrics_retrieval.py +147 -0
- ragcheck_eval-0.1.0/tests/test_metrics_robustness.py +137 -0
- ragcheck_eval-0.1.0/tests/test_regression.py +79 -0
- ragcheck_eval-0.1.0/tests/test_report.py +44 -0
- ragcheck_eval-0.1.0/tests/test_runner.py +119 -0
- ragcheck_eval-0.1.0/tests/test_synthetic.py +111 -0
- ragcheck_eval-0.1.0/tests/test_validation.py +71 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something broken or behaving unexpectedly
|
|
4
|
+
labels: bug
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**What happened**
|
|
8
|
+
|
|
9
|
+
**What you expected**
|
|
10
|
+
|
|
11
|
+
**Reproduction**
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# minimal command / config that triggers it
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Environment**
|
|
18
|
+
- ragcheck version:
|
|
19
|
+
- Python version:
|
|
20
|
+
- Judge provider/model (if metric-related):
|
|
21
|
+
- Judge prompt version (from the report JSON):
|
|
22
|
+
|
|
23
|
+
**Report snippet** (if applicable — the relevant `MetricResult` from your report JSON, with `judge_model` and `prompt_version`)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Propose a metric, adapter, or capability
|
|
4
|
+
labels: enhancement
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**Problem it solves**
|
|
8
|
+
What can't you measure or do today?
|
|
9
|
+
|
|
10
|
+
**Proposed behavior**
|
|
11
|
+
CLI/config/API sketch if you have one.
|
|
12
|
+
|
|
13
|
+
**Alternatives considered**
|
|
14
|
+
|
|
15
|
+
**Scope check**
|
|
16
|
+
RAGCheck deliberately excludes web UIs, hosted services, and non-eval features (see README "Explicit non-goals"). If this touches those, say why it should be an exception.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
checks:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.12"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
cache: pip
|
|
20
|
+
- run: pip install -e ".[dev]"
|
|
21
|
+
- run: ruff check .
|
|
22
|
+
- run: mypy ragcheck/
|
|
23
|
+
- run: pytest -q
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Live smoke eval on merges to main: runs the toy example against a real judge
|
|
2
|
+
# and regression-checks it against the committed baseline (dogfooding
|
|
3
|
+
# `ragcheck compare`). Skips gracefully when no API key secret is configured.
|
|
4
|
+
name: smoke-eval
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
smoke:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
env:
|
|
15
|
+
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
|
16
|
+
RAGCHECK_MODEL: ${{ vars.RAGCHECK_MODEL || 'meta-llama/llama-4-scout-17b-16e-instruct' }}
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- name: Check for API key
|
|
20
|
+
id: gate
|
|
21
|
+
run: |
|
|
22
|
+
if [ -n "$GROQ_API_KEY" ]; then
|
|
23
|
+
echo "run=true" >> "$GITHUB_OUTPUT"
|
|
24
|
+
else
|
|
25
|
+
echo "No GROQ_API_KEY secret configured - skipping live smoke eval."
|
|
26
|
+
fi
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
if: steps.gate.outputs.run == 'true'
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.12"
|
|
31
|
+
cache: pip
|
|
32
|
+
- name: Run toy eval
|
|
33
|
+
if: steps.gate.outputs.run == 'true'
|
|
34
|
+
run: |
|
|
35
|
+
pip install -e .
|
|
36
|
+
ragcheck run examples/toy_config_groq.yaml --yes
|
|
37
|
+
- name: Regression check against baseline
|
|
38
|
+
if: steps.gate.outputs.run == 'true'
|
|
39
|
+
run: |
|
|
40
|
+
if [ -f examples/smoke_baseline.json ]; then
|
|
41
|
+
ragcheck compare examples/smoke_baseline.json ragcheck_output/toy-demo-groq.json \
|
|
42
|
+
--fail-if "faithfulness<-0.1" --fail-if "hit_rate@3<-0.1"
|
|
43
|
+
else
|
|
44
|
+
echo "No baseline committed yet - skipping compare."
|
|
45
|
+
fi
|
|
46
|
+
- name: Upload report
|
|
47
|
+
if: steps.gate.outputs.run == 'true'
|
|
48
|
+
uses: actions/upload-artifact@v4
|
|
49
|
+
with:
|
|
50
|
+
name: smoke-report
|
|
51
|
+
path: ragcheck_output/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (unreleased)
|
|
4
|
+
|
|
5
|
+
Initial release.
|
|
6
|
+
|
|
7
|
+
- `RAGAdapter` / `FunctionAdapter` / `LangChainAdapter` pipeline wrappers
|
|
8
|
+
- Retrieval metrics: `hit_rate@k`, `mrr`, `context_precision`, `context_recall`
|
|
9
|
+
- Generation metrics: `faithfulness`, `answer_relevance`, `citation_accuracy`
|
|
10
|
+
- Robustness metrics: `refusal_calibration`, `paraphrase_consistency`
|
|
11
|
+
- Judge validation: `ragcheck validate-judge` (Cohen's kappa, confusion matrix vs. human labels)
|
|
12
|
+
- Synthetic datasets: `ragcheck generate-dataset` (difficulty tiers, unanswerables, paraphrase groups, resumable)
|
|
13
|
+
- SQLite judgment cache keyed on judge model + prompt version + inputs
|
|
14
|
+
- Versioned judge prompts (markdown + YAML frontmatter)
|
|
15
|
+
- Judge providers: Anthropic, Groq; parallel judging with rate-limit backoff
|
|
16
|
+
- JSON + self-contained HTML reports with worst-failing-sample drilldowns
|
|
17
|
+
- `ragcheck compare` regression diffing with `--fail-if` thresholds (CI-friendly exit codes)
|
|
18
|
+
- Reproducible 4-architecture benchmark (naive / hybrid / reranked / agentic RAG) on SEC 10-Ks
|
|
19
|
+
- Cost guard: large LLM-judged runs require `--yes`
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
This project follows the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
|
|
4
|
+
|
|
5
|
+
In short: be respectful, be constructive, assume good faith. Harassment, personal attacks, and derailing behavior are not tolerated.
|
|
6
|
+
|
|
7
|
+
Report unacceptable behavior by opening a confidential issue or emailing the maintainer. All reports are reviewed and handled with discretion.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Contributing to RAGCheck
|
|
2
|
+
|
|
3
|
+
Thanks for considering a contribution. This project values honest metrics and small, reviewable changes.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/Ekta-Shah/ragcheck && cd ragcheck
|
|
9
|
+
python3.10+ -m venv .venv && source .venv/bin/activate
|
|
10
|
+
pip install -e ".[dev]"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Before you open a PR
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
ruff check . # lint (line length 100)
|
|
17
|
+
mypy ragcheck/ # types are required on all public APIs
|
|
18
|
+
pytest # must stay green; no live API calls in unit tests
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- **No live LLM calls in unit tests.** Judges are mocked (see `tests/conftest.py`). Live calls belong only in the smoke-eval workflow and `benchmarks/`.
|
|
22
|
+
- **Every LLM-judged metric needs known-answer tests** — hand-constructed cases where the correct score is verifiable by inspection (aim for 3+).
|
|
23
|
+
- **Judge prompts are versioned.** Changing a prompt's wording means bumping `version:` in its frontmatter; cached judgments key on the version, so old results stay traceable.
|
|
24
|
+
- **Docstrings (Google style) + type hints** on all public classes and functions.
|
|
25
|
+
- One logical change per commit; keep PRs focused.
|
|
26
|
+
|
|
27
|
+
## What's welcome
|
|
28
|
+
|
|
29
|
+
- New metrics (deterministic ones are the easiest entry point — see `ragcheck/metrics/retrieval/mrr.py` as a template)
|
|
30
|
+
- Judge prompt improvements (with before/after validation via `ragcheck validate-judge`)
|
|
31
|
+
- Adapters for other frameworks (LlamaIndex, Haystack) — open an issue first
|
|
32
|
+
- Benchmark reproductions and corrections
|
|
33
|
+
|
|
34
|
+
## Filing issues
|
|
35
|
+
|
|
36
|
+
Use the issue templates. For metric-quality issues, include the judge model, prompt version, and a minimal failing sample — all three are recorded in every report.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ekta Shah
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ragcheck-eval
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: pytest for RAG systems - evaluate retrieval-augmented generation pipelines
|
|
5
|
+
Author: Ekta Shah
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Ekta Shah
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Requires-Dist: anthropic>=0.40
|
|
30
|
+
Requires-Dist: httpx>=0.27
|
|
31
|
+
Requires-Dist: pydantic>=2.5
|
|
32
|
+
Requires-Dist: pyyaml>=6.0
|
|
33
|
+
Requires-Dist: rich>=13.0
|
|
34
|
+
Requires-Dist: typer>=0.12
|
|
35
|
+
Provides-Extra: benchmarks
|
|
36
|
+
Requires-Dist: matplotlib>=3.8; extra == 'benchmarks'
|
|
37
|
+
Requires-Dist: rank-bm25>=0.2; extra == 'benchmarks'
|
|
38
|
+
Requires-Dist: sentence-transformers>=3.0; extra == 'benchmarks'
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
41
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
43
|
+
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# RAGCheck
|
|
47
|
+
|
|
48
|
+
[](https://github.com/Ekta-Shah/ragcheck/actions/workflows/ci.yaml)
|
|
49
|
+
[](LICENSE)
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
**pytest for RAG systems.** Wrap your retrieval-augmented generation pipeline in a one-method adapter and get quality scores, a failure taxonomy, cost/latency profiles, and CI-friendly regression checks — with a judge you can actually verify.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install ragcheck-eval # imports and CLI are `ragcheck`
|
|
56
|
+
ragcheck demo # no API key needed - see the whole workflow in 10 seconds
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The demo runs a canned pipeline with two planted failures — faithfulness catches the hallucinated claim, refusal calibration flags the invented answer to an unanswerable question, and the HTML report shows both with their retrieved contexts. ([Or open the quickstart in Colab.](https://colab.research.google.com/github/Ekta-Shah/ragcheck/blob/main/examples/quickstart.ipynb))
|
|
60
|
+
|
|
61
|
+
For real runs:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
export ANTHROPIC_API_KEY=sk-ant-... # or GROQ_API_KEY for open-weight judges
|
|
65
|
+
ragcheck run examples/toy_config.yaml
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Real output from the bundled toy pipeline (keyword retrieval over a 10-doc corpus):
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
RAGCheck - toy-demo-groq
|
|
72
|
+
┏━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
73
|
+
┃ Metric ┃ Score ┃ Samples ┃ Judge ┃
|
|
74
|
+
┡━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
|
|
75
|
+
│ hit_rate@3 │ 1.000 │ 10 │ - │
|
|
76
|
+
│ faithfulness │ 1.000 │ 10 │ llama-3.3-70b-versatile (v1) │
|
|
77
|
+
└──────────────┴───────┴─────────┴──────────────────────────────┘
|
|
78
|
+
pipeline tokens: {'input_tokens': 1410, 'output_tokens': 184} |
|
|
79
|
+
judge tokens: {'input_tokens': 4338, 'output_tokens': 274} | cache: 0 hits / 26 misses
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Alongside the terminal scorecard you get a JSON report and a **self-contained HTML report** — scorecard, judge-validation status, latency/cost, and the worst-failing samples with question, answer, and retrieved context inline.
|
|
83
|
+
|
|
84
|
+
## Why another RAG eval tool?
|
|
85
|
+
|
|
86
|
+
Existing tools score your pipeline with an LLM judge and stop there. RAGCheck is built around the questions that actually block shipping:
|
|
87
|
+
|
|
88
|
+
- **Can you trust the judge?** `ragcheck validate-judge labels.jsonl --metric faithfulness` measures LLM-judge vs. human agreement (Cohen's kappa + confusion matrix) *before* you trust judged metrics. On our own labels it caught a real miscalibration: the default pass threshold scored κ=0.40; the corrected threshold scores κ=1.00. Reports display "validated at κ=0.XX" — or a visible "not validated" flag.
|
|
89
|
+
- **Does your system know when to say "I don't know"?** `refusal_calibration` reports the false-answer rate (hallucinated answers to unanswerable questions) and over-refusal rate separately.
|
|
90
|
+
- **Is it robust?** `paraphrase_consistency` judges pairwise answer agreement within paraphrase groups — catching pipelines that only work on one phrasing.
|
|
91
|
+
- **No eval set?** `ragcheck generate-dataset corpus_dir/` builds one from your documents: easy/medium/hard tiers, unanswerable questions, paraphrase groups, chunk-level provenance. Cached and resumable.
|
|
92
|
+
- **What does quality cost?** The benchmark harness produces a cost-quality frontier across architectures.
|
|
93
|
+
- **Did this PR make it worse?** `ragcheck compare old.json new.json --fail-if "faithfulness<-0.05"` exits non-zero on regression and prints a markdown diff for PR comments.
|
|
94
|
+
|
|
95
|
+
## Compared to existing tools
|
|
96
|
+
|
|
97
|
+
| Capability | RAGCheck | RAGAS | DeepEval | TruLens |
|
|
98
|
+
|---|:---:|:---:|:---:|:---:|
|
|
99
|
+
| Core RAG metrics (faithfulness, context precision/recall, relevance) | ✅ | ✅ | ✅ | ✅ |
|
|
100
|
+
| Built-in judge validation vs. human labels (Cohen's κ) | ✅ | ❌ | ❌ | ❌ |
|
|
101
|
+
| Refusal calibration (false-answer + over-refusal rates) | ✅ | ❌ | ❌ | ❌ |
|
|
102
|
+
| Paraphrase-consistency robustness testing | ✅ | ❌ | ❌ | ❌ |
|
|
103
|
+
| Persistent judgment cache keyed on judge model + prompt version | ✅ | ❌ | partial | ❌ |
|
|
104
|
+
| Report diffing with CI exit codes | ✅ | ❌ | ✅ | ❌ |
|
|
105
|
+
| Runs entirely local — no dashboard, account, or service | ✅ | ✅ | partial | partial |
|
|
106
|
+
|
|
107
|
+
*Comparison reflects our reading of each tool's docs at the time of writing; corrections welcome via issues.*
|
|
108
|
+
|
|
109
|
+
## How it works
|
|
110
|
+
|
|
111
|
+
Wrap any pipeline in a `RAGAdapter` (or just a function):
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from ragcheck import RAGResponse, RetrievedChunk
|
|
115
|
+
from ragcheck.adapters import FunctionAdapter
|
|
116
|
+
|
|
117
|
+
def my_pipeline(question: str) -> RAGResponse:
|
|
118
|
+
chunks = my_retriever(question)
|
|
119
|
+
answer = my_generator(question, chunks)
|
|
120
|
+
return RAGResponse(
|
|
121
|
+
answer=answer,
|
|
122
|
+
retrieved_chunks=[RetrievedChunk(content=c.text, source_id=c.id) for c in chunks],
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
adapter = FunctionAdapter(my_pipeline)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
LangChain users: `LangChainAdapter(retriever, chain)` wraps a retriever + chain directly.
|
|
129
|
+
|
|
130
|
+
Point a YAML config at your adapter, dataset, and metrics, then `ragcheck run config.yaml`. Every LLM judgment is cached in SQLite (keyed on judge model, prompt version, and inputs) so re-runs on unchanged data are free — and every judged result records exactly which judge and prompt produced it.
|
|
131
|
+
|
|
132
|
+
## Benchmark: which RAG architecture?
|
|
133
|
+
|
|
134
|
+
Four pipelines — naive dense, hybrid (BM25+dense RRF), cross-encoder reranked, and agentic (query decomposition) — on 8 SEC 10-K filings, identical chunking/generator, retrieval as the only variable. Preliminary 12-sample free-tier run (differences under ~0.2 are noise; full 300-500 sample run pending):
|
|
135
|
+
|
|
136
|
+
| Pipeline | faithfulness | refusal_calibration | paraphrase_consistency | tok/q | retrieval p50 |
|
|
137
|
+
|---|---:|---:|---:|---:|---:|
|
|
138
|
+
| naive (dense) | 0.950 | 0.583 | 0.700 | 1535 | 43 ms |
|
|
139
|
+
| hybrid (BM25+dense RRF) | 0.958 | 0.500 | **1.000** | 1448 | 75 ms |
|
|
140
|
+
| reranked (cross-encoder) | 0.958 | 0.500 | **1.000** | 1511 | 359 ms |
|
|
141
|
+
| agentic (decomposition) | 0.958 | **0.750** | 0.450 | 2683 | 6602 ms |
|
|
142
|
+
|
|
143
|
+
Early hypothesis worth noticing: agentic RAG refuses unanswerables best but answers paraphrases least consistently, at 1.75× the cost — exactly the trade-offs single-metric evals miss. Methodology, frontier chart, and honest limitations: [benchmarks/](benchmarks/README.md).
|
|
144
|
+
|
|
145
|
+
## Design decisions
|
|
146
|
+
|
|
147
|
+
- **Judge validation is a feature, not a footnote.** Judged metrics are untrustworthy by default; the κ workflow makes trust measurable, and reports flag unvalidated judges.
|
|
148
|
+
- **Deterministic first.** Where a metric can be computed without an LLM (hit_rate, MRR), it is. LLM judging is reserved for what actually needs it.
|
|
149
|
+
- **Everything cached, everything versioned.** Judgments are cached on (judge model, prompt version, inputs); prompts live as versioned markdown. Interrupted dataset generation resumes for free; changing a prompt never silently reuses stale verdicts.
|
|
150
|
+
- **CI is the target environment.** JSON reports, exit codes, markdown diffs, an env-gated smoke workflow, and a cost guard (`--yes` required above a configurable sample count).
|
|
151
|
+
- **No UI.** The self-contained HTML report is the only visual output. No dashboard, no server, no account.
|
|
152
|
+
|
|
153
|
+
## Docs
|
|
154
|
+
|
|
155
|
+
[Quickstart](docs/quickstart.md) · [Metrics reference](docs/metrics.md) · [Judge validation](docs/judge-validation.md) · [CI integration](docs/ci-integration.md)
|
|
156
|
+
|
|
157
|
+
## Status
|
|
158
|
+
|
|
159
|
+
| Area | Status |
|
|
160
|
+
|---|---|
|
|
161
|
+
| Zero-key demo (`ragcheck demo`) + Colab quickstart notebook | ✅ |
|
|
162
|
+
| Adapters: `FunctionAdapter`, `LangChainAdapter` | ✅ |
|
|
163
|
+
| 9 metrics: retrieval (4) · generation (3) · robustness (2) | ✅ |
|
|
164
|
+
| Judge validation (`validate-judge`, Cohen's κ) | ✅ |
|
|
165
|
+
| Synthetic datasets (`generate-dataset`) | ✅ |
|
|
166
|
+
| JSON + self-contained HTML reports | ✅ |
|
|
167
|
+
| Regression diffing (`compare --fail-if`) | ✅ |
|
|
168
|
+
| CI workflows (lint/type/test + env-gated live smoke eval) | ✅ |
|
|
169
|
+
| 4-architecture benchmark harness | ✅ |
|
|
170
|
+
| 4-pipeline benchmark: preliminary 12-sample results | ✅ |
|
|
171
|
+
| Full benchmark run (300-500 samples) | 🔜 needs funded API budget |
|
|
172
|
+
| PyPI release (`ragcheck-eval` 0.1.0) | 🔜 with benchmark results |
|
|
173
|
+
| LlamaIndex/Haystack adapters | ❌ not in v0.1 (issues welcome) |
|
|
174
|
+
|
|
175
|
+
Explicit non-goals: web UI/dashboards, hosted services, fine-tuning or embedding-model evaluation.
|
|
176
|
+
|
|
177
|
+
## Development
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
pip install -e ".[dev]"
|
|
181
|
+
pytest # unit tests (no API calls - judges are mocked)
|
|
182
|
+
ruff check .
|
|
183
|
+
mypy ragcheck/
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# RAGCheck
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Ekta-Shah/ragcheck/actions/workflows/ci.yaml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
**pytest for RAG systems.** Wrap your retrieval-augmented generation pipeline in a one-method adapter and get quality scores, a failure taxonomy, cost/latency profiles, and CI-friendly regression checks — with a judge you can actually verify.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install ragcheck-eval # imports and CLI are `ragcheck`
|
|
11
|
+
ragcheck demo # no API key needed - see the whole workflow in 10 seconds
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The demo runs a canned pipeline with two planted failures — faithfulness catches the hallucinated claim, refusal calibration flags the invented answer to an unanswerable question, and the HTML report shows both with their retrieved contexts. ([Or open the quickstart in Colab.](https://colab.research.google.com/github/Ekta-Shah/ragcheck/blob/main/examples/quickstart.ipynb))
|
|
15
|
+
|
|
16
|
+
For real runs:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
export ANTHROPIC_API_KEY=sk-ant-... # or GROQ_API_KEY for open-weight judges
|
|
20
|
+
ragcheck run examples/toy_config.yaml
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Real output from the bundled toy pipeline (keyword retrieval over a 10-doc corpus):
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
RAGCheck - toy-demo-groq
|
|
27
|
+
┏━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
28
|
+
┃ Metric ┃ Score ┃ Samples ┃ Judge ┃
|
|
29
|
+
┡━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
|
|
30
|
+
│ hit_rate@3 │ 1.000 │ 10 │ - │
|
|
31
|
+
│ faithfulness │ 1.000 │ 10 │ llama-3.3-70b-versatile (v1) │
|
|
32
|
+
└──────────────┴───────┴─────────┴──────────────────────────────┘
|
|
33
|
+
pipeline tokens: {'input_tokens': 1410, 'output_tokens': 184} |
|
|
34
|
+
judge tokens: {'input_tokens': 4338, 'output_tokens': 274} | cache: 0 hits / 26 misses
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Alongside the terminal scorecard you get a JSON report and a **self-contained HTML report** — scorecard, judge-validation status, latency/cost, and the worst-failing samples with question, answer, and retrieved context inline.
|
|
38
|
+
|
|
39
|
+
## Why another RAG eval tool?
|
|
40
|
+
|
|
41
|
+
Existing tools score your pipeline with an LLM judge and stop there. RAGCheck is built around the questions that actually block shipping:
|
|
42
|
+
|
|
43
|
+
- **Can you trust the judge?** `ragcheck validate-judge labels.jsonl --metric faithfulness` measures LLM-judge vs. human agreement (Cohen's kappa + confusion matrix) *before* you trust judged metrics. On our own labels it caught a real miscalibration: the default pass threshold scored κ=0.40; the corrected threshold scores κ=1.00. Reports display "validated at κ=0.XX" — or a visible "not validated" flag.
|
|
44
|
+
- **Does your system know when to say "I don't know"?** `refusal_calibration` reports the false-answer rate (hallucinated answers to unanswerable questions) and over-refusal rate separately.
|
|
45
|
+
- **Is it robust?** `paraphrase_consistency` judges pairwise answer agreement within paraphrase groups — catching pipelines that only work on one phrasing.
|
|
46
|
+
- **No eval set?** `ragcheck generate-dataset corpus_dir/` builds one from your documents: easy/medium/hard tiers, unanswerable questions, paraphrase groups, chunk-level provenance. Cached and resumable.
|
|
47
|
+
- **What does quality cost?** The benchmark harness produces a cost-quality frontier across architectures.
|
|
48
|
+
- **Did this PR make it worse?** `ragcheck compare old.json new.json --fail-if "faithfulness<-0.05"` exits non-zero on regression and prints a markdown diff for PR comments.
|
|
49
|
+
|
|
50
|
+
## Compared to existing tools
|
|
51
|
+
|
|
52
|
+
| Capability | RAGCheck | RAGAS | DeepEval | TruLens |
|
|
53
|
+
|---|:---:|:---:|:---:|:---:|
|
|
54
|
+
| Core RAG metrics (faithfulness, context precision/recall, relevance) | ✅ | ✅ | ✅ | ✅ |
|
|
55
|
+
| Built-in judge validation vs. human labels (Cohen's κ) | ✅ | ❌ | ❌ | ❌ |
|
|
56
|
+
| Refusal calibration (false-answer + over-refusal rates) | ✅ | ❌ | ❌ | ❌ |
|
|
57
|
+
| Paraphrase-consistency robustness testing | ✅ | ❌ | ❌ | ❌ |
|
|
58
|
+
| Persistent judgment cache keyed on judge model + prompt version | ✅ | ❌ | partial | ❌ |
|
|
59
|
+
| Report diffing with CI exit codes | ✅ | ❌ | ✅ | ❌ |
|
|
60
|
+
| Runs entirely local — no dashboard, account, or service | ✅ | ✅ | partial | partial |
|
|
61
|
+
|
|
62
|
+
*Comparison reflects our reading of each tool's docs at the time of writing; corrections welcome via issues.*
|
|
63
|
+
|
|
64
|
+
## How it works
|
|
65
|
+
|
|
66
|
+
Wrap any pipeline in a `RAGAdapter` (or just a function):
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from ragcheck import RAGResponse, RetrievedChunk
|
|
70
|
+
from ragcheck.adapters import FunctionAdapter
|
|
71
|
+
|
|
72
|
+
def my_pipeline(question: str) -> RAGResponse:
|
|
73
|
+
chunks = my_retriever(question)
|
|
74
|
+
answer = my_generator(question, chunks)
|
|
75
|
+
return RAGResponse(
|
|
76
|
+
answer=answer,
|
|
77
|
+
retrieved_chunks=[RetrievedChunk(content=c.text, source_id=c.id) for c in chunks],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
adapter = FunctionAdapter(my_pipeline)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
LangChain users: `LangChainAdapter(retriever, chain)` wraps a retriever + chain directly.
|
|
84
|
+
|
|
85
|
+
Point a YAML config at your adapter, dataset, and metrics, then `ragcheck run config.yaml`. Every LLM judgment is cached in SQLite (keyed on judge model, prompt version, and inputs) so re-runs on unchanged data are free — and every judged result records exactly which judge and prompt produced it.
|
|
86
|
+
|
|
87
|
+
## Benchmark: which RAG architecture?
|
|
88
|
+
|
|
89
|
+
Four pipelines — naive dense, hybrid (BM25+dense RRF), cross-encoder reranked, and agentic (query decomposition) — on 8 SEC 10-K filings, identical chunking/generator, retrieval as the only variable. Preliminary 12-sample free-tier run (differences under ~0.2 are noise; full 300-500 sample run pending):
|
|
90
|
+
|
|
91
|
+
| Pipeline | faithfulness | refusal_calibration | paraphrase_consistency | tok/q | retrieval p50 |
|
|
92
|
+
|---|---:|---:|---:|---:|---:|
|
|
93
|
+
| naive (dense) | 0.950 | 0.583 | 0.700 | 1535 | 43 ms |
|
|
94
|
+
| hybrid (BM25+dense RRF) | 0.958 | 0.500 | **1.000** | 1448 | 75 ms |
|
|
95
|
+
| reranked (cross-encoder) | 0.958 | 0.500 | **1.000** | 1511 | 359 ms |
|
|
96
|
+
| agentic (decomposition) | 0.958 | **0.750** | 0.450 | 2683 | 6602 ms |
|
|
97
|
+
|
|
98
|
+
Early hypothesis worth noticing: agentic RAG refuses unanswerables best but answers paraphrases least consistently, at 1.75× the cost — exactly the trade-offs single-metric evals miss. Methodology, frontier chart, and honest limitations: [benchmarks/](benchmarks/README.md).
|
|
99
|
+
|
|
100
|
+
## Design decisions
|
|
101
|
+
|
|
102
|
+
- **Judge validation is a feature, not a footnote.** Judged metrics are untrustworthy by default; the κ workflow makes trust measurable, and reports flag unvalidated judges.
|
|
103
|
+
- **Deterministic first.** Where a metric can be computed without an LLM (hit_rate, MRR), it is. LLM judging is reserved for what actually needs it.
|
|
104
|
+
- **Everything cached, everything versioned.** Judgments are cached on (judge model, prompt version, inputs); prompts live as versioned markdown. Interrupted dataset generation resumes for free; changing a prompt never silently reuses stale verdicts.
|
|
105
|
+
- **CI is the target environment.** JSON reports, exit codes, markdown diffs, an env-gated smoke workflow, and a cost guard (`--yes` required above a configurable sample count).
|
|
106
|
+
- **No UI.** The self-contained HTML report is the only visual output. No dashboard, no server, no account.
|
|
107
|
+
|
|
108
|
+
## Docs
|
|
109
|
+
|
|
110
|
+
[Quickstart](docs/quickstart.md) · [Metrics reference](docs/metrics.md) · [Judge validation](docs/judge-validation.md) · [CI integration](docs/ci-integration.md)
|
|
111
|
+
|
|
112
|
+
## Status
|
|
113
|
+
|
|
114
|
+
| Area | Status |
|
|
115
|
+
|---|---|
|
|
116
|
+
| Zero-key demo (`ragcheck demo`) + Colab quickstart notebook | ✅ |
|
|
117
|
+
| Adapters: `FunctionAdapter`, `LangChainAdapter` | ✅ |
|
|
118
|
+
| 9 metrics: retrieval (4) · generation (3) · robustness (2) | ✅ |
|
|
119
|
+
| Judge validation (`validate-judge`, Cohen's κ) | ✅ |
|
|
120
|
+
| Synthetic datasets (`generate-dataset`) | ✅ |
|
|
121
|
+
| JSON + self-contained HTML reports | ✅ |
|
|
122
|
+
| Regression diffing (`compare --fail-if`) | ✅ |
|
|
123
|
+
| CI workflows (lint/type/test + env-gated live smoke eval) | ✅ |
|
|
124
|
+
| 4-architecture benchmark harness | ✅ |
|
|
125
|
+
| 4-pipeline benchmark: preliminary 12-sample results | ✅ |
|
|
126
|
+
| Full benchmark run (300-500 samples) | 🔜 needs funded API budget |
|
|
127
|
+
| PyPI release (`ragcheck-eval` 0.1.0) | 🔜 with benchmark results |
|
|
128
|
+
| LlamaIndex/Haystack adapters | ❌ not in v0.1 (issues welcome) |
|
|
129
|
+
|
|
130
|
+
Explicit non-goals: web UI/dashboards, hosted services, fine-tuning or embedding-model evaluation.
|
|
131
|
+
|
|
132
|
+
## Development
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
pip install -e ".[dev]"
|
|
136
|
+
pytest # unit tests (no API calls - judges are mocked)
|
|
137
|
+
ruff check .
|
|
138
|
+
mypy ragcheck/
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# RAGCheck Architecture Benchmark
|
|
2
|
+
|
|
3
|
+
Reproducible comparison of RAG retrieval architectures on a financial-filings corpus. Everything except the retrieval strategy is held constant: same chunking (1200 chars, 200 overlap), same embedding model (`all-MiniLM-L6-v2`), same generator model and prompt.
|
|
4
|
+
|
|
5
|
+
> **Status: preliminary results below (12 samples, free-tier run).** All 4 pipelines and the metric suite run end-to-end. The full run (300-500 samples) needs a funded API budget and will replace these numbers; at n=12, differences under ~0.2 are noise.
|
|
6
|
+
|
|
7
|
+
## Pipelines
|
|
8
|
+
|
|
9
|
+
| Pipeline | Retrieval |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `naive_rag` | Dense (MiniLM cosine) top-5 |
|
|
12
|
+
| `hybrid_rag` | BM25 + dense top-20 each, reciprocal rank fusion (k=60), top-5 |
|
|
13
|
+
| `reranked_rag` | Dense top-20 → cross-encoder (`ms-marco-MiniLM-L-6-v2`) rerank → top-5 |
|
|
14
|
+
| `agentic_rag` | LLM query decomposition → iterative dense retrieval (max 3 rounds) → synthesis |
|
|
15
|
+
|
|
16
|
+
All four share the same chunking, embeddings, generator model, and generation prompt (which requires `[source_id]` citations) - the retrieval strategy is the only variable. Agentic's decomposition/sufficiency LLM calls are charged to its cost per query. Generator/judge models are selected with `RAGCHECK_BENCH_MODEL` / `RAGCHECK_JUDGE_MODEL`.
|
|
17
|
+
|
|
18
|
+
## Corpus
|
|
19
|
+
|
|
20
|
+
Latest 10-K filings of Apple, Microsoft, NVIDIA, Alphabet, Amazon, Meta, Tesla, and JPMorgan, fetched from SEC EDGAR and converted to plain text (~3.8 MB). Raw data is gitignored; the fetch script is deterministic.
|
|
21
|
+
|
|
22
|
+
## Reproduce
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install -e ".[benchmarks]"
|
|
26
|
+
export GROQ_API_KEY=...
|
|
27
|
+
export RAGCHECK_BENCH_MODEL=meta-llama/llama-4-scout-17b-16e-instruct
|
|
28
|
+
python benchmarks/corpus/fetch_corpus.py # ~4 MB from EDGAR
|
|
29
|
+
ragcheck generate-dataset benchmarks/corpus/data --n 400 \
|
|
30
|
+
--paraphrase-groups 20 --out benchmarks/synthetic_dataset.jsonl \
|
|
31
|
+
--provider groq --model $RAGCHECK_BENCH_MODEL # resumable
|
|
32
|
+
python benchmarks/run_benchmark.py # full; --quick for 20 samples
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Outputs land in `benchmarks/results/`: per-pipeline JSON reports, `comparison.json`/`comparison.md`, and `cost_quality_frontier.png` (cost/query vs. faithfulness). `--pipelines`, `--metrics`, and `--n` subset a run for cheap smoke tests.
|
|
36
|
+
|
|
37
|
+
## Preliminary results (4 pipelines, 12 stratified samples, 2026-07-06)
|
|
38
|
+
|
|
39
|
+
Generator + judge: `meta-llama/llama-4-scout-17b-16e-instruct`. Samples include 2 unanswerable questions and one full paraphrase group. Chunk-level judged metrics (context precision/recall, citation accuracy) were excluded from this budget-constrained run.
|
|
40
|
+
|
|
41
|
+
| Pipeline | hit_rate@5 | mrr | faithfulness | answer_relevance | refusal_calibration | paraphrase_consistency | tok/q | $/q | retrieval p50 |
|
|
42
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
43
|
+
| naive_rag | 0.500 | 0.450 | 0.950 | 0.917 | 0.583 | 0.700 | 1535 | $0.00018 | 43 ms |
|
|
44
|
+
| hybrid_rag | 0.400 | 0.245 | 0.958 | 0.938 | 0.500 | **1.000** | 1448 | $0.00017 | 75 ms |
|
|
45
|
+
| reranked_rag | 0.500 | 0.183 | 0.958 | 0.917 | 0.500 | **1.000** | 1511 | $0.00018 | 359 ms |
|
|
46
|
+
| agentic_rag | 0.500 | 0.450 | 0.958 | 0.854 | **0.750** | 0.450 | 2683 | $0.00032 | 6602 ms |
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+
|
|
50
|
+
**Preliminary reads (all within noise at this sample size, offered as hypotheses for the full run):**
|
|
51
|
+
- **Agentic RAG is the refusal-calibration leader but the consistency laggard** - its multi-round retrieval gathers enough to decline unanswerables more often (0.75 vs 0.50-0.58), but different decompositions per paraphrase produce different answers (0.45 consistency). It also costs 1.75x tokens/query and ~90x retrieval latency.
|
|
52
|
+
- **Hybrid and reranked are perfectly paraphrase-consistent** on this subset - stabler retrieval under rephrasing.
|
|
53
|
+
- **Faithfulness is uniformly high (~0.95)** across architectures: the shared generator rarely asserts beyond its context; retrieval quality, robustness, and cost are where architectures actually differ.
|
|
54
|
+
|
|
55
|
+
## Early results (2 pipelines, 16 samples, 2026-07-02)
|
|
56
|
+
|
|
57
|
+
| Pipeline | hit_rate@5 | faithfulness | tokens/query | retrieval p50 (ms) |
|
|
58
|
+
|---|---:|---:|---:|---:|
|
|
59
|
+
| `naive_rag` (dense) | 0.438 | 1.000 | 1666 | 40 |
|
|
60
|
+
| `hybrid_rag` (BM25 + dense, RRF) | **0.750** | 0.948 | 1555 | 47 |
|
|
61
|
+
|
|
62
|
+
**Read:** on 10-K filings — dense-heavy with exact figures, entity names, and legal terms — adding BM25 lexical matching via reciprocal rank fusion lifts hit_rate@5 from 0.44 to 0.75 for ~7 ms extra retrieval latency. Both pipelines stay highly faithful *to what they retrieve*; the naive pipeline's perfect faithfulness with poor retrieval illustrates why retrieval and generation metrics must be read together (a pipeline can faithfully report that the answer isn't in its badly-retrieved context).
|
|
63
|
+
|
|
64
|
+
## Honest limitations
|
|
65
|
+
|
|
66
|
+
- **Small sample (16).** Differences of a few points are noise at this size.
|
|
67
|
+
- **QA pairs are single-chunk and auto-generated.** Each question is grounded in exactly one chunk, and `hit_rate` counts only that chunk as relevant - a retrieved *duplicate* passage with the same fact counts as a miss. Some generated questions lean on filing boilerplate rather than substantive financials.
|
|
68
|
+
- **No judge validation yet.** Faithfulness scores come from an unvalidated LLM judge; the judge-vs-human agreement module is Phase 2.
|
|
69
|
+
- **Corpus text extraction is rough.** HTML tables flatten into text streams; some chunks are noisy.
|
|
70
|
+
- **10-K filings shift over time.** The fetch script pulls each company's *latest* 10-K, so exact numbers will drift as new filings land; re-generate the dataset after re-fetching.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{"question": "When was Ms. Hood appointed Executive Vice President and Chief Financial Officer of Microsoft?", "ground_truth_answer": "July 2013", "relevant_source_ids": ["MSFT_10K_0085"], "difficulty": "easy"}
|
|
2
|
+
{"question": "What type of breaches provides evidence of an external environment hostile to information security, according to MSFT's 10K filing?", "ground_truth_answer": "high-profile data breaches", "relevant_source_ids": ["MSFT_10K_0117"], "difficulty": "easy"}
|
|
3
|
+
{"question": "What international standard for information security management does NVDA follow?", "ground_truth_answer": "ISO 27001", "relevant_source_ids": ["NVDA_10K_0194"], "difficulty": "easy"}
|
|
4
|
+
{"question": "What percentage of NVIDIA's outstanding common stock would trigger Microsoft's first and last rights of refusal to purchase the stock?", "ground_truth_answer": "30%", "relevant_source_ids": ["NVDA_10K_0193"], "difficulty": "easy"}
|
|
5
|
+
{"question": "What was the balance of other long-term liabilities for NVDA as of January 25, 2026?", "ground_truth_answer": "$7,306", "relevant_source_ids": ["NVDA_10K_0314"], "difficulty": "easy"}
|
|
6
|
+
{"question": "What is the date listed for the signature of John O. Dabiri in the NVDA 10-K filing?", "ground_truth_answer": "February 25, 2026", "relevant_source_ids": ["NVDA_10K_0362"], "difficulty": "easy"}
|
|
7
|
+
{"question": "What is the date of the Officer\u2019s Certificate of the Registrant mentioned in 4.12?", "ground_truth_answer": "August 4, 2016", "relevant_source_ids": ["AAPL_10K_0211"], "difficulty": "easy"}
|
|
8
|
+
{"question": "In what year did the USG publish the AI Diffusion IFR in the Federal Register, according to NVDA's 10-K filing?", "ground_truth_answer": "2025", "relevant_source_ids": ["NVDA_10K_0160"], "difficulty": "easy"}
|
|
9
|
+
{"question": "What operating system do Microsoft's server software applications support?", "ground_truth_answer": "Windows Server", "relevant_source_ids": ["MSFT_10K_0056"], "difficulty": "easy"}
|
|
10
|
+
{"question": "What entity might NVIDIA need to notify regarding details of some of its Trustworthy AI processes?", "ground_truth_answer": "European Commission", "relevant_source_ids": ["NVDA_10K_0175"], "difficulty": "easy"}
|
|
11
|
+
{"question": "What was the effective tax rate of Apple as of September 27, 2025?", "ground_truth_answer": "15.6%", "relevant_source_ids": ["AAPL_10K_0171"], "difficulty": "easy"}
|
|
12
|
+
{"question": "What is the name of the AI-powered business and productivity solutions platform mentioned in the MSFT 10K excerpt?", "ground_truth_answer": "Microsoft 365 Commercial", "relevant_source_ids": ["MSFT_10K_0050"], "difficulty": "easy"}
|
|
13
|
+
{"question": "What department's list is mentioned in the NVDA 10K excerpt as having restricted parties?", "ground_truth_answer": "U.S. Department of Commerce", "relevant_source_ids": ["NVDA_10K_0149"], "difficulty": "easy"}
|
|
14
|
+
{"question": "What is the name of the independent registered public accounting firm that audited NVDA's internal control over financial reporting as of January 25, 2026?", "ground_truth_answer": "PricewaterhouseCoopers LLP", "relevant_source_ids": ["NVDA_10K_0244"], "difficulty": "easy"}
|
|
15
|
+
{"question": "What date was the Officer\u2019s Certificate of the Registrant issued that included forms of global notes representing the 0.875% Notes due 2025 and 1.375% Notes due 2029 for AAPL?", "ground_truth_answer": "May 24, 2017", "relevant_source_ids": ["AAPL_10K_0212"], "difficulty": "easy"}
|
|
16
|
+
{"question": "What method does AAPL use to recognize revenue for product-related bundled services?", "ground_truth_answer": "straight-line basis", "relevant_source_ids": ["AAPL_10K_0154"], "difficulty": "easy"}
|