Skip to content

Add experimental ReAnchor calibration optimizer for Predict - #10475

Merged
isaacbmiller merged 13 commits into
mainfrom
feat/decide-optimizer
Sep 25, 2026
Merged

isaacbmiller merged 13 commits into
mainfrom
feat/decide-optimizer

Conversation

@isaacbmiller

@isaacbmiller isaacbmiller commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #10463.

dspy.experimental.ReAnchor calibrates decision outputs in Predict programs against a whole-program metric. It works with TypeSafe and generative LMs, without changing instructions, criteria, or demos.

Output Fitted setting Effect
Noul / bool threshold Converts P(True) to a Boolean
Score cuts Selects .level; leaves continuous .value unchanged
Choice / Literal weights Reweights option probabilities to select a value
from dspy.experimental import ReAnchor

optimizer = ReAnchor(metric=metric)
tuned = optimizer.compile(program, trainset=trainset, valset=valset)
print(optimizer.report)
tuned.save("tuned.json")

Search and acceptance

Candidates come from observed probabilities. Threshold search also tries zero when P(True)=0 is observed. A setting changes only when it improves the training score and passes a fold check: settings selected on the other folds must improve the combined held-out score. This discourages gains confined to small portions of the dataset, but can accept them when they recur across folds.

On generative LMs, ReAnchor can ask native bool and Literal outputs for probabilities while preserving their Python return types. It keeps this change only when the calibrated output beats the native output under the same fold check.

Execution and saved state

  • compile returns a copy; the original program is unchanged.
  • Numeric settings live in each predictor’s fields and use normal program save/load.
  • Identical requests reuse cached answers. Changed upstream decisions can produce new downstream requests; native-output promotion also changes requests. Caching is required by default, but does not guarantee a fixed request count.
  • Evidence collection is scoped to the current execution context and its DSPy workers.
  • report includes training scores, fitted settings, observed evidence summaries, and fold-check results. Optional validation data is scored but never fitted. log_dir writes report.json.
  • The API is experimental. RLM decision outputs are unsupported. Single-option Choice calibration currently raises an error.

Author-reported Jev results

Calibration-only results from the original PR evaluation; these benchmarks have not been rerun for the subsequent implementation changes.

Benchmark Before After
Email triage (1,000 test emails, unseen senders) 0.692 0.749
Trend-report judge (8 test files) 0.813 0.874

@greptile-apps

greptile-apps Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

Safe to merge; there are no outstanding blocking issues.

Summary

  • Adds the experimental ReAnchor optimizer for fitting Boolean thresholds, score cuts, and choice weights against whole-program metrics.
  • Preserves existing settings unless a fitted candidate produces a strictly better, fold-validated result.
  • Handles adjacent floating-point Boolean probabilities by retaining the upper separating threshold.

Merge Safety

Safe to merge.

Reviews (16) · Last reviewed commit: "Retain Boolean thresholds between adjace..."

@isaacbmiller
isaacbmiller added this pull request to stack #10476 September 22, 2026 19:51
@dbreunig
dbreunig force-pushed the feat/decide-optimizer branch from 3a7c8bd to 761de23 Compare September 23, 2026 06:13
@dbreunig dbreunig changed the title Add experimental numeric optimizer for Decide Add experimental ReAnchor calibration optimizer for Decide Sep 23, 2026
Comment thread dspy/teleprompt/reanchor/calibrate.py Outdated
@greptile-apps

This comment has been minimized.

@dbreunig
dbreunig force-pushed the feat/decide-optimizer branch 3 times, most recently from 78714d0 to e914801 Compare September 23, 2026 22:24
@isaacbmiller isaacbmiller changed the title Add experimental ReAnchor calibration optimizer for Decide Add experimental ReAnchor calibration optimizer for Predict Sep 24, 2026
@isaacbmiller
isaacbmiller force-pushed the feat/decide-optimizer branch 3 times, most recently from efe4aca to 97d165c Compare September 24, 2026 21:10
Base automatically changed from feat/decide-system-one to main September 24, 2026 21:24
@isaacbmiller
isaacbmiller force-pushed the feat/decide-optimizer branch 2 times, most recently from eedb9e7 to 588dab0 Compare September 24, 2026 23:37
dbreunig and others added 12 commits September 24, 2026 19:43
ReAnchor fits every Decide's numeric settings against a whole-program
metric: Boolean thresholds, rich Score cuts, and Choice weights. Each
setting changes how Decide reads the System One model's probabilities
and leaves the request unchanged, so the search runs on cached answers.
Outputs the metric never reads are detected and skipped. A valset is
scored before and after calibration and never fitted on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
dspy.experimental now exports ReAnchor, as its docs and tests expect.

Threshold calibration now scores the starting threshold before sweeping
the grid, and keeps it unless a grid value scores strictly better. A
threshold off the 0.05 grid, such as 0.73, was previously replaced by
the best grid value even when that scored worse. The report row gains
train_score_at_start, as cuts and weights rows already have.

The docs add a section on calibrating with ReAnchor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
Calibrate every Predict with decision outputs now that Decide is gone. Fill
partial field entries from the type defaults before fitting. On a generative
LM, give native bool and Literal outputs a fields entry and keep it only when
the fitted setting beats the native output. Raise when a client does not cache
responses, unless require_cache=False. Move the docs to their own page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
Record the evidence each decision output is decoded from, and try the
midpoints between neighbouring observed values in place of fixed grids. This
covers every distinct outcome, including probabilities bunched near 0 or 1,
and Choice multipliers outside 0.1 to 10. Ties go to the widest gap. Reports
now summarize the observed values and the candidates tried.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
Read evidenced outputs and effective settings from DecisionState directly.
Drop the derived choice key from the Choice fake, since decoding no longer adds it.
Document that ReAnchor stops at the first error and does not support RLM, and
link ReAnchor from the TypeSafe compatibility notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
A candidate threshold, cut, or weight now replaces the current setting only when
picking it on four fifths of the training set also scores better on the held-out
fifth, over five folds. Promotion of native outputs uses the same check. Gains
that rest on one or two examples no longer move a setting. Report rows gain a
fold_check count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
Remove unused calibration filters and silently ignored compile arguments. Search outputs instead of inferring metric independence from extreme settings, with a regression for repeated predictor calls. Preserve separate observation and scoring passes.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0bf8d-a1dd-7603-906c-ea06c46ff7f7

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
Isolate evidence collectors through DSPy execution contexts, preserving worker propagation. Include zero as a threshold candidate when observed and clarify cache and fold-check guarantees.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0bf8d-a1dd-7603-906c-ea06c46ff7f7
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
Try probability-based execution for every compatible output and restore its original configuration unless calibration is accepted. Keep client inspection confined to the optional cache check.

Skip unrepresentable gap midpoints to prevent duplicate Score cuts found in the live trend benchmark.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0bf8d-a1dd-7603-906c-ea06c46ff7f7
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
Comment thread dspy/teleprompt/reanchor/calibrate.py
Try the upper observed probability when no interior float exists, while preserving the shared midpoint guard for Score cuts.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0bf8d-a1dd-7603-906c-ea06c46ff7f7
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
@isaacbmiller
isaacbmiller merged commit e8ec009 into main Sep 25, 2026
23 checks passed
AtlantisPleb added a commit to OpenAgentsInc/openagents that referenced this pull request Sep 25, 2026
Add `coder-one study run decision-fit`, a tier 0 study that fits Noul
thresholds, Score cuts, and Choice weights on recorded Jev answers and
labeled outcomes, with no Jev call. Candidates are midpoints between
neighboring observed probabilities, at most 40 per setting; Choice
weights are searched on a log scale between the points where a pick
flips. A candidate replaces the current value only when it scores
strictly better on the fit partition and passes a 5-fold held-out check
that keeps each group in one fold. Ties go to the widest gap.

The study reads the stall detector's checkpoints (`control.stall`), the
truthful-check rows (`checks.verdict`), or any question set with one
recorded answer per line, and writes the fitted settings as a proposal
in the `decision` block format. No policy changes.
`checks::verdict::corroborated_at` takes the corroboration threshold as
an argument; `corroborated` calls it with the setting in effect.

The offline run is in docs/terminal-bench/2026-09-25-decision-fit.md.
On each component's own objective, no fitted setting beat the
hard-coded one: the stall fit moved `stall.progress` to 0.97, which
reproduces the code-suspect-only detector, with an evaluation interval
that includes zero, and the truthful check's candidate failed the
held-out check, so 0.8 stays.

Reimplemented from DSPy 3.4.0's ReAnchor (stanfordnlp/dspy#10475, MIT);
no source copied.

Refs #9659

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants