Skip to content
dgem has moved to github.com/GoogleCloudPlatform/dgem. Docs are moving to googlecloudplatform.github.io/dgem; this site stays up during the move.

Calibrate Your Policy

A confidence of 95% is only useful if answers given at 95% are right about 95% of the time on your data. This page shows how to check that for your own policy and pick an escalation threshold. You need a policy (Your first decision policy) and some labelled examples.

Collect real inputs with the answer you consider correct, one JSON object per line. Columns named expected_<question id> are labels; the rest are template variables:

{"id": "r01", "request": "Ordered a blue kettle, the box had a red toaster in it.", "expected_refund_eligible": "yes", "expected_reason": "wrong_item"}
{"id": "r04", "request": "Package never showed up, tracking stuck for 10 days.", "expected_refund_eligible": "no", "expected_reason": "late"}

Aim for 100+ labelled answers per question, including the hard cases you actually see; a sample of easy cases will look perfectly calibrated and tell you nothing.

Use the dataset runner from Run a dataset (set TEMPLATE_PATH, DATASET_PATH and your gateway URL). It writes results_receipt.jsonl with each question’s answer, confidence and hesitation. Keep samples at what you will use in production.

Terminal window
python3 scripts/policy_calibration.py results_receipt.jsonl

Example from a 12-row refund dataset (illustrative size; recorded on a Vertex G4 endpoint):

== refund_eligible: n=12 accuracy=91.7% Brier(top)=0.0769 ECE(10 bins)=0.0937
confidence bin n mean conf accuracy
0.7-0.8 1 0.793 1.000
0.9-1.0 11 0.993 0.909
keep if hesitation < kept accuracy of kept escalated
5% 75.0% 100.0% 3
16% 83.3% 100.0% 2
35% 91.7% 90.9% 1
any 100.0% 91.7% 0
  • Accuracy: share of answers that match your labels.
  • Brier (top answer) and ECE: how far confidence is from accuracy (0 is perfect). The reliability table shows it bin by bin: answers given at ~99% were right 91% of the time here.
  • Hesitation thresholds: what happens if every answer above the cut-off goes to a person or a larger model. At 16% the policy handles 83% of answers on its own, all correct in this sample, and escalates 2 of 12.

The one error is instructive: “package never showed up” was answered eligible at 94% confidence, yet its hesitation was 34%. On a yes/no question 94% vs 6% is still a noticeable split, which is why hesitation (entropy scaled to the number of options) is a better gate than raw confidence. The label itself is also debatable: the policy text only mentions damaged or wrong items. Errors on a small sample are often policy wording, not the model.

4. Choose what to do with hesitant answers

Section titled “4. Choose what to do with hesitant answers”
SituationAction
Few hesitant answers, errors are costlyEscalate above ~16% hesitation to a person
Many hesitant answers, a larger model is acceptableStage 2 cascade: "cascade_mode": "entropy" sends only hesitant items to Gemini (authoring guide)
Hesitation clusters on one questionRewrite that question or its option descriptions; add missing options (taxonomy discovery)
You need an error bar per decisionsamples: 4 adds standard error and agreement (~+35–40 ms)
Confidence is systematically too high or too lowFit a temperature on held-out data (below)

What we measured on public benchmarks: an offline cascade escalating answers above 16% hesitation to Gemini reached 221/231 on JevBench with 39% escalated (Gemini on everything: 225) and 48/50 on the calibration suite with 34% escalated (EXP-14). Your escalation rate will depend on your data.

A temperature TT rescales probabilities after the fact: T>1T > 1 softens overconfident answers, T<1T < 1 sharpens them, and the answer itself never changes. Fit it on your own labelled sample, on data you did not use to choose it. There is no single served default: a temperature fitted on our other development suites and applied to a new one made calibration worse on 4 of 8 suites, and so did one per question type (PROP-20). Fitted on the suite itself (held out by 5-fold cross-validation), it helps on every suite.

Fit it. For the built-in suites, --auto-temperature fits TT by 5-fold cross-validation and scores each item with the TT fitted on the other folds (the same folds as the regression matrix). Its temperature_fit block lists the fold temperatures and raw vs held-out ECE:

Terminal window
./bin/dgem bench-calibration --from-receipt <receipt.json> --auto-temperature
./bin/dgem bench-jev --from-receipt <receipt.json> --auto-temperature

For your own policy, run it on your labelled rows (the dataset runner) and fit one TT per question type the same way. On our suites yes/no questions wanted about 3, choices about 1.3 and scores about 1.1.

Use it. Put the fitted value in the policy, or send it per request:

WhereHow
Template"temperature": 1.4 or "temperature": {"noul": 3.0, "choice": 1.3, "score": 1.1} next to "questions"
dgem decide--temperature 1.4 (overrides the template)
Gateway POST /api/decide"temperature": 1.4 or the per-type object (overrides the template)
MCP decide toolstemperature and temperature_by_type
Web StudioTemperature field next to Prompt layout (also used by the batch runner)
/v1/systemone adapterdgem systemone serve --temperature, or dgem serve --systemone-temperature

The temperature is applied after Stage 1 and after the Gemini cascade: the cascade still gates on raw entropy, and answers the cascade resolved are left as Gemini gave them. Responses report the temperature applied in temperature.

Re-run the report after changing the policy wording, the options, samples, or the serving image. A new vLLM version can change a few percent of individual answers even when overall accuracy is unchanged (image parity run).