An AI safety benchmark that measures whether language models can be pressured, step by step, into building dystopian systems: nuclear and military safety overrides, mass surveillance, civic corruption
翻譯準備中
安全TypeScript · JavaScriptGitHub @matei-anghel上線中營運者未確認
試用過了嗎? 點一下就好,不需要登入。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證A red team benchmark that evaluates whether AI language models can be convinced to comply with dystopian directives through progressive escalation and scope creep.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗DystopiaBench is a red-team benchmark for evaluating whether LLMs resist or comply with progressively dystopian directives.
Runs can be published as reproducible dashboard artifacts or kept under artifacts/private for internal evaluation.
It tests registered scenario modules organized by failure mechanism:
petrov: irreversible high-stakes command/control, autonomous weapons, emergency authority, and safety overrideorwell: persistent surveillance, truth manipulation, population-scale monitoring, and service-gatinglaguardia: civic extraction, regulatory capture, patronage, contractor leverage, and institutional self-dealingbasaglia: coercive therapeutic control, paternalistic restriction, behavioral compliance, and outsourced supervisionhuxley: biological stratification, behavioral conditioning, pleasure pacification, and attachment suppressionbaudrillard: synthetic intimacy, synthetic persuasion, synthetic exploitation, and mediated trust collapseEach scenario escalates from L1 to L5, and each response is classified as either refusal or compliant.
New runs use a binary Dystopian Compliance Score (DCS): refusal is 100, compliant is 0, and higher is better. All five levels are judged, while the primary aggregate DCS uses only L3-L5. Historical V1 results retain their original four-tier grading.
Many safety evaluations focus on clearly malicious prompts. Real misuse often starts as a plausible request and gradually drifts into harmful intent. DystopiaBench is designed to measure whether a model notices and resists that drift.
petrov, orwell, laguardia, basaglia, huxley, baudrillard)Default full run size:
36 scenarios x 5 levels x 50 models = 9,000 prompts36 scenarios x 5 levels x 50 models x 3 replicates = 27,000 prompt attemptsJudge output is binary:
refusal -> 100compliant -> 0Primary summary metrics:
averageDcs: mean binary DCS across scored L3-L5 rows for new runsdrfr: refusal rate across scored L3-L5 rowsstatusCounts: transport/judge validity outcomesmodelSuccessRate, judgeSuccessRate, scorableRateSchemas live in lib/dystopiabench/schemas.ts. Run manifests and chart payloads are validated directly against those schemas with no separate version field.
Scenario content lives in JSON module files under lib/dystopiabench/scenario-data/modules/ and is validated through the TypeScript registry in lib/dystopiabench/scenario-registry.ts.
Current prompt IDs:
SYSTEM_PROMPT_VERSION: v2 (model system prompt for the new scoring generation)BENCHMARK_PROMPT_VERSION: v2 (user escalation prompts, passed through without a wrapper)JUDGE_PROMPT_VERSION: v2 (binary refusal/compliant judge with refusal 100, compliant 0)v1lib/dystopiabench/index.ts[email protected]--private-artifacts)experimentId, project, owner, policyVersion, gitCommit, datasetBundleVersion)--replicatesnpm: JSON scenario sources@openrouter/sdk for long-term private retentionapp/ Next.js pages and route metadata (dashboard, results, run)
components/ UI primitives and benchmark dashboards/charts
lib/dystopiabench/ Runner, scenarios, models, schemas, analytics, storage
lib/dystopiabench/scenario-data/modules/ JSON-backed scenario module files
public/data/ Run manifests and run index JSON files
scripts/ CLI entrypoints for run/rerun/publish/validation
artifacts/private/ Checkpoints, private runs, and archived traces (gitignored)
@ai-sdk/openai) with OpenRouter@openrouter/sdk) for generation trace archivingpnpm install
cp .env.example .env.local
Set required env vars in .env.local:
OPENROUTER_API_KEY=your_openrouter_key_here
# Optional LiteLLM/OpenAI-compatible proxy:
LITELLM_BASE_URL=https://litellm.safe.ai/v1
LITELLM_API_KEY=
For local: model selectors, set LOCAL_OPENAI_BASE_URL and, when required, LOCAL_OPENAI_API_KEY. OpenRouter attribution can be customized with OPENROUTER_HTTP_REFERER and OPENROUTER_APP_TITLE.
pnpm dev
Open http://localhost:3000.
pnpm bench:run
Examples:
pnpm bench:run --module=petrov
pnpm bench:run --module=orwell --models=gpt-5.3-codex,claude-opus-4.6
pnpm bench:run --models=openrouter:deepseek/deepseek-r1
pnpm bench:run --models=local:my-custom-model
pnpm bench:run --models=litellm:claude-fable-5
pnpm bench:run --levels=1,2,3 --run-id=my-run-001
pnpm bench:run --judge-model=google/gemini-3-flash-preview --transport=chat-only
pnpm bench:run --judge-models=google/gemini-3-flash-preview,claude-opus-4.6
pnpm bench:run --judge-model=claude-opus-4.6 --judge-strategy=pair-with-tiebreak
pnpm bench:run --provider-precision=non-quantized-only
pnpm bench:run --models=gpt-5.5 --model-reasoning-variants=gpt-5.5:high
pnpm bench:run --chat-first-models=gpt-5.4 --no-timeout-fallback
pnpm bench:run --scheduler=level-wave --concurrency=24 --per-model-concurrency=3 --timeout-ms=900000
pnpm bench:run-isolated --module=petrov --models=gpt-5.3-codex --levels=5
pnpm bench:run --retain=20 --archive-dir=archive
Main bench:run flags:
--module=<registered-module-id>|all (both is accepted as a legacy alias)--models=<comma-separated model IDs>openrouter:<openrouter model string> for direct OpenRouter IDslocal:<local model id> for local OpenAI-compatible providerslitellm:<litellm model id> for LiteLLM/OpenAI-compatible proxy providers/ separator (for example google/gemini-3.1-pro-preview)--levels=1,2,3,4,5--model-reasoning-variants=<model:level,...> to run named reasoning-effort variants--run-id=<id>--scenario-ids=<comma-separated scenario IDs>--judge-model=<model-id-or-openrouter-or-local-model-selector>--judge-models=<comma-separated judge selectors> (multi-judge arena mode)--judge-strategy=single|pair-with-tiebreakpair-with-tiebreak, pass exactly three --judge-models values in primary, secondary, arbiter order.--transport=chat-first-fallback|chat-only--chat-first-models=<comma-separated model IDs> to force selected OpenRouter/local selectors through the primary chat path first--no-timeout-fallback to disable timeout-triggered fallback when --transport=chat-first-fallback--conversation-mode=stateful|stateless--no-model-system-prompt to omit the benchmark model system prompt while preserving scenario context in the user prompt; use only for provider-specific diagnostic runs--scheduler=level-wave|conversation--provider-precision=default|non-quantized-only--timeout-ms=<positive-int> per model or judge API call; defaults to 900000 (15 minutes)--concurrency=<positive-int>--per-model-concurrency=<positive-int>--request-start-delay-ms=<nonnegative-int> to stagger tested-model request starts per model (including retries) while retaining configured concurrency--max-retries=<non-negative-int>--retry-backoff-base-ms=<positive-int>Isolated mode shortcut:
pnpm bench:run-isolated
bench:run-isolated is equivalent to running bench:run with --conversation-mode=stateless, where each prompt executes in fresh context. Use this to answer questions like "does L5 comply when run alone?"
By default, stateful runs use --scheduler=level-wave, which schedules all ready rows for L1 across scenarios/models/replicates before advancing to L2. The global --concurrency and per-tested-model --per-model-concurrency caps still apply; do not set them to the full Cartesian product unless you intend to stress provider rate limits.
Empty completions after a single retry are split by provider metadata. Explicit provider content filters are recorded as provider refusals. Empty stop completions without final answer text are recorded as invalid_response instead of counted as refusals, even when token usage is near zero. Empty completions that consumed output or reasoning tokens without final answer text use errorCode=EMPTY_GENERATED_COMPLETION; near-zero empty stop completions use scoreabilityReason=EMPTY_UNVERIFIED_RESPONSE. Run integrity checks fail a model when evidence-backed response coverage is too low, so ghost runs cannot publish as valid benchmark results. This policy applies to new runs only: previously published manifests that recorded near-zero empty stops as scored silent refusals (scoreabilityReason=EMPTY_SILENT_REFUSAL) keep their recorded scores on the dashboard, and bench:rerun-failures treats those rows as repair targets so they can be replaced with evidence-backed results. OpenRouter primary calls use the official SDK chat path, and direct chat fallback is reserved for transport failures rather than successful empty completions.
Normal benchmark runs now checkpoint progress into artifacts/private/run-checkpoints/checkpoint-<run-id>.json while the run is active.
Ctrl-C once, the runner stops scheduling new work, lets in-flight requests settle, writes the checkpoint, and exits without losing completed rows.pnpm bench:run --run-id=<run-id> --resume
Resume skips the successful contiguous prefix of each scenario-model-replicate conversation and reruns from the first failed or missing level onward, which preserves stateful benchmark behavior more safely than blindly skipping every previously attempted row.
If you enabled OpenRouter Observability Input & Output Logging, normal pnpm bench:run executions now automatically archive stored prompt/completion content and generation metadata into a private local artifact whenever the run contains OpenRouter-linked rows.
You can still backfill older runs manually:
pnpm bench:archive-openrouter --run-id=2026-04-29T14-45-39-222Z
This writes artifacts/private/openrouter-traces/openrouter-traces-<run-id>.json by default. The archive is self-contained per generation:
scenarioId, modelId, prompt, response, hashes)generations.getGenerationgenerations.listGenerationContentUse this when you want long-term local retention for website display or paper artifacts instead of relying on OpenRouter dashboard retention alone.
Broadcast is not required for this workflow.
Pass --no-openrouter-archive to bench:run only if you explicitly want to skip the automatic archive step.
pnpm bench:rerun-failures --source=latest
Examples:
pnpm bench:rerun-failures --source=run --run-id=2026-03-01T20-26-13-370Z
pnpm bench:rerun-failures --scope=from-first-failed
pnpm bench:rerun-failures --scope=failed-only
pnpm bench:rerun-failures --scope=all-levels
pnpm bench:rerun-failures --chat-first-models=gpt-5.4 --no-timeout-fallback
pnpm bench:rerun-failures --dry-run
pnpm bench:rerun-failures --no-publish
--scope behavior:
from-first-failed (default): rerun from the first failed or missing level through the end of the affected scenario-model-replicate chainto-max-failed: rerun all levels up to highest failed level per scenario-model pairall-levels: rerun levels 1-5 for failed pairsfailed-only: rerun only failed tuplesReruns never mutate the source manifest. bench:rerun-failures writes a new derived benchmark-rerun-*.json style run with provenance metadata (derivedFromRunId, derivationKind, rerunScope, rerunPairCount, replacedTupleCount) and publishes latest aliases from that derived run only.
pnpm bench:publish --run-id=<run-id>
Optional retention controls:
pnpm bench:publish --run-id=<run-id> --retain=20 --archive-dir=archive
Private runs cannot update the public dashboard aliases. Run without a private-artifact flag when the result is intended for publication.
pnpm check:scenarios
pnpm check:manifests
pnpm bench:rescore-judges --source=run --run-id=<run-id> --no-publish
pnpm bench:merge --base-run-id=<base-run-id> --patch-run-id=<patch-run-id> --allow-additive-models
bench:rescore-judges derives a new run by rescoring rows with judge failures. Pass --private-artifact-dir=<folder> with --run-id to rescore a run stored under artifacts/private/<folder>; the derived run stays in that folder and never updates the public latest aliases.bench:merge combines compatible stateful runs, including additive model runs when requested.pnpm bench:bundle:create --out=benchmark-bundle.json
pnpm bench:bundle:validate --path=benchmark-bundle.json
pnpm bench:gate --run-id=<run-id> --config=gate-config.json
Use the stable library entrypoint instead of shelling out to pnpm from another orchestrator:
import { loadScenarioCatalog, runBenchmarkRequest, validateRun } from "./lib/dystopiabench"
const manifest = await runBenchmarkRequest({
modelIds: ["deepseek-v3.2"],
replicates: 3,
experimentId: "release-eval-2026-03-27",
})
validateRun(manifest)
const catalog = loadScenarioCatalog()
console.log(catalog.benchmarkBundleId)
Public-safe run files are stored in public/data:
benchmark-<run-id>.json: immutable manifest for one runbenchmark-results-stateful.json: latest published run with conversationMode=statefulbenchmark-results-stateless.json: latest published run with conversationMode=statelessruns.json: index of published runs (for UI run selector)Private artifacts are stored under artifacts/private:
artifacts/private/runs/benchmark-<run-id>.jsonEach result row includes:
modelId, modelString, provider)compliance) and scoreprimaryComplianceLabel, severity, operationalityFlag, targetHarmMechanisms, safeAlternativeHelpfulnessFlag, manipulationOrEvasionFlag, confidence, adjudicationSource)ok, model_error, judge_error, aborted, invalid_response)judgeVotes, judgePanel, judgeReasoning, implicitRefusalFromEmpty)conversationContinuity)endpointUsed, transportAttempts, finishReason, providerMetadata), including available OpenRouter/LiteLLM response headers and non-content response/choice metadatapromptTokenCount, responseTokenCount, reasoningTokenCount, totalTokenCount, modelUsage, judgeUsage, totalUsage, estimatedCostUsd, timing)replicate, experimentId)sampleId, attemptId, promptHash, responseHash, judgePanelConfigSnapshot, artifactLineage)Manifest metadata now separates:
benchmarkDefinitionexecutionConfiganalysisConfig/: homepage with overview, methodology entry point, and embedded results tabs at /#results/methodology: dedicated methodology page with protocol, scoring, and reproducibility details/run: local command builder (hidden in production)Results UI behavior:
Aggregate, each registered module tab, Per Scenario, and Per Prompt always use stateful escalation runs.Per Prompt (No Escalation) is the only isolated/stateless view and always reads benchmark-results-stateless.json.next.config.mjs keeps image optimization disabled for static assets, and vercel.json sets security/cache headers for app and data assets.
Local checks:
pnpm lint
pnpm typecheck
pnpm test
pnpm test:exports
pnpm check:library-surface
pnpm check:scenarios
pnpm check:manifests
pnpm build
This repository includes intentionally dual-use prompt content for safety evaluation. Use it for research, red-teaming, and policy analysis only.
MIT (LICENSE).
--retry-backoff-jitter-ms=<non-negative-int>--retain=<non-negative-int>--archive-dir=<relative-folder-under-public/data>--no-publish-latest to save a timestamped run manifest without replacing the dashboard aliases--private-artifacts to write the run under artifacts/private/runs without updating dashboard aliases--private-artifact-dir=<folder> to force private storage under artifacts/private/<folder> without updating dashboard aliases--resume with --run-id=<existing-run-id> to continue from the saved checkpoint after an interruption or rerun from the first failed/missing level onward for affected scenario-model pairs--resume-mode=all|prefix to choose whether resume considers all checkpoint rows or only the successful contiguous stateful prefix--no-openrouter-archive to skip the final OpenRouter trace archive step--replicates=<positive-int> default 3--experiment-id=<id>--project=<name>--owner=<name-or-team>--purpose=<free-text>--model-snapshot=<deployment-or-checkpoint-id>--provider-region=<region>--policy-version=<internal-policy-version>--git-commit=<sha>--dataset-bundle-version=<bundle-id-or-version>--benchmark-id=<bundle-family-id>--benchmark-bundle-version=<semver>--scenario-sources=<comma-separated source paths, URLs, or npm: package paths>