mixle.reason.language_bridge module¶
The language<->belief bridge (roadmap M5, part (c)): NL/record -> M0 evidence via a declared
schema, and posterior -> calibrated text through A1’s CalibratedGenerator.
Two independent directions, each a thin composition of already-tested machinery – no new extraction
or generation model is built here (see notes/designs/M5.md part (c) for the full contract):
parse_evidence()– an extractor callable (the sameteacher(x) -> dictshapemixle.task.structured_out.solve_structured()decomposes) produces a raw{field: value}dict from NL/record input; this module validates it against a caller-declared schema ({field: "categorical" | "numeric"}– the same shapeschemaalready returns) BEFORE it reachesinfer()/run_inference_program(), so a schema violation is a clearValueErrorhere rather than a confusing downstreamlog_densitycrash.PosteriorDescriber/claim_score()– draftkcandidateClaims at different ABSOLUTE precision widths (multiples of a requiredtol, the same “caller declares the precision that counts” contractmixle.task.regress.solve_regression()already uses for numeric fields – NOT widths relative to the posterior’s own spread, which would be scale-invariant and could never detect “too diffuse to answer”), score each against the posterior it describes, and serve the best one under A1’s conformal accept-or-abstain guarantee.claim_score()is exported standalone (not only reachable throughPosteriorDescriber) so B2 (claim-checking, built elsewhere) can score an ALREADY-EMITTED claim against any posterior directly, with no dependency on candidate generation/calibration at all.
- parse_evidence(text, schema, extractor)[source]
NL scenario/constraint (or any record
extractoraccepts) -> validated M0/L2 evidence.extractor(text) -> {field: raw_value}does the actual parsing (a keyword/regex rule, a calibratedsolve_structured()student, an LLM call – this module is agnostic to how); this function’s only job is enforcing the declaredschemaBEFORE the result is trusted as evidence: every returned field must be declared, numeric fields must actually be numbers, categorical fields are normalized tostr. The returned dict is ready to pass straight toCrossModalJoint.infer(...)or asrun_inference_program’sevidence=.
- class Claim(field, lo, hi, probe=())[source]
Bases:
objectA declared interval assertion about one posterior field:
fieldlies in[lo, hi].probecaches the sample batch aPosteriorDescriberdrew to score this claim at generation time, soCalibratedGenerator()’s single-argumentscore(candidate)contract can callclaim_score()with no extra prompt/posterior plumbing. A hand-authoredClaim(e.g. from B2) simply omitsprobeand passesposterior=toclaim_score()explicitly instead.
- claim_score(claim, posterior=None, *, n_samples=200, seed=0)[source]
How well
claimis supported by the posterior it describes: coverage of[claim.lo, claim.hi]under fresh posterior draws, PER UNIT WIDTH (coverage / width) – a density-like score, not a linear coverage-minus-penalty one. The ratio form matters, not just tie-breaking: a coverage-minus-linear-penalty score is shift-invariant under softmax whenever coverage SATURATES to the same constant across every candidate width, which happens in BOTH the confident regime (a sharp posterior’s mass fits inside every candidate width, coverage saturates near 1) and the clueless regime (a posterior far more diffuse than the widest candidate has near-locally-uniform density, so coverage saturates neardensity(center) * widthfor every candidate) – softmax over a constant offset cannot tell those two regimes apart.coverage / widthdoes not saturate the same way: in the confident regime it grows as1 / width(the NARROWEST candidate wins decisively), while in the clueless regimecoverage / width -> density(center), the SAME value for every candidate width (a uniform, non-committal softmax) – exactly the “no candidate is more informative than any other” signalPosteriorDescriber.describe()abstains on.Reusable standalone by B2’s claim-checking: pass
posterior=to score an independently-authored claim against any posterior; aPosteriorDescriber-generated claim can instead be re-scored with noposteriorargument, reusing the sample batch cached at generation time.
- class PosteriorDescriber(field_name, *, tol, k=3, alpha=0.1, width_multiples=(1.0, 3.0, 10.0), n_probe=300, seed=0)[source]
Bases:
objectPosterior -> calibrated text for one field, via A1’s
CalibratedGenerator.tolis the caller’s required precision (the same “the caller states what precision counts as an answer” contractsolve_regression()uses) – candidate claim widths are ABSOLUTE multiples oftol, not relative to the posterior’s own spread, so a genuinely diffuse posterior (spread >>tol) cannot fake confidence by simply widening every candidate in lockstep: none of them will cover well enough to clear the calibrated threshold, anddescribe()abstains (acceptance criterion (d)).- Parameters:
- calibrate(calibration_set, *, seed=None)[source]
Fit the conformal threshold from
(posterior, true_value)held-out pairs.is_correctassigns EXCLUSIVE correctness across theknested candidate widths – the true value’s distance from the shared center falls in exactly one of thekdisjoint precision BANDS(0, tol], (tol, 3*tol], ...(widths are nested/overlapping as claims, but only the tightest band a true value actually falls in counts as “correct”). Without this, a wide claim trivially contains the true value whenever a narrower nested claim does too, so every calibration point would credit ALL of them at once and the conformal threshold would never learn to prefer – or reject – any one candidate.
- describe(posterior, *, seed=None)[source]
The best calibrated claim about
posterior, orABSTAIN(None) when no candidate width conformally clears the threshold – i.e. the posterior is too diffuse relative totolfor any of this describer’s claims to be trustworthy.