98d616d82b
The learning point is not the hypothesis, it is the QUESTION — and confirmed/refuted is too coarse.
"partial, only critical suppliers" or "certified but not lived" are not "wrong", they are valuable
knowledge. So the chain is Hypothesis -> Question -> Observation -> (Review) -> Hypothesis, and the
observation model must be defined cleanly before any store/API (else thousands of too-coarse
observations get migrated later).
compliance/onboarding/observations.py:
- ObservationType: confirmed / partial / refuted / not_applicable / unknown (richer than binary).
- Observation: {hypothesis_id, capability, question, answer (free text), observation_type,
scope_note ("only critical suppliers"), evidence_uploaded, reviewed, reviewed_by}.
- empirical_distribution() -> a DISTRIBUTION (confirmed 61 / partial 31 / refuted 8), not one %.
- empirical_confidence() -> (confirmed + 0.5*partial) / (confirmed+partial+refuted); n.a./unknown
excluded; None until calibrated.
- REVIEW GATE: only reviewed observations calibrate — a raw answer never changes a hypothesis (no
learning from outliers).
Refactor: the hypothesis is now PURE curated knowledge — the binary observations counter and any
confidence are removed from CapabilityHypothesis and the YAML; confidence is COMPUTED from the separate
reviewed observation stream. Pure, mypy --strict clean. Persistence/aggregation/calibration are 59b/c/d.
Non-runtime -> no deploy. 12 tests pass, check-loc 0.
55 lines
2.5 KiB
Python
55 lines
2.5 KiB
Python
"""Certification Capability Hypotheses — capability-centric, with EMPIRICAL (computed) confidence.
|
|
|
|
Each hypothesis is its own knowledge object: "IF a company holds one of `supported_by` certs, we EXPECT
|
|
`capability` (verification required)" — Welt-1, never "erfüllt". Written ONCE per capability with a list
|
|
of supporting certs (reuse, not redundancy), so multi-certification merges AUTOMATICALLY.
|
|
|
|
`confidence` is NOT an expert/LLM score: it is COMPUTED from real-onboarding observations
|
|
(confirmed / (confirmed+refuted)), `None` until any are seen. This is the empirical learning loop — the
|
|
long-term moat. The library is DATA, loaded outside this module and injected. Python 3.9 compatible.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Dict, List, Sequence
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class CapabilityHypothesis(BaseModel):
|
|
"""Curated knowledge only. Confidence is NOT stored here — it is computed from the reviewed
|
|
observation stream (see observations.py); a raw answer never changes a hypothesis (review gate)."""
|
|
|
|
id: str
|
|
capability: str
|
|
supported_by: List[str] = Field(default_factory=list) # certifications that suggest this capability
|
|
relationship: str = "supports" # supports / partially_supports
|
|
verification_required: bool = True # Welt-1: never auto-satisfied
|
|
question_intent: str = "verify_existence"
|
|
expected_evidence: List[str] = Field(default_factory=list)
|
|
kind: str = "shared" # shared / specific
|
|
|
|
|
|
def inferred_hypotheses(
|
|
certifications: Sequence[str], library: Sequence[CapabilityHypothesis]
|
|
) -> List[CapabilityHypothesis]:
|
|
"""Every hypothesis whose `supported_by` intersects the company's certs — the auto multi-cert merge."""
|
|
certs = set(certifications)
|
|
return [h for h in library if certs & set(h.supported_by)]
|
|
|
|
|
|
def resolve_for_certifications(
|
|
certifications: Sequence[str], library: Sequence[CapabilityHypothesis]
|
|
) -> Dict[str, List[str]]:
|
|
"""Adapt the capability-centric library to the Advisor's `cert -> [capability]` input.
|
|
|
|
For each held certification, the capabilities its hypotheses suggest (deduped, deterministic order).
|
|
"""
|
|
certs = set(certifications)
|
|
out: Dict[str, List[str]] = {}
|
|
for h in library:
|
|
for cert in h.supported_by:
|
|
if cert in certs and h.capability not in out.setdefault(cert, []):
|
|
out[cert].append(h.capability)
|
|
return {c: out[c] for c in sorted(out)}
|