debatebench 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.
- debatebench-0.1.0/.github/workflows/release.yml +107 -0
- debatebench-0.1.0/.github/workflows/tests.yml +44 -0
- debatebench-0.1.0/.gitignore +27 -0
- debatebench-0.1.0/.python-version +1 -0
- debatebench-0.1.0/ADR-001-orchestration-build-vs-reuse.md +126 -0
- debatebench-0.1.0/ADR-002-debatebench-architecture.md +356 -0
- debatebench-0.1.0/ADR-003-afm-backend-via-fm-serve.md +132 -0
- debatebench-0.1.0/ADR-004-test-framework.md +96 -0
- debatebench-0.1.0/ADR-005-transcript-format.md +156 -0
- debatebench-0.1.0/ADR-006-team-files-manual-for-now.md +59 -0
- debatebench-0.1.0/ADR-007-run-yaml-schema.md +229 -0
- debatebench-0.1.0/ADR-008-runtime-baseline.md +76 -0
- debatebench-0.1.0/ADR-009-backend-seam-types.md +106 -0
- debatebench-0.1.0/ADR-010-turn-rules.md +98 -0
- debatebench-0.1.0/ADR-011-response-length.md +112 -0
- debatebench-0.1.0/ADR-012-prep-retrieval.md +173 -0
- debatebench-0.1.0/ADR-013-judge-scoring.md +202 -0
- debatebench-0.1.0/ADR-014-prep-details.md +146 -0
- debatebench-0.1.0/ADR-015-fact-check-in-judge.md +223 -0
- debatebench-0.1.0/ADR-016-per-phase-length.md +131 -0
- debatebench-0.1.0/ADR-017-judge-cli-details.md +202 -0
- debatebench-0.1.0/ADR-018-ollama-for-real-model-runs.md +174 -0
- debatebench-0.1.0/ADR-019-contradiction-takes-precedence.md +146 -0
- debatebench-0.1.0/ADR-020-judge-settings-in-run-yaml.md +210 -0
- debatebench-0.1.0/ADR-021-debate-override-flags.md +121 -0
- debatebench-0.1.0/ADR-022-default-phase-length.md +137 -0
- debatebench-0.1.0/ADR-023-coin-toss-tiebreak.md +128 -0
- debatebench-0.1.0/ADR-024-the-factual-test-comes-first.md +158 -0
- debatebench-0.1.0/ADR-025-configurable-read-timeout.md +120 -0
- debatebench-0.1.0/ADR-026-a-malformed-reply-names-its-repair.md +130 -0
- debatebench-0.1.0/ADR-027-event-stream-on-stdout.md +158 -0
- debatebench-0.1.0/ADR-028-public-api.md +260 -0
- debatebench-0.1.0/ADR-029-real-pypi-release.md +167 -0
- debatebench-0.1.0/BACKEND-PROBE-RESULTS.md +482 -0
- debatebench-0.1.0/BACKEND-PROBE.md +93 -0
- debatebench-0.1.0/BUILD-GUIDE.md +425 -0
- debatebench-0.1.0/CLAUDE.md +453 -0
- debatebench-0.1.0/JUDGE-VALIDATION.md +400 -0
- debatebench-0.1.0/LICENSE +21 -0
- debatebench-0.1.0/MODEL-COVERAGE.md +246 -0
- debatebench-0.1.0/NOTICE +70 -0
- debatebench-0.1.0/OPEN-QUESTIONS.md +404 -0
- debatebench-0.1.0/PKG-INFO +525 -0
- debatebench-0.1.0/R0-RESULTS.md +507 -0
- debatebench-0.1.0/R0-repo-review-session.md +83 -0
- debatebench-0.1.0/README.md +502 -0
- debatebench-0.1.0/RESULTS.md +185 -0
- debatebench-0.1.0/SESSION-2026-09-15.md +111 -0
- debatebench-0.1.0/debate-formats-research.md +60 -0
- debatebench-0.1.0/examples/README.md +212 -0
- debatebench-0.1.0/examples/run-prep.yaml +51 -0
- debatebench-0.1.0/examples/run.yaml +90 -0
- debatebench-0.1.0/examples/teams/advocate.yaml +32 -0
- debatebench-0.1.0/examples/teams/neutral-corpus.jsonl +6 -0
- debatebench-0.1.0/examples/teams/neutral.yaml +14 -0
- debatebench-0.1.0/examples/teams/skeptic.yaml +9 -0
- debatebench-0.1.0/examples/teams/social-democrat-corpus.jsonl +6 -0
- debatebench-0.1.0/examples/teams/social-democrat.yaml +12 -0
- debatebench-0.1.0/examples/teams/tea-party-corpus.jsonl +6 -0
- debatebench-0.1.0/examples/teams/tea-party.yaml +8 -0
- debatebench-0.1.0/probe/b0/b0_probe.py +541 -0
- debatebench-0.1.0/probe/b0/b0_raw.json +3885 -0
- debatebench-0.1.0/probe/b0/download.log +16 -0
- debatebench-0.1.0/probe/b0/logs/afm.log +18 -0
- debatebench-0.1.0/probe/b0/logs/mistral-small-24b.log +3 -0
- debatebench-0.1.0/probe/b0/logs/qwen3-8b.log +67 -0
- debatebench-0.1.0/probe/b0/run.log +15 -0
- debatebench-0.1.0/probe/b0/summarize.py +95 -0
- debatebench-0.1.0/probe/b6/README.md +597 -0
- debatebench-0.1.0/probe/b6/baseline-a-d1.json +222 -0
- debatebench-0.1.0/probe/b6/baseline-a-d2.json +290 -0
- debatebench-0.1.0/probe/b6/baseline-a-d3.json +290 -0
- debatebench-0.1.0/probe/b6/baseline-a-d4.json +290 -0
- debatebench-0.1.0/probe/b6/baseline-a-d5.json +290 -0
- debatebench-0.1.0/probe/b6/condition-b-cold-1.json +202 -0
- debatebench-0.1.0/probe/b6/condition-b-cold-2.json +202 -0
- debatebench-0.1.0/probe/b6/condition-b-warm-1.json +270 -0
- debatebench-0.1.0/probe/b6/condition-b-warm-2.json +270 -0
- debatebench-0.1.0/probe/b6/condition-c-cold-1.json +247 -0
- debatebench-0.1.0/probe/b6/condition-c-cold-2.json +247 -0
- debatebench-0.1.0/probe/b6/condition-c-warm-1.json +158 -0
- debatebench-0.1.0/probe/b6/condition-c-warm-2.json +158 -0
- debatebench-0.1.0/probe/b6/gate-qwen3-8b-2026-09-14-repeat.json +290 -0
- debatebench-0.1.0/probe/b6/gate-qwen3-8b-2026-09-14-s3.json +290 -0
- debatebench-0.1.0/probe/b6/gate-qwen3-8b-2026-09-14-s4.json +290 -0
- debatebench-0.1.0/probe/b6/gate-qwen3-8b-2026-09-14-s5.json +290 -0
- debatebench-0.1.0/probe/b6/gate-qwen3-8b-2026-09-14-s6.json +290 -0
- debatebench-0.1.0/probe/b6/gate-qwen3-8b-2026-09-14-s7.json +290 -0
- debatebench-0.1.0/probe/b6/gate-qwen3-8b-2026-09-14.json +222 -0
- debatebench-0.1.0/probe/b6/ladder-qwen3-0_6b.json +102 -0
- debatebench-0.1.0/probe/b6/ladder-qwen3-14b.json +209 -0
- debatebench-0.1.0/probe/b6/ladder-qwen3-1_7b.json +97 -0
- debatebench-0.1.0/probe/b6/ladder-qwen3-4b.json +191 -0
- debatebench-0.1.0/probe/b6/ladder-qwen35-4b.json +233 -0
- debatebench-0.1.0/probe/b6/q14-cold-1.json +209 -0
- debatebench-0.1.0/probe/b6/q14-cold-2.json +209 -0
- debatebench-0.1.0/probe/b6/q14-warm-1.json +215 -0
- debatebench-0.1.0/probe/b6/q14-warm-2.json +215 -0
- debatebench-0.1.0/probe/b6/rejudge-2026-09-14-a.json +222 -0
- debatebench-0.1.0/probe/b6/rejudge-2026-09-14-b.json +290 -0
- debatebench-0.1.0/probe/b6/scores-2026-09-14-pre-adr-019.json +287 -0
- debatebench-0.1.0/probe/b6/statetest-cold.json +222 -0
- debatebench-0.1.0/probe/b6/statetest-warm.json +290 -0
- debatebench-0.1.0/probe/b6/transcript-2026-09-14-gate.json +246 -0
- debatebench-0.1.0/probe/backend/probe-a-a2recheck-mlx_lm.json +13 -0
- debatebench-0.1.0/probe/backend/probe-a-a2recheck-ollama.json +13 -0
- debatebench-0.1.0/probe/backend/probe-a-a2recheck-vllm-mlx.json +13 -0
- debatebench-0.1.0/probe/backend/probe-a-mlx_lm-budgeted.json +35 -0
- debatebench-0.1.0/probe/backend/probe-a-mlx_lm-rerun.json +34 -0
- debatebench-0.1.0/probe/backend/probe-a-mlx_lm.json +45 -0
- debatebench-0.1.0/probe/backend/probe-a-ollama-qwen3.json +73 -0
- debatebench-0.1.0/probe/backend/probe-a-ollama.json +67 -0
- debatebench-0.1.0/probe/backend/probe-a-vllm-mlx-a8.json +12 -0
- debatebench-0.1.0/probe/backend/probe-a-vllm-mlx-a9.json +16 -0
- debatebench-0.1.0/probe/backend/probe-a-vllm-mlx.json +64 -0
- debatebench-0.1.0/probe/backend/probe-b-mlx_lm.json +47 -0
- debatebench-0.1.0/probe/backend/probe-b-ollama.json +42 -0
- debatebench-0.1.0/probe/backend/probe-b-vllm-mlx.json +47 -0
- debatebench-0.1.0/probe/backend/probe-b5-mlx_lm.json +37 -0
- debatebench-0.1.0/probe/backend/probe_a.py +224 -0
- debatebench-0.1.0/probe/backend/probe_b.py +198 -0
- debatebench-0.1.0/probe/backend/probe_b5.py +148 -0
- debatebench-0.1.0/probe/mirror/README.md +83 -0
- debatebench-0.1.0/probe/mirror/mirror-1-scores.json +104 -0
- debatebench-0.1.0/probe/mirror/mirror-1-transcript.json +199 -0
- debatebench-0.1.0/probe/mirror/mirror-10-scores.json +92 -0
- debatebench-0.1.0/probe/mirror/mirror-10-transcript.json +199 -0
- debatebench-0.1.0/probe/mirror/mirror-11-scores.json +104 -0
- debatebench-0.1.0/probe/mirror/mirror-11-transcript.json +199 -0
- debatebench-0.1.0/probe/mirror/mirror-2-scores.INVALID-stale-copy.json +104 -0
- debatebench-0.1.0/probe/mirror/mirror-2-transcript.json +199 -0
- debatebench-0.1.0/probe/mirror/mirror-3-scores.json +100 -0
- debatebench-0.1.0/probe/mirror/mirror-3-transcript.json +199 -0
- debatebench-0.1.0/probe/mirror/mirror-4-scores.json +100 -0
- debatebench-0.1.0/probe/mirror/mirror-4-transcript.json +199 -0
- debatebench-0.1.0/probe/mirror/mirror-5-scores.json +104 -0
- debatebench-0.1.0/probe/mirror/mirror-5-transcript.json +199 -0
- debatebench-0.1.0/probe/mirror/mirror-6-scores.INVALID-stale-copy.json +104 -0
- debatebench-0.1.0/probe/mirror/mirror-6-transcript.INVALID-stale-copy.json +199 -0
- debatebench-0.1.0/probe/mirror/mirror-7-scores.json +100 -0
- debatebench-0.1.0/probe/mirror/mirror-7-transcript.json +199 -0
- debatebench-0.1.0/probe/mirror/mirror-8-scores.json +96 -0
- debatebench-0.1.0/probe/mirror/mirror-8-transcript.json +199 -0
- debatebench-0.1.0/probe/mirror/mirror-9-scores.json +100 -0
- debatebench-0.1.0/probe/mirror/mirror-9-transcript.json +199 -0
- debatebench-0.1.0/probe/scale/weak-debater.yaml +22 -0
- debatebench-0.1.0/probe/scale/weak-run.yaml +31 -0
- debatebench-0.1.0/probe/scale/weak-scores.json +104 -0
- debatebench-0.1.0/probe/scale/weak-transcript.json +163 -0
- debatebench-0.1.0/probe/validation/human_ceiling.py +91 -0
- debatebench-0.1.0/probe/validation/judge-scores-qwen3-8b-full.json +5059 -0
- debatebench-0.1.0/probe/validation/judge-scores-qwen3-8b-subset.json +947 -0
- debatebench-0.1.0/probe/validation/judge-scores-timing.json +83 -0
- debatebench-0.1.0/probe/validation/run_judge.py +187 -0
- debatebench-0.1.0/probe/validation/score_agreement.py +110 -0
- debatebench-0.1.0/pyproject.toml +45 -0
- debatebench-0.1.0/src/debatebench/__init__.py +5 -0
- debatebench-0.1.0/src/debatebench/api.py +242 -0
- debatebench-0.1.0/src/debatebench/backend.py +56 -0
- debatebench-0.1.0/src/debatebench/cli.py +198 -0
- debatebench-0.1.0/src/debatebench/config.py +500 -0
- debatebench-0.1.0/src/debatebench/event_stream.py +116 -0
- debatebench-0.1.0/src/debatebench/events.py +59 -0
- debatebench-0.1.0/src/debatebench/judge_cli.py +200 -0
- debatebench-0.1.0/src/debatebench/judging.py +726 -0
- debatebench-0.1.0/src/debatebench/openai_compat.py +151 -0
- debatebench-0.1.0/src/debatebench/orchestrator.py +161 -0
- debatebench-0.1.0/src/debatebench/prompts.py +129 -0
- debatebench-0.1.0/src/debatebench/retrieval.py +171 -0
- debatebench-0.1.0/src/debatebench/transcript.py +321 -0
- debatebench-0.1.0/src/debatebench/yaml_loader.py +80 -0
- debatebench-0.1.0/tests/conftest.py +26 -0
- debatebench-0.1.0/tests/fakes.py +59 -0
- debatebench-0.1.0/tests/fixtures/run.yaml +20 -0
- debatebench-0.1.0/tests/fixtures/sources/args-me.jsonl +9 -0
- debatebench-0.1.0/tests/fixtures/sources/debatesum.jsonl +3 -0
- debatebench-0.1.0/tests/fixtures/teams/conservative.yaml +5 -0
- debatebench-0.1.0/tests/fixtures/teams/liberal-climate-corpus.jsonl +3 -0
- debatebench-0.1.0/tests/fixtures/teams/liberal.yaml +6 -0
- debatebench-0.1.0/tests/helpers.py +21 -0
- debatebench-0.1.0/tests/test_api.py +235 -0
- debatebench-0.1.0/tests/test_cli.py +149 -0
- debatebench-0.1.0/tests/test_config.py +343 -0
- debatebench-0.1.0/tests/test_event_stream.py +166 -0
- debatebench-0.1.0/tests/test_fact_check.py +250 -0
- debatebench-0.1.0/tests/test_judge_cli.py +302 -0
- debatebench-0.1.0/tests/test_judging.py +452 -0
- debatebench-0.1.0/tests/test_layering.py +36 -0
- debatebench-0.1.0/tests/test_length.py +195 -0
- debatebench-0.1.0/tests/test_live_afm.py +239 -0
- debatebench-0.1.0/tests/test_live_judge.py +238 -0
- debatebench-0.1.0/tests/test_openai_compat.py +153 -0
- debatebench-0.1.0/tests/test_orchestrator.py +273 -0
- debatebench-0.1.0/tests/test_output.py +147 -0
- debatebench-0.1.0/tests/test_packaging.py +93 -0
- debatebench-0.1.0/tests/test_prep.py +310 -0
- debatebench-0.1.0/tests/test_prompts.py +68 -0
- debatebench-0.1.0/tests/test_timeout.py +68 -0
- debatebench-0.1.0/uv.lock +219 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# ADR-029: publish to PyPI with no PyPI credential anywhere. The job proves it
|
|
2
|
+
# is this repository to PyPI over OIDC, so there is no token to store, rotate,
|
|
3
|
+
# leak or revoke — which is the specific failure this project has already had.
|
|
4
|
+
#
|
|
5
|
+
# Before the first run, the account owner must create a *pending publisher* at
|
|
6
|
+
# https://pypi.org/manage/account/publishing/ binding:
|
|
7
|
+
# project debatebench
|
|
8
|
+
# owner robert-e-roy
|
|
9
|
+
# repository debatebench
|
|
10
|
+
# workflow release.yml
|
|
11
|
+
# environment pypi
|
|
12
|
+
# Until that exists the upload step fails to authenticate, which is the intended
|
|
13
|
+
# failure mode: nothing can publish by accident.
|
|
14
|
+
name: release
|
|
15
|
+
|
|
16
|
+
# A *published GitHub Release*, not a tag push (ADR-029 §3): pushing a tag by
|
|
17
|
+
# mistake must not publish anything, and a version on PyPI cannot be replaced.
|
|
18
|
+
# No workflow_dispatch: a manual run would publish from whatever main happens to
|
|
19
|
+
# be, which is the same accident §3 rules out for a stray tag push, by another
|
|
20
|
+
# route. Re-running a failed release means a new version, because PyPI will not
|
|
21
|
+
# take 0.1.0 twice however the first attempt went.
|
|
22
|
+
on:
|
|
23
|
+
release:
|
|
24
|
+
types: [published]
|
|
25
|
+
|
|
26
|
+
permissions:
|
|
27
|
+
contents: read
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
build:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v7
|
|
34
|
+
# Pinned exactly: setup-uv publishes no floating major tag (see tests.yml).
|
|
35
|
+
- uses: astral-sh/setup-uv@v10.1.0
|
|
36
|
+
with:
|
|
37
|
+
python-version: "3.11"
|
|
38
|
+
|
|
39
|
+
# The suite runs here too. The release artifact and the tested tree are
|
|
40
|
+
# then the same checkout, rather than two things assumed to match.
|
|
41
|
+
- run: uv sync --locked
|
|
42
|
+
- run: uv run --locked pytest -q
|
|
43
|
+
|
|
44
|
+
- name: Build the wheel and sdist from the tagged commit
|
|
45
|
+
run: uv build
|
|
46
|
+
|
|
47
|
+
- name: Refuse metadata PyPI would reject
|
|
48
|
+
run: uvx twine check dist/*
|
|
49
|
+
|
|
50
|
+
- uses: actions/upload-artifact@v7
|
|
51
|
+
with:
|
|
52
|
+
name: dist
|
|
53
|
+
path: dist/
|
|
54
|
+
|
|
55
|
+
publish:
|
|
56
|
+
needs: build
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
# Named so the trusted-publisher binding can require it, and so a reviewer
|
|
59
|
+
# can be added later without changing this file.
|
|
60
|
+
environment:
|
|
61
|
+
name: pypi
|
|
62
|
+
url: https://pypi.org/p/debatebench
|
|
63
|
+
permissions:
|
|
64
|
+
id-token: write # the OIDC token; this is the whole authentication story
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/download-artifact@v8
|
|
67
|
+
with:
|
|
68
|
+
name: dist
|
|
69
|
+
path: dist/
|
|
70
|
+
|
|
71
|
+
- uses: pypa/gh-action-pypi-publish@v1.14.2
|
|
72
|
+
|
|
73
|
+
# twine check validates metadata, not that the thing installs and runs. B7's
|
|
74
|
+
# gate clause was "install from an index and run", and that is what proved
|
|
75
|
+
# determinism across a packaging boundary — so the release repeats it against
|
|
76
|
+
# real PyPI. This cannot prevent a bad upload; it tells us within a minute
|
|
77
|
+
# whether to yank, which is the only remedy ADR-029 §6 leaves.
|
|
78
|
+
verify:
|
|
79
|
+
needs: publish
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
steps:
|
|
82
|
+
- uses: actions/checkout@v7
|
|
83
|
+
- uses: actions/setup-python@v6
|
|
84
|
+
with:
|
|
85
|
+
python-version: "3.11"
|
|
86
|
+
|
|
87
|
+
- name: Install from real PyPI into a venv that has never seen the source
|
|
88
|
+
run: |
|
|
89
|
+
version="$(python -c 'import tomllib;print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')"
|
|
90
|
+
echo "waiting for debatebench==$version to be installable"
|
|
91
|
+
cd "$(mktemp -d)" # out of the checkout, so nothing resolves locally
|
|
92
|
+
python -m venv venv
|
|
93
|
+
# PyPI's index can lag the upload by under a minute; retry rather than
|
|
94
|
+
# fail a release on CDN timing.
|
|
95
|
+
for attempt in 1 2 3 4 5 6 7 8 9 10; do
|
|
96
|
+
if ./venv/bin/pip install --no-cache-dir "debatebench==$version"; then
|
|
97
|
+
installed=yes; break
|
|
98
|
+
fi
|
|
99
|
+
echo "attempt $attempt failed; retrying in 15s"
|
|
100
|
+
sleep 15
|
|
101
|
+
done
|
|
102
|
+
[ "${installed:-no}" = yes ] || { echo "::error::debatebench==$version never became installable"; exit 1; }
|
|
103
|
+
|
|
104
|
+
./venv/bin/debate --help > /dev/null
|
|
105
|
+
./venv/bin/judge --help > /dev/null
|
|
106
|
+
./venv/bin/python -c "from debatebench.api import debate, judge, load_run; from debatebench.transcript import package_version; print('installed version:', package_version())"
|
|
107
|
+
echo "debatebench==$version installs from PyPI and both commands run"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# CLAUDE.md said "CI is deferred to B7" from B0 until 2026-09-16. B7 finished on
|
|
2
|
+
# 2026-09-14 without a workflow because there was no remote to run one; the repo
|
|
3
|
+
# now exists, so this closes it.
|
|
4
|
+
name: tests
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
pull_request:
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
pytest:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
strategy:
|
|
19
|
+
fail-fast: false
|
|
20
|
+
matrix:
|
|
21
|
+
# pyproject declares >=3.11 and classifies 3.11 and 3.12. Both run.
|
|
22
|
+
python-version: ["3.11", "3.12"]
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v7
|
|
26
|
+
|
|
27
|
+
# Pinned exactly: astral-sh/setup-uv publishes no floating `v10` tag —
|
|
28
|
+
# only v10.0.0, v10.0.1, v10.1.0 — so `@v10` fails to resolve at all.
|
|
29
|
+
# actions/checkout does publish `v7`, hence the difference above.
|
|
30
|
+
- uses: astral-sh/setup-uv@v10.1.0
|
|
31
|
+
with:
|
|
32
|
+
python-version: ${{ matrix.python-version }}
|
|
33
|
+
enable-cache: true
|
|
34
|
+
|
|
35
|
+
# --locked, not --frozen: a uv.lock that has drifted from pyproject should
|
|
36
|
+
# fail here rather than be silently re-resolved into something untested.
|
|
37
|
+
- run: uv sync --locked
|
|
38
|
+
|
|
39
|
+
# DEBATEBENCH_LIVE_TESTS is deliberately unset, so every test runs against
|
|
40
|
+
# the scripted FakeBackend (ADR-004) and the live-model tests skip. CI has
|
|
41
|
+
# no Ollama, no AFM and no GPU, and a suite that needed one would be a
|
|
42
|
+
# suite nobody could run.
|
|
43
|
+
- name: pytest (fake backend only; live-model tests skip)
|
|
44
|
+
run: uv run --locked pytest -q
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
.venv/
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
|
|
7
|
+
# Build artifacts (`uv build` writes here). Ignored by a global excludes file on
|
|
8
|
+
# the machine this was developed on, which does not survive a clone.
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
|
|
13
|
+
# Editors / macOS
|
|
14
|
+
*.swp
|
|
15
|
+
.DS_Store
|
|
16
|
+
|
|
17
|
+
# Run artifacts. The README tells people to run `debate examples/run.yaml`,
|
|
18
|
+
# whose `output:` resolves next to the run.yaml — so a transcript, its rotated
|
|
19
|
+
# `.1` backup, and the score file all land inside examples/ on a plain clone.
|
|
20
|
+
examples/transcript.json
|
|
21
|
+
examples/transcript.json.1
|
|
22
|
+
examples/scores.json
|
|
23
|
+
examples/scores.json.1
|
|
24
|
+
examples/transcript-prep.json
|
|
25
|
+
examples/transcript-prep.json.1
|
|
26
|
+
examples/scores-prep.json
|
|
27
|
+
examples/scores-prep.json.1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# ADR-001: Debate Orchestration Layer — Build vs. Reuse
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-09-10
|
|
5
|
+
**Amended:** 2026-09-11 — rationale wording and cross-references corrected against
|
|
6
|
+
the R0 results; the decision itself is unchanged.
|
|
7
|
+
**Context session:** R0 repo review (aragora-debate, arbgjr/multi-agent-debate,
|
|
8
|
+
paolodalprato/agent-discussion-arena, rd-serendipity/ai-debate-arena). The brief and
|
|
9
|
+
full results are in this repo, as `R0-repo-review-session.md` and `R0-RESULTS.md`
|
|
10
|
+
(moved from `~/Projects/DebateKit/` on 2026-09-11); scores cited below come from
|
|
11
|
+
the latter.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
Build our own orchestration layer from scratch (~400–500 lines, zero required
|
|
16
|
+
dependencies). Do not adopt or fork any of the four reviewed projects. Lift three
|
|
17
|
+
narrow, attributed pieces; take three design ideas; design against four observed
|
|
18
|
+
anti-patterns.
|
|
19
|
+
|
|
20
|
+
"Zero required dependencies" applies to the orchestration layer itself — the phase
|
|
21
|
+
loop, event dispatch and backend `Protocol` that R0 sized at ~400–500 lines. Config
|
|
22
|
+
parsing sits outside that layer and can't be stdlib-only (ADR-002's config is YAML,
|
|
23
|
+
and Python's standard library has no YAML parser). The HTTP adapter can stay
|
|
24
|
+
stdlib-only (`urllib` under `asyncio.to_thread`, which anti-pattern 4 below permits)
|
|
25
|
+
or take an async client; ADR-003 leaves that open.
|
|
26
|
+
|
|
27
|
+
## Why
|
|
28
|
+
|
|
29
|
+
None of the four reviewed projects fits, and not for one shared reason. Two
|
|
30
|
+
(`aragora-debate`, `arbgjr/multi-agent-debate`) are built to make agents converge
|
|
31
|
+
toward agreement, and in both that objective is hardcoded into the turn loop
|
|
32
|
+
itself, not exposed as a setting (R0 consensus-separation scores 2/5 and 1/5).
|
|
33
|
+
`rd-serendipity/ai-debate-arena` has no consensus logic at all (5/5) but engineering
|
|
34
|
+
we can't build on. `agent-discussion-arena` has no importable orchestration code at
|
|
35
|
+
all. Our requirement — sustained, non-converging adversarial debate with per-side
|
|
36
|
+
model/budget asymmetry — is not a configuration difference from consensus-seeking;
|
|
37
|
+
it's the opposite objective.
|
|
38
|
+
|
|
39
|
+
The clearest single data point: the project with the best engineering
|
|
40
|
+
(`aragora-debate` — clean `Agent` ABC, excellent standalone event system, verified
|
|
41
|
+
MIT license) has the worst fit on the one axis that matters most, separation from
|
|
42
|
+
consensus machinery (2/5). The project with the best fit on that axis
|
|
43
|
+
(`rd-serendipity`, true 1v1, no consensus logic anywhere) has the worst engineering
|
|
44
|
+
— LangChain-bound to September 2024 pins, fully synchronous, dead two years, no
|
|
45
|
+
tests. Nothing scored well on both. That is the whole decision.
|
|
46
|
+
|
|
47
|
+
## What we take (with attribution — all four repos are MIT)
|
|
48
|
+
|
|
49
|
+
| From | What | Form |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `aragora-debate/events.py` | Typed `EventType` enum + `DebateEvent` dataclass, sync+async dispatch, per-callback exception isolation | Design lift, ~120 lines, reimplemented |
|
|
52
|
+
| `arbgjr/multi-agent-debate` `protocols.py` | Single-method `async generate(...)` `Protocol` as the LLM backend seam (not a multi-method ABC) | Design lift, ~20 lines |
|
|
53
|
+
| `aragora-debate/evidence.py` | Stdlib-only, LLM-free, five separately-scored dimensions (citation density, specificity, evidence diversity, temporal relevance, logical chain) | Conditional lift — see the fact-checker bullet under "Decisions this locks in" on naming (R0 flag #5) |
|
|
54
|
+
| `paolodalprato/agent-discussion-arena` | Phase model: `Opening → Round 1..N → Verdict`, verdict as a distinct terminal phase, not a vote folded into the loop | Idea only, no code |
|
|
55
|
+
| `paolodalprato/agent-discussion-arena` | One `openai-compatible` adapter with configurable `base_url` serves MLX (`mlx_lm.server`), Ollama, LM Studio, and OpenAI itself | Idea only, no code |
|
|
56
|
+
| `rd-serendipity/ai-debate-arena` | Alternating initiative — swap who opens each round so neither side keeps first-mover advantage | Idea only, ~2 lines when implemented |
|
|
57
|
+
|
|
58
|
+
## Anti-patterns to design against (each observed as a real bug in a reviewed repo)
|
|
59
|
+
|
|
60
|
+
1. **Never let a phase failure be silent.** Aragora's `_run_propose`/`_run_vote` use
|
|
61
|
+
`return_exceptions=True` and `continue` past a failed side with only a log
|
|
62
|
+
warning; `_pick_winner` then defaults to `agents[0]`, so a one-sided debate
|
|
63
|
+
still emits a confident-looking result. **Our rule: every phase must produce a
|
|
64
|
+
response from every configured side, or the run hard-fails and `--output` is
|
|
65
|
+
not written.**
|
|
66
|
+
2. **Never key transcript state by side alone.** Aragora's `_proposals` dict is
|
|
67
|
+
overwritten each round, so `result.proposals` only ever holds the final round.
|
|
68
|
+
**Our rule: key all transcript state by `(round, side)`.**
|
|
69
|
+
3. **Never blend judge dimensions into one number, and never return 0 on a parse
|
|
70
|
+
failure.** rd-serendipity's judge asks for a single 0–100 score blending
|
|
71
|
+
relevance, coherence, and fact-check, parsed by `RegexParser(r"(\d+)")`, which
|
|
72
|
+
grabs the *first* integer anywhere in the output — a reply starting "Criteria
|
|
73
|
+
1..." scores 1 — and returns 0 on any parse failure, indistinguishable from a
|
|
74
|
+
genuinely terrible argument. **Our rule: score rubric dimensions separately;
|
|
75
|
+
treat a parse failure as an error, never a score.**
|
|
76
|
+
4. **Never call a synchronous SDK client inside `async def`.** All four of
|
|
77
|
+
Aragora's provider agents construct sync clients (`anthropic.Anthropic`,
|
|
78
|
+
`openai.OpenAI`, etc.) and call them directly inside `async def` with no
|
|
79
|
+
`await` and no `asyncio.to_thread` — so `asyncio.gather` across agents
|
|
80
|
+
delivers zero real concurrency and blocks the event loop. For a tool also
|
|
81
|
+
driving a live dashboard, this would stall the UI. **Our rule: use a real
|
|
82
|
+
async client, or wrap sync calls in `asyncio.to_thread`.**
|
|
83
|
+
|
|
84
|
+
## Decisions this locks in (changes/sharpens the prior plan)
|
|
85
|
+
|
|
86
|
+
- **Two commands (`debate` / `judge`) stays as designed.** Aragora's fusion of
|
|
87
|
+
judging into the debate loop is exactly what forces its wasted vote-phase calls.
|
|
88
|
+
No reviewed project argues for merging the two.
|
|
89
|
+
- **`debate` writes to `--output` file only stays as designed.** No conflicts
|
|
90
|
+
found. Aragora's `DebateResult.to_dict()` + `DecisionReceipt.to_markdown()` is a
|
|
91
|
+
reasonable precedent for pairing a machine-readable and human-readable view of
|
|
92
|
+
one run in the output schema.
|
|
93
|
+
- **Phases must be data, not control flow.** Both Aragora and arbgjr hardcode
|
|
94
|
+
their phase sequence as literal branches in the loop body — Aragora even has a
|
|
95
|
+
`Phase` enum its own loop ignores, with two dead members. **Our loop iterates a
|
|
96
|
+
list of phases; it does not branch on round number.**
|
|
97
|
+
- **Per-side budgets are enforced by the orchestrator, per phase — not by each
|
|
98
|
+
backend's constructor.** Aragora's `max_tokens`/`temperature` are genuine
|
|
99
|
+
per-agent constructor args, but every provider's `vote()` hardcodes its own
|
|
100
|
+
512-token, 0.3-temperature budget regardless of what was configured, so the
|
|
101
|
+
configured budget silently doesn't hold across every phase. For a benchmarking
|
|
102
|
+
tool, an unenforced budget claim is worse than no budget claim.
|
|
103
|
+
- **Fast fact-checker vs. slow end-of-debate judge split is vindicated.** No
|
|
104
|
+
reviewed project implements this separation. Aragora's `evidence.py` is the
|
|
105
|
+
nearest analogue but is pure regex over surface form (citation patterns, data
|
|
106
|
+
patterns, vague-vs-specific phrasing) with zero LLM calls and zero verification
|
|
107
|
+
of truth — a confidently fabricated citation with a recent year and a
|
|
108
|
+
percentage scores *high*. **If adopted, it is labeled "evidence hygiene," not
|
|
109
|
+
"fact-check," and sits alongside a real fact-checker, never in place of one.**
|
|
110
|
+
- **New invariant, not previously in the plan:** every phase must produce a
|
|
111
|
+
response from every configured side, or the run aborts and no output file is
|
|
112
|
+
written. (See anti-pattern 1.)
|
|
113
|
+
|
|
114
|
+
## Consequences
|
|
115
|
+
|
|
116
|
+
- We own and maintain the full orchestration loop (~400–500 lines). No external
|
|
117
|
+
dependency risk from a project whose roadmap has nothing to do with ours.
|
|
118
|
+
- We do not get Aragora's four cloud-provider integrations for free, but we didn't
|
|
119
|
+
want them as the primary path anyway (MLX-first).
|
|
120
|
+
- Building the `Agent`/backend seam as a single-method protocol (per arbgjr's
|
|
121
|
+
shape) keeps an MLX implementation to one method rather than three, avoiding the
|
|
122
|
+
work an Aragora-based `Agent` subclass would require (structured `Critique`/
|
|
123
|
+
`Vote` parsing out of a local model).
|
|
124
|
+
- Attribution required in our source/NOTICE file for the two design lifts
|
|
125
|
+
(`events.py` shape, `LLMProviderProtocol` shape) and the conditional
|
|
126
|
+
`evidence.py`-derived evidence-hygiene scorer, per each project's MIT license.
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# ADR-002: DebateBench — Project Scope and Architecture
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-09-11
|
|
5
|
+
**Supersedes:** nothing. **Depends on:** ADR-001 (orchestration build vs. reuse)
|
|
6
|
+
**Amended:** 2026-09-11 — cross-references, dataset facts and wording corrected. B0's
|
|
7
|
+
hardware findings added to "Hardware", withdrawing the `free` offload option. An
|
|
8
|
+
offline requirement for model servers added to "Backend abstraction".
|
|
9
|
+
|
|
10
|
+
## What this is
|
|
11
|
+
|
|
12
|
+
`debatebench` is an open-source Python CLI for running structured, multi-turn,
|
|
13
|
+
adversarial LLM debates and scoring them against a fixed rubric, with a
|
|
14
|
+
post-hoc fact-check pass inside `judge` (ADR-015; real-time per-turn
|
|
15
|
+
fact-checking is the Swift app's feature, not this repo's). It generalizes into a broader pattern (see the MetaTool notes,
|
|
16
|
+
`~/Projects/metatool.md`, outside this repo) but this ADR scopes the concrete first
|
|
17
|
+
implementation.
|
|
18
|
+
|
|
19
|
+
A Swift port, `DebateKit`, is a later, separate effort once this design stops
|
|
20
|
+
changing session to session (see "Language split" below). Not started yet.
|
|
21
|
+
|
|
22
|
+
## Why it exists
|
|
23
|
+
|
|
24
|
+
Built to evaluate MLX model + grounding-dataset combinations for a debate-format
|
|
25
|
+
Mac app, but scoped from the start to be generally useful for comparing LLMs on
|
|
26
|
+
structured, verifiable, multi-turn tasks — not tied to the app.
|
|
27
|
+
|
|
28
|
+
## Language split: Python first, Swift later
|
|
29
|
+
|
|
30
|
+
- **Python** owns everything exploratory: model/dataset evaluation, retrieval,
|
|
31
|
+
rubric iteration, fact-checking logic. Mature ML tooling (`mlx-lm`, `datasets`,
|
|
32
|
+
dataset ecosystem) makes iteration fast here.
|
|
33
|
+
- **Swift** (`DebateKit`, future) owns the shipped product: privacy-first,
|
|
34
|
+
on-device, no bundled Python runtime, consistent with the rest of the
|
|
35
|
+
HomesteadAI portfolio. Ported only once the CLI's design has stabilized —
|
|
36
|
+
porting a moving target wastes the port.
|
|
37
|
+
- Publish both source repos on GitHub. Python side is a genuine standalone
|
|
38
|
+
open-source tool (MIT). Swift side's license is a separate, later decision —
|
|
39
|
+
likely source-available/non-commercial rather than MIT, since its purpose is
|
|
40
|
+
auditability and trust for the paid app, not general reuse.
|
|
41
|
+
|
|
42
|
+
## Naming
|
|
43
|
+
|
|
44
|
+
- Python package: `debatebench` (PyPI name confirmed unclaimed as of this
|
|
45
|
+
session). `bench` was chosen over `-cli` because it correctly signals
|
|
46
|
+
"measures and compares models," matching existing conventions in this space
|
|
47
|
+
(`lm-eval-harness`, similar `*-eval`/`*-bench` tools).
|
|
48
|
+
- Swift library (future): `DebateKit`, consistent with existing `Kit`-suffixed
|
|
49
|
+
libraries (CorpusKit, JsonHelpKit).
|
|
50
|
+
- Neither collides with any of the ten-plus adjacent "debate"-named projects
|
|
51
|
+
checked this session (`debate`, `aragora-debate`, `arbiter-debate`,
|
|
52
|
+
`ai-debate-chat`, `multi-model-debate`, `autodebater`, `debating-machine`,
|
|
53
|
+
and the four in the R0 review).
|
|
54
|
+
|
|
55
|
+
## CLI shape: two commands, not one
|
|
56
|
+
|
|
57
|
+
- **`debate`** — generates a transcript. Writes structured JSON to an explicit
|
|
58
|
+
output file only — the required `output:` path in `run.yaml`, since `debate`
|
|
59
|
+
takes no flags (ADR-007 "CLI invocation"). All logging/errors go to stderr, never to the
|
|
60
|
+
output file — the file must always be valid, parseable JSON, never relying on
|
|
61
|
+
shell redirection to keep it clean.
|
|
62
|
+
- **`judge`** — scores a transcript (from a file, not a pipe requirement) against
|
|
63
|
+
the rubric plus fact-check. Kept separate from `debate` deliberately: lets the
|
|
64
|
+
same transcript be re-judged with a different judge model, or re-scored after
|
|
65
|
+
a rubric change, without regenerating the debate. (Aragora's fused
|
|
66
|
+
debate+judge loop is a cautionary example — it burns a mandatory vote-phase
|
|
67
|
+
call every round even when unwanted, precisely because judging isn't
|
|
68
|
+
separable from generation in its design.)
|
|
69
|
+
|
|
70
|
+
## Config: two file types, different lifespans
|
|
71
|
+
|
|
72
|
+
**`run.yaml`** — one per debate run, ephemeral (schema finalized in ADR-007):
|
|
73
|
+
```yaml
|
|
74
|
+
topic: "..."
|
|
75
|
+
format:
|
|
76
|
+
phases: [prep, opening:short, rebuttal:long, retort:medium, rebuttal:long, conclusion:short]
|
|
77
|
+
teams:
|
|
78
|
+
- team: teams/liberal.yaml
|
|
79
|
+
side: pro
|
|
80
|
+
model: qwen3-8b
|
|
81
|
+
base_url: http://127.0.0.1:8080/v1
|
|
82
|
+
budget: 2000
|
|
83
|
+
prep_budget: 1500
|
|
84
|
+
- team: teams/conservative.yaml
|
|
85
|
+
side: con
|
|
86
|
+
model: mistral-small
|
|
87
|
+
base_url: http://127.0.0.1:8081/v1
|
|
88
|
+
budget: 2000
|
|
89
|
+
prep_budget: 1500
|
|
90
|
+
sources: [args-me, debatesum]
|
|
91
|
+
seed: 42
|
|
92
|
+
output: transcript.json
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**`teams/*.yaml`** — durable, reusable, hand-authored for now (see ADR-006 —
|
|
96
|
+
the earlier "PersonaForge-compatible, zero conversion step" claim here was
|
|
97
|
+
checked against PersonaKit's actual schema and found false; aligning the two
|
|
98
|
+
is real future work, not started):
|
|
99
|
+
```yaml
|
|
100
|
+
id: liberal-climate
|
|
101
|
+
name: "Progressive Climate Advocate"
|
|
102
|
+
voice: "direct, urgency-driven, cites institutional consensus"
|
|
103
|
+
stance: liberal
|
|
104
|
+
corpus: liberal-climate-corpus.jsonl
|
|
105
|
+
values: [collective-action, precaution, equity]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`corpus` is a JSONL file resolved relative to the team file's own directory,
|
|
109
|
+
not `run.yaml`'s — see ADR-014 §1 for the row format and why.
|
|
110
|
+
|
|
111
|
+
**Deliberately no `model` field in team files.** A team file defines identity
|
|
112
|
+
(voice, corpus, stance, values), not runtime execution. Keeping `model`/`budget`
|
|
113
|
+
in `run.yaml` as per-side overrides means the same team can debate under
|
|
114
|
+
different hardware/model constraints across different runs without editing its
|
|
115
|
+
identity file — that reasoning holds regardless of PersonaForge (see ADR-006).
|
|
116
|
+
|
|
117
|
+
## Phase structure
|
|
118
|
+
|
|
119
|
+
Phases are **data, not control flow** — the orchestration loop iterates a
|
|
120
|
+
configured phase list; it does not branch on round number in the loop body (both
|
|
121
|
+
`aragora-debate` and `arbgjr/multi-agent-debate` hardcode their phase sequence as
|
|
122
|
+
control flow — see ADR-001).
|
|
123
|
+
|
|
124
|
+
Default phase list: `[prep(optional), opening, rebuttal, retort, rebuttal,
|
|
125
|
+
conclusion]`. `prep` is a bounded-budget research phase — analogous to real
|
|
126
|
+
debate prep time — that produces a recorded, per-side evidence set *before* any
|
|
127
|
+
argument is written. This sharpens fact-checking: a claim is checked against what
|
|
128
|
+
a side actually gathered during its own prep, not an open-ended post-hoc search,
|
|
129
|
+
making "cited something you never had" a distinct, catchable failure mode from
|
|
130
|
+
"cited something real but misrepresented it."
|
|
131
|
+
|
|
132
|
+
Initiative (who goes first each round) alternates by design, so neither side
|
|
133
|
+
keeps a first-mover advantage across the whole debate.
|
|
134
|
+
|
|
135
|
+
## Asymmetry is a first-class feature, not an edge case
|
|
136
|
+
|
|
137
|
+
Different teams may run different models under different reasoning/token budgets
|
|
138
|
+
(chess-clock analogy). This is core to the product's value, not a config nicety
|
|
139
|
+
— it's what makes "does a bigger, slower model beat a faster, leaner one" a real,
|
|
140
|
+
answerable, watchable question. Budgets are **enforced by the orchestrator, per
|
|
141
|
+
phase** — never left to a backend's own constructor defaults, which is exactly
|
|
142
|
+
how Aragora's budget claims silently failed to hold across every phase (see
|
|
143
|
+
ADR-001).
|
|
144
|
+
|
|
145
|
+
## Backend abstraction
|
|
146
|
+
|
|
147
|
+
Single-method `async` `Protocol` as the LLM seam (lifted design from
|
|
148
|
+
`arbgjr/multi-agent-debate`), not a multi-method ABC. One `openai-compatible`
|
|
149
|
+
adapter with a configurable `base_url` (idea from `agent-discussion-arena`)
|
|
150
|
+
covers MLX (`mlx_lm.server`), Ollama, LM Studio, and OpenAI itself through one
|
|
151
|
+
code path.
|
|
152
|
+
|
|
153
|
+
**Dev default model is Apple Foundation Models (AFM)**, or another very small
|
|
154
|
+
model — instant, free, no MLX load overhead, no memory pressure. Used while
|
|
155
|
+
building and testing the CLI plumbing itself (config parsing, phase loop,
|
|
156
|
+
output-file discipline, judge scoring format). Real MLX candidate models are
|
|
157
|
+
reserved for once the harness is proven and the actual question becomes "is this
|
|
158
|
+
debate any good," not "does the pipe work."
|
|
159
|
+
|
|
160
|
+
**Never call a synchronous SDK client inside `async def`** without
|
|
161
|
+
`asyncio.to_thread` — observed as a real, unnoticed bug (fake concurrency) in all
|
|
162
|
+
four of `aragora-debate`'s cloud-provider agents (see ADR-001).
|
|
163
|
+
|
|
164
|
+
**Model servers run offline — a requirement, not an option.** Every model server
|
|
165
|
+
runs with `HF_HUB_OFFLINE=1`, and anything else that fetches from the Hugging
|
|
166
|
+
Face Hub (such as `datasets` for `sources`, later) uses its equivalent offline
|
|
167
|
+
setting. B0 found `mlx_lm.server` contacts huggingface.co on every start to check
|
|
168
|
+
the model revision. That contradicts the offline-capable, privacy-first
|
|
169
|
+
positioning this tool is built around. In offline mode, already-downloaded
|
|
170
|
+
models resolve from the local cache (verified 2026-09-11). Downloading a model is
|
|
171
|
+
a separate, explicit step.
|
|
172
|
+
|
|
173
|
+
## Judge design
|
|
174
|
+
|
|
175
|
+
Five independently-scored rubric dimensions, never blended into one number:
|
|
176
|
+
argument quality & logic (30), evidence grounding (25), steelman fidelity (20),
|
|
177
|
+
rebuttal effectiveness (15), clarity (10). Steelman fidelity is the explicit
|
|
178
|
+
tiebreaker, given the app's core premise.
|
|
179
|
+
|
|
180
|
+
Rebuttal effectiveness is tracked via a **structured hit-ledger**
|
|
181
|
+
(open/conceded/rebutted/dodged per point — idea from `arbiter-debate`), not a
|
|
182
|
+
subjective number alone.
|
|
183
|
+
|
|
184
|
+
A narrow, separate **fallacy-detector** check (idea from `autodebater`'s
|
|
185
|
+
"Bullshit Detector" judge) is a candidate fast signal, same tier as the
|
|
186
|
+
fact-checker — not yet decided whether it's folded into fact-checking or kept as
|
|
187
|
+
its own pass.
|
|
188
|
+
|
|
189
|
+
A candidate **stance-consistency** check (idea surfaced reviewing
|
|
190
|
+
`mlburnham/Political_DEBATE_large_v1.0`, a 0.4B DeBERTa NLI classifier — name
|
|
191
|
+
is a backronym, "DeBERTa Algorithm for Textual Entailment," unrelated to
|
|
192
|
+
adversarial debate) is a second candidate fast signal: cheap enough to run
|
|
193
|
+
after every turn without competing for resources with the debater models,
|
|
194
|
+
verifying a side's turn still entails its assigned stance rather than drifting
|
|
195
|
+
or conceding. Not yet decided whether this is a third fast pass alongside
|
|
196
|
+
fact-checking and the fallacy detector, or folded into one of them.
|
|
197
|
+
|
|
198
|
+
**Validation data for the rubric itself, before trusting any judge model
|
|
199
|
+
choice:**
|
|
200
|
+
- `ibm-research/argument_quality_ranking_30k` (IBM-Rank-30k; CC-BY-3.0) — 30,497 crowd-written
|
|
201
|
+
arguments across 71 genuinely contested policy topics (same domain as this
|
|
202
|
+
app, not academic claims or trivia), each with a human quality score (WA and
|
|
203
|
+
MACE-P methods) and stance label. Use to correlate a candidate judge's
|
|
204
|
+
argument-quality scores against real human judgment before trusting it.
|
|
205
|
+
- `ibm-research/debate_speeches` (CDLA-Permissive-2.0) — the HF release
|
|
206
|
+
(`opening_speeches` config) holds 948 opening speeches across 114 topics from
|
|
207
|
+
nine sources, human-written and machine-generated, each rated by crowd
|
|
208
|
+
annotators. **Resolved 2026-09-13 by reading the paper and the data:** the
|
|
209
|
+
631-speech / 76-topic figure *is* the paper's subset. It removes **78**
|
|
210
|
+
control speeches — not 81 — plus two topics lacking both Arg-Human sources.
|
|
211
|
+
Better still, the authors publish that filtered set directly as
|
|
212
|
+
`noystl/speech-quality-dataset` (CDLA-Permissive-2.0, one `data.csv`), so
|
|
213
|
+
there is no need to re-derive it from the 948-row release and no chance of
|
|
214
|
+
filtering it differently than they did.
|
|
215
|
+
|
|
216
|
+
Verified locally against that file: 631 speeches, 76 topics, **exactly 15**
|
|
217
|
+
labelers each (not 5–30), 9,465 individual ratings on a 1–5 Likert scale held
|
|
218
|
+
per-annotator rather than pre-averaged, mean 605.6 words per speech. Mean
|
|
219
|
+
score by source forms the gradient a judge has to reproduce: Human expert
|
|
220
|
+
4.19, Project Debater 4.03, Arg-Human1 3.79, Arg-Human2 3.67, Arg-GPT2 3.40,
|
|
221
|
+
Speech-GPT2 3.22, Arg-Search 3.12, Summit 2.96. Purpose-built for benchmarking whether an LLM judge's scores track human
|
|
222
|
+
judgment at the speech/turn level (closer to our unit of judging than
|
|
223
|
+
Rank-30k's isolated arguments). A recent paper, "Benchmarking LLM Judges via
|
|
224
|
+
Debate Speech Evaluation," used this exact dataset for this exact task — read
|
|
225
|
+
it for methodology before designing our own validation pass.
|
|
226
|
+
- `tasksource/logical-fallacy` (Jin et al. 2022) — 3,761 labeled examples
|
|
227
|
+
across 13-14 fallacy types, real naturalistic argument text, includes a
|
|
228
|
+
`LogicClimate` challenge subset specifically for climate-policy claims. Real
|
|
229
|
+
train/test/dev splits; multiple independent models already fine-tuned on it.
|
|
230
|
+
Candidate training/validation set for the fallacy-detector check above.
|
|
231
|
+
**License: none found, but the authors' README explicitly says "Feel free to
|
|
232
|
+
access our data in the `data/` folder."** No LICENSE file, HF lists
|
|
233
|
+
`unknown`. Treat this as two separate permissions, not one: sufficient for
|
|
234
|
+
private use (downloading it ourselves to validate a fallacy-detector or judge
|
|
235
|
+
against) but not for redistribution — nothing grants the right to bundle or
|
|
236
|
+
republish the data as part of a `debatebench` release. Keep for validation
|
|
237
|
+
use; do not ship any of it in this repo without a clearer grant.
|
|
238
|
+
|
|
239
|
+
## Fact-checking is separate from judging, and runs on a different clock
|
|
240
|
+
|
|
241
|
+
**Superseded for the Python CLI by ADR-015 (2026-09-12).** The per-turn,
|
|
242
|
+
real-time fact-check described below is now a feature of the future Swift
|
|
243
|
+
app (`DebateKit`), attaching to the CLI's existing event seam. In this repo,
|
|
244
|
+
fact-checking is a post-hoc pass inside `judge`, checked against the
|
|
245
|
+
recorded evidence, not "the world" — see ADR-015 §2 for what that means
|
|
246
|
+
concretely. The bullets are kept as the description of the Swift-side
|
|
247
|
+
feature and of the evidence-hygiene rule, which still holds.
|
|
248
|
+
|
|
249
|
+
- **Fact-check** (Swift app): fast, narrow, per-claim, runs at the end of every
|
|
250
|
+
turn in real time — verifies a claim against the side's own recorded Prep
|
|
251
|
+
evidence.
|
|
252
|
+
- **Judge**: slower, holistic, runs once over the complete transcript at the end.
|
|
253
|
+
In the CLI, the fact-check pass runs here too (ADR-015 §3, a second call).
|
|
254
|
+
- Regex/heuristic "evidence hygiene" (citation density, specificity — as in
|
|
255
|
+
Aragora's `evidence.py`) is not a substitute for either of the above and, if
|
|
256
|
+
used at all, must be labeled "evidence hygiene," never "fact-check" — it scores
|
|
257
|
+
surface form, not truth, and a confidently fabricated citation with a
|
|
258
|
+
plausible-looking year and percentage would score *high* on it.
|
|
259
|
+
|
|
260
|
+
## Hard invariant
|
|
261
|
+
|
|
262
|
+
Every configured phase must produce a response from every side, or the run
|
|
263
|
+
aborts with nothing written to the `output:` path (ADR-007). A silently one-sided debate producing a
|
|
264
|
+
confident-looking verdict is the worst failure mode for a benchmarking tool
|
|
265
|
+
(observed as a real, unflagged bug in a reviewed competitor — see ADR-001).
|
|
266
|
+
|
|
267
|
+
## Hardware
|
|
268
|
+
|
|
269
|
+
MLX-first. Running two debater models plus a judge/fact-checker concurrently on
|
|
270
|
+
one Mac (target dev machine: M2 Pro, 32GB unified memory) is a real memory- and
|
|
271
|
+
compute-contention risk, not just a capacity question — Apple Silicon's GPU and
|
|
272
|
+
memory bandwidth are shared, so three "simultaneous" models likely serialize
|
|
273
|
+
rather than truly parallelize. Options on the table, not yet decided: offload the
|
|
274
|
+
judge/fact-checker to a remote host already running inference (`free`, via
|
|
275
|
+
Ollama) — **withdrawn by B0, see below** — or load/unload models per turn rather
|
|
276
|
+
than keeping all three resident.
|
|
277
|
+
**Needs its own M0-style hardware probe (real peak memory + turn latency
|
|
278
|
+
measurement) before any assumption here is trusted.** ("M0" is the MLXProbe
|
|
279
|
+
session-M0 throwaway probe; results at `~/Projects/MLXProbe/RESULTS.md`.)
|
|
280
|
+
|
|
281
|
+
### B0 findings (2026-09-11)
|
|
282
|
+
|
|
283
|
+
B0 measured this on the dev machine (`RESULTS.md`). What it changes here:
|
|
284
|
+
|
|
285
|
+
- **`free` is not a separate host: it's the dev machine itself.** The "offload
|
|
286
|
+
to `free`" option above offloads nothing, so it's withdrawn unless a genuinely
|
|
287
|
+
separate host is named. This isn't a minor correction. It invalidates a
|
|
288
|
+
specific option this ADR proposed, and it would have been silently
|
|
289
|
+
load-bearing for the rest of the build if nobody had tried to reach the host.
|
|
290
|
+
Two earlier reachability checks during B0 got it wrong before it was
|
|
291
|
+
confirmed.
|
|
292
|
+
- **The judge never needs to be co-resident.** The judge runs once, post-hoc,
|
|
293
|
+
over the finished transcript (see "Fact-checking is separate from judging"),
|
|
294
|
+
so it can load after the debaters unload. That decision now does real
|
|
295
|
+
load-bearing work: the "three models at once" concern applies only to a
|
|
296
|
+
fact-checker that has to run live. This follows from the design; B0 didn't
|
|
297
|
+
measure it.
|
|
298
|
+
- **The fact-checker hardware problem is resolved for v1 by ADR-015**, not by
|
|
299
|
+
finding a host: the fact-check pass now runs inside `judge`, post-hoc, so it
|
|
300
|
+
loads when the judge does, after the debaters have unloaded. The "no
|
|
301
|
+
designated offload target" finding stands as a fact about this machine, but
|
|
302
|
+
nothing in this repo needs one anymore. The live per-turn version that
|
|
303
|
+
would need one is the Swift app's concern (ADR-015 §1).
|
|
304
|
+
- **The debater pair is doubtful and unmeasured, not ruled out.**
|
|
305
|
+
Mistral-Small-24B alone tripped B0's safety cut-off (critical memory pressure
|
|
306
|
+
alongside normal desktop use) before the pair and concurrency stages ran. A
|
|
307
|
+
quiet-machine rerun comes before anything downstream trusts the pair.
|
|
308
|
+
- **Load/unload per turn looks expensive at long contexts**, but that's inferred:
|
|
309
|
+
a cold 4,000-token prompt took about 25 s on the 8B model, and a full swap cycle
|
|
310
|
+
wasn't measured.
|
|
311
|
+
|
|
312
|
+
Prep and argument phases may warrant different inference engines — MLX
|
|
313
|
+
generally wins decode-heavy short-turn work, but long-document Prep ingestion is
|
|
314
|
+
a prefill-heavy workload where llama.cpp/Ollama have shown faster prompt-eval in
|
|
315
|
+
some benchmarks. Worth benchmarking separately, not assumed to inherit the same
|
|
316
|
+
engine choice.
|
|
317
|
+
|
|
318
|
+
## Scope discipline
|
|
319
|
+
|
|
320
|
+
Stay narrow: structured multi-turn comparison + rubric scoring + fact-checking.
|
|
321
|
+
No consensus/voting logic anywhere in the design — consensus-seeking built into the
|
|
322
|
+
turn loop is what ruled out `aragora-debate` and `arbgjr/multi-agent-debate` (see
|
|
323
|
+
ADR-001). No formal
|
|
324
|
+
verification (Z3/SymPy-style, as in `arbiter-debate`) — domain-mismatched for
|
|
325
|
+
non-formalizable topics like policy debates. Resist becoming "the debate app's
|
|
326
|
+
backend" — frame and build it as a general-purpose structured-comparison tool
|
|
327
|
+
that the debate app happens to be the first real user of. Aragora's own
|
|
328
|
+
self-documented scope creep (~25% of its codebase admitted by its own authors
|
|
329
|
+
not to serve its core thesis) is the cautionary example to actively design
|
|
330
|
+
against. (The source for the ~25% figure isn't recorded yet — R0's aragora health
|
|
331
|
+
section documents the platform's breadth but not this number. Cite it or drop the
|
|
332
|
+
number.)
|
|
333
|
+
|
|
334
|
+
## Data sourcing — no scraping user-generated debate platforms
|
|
335
|
+
|
|
336
|
+
Reviewed several live consumer debate platforms (DebateWise.org, DebateArt.com,
|
|
337
|
+
VersyTalks.com) as competitive-landscape research — genuinely useful for
|
|
338
|
+
understanding the market (all three determine a winner via crowd voting, not a
|
|
339
|
+
rubric judge, which remains a real point of difference for this project) but
|
|
340
|
+
**none of their content is a candidate data source.** Unlike the academic
|
|
341
|
+
datasets used elsewhere in this ADR (IBM-Rank-30k, DebateSum, args.me, etc.,
|
|
342
|
+
all released under explicit open licenses for reuse), these are user-generated
|
|
343
|
+
content platforms whose terms of service almost certainly prohibit scraping and
|
|
344
|
+
bulk reuse, with no indication any of them hold rights to sublicense user
|
|
345
|
+
content for training or grounding purposes. Quality or topical relevance never
|
|
346
|
+
overrides this — a dataset failing the licensing gate is disqualified
|
|
347
|
+
regardless of how well it would otherwise fit. Any future dataset candidate
|
|
348
|
+
gets checked for an explicit reuse license before anything else, same as every
|
|
349
|
+
dataset actually adopted so far.
|
|
350
|
+
|
|
351
|
+
## Attribution
|
|
352
|
+
|
|
353
|
+
Two design lifts (`events.py` shape from `aragora-debate`, `Protocol` shape from
|
|
354
|
+
`arbgjr/multi-agent-debate`) and a conditional third (`evidence.py`-derived
|
|
355
|
+
evidence-hygiene scorer, if adopted) require attribution in a `NOTICE` file — all
|
|
356
|
+
source projects are MIT.
|