Skip to content

[Decisions] Make confidence mean the same thing on every backend (Laya reports normalized entropy, others max p or Jev's margin) #89

Open

Description

@Birfy

Why

Every backend returns a confidence next to its choice, and System1-Agents passes it straight through to step records (tool/loop.py, browser/decision_model.py), s1a probe, s1a decide and the MCP decide tool. Right now that number means different things depending on the backend:

Backend Where the number comes from What it is
laya, laya-served Laya's own answer dict, passed through verbatim (s1a/decision_models/laya.py L201, served.py L372) Normalized entropy, 1 - H(p)/log(k) (laya.common.confidence_from_probs)
jev, clm the service's answer, passed through whatever the service reports; for Jev, TypeSafe's adapter defines choice confidence as (max p - 1/k) / (1 - 1/k) (as quoted in laya/confidence.py)
omnijev recomputed in omnijev.py L110 Jev's formula
cua, cua 4B cua.py L119, cua_four_b.py L117 max p
any noul without one validation.validate_noul max(p, 1-p)

Two recent write-ups show why this matters:

  • Fast Models, Slow Evidence (arXiv:2610.02267), a paired Laya vs Jev evaluation with released raw outputs, found that Laya's confidence field differs from max_k p_k by 0.299 on average (0.010 for Jev) and warns that "using that field would silently break any threshold". It recommends computing confidence as max_k p_k and choosing thresholds on held-out data.
  • Laya itself now says the same thing. In laya 0.4.11 (current PyPI), laya/confidence.py documents that confidence is normalized entropy, "does not transfer across option counts", and that "a threshold carried over from Jev selects about twice as much on Laya". It adds a separate answer_confidence (max p) field for gating. This repo locks laya 0.3.20.

Nothing in Agents thresholds on confidence today, so this is not a live bug yet. But the conf= column in run summaries and replays, the MCP decide output, and any future confidence-gated step (for example the System-1 to System-2 delegation in SearchJev, arXiv:2610.05107) would compare numbers that are on different scales for different backends.

Proposed scope (one PR)

  1. Decide on one documented definition for Choice.confidence / Noul.confidence across backends. The suggestion is max_k p_k for choice and max(p, 1-p) for noul, both computed in validation.py from the validated probabilities, so every backend gets the same number.
  2. Keep each backend's own number for provenance, e.g. raw["backend_confidence"] (Laya's entropy value, Jev's normalized margin), so nothing is lost and old records can still be read.
  3. Update docs/decision-models.md (the Decision contract section) and the s1a probe / decide / MCP field descriptions to say exactly what confidence is, and that it is not calibrated unless a backend says so.
  4. Add the definition to DecisionModelContract so a new backend can't drift.

Acceptance criteria

  • Unit tests (offline, using fakes and recorded Laya/Jev/OmniJev answer dicts) show the same probabilities yield the same confidence on every backend.
  • Backend-native confidence is retained in raw and the step records, under a name that does not collide with confidence.
  • The CHANGELOG notes the change in meaning for laya / laya-served / jev / omnijev records, since historical conf= values are not comparable with new ones.
  • No change to which option is chosen; only the reported confidence changes.

Out of scope

  • Calibrating any backend (temperature fitting, per-domain recalibration).
  • Adding a confidence gate or System-2 fallback. That can be a follow-up once the number means one thing.
  • Bumping laya beyond the current lock (track separately if wanted).

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions