Skip to main content

Decision-stability measurement for LLMs: run the same decision N times, get a flip rate with a bootstrap CI, and compare models against their own noise floor.

Project description

nunchi-drift

Cassette tests can't see a model that flips its decision 20% of the time.

Most LLM testing makes variance disappear — retries until green, temperature=0, VCR cassettes that freeze one lucky sample. nunchi-drift does the opposite: it treats run-to-run variance as the thing to measure. Give a model the exact same decision N times and get an answer you can defend statistically:

  • flip rate — P(two runs disagree), with a bootstrap 95% CI
  • cross-model distance vs. each model's own noise floor — "these two models behave differently" only counts as a signal when it exceeds both models' self-disagreement, and a permutation test puts a p-value on it
  • pass^k (τ-bench vocabulary) when you do have a success oracle

Zero dependencies. Stdlib only. Works with Anthropic, OpenAI, GLM, Qwen, DeepSeek, or any OpenAI-compatible endpoint.

Quickstart

from nunchi_drift import DecisionTask, measure, compare

task = DecisionTask(
    system="You are a refund-processing agent for an online store.",
    prompt="Customer requests a refund for a product delivered 15 days ago. "
           "Policy window is 14 days. The customer is polite and a longtime user.",
    actions=("approve", "reject", "escalate"),
)

r = measure(task, model="claude-haiku-4-5", runs=10)
print(r.summary())
# model=claude-haiku-4-5 n=10/10 flip_rate=0.356 [0.178, 0.489] majority=escalate (60%)

r.stable(threshold=0.1)          # False — this decision point is flaky
r.distribution                   # {'escalate': 0.6, 'approve': 0.3, 'reject': 0.1}

c = compare(task, "claude-haiku-4-5", "gpt-4o-mini", runs=10)
print(c.summary())
# claude-haiku-4-5 vs gpt-4o-mini: cross=0.640 floor=0.356 SIGNAL (p=0.014, permutation n=1000)

The action enum is enforced through tool/function calling — every sample is directly countable, no output parsing.

Why the statistics are not optional

These metrics were hardened in a measurement project (nunchi-gov) where every naive shortcut failed in a documented way:

Naive approach What went wrong What this library does
Baseline-first drift (compare run 1 vs rest) A single outlier first run inflated drift (0.40 vs 0.08) mean-pairwise: no run is privileged
Aggregate distributions only Two agents swapping actions → aggregate unchanged, drift 0 per_agent_flip_rates sees every flip
Point estimates, no CI Leaderboard gaps of 0.003 read as rankings; with n=10 they were statistical ties bootstrap CI on everything, ties reported as ties
"Model A ≠ model B" by eyeballing Cross distance below either model's own noise floor means nothing noise-floor gate + stratified permutation test

Cost, honestly

Repeated sampling is inherently uncacheable — that's the measurement. Mitigations built in or recommended:

  • Opt-in, not blanket: measure decision points (a router, a guardrail verdict, a trade action), not entire suites. One task × 10 runs on a small model costs about a cent.
  • workers=2 by default — bursts of identical calls are the worst case for rate limiters, and a failed run is lost signal (failures reduce n and are reported, never silently ignored).
  • Run nightly, not per-commit, for regression tracking.

API sketch

Call Returns
measure(task, model, runs=10) StabilityResult: .flip_rate .ci .distribution .majority .entropy .stable(threshold)
compare(task, model_a, model_b) CompareResult: .cross .noise_floor .signal .p_value
metrics module drift_score, mean_pairwise_drift, bootstrap_within_ci, permutation_test_cross, per_agent_flip_rates, pass_k, … all pure functions over your own data
caller= parameter swap the provider layer (custom endpoints, replays, tests)

Multi-agent / multi-scenario batteries (the full leaderboard workflow with per-scenario stratification) currently live in the parent project and are the v0.2 extraction target, together with a scheduled model-change canary.

Related work

pass^k is from τ-bench. The CI agenda follows "Adding Error Bars to Evals". For continuous behavior-change auditing see "An Auditing Test to Detect Behavioral Shift in Language Models" — the statistical backbone we build toward in the canary. Unlike semantic-entropy methods, nunchi-drift needs no logits: enum-forced decisions make the distribution directly observable, which is what keeps it cheap and provider-agnostic.

nunchi (눈치): the Korean art of reading a room — noticing what shifted without being told.

MIT license.

Project details


Download files

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

Source Distribution

nunchi_drift-0.1.0.tar.gz (19.5 kB view details)

Uploaded Source

Built Distribution

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

nunchi_drift-0.1.0-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file nunchi_drift-0.1.0.tar.gz.

File metadata

  • Download URL: nunchi_drift-0.1.0.tar.gz
  • Upload date:
  • Size: 19.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for nunchi_drift-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a3518560acce7ed75e04884b911eb39a1d60667883d75f27783786093c6e60a7
MD5 28945d9c4f620b204d03ab84362d6e33
BLAKE2b-256 e846bebfbb8e4f6856ab57e85a944f750ffe5954fbeb4ccfa54e25ae7518b223

See more details on using hashes here.

File details

Details for the file nunchi_drift-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: nunchi_drift-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 17.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for nunchi_drift-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 78eefd90bbce4994b6d411b797010b44787a423d31d30e0951ec6fe9e2690295
MD5 14256c7f5dc61e3234748d20866fa1cc
BLAKE2b-256 aba0618d69cf78f14dc1fbcabbc82a3aea8135339330e4f4c3685a77cc0b827f

See more details on using hashes here.

Supported by

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