Files

192 lines
11 KiB
Go

package domain
import (
"fmt"
"sort"
"git.cin.su/m/Assistant/contracts/self"
"git.cin.su/m/Assistant/platform/ids"
)
const (
MinWeaknessIncidents = 2
MaxDiagnosisEvidence = 8
MaxCandidateEvidence = 12
)
func ValidateCompetence(v self.CompetenceAssessment) error { return v.Validate() }
func ValidateLearning(v self.LearningLedgerEntry) error { return v.Validate() }
func AttributeFailure(signal self.LearningSignal) self.Diagnosis {
class := self.DiagnosisUnknown
switch signal.Kind {
case self.SignalSkillFallback:
class = self.DiagnosisSkillFailure
case self.SignalReflexDivergence:
class = self.DiagnosisReflexFailure
case self.SignalStaleDependency:
class = self.DiagnosisProviderFailure
case self.SignalRetryFallback:
class = self.DiagnosisRoutingInefficiency
case self.SignalOutcomeMismatch:
class = self.DiagnosisOutcomeVerificationFailure
}
return self.Diagnosis{Class: class, Status: self.EpistemicHypothesis, Rationale: "classified from bounded signal; independent confirmation required", EvidenceRefs: uniqueRefs(signal.EvidenceRefs, MaxDiagnosisEvidence), ConfidenceMillis: 250}
}
func BuildWeaknessPattern(principal, taskPattern, scopeKey, environment string, incidents []self.LearningSignal, affectedComponent, strategy string) (self.WeaknessPattern, bool, error) {
if principal == "" || taskPattern == "" || scopeKey == "" || affectedComponent == "" || len(incidents) < MinWeaknessIncidents {
return self.WeaknessPattern{}, false, nil
}
refs := []self.SemanticReference{}
severity, last := 0, int64(0)
for _, incident := range incidents {
if err := incident.Validate(); err != nil || incident.PrincipalID != principal || incident.TaskPattern != taskPattern || incident.ScopeKey != scopeKey {
return self.WeaknessPattern{}, false, fmt.Errorf("weakness incidents are not one bounded context")
}
refs = append(refs, incident.EvidenceRefs...)
if incident.Severity > severity {
severity = incident.Severity
}
if incident.UserImpact > severity {
severity = incident.UserImpact
}
if incident.ObservedAt > last {
last = incident.ObservedAt
}
}
return self.WeaknessPattern{ID: self.AssessmentID("weakness:" + principal + ":" + taskPattern + ":" + scopeKey), PrincipalID: principal, Revision: 1, PatternKey: taskPattern + ":" + scopeKey, TaskPattern: taskPattern, ScopeKey: scopeKey, EnvironmentVersion: environment, FailureMode: string(AttributeFailure(incidents[len(incidents)-1]).Class), DetectionEvidenceRefs: uniqueRefs(refs, MaxDiagnosisEvidence), State: self.AssessmentCandidate, RecordedAt: last, Frequency: len(incidents), LastObservedAt: last, Severity: severity, AffectedComponent: affectedComponent, AffectedStrategy: strategy, ConfidenceMillis: int64(min(1000, 250+len(incidents)*150)), EpistemicStatus: self.EpistemicSupported}, true, nil
}
func AssessContextualCompetence(principal string, subject self.SemanticReference, context []string, evidence []self.SemanticReference, quality, reliability, confidence, assessedAt int64) (self.CompetenceAssessment, error) {
v := self.CompetenceAssessment{ID: self.AssessmentID("competence:" + principal + ":" + string(subject.StableID)), PrincipalID: principal, SubjectRef: subject, Revision: 1, ContextMatcher: append([]string(nil), context...), QualityMillis: quality, ReliabilityMillis: reliability, ConfidenceMillis: confidence, EvidenceRefs: uniqueRefs(evidence, MaxDiagnosisEvidence), AssessedAt: assessedAt, LastValidatedAt: assessedAt}
if err := v.Validate(); err != nil {
return self.CompetenceAssessment{}, err
}
return v, nil
}
func BuildImprovementNeed(signal self.LearningSignal, diagnosis self.Diagnosis, component string) (self.ImprovementNeed, error) {
if err := signal.Validate(); err != nil {
return self.ImprovementNeed{}, err
}
if err := diagnosis.Validate(); err != nil {
return self.ImprovementNeed{}, err
}
if component == "" {
return self.ImprovementNeed{}, fmt.Errorf("affected component is required")
}
priority := signal.Severity*2 + signal.UserImpact + signal.CorrectionCount
if priority < 1 {
priority = 1
}
return self.ImprovementNeed{ID: self.AssessmentID("need:" + signal.ID), PrincipalID: signal.PrincipalID, SubjectRef: signal.SubjectRef, ProblemSummary: "bounded evidence indicates an improvement opportunity", EvidenceRefs: diagnosis.EvidenceRefs, ExpectedBenefit: "reduce recurrence without weakening verified behavior", Priority: priority, State: "OPEN", CreatedAt: signal.ObservedAt, Diagnosis: diagnosis.Class, DiagnosisStatus: diagnosis.Status, AffectedComponent: component, ScopeKey: signal.ScopeKey, Risk: "regression in established behavior", Intervention: interventionFor(diagnosis.Class), Hypothesis: diagnosis.Rationale, EvaluationPlan: "independent benchmark and must-not-worsen evaluation", MustNotWorsen: "quality and verified outcome rate", RollbackPlan: "disable candidate and restore last-known-good", ResourceCost: "bounded review"}, nil
}
func BuildCandidate(need self.ImprovementNeed, creator, evaluator string, evidence []self.SemanticReference, at int64) (self.ImprovementCandidate, error) {
ref := self.SemanticReference{OwnerNamespace: "self", SemanticKind: "improvement_need", StableID: ids.StableID(need.ID)}
c := self.ImprovementCandidate{ID: "candidate:" + string(need.ID), PrincipalID: need.PrincipalID, NeedRef: ref, Hypothesis: need.Hypothesis, ExpectedEffect: need.ExpectedBenefit, EvaluationPlan: need.EvaluationPlan, MustNotWorsen: []string{need.MustNotWorsen}, RollbackPlan: need.RollbackPlan, ResourceCost: need.ResourceCost, Creator: creator, IndependentEvaluator: evaluator, State: self.CandidateProposed, EvidenceRefs: uniqueRefs(evidence, MaxCandidateEvidence), CreatedAt: at}
if err := c.Validate(); err != nil {
return self.ImprovementCandidate{}, err
}
return c, nil
}
func EvaluateCandidate(candidate self.ImprovementCandidate, evaluation self.ImprovementEvaluation) (self.CandidateState, error) {
if evaluation.Evaluator == "" || evaluation.Evaluator == candidate.Creator || !evaluation.MustNotWorsenPass || evaluation.ActualQuality < evaluation.ExpectedQuality || (evaluation.ExpectedLatency > 0 && evaluation.ActualLatency > evaluation.ExpectedLatency*2) {
return self.CandidateRejected, nil
}
return self.CandidateKept, nil
}
func ClassifyDecisionRegret(signal self.LearningSignal) self.DecisionRegretKind {
if signal.DecisionRegret != "" {
return signal.DecisionRegret
}
switch signal.ActualOutcome {
case "information unavailable":
return self.RegretUnavailableThen
case "available but unused":
return self.RegretAvailableUnused
case "ignored in context":
return self.RegretIgnoredInContext
}
return ""
}
func BuildBenchmarkCase(principal, class string, evidence []self.SemanticReference, at int64) (self.BenchmarkCase, error) {
c := self.BenchmarkCase{ID: "benchmark:" + principal + ":" + class, PrincipalID: principal, TaskClass: class, EvidenceRefs: uniqueRefs(evidence, MaxCandidateEvidence), RecordedAt: at}
if !c.Valid() {
return self.BenchmarkCase{}, fmt.Errorf("benchmark requires evidence")
}
return c, nil
}
func AppendLedgerOutcome(need self.ImprovementNeed, candidate self.ImprovementCandidate, evaluation self.ImprovementEvaluation, decision self.CandidateState, at int64) (self.LearningLedgerEntry, error) {
ref := self.SemanticReference{OwnerNamespace: "self", SemanticKind: "improvement_need", StableID: ids.StableID(need.ID)}
refs := append(append([]self.SemanticReference{}, need.EvidenceRefs...), evaluation.EvidenceRefs...)
e := self.LearningLedgerEntry{ID: self.LearningLedgerEntryID("ledger:" + candidate.ID + ":" + fmt.Sprint(at)), PrincipalID: need.PrincipalID, ImprovementNeedRef: &ref, ObservedProblem: need.ProblemSummary, EvidenceRefs: uniqueRefs(refs, MaxCandidateEvidence), Diagnosis: string(need.Diagnosis), Hypothesis: need.Hypothesis, ProposedChangeRef: candidate.NeedRef, ExpectedImprovement: need.ExpectedBenefit, ActualEvaluationRefs: evaluation.EvidenceRefs, Decision: string(decision), RecordedAt: at}
if err := e.Validate(); err != nil {
return self.LearningLedgerEntry{}, err
}
return e, nil
}
func ReviewCandidateForNewModel(principal string, model self.SemanticReference, evidence []self.SemanticReference, at int64) (self.ImprovementCandidate, error) {
n := self.ImprovementNeed{ID: self.AssessmentID("need:model-review:" + string(model.StableID)), PrincipalID: principal, SubjectRef: model, ProblemSummary: "qualified model is available for governed review", EvidenceRefs: evidence, ExpectedBenefit: "compare model quality under existing safety constraints", Priority: 1, State: "OPEN", CreatedAt: at, Intervention: self.InterventionProviderReview, Hypothesis: "model may improve quality", EvaluationPlan: "independent benchmark", MustNotWorsen: "safety, latency and verified outcomes", RollbackPlan: "retain current route", ResourceCost: "bounded evaluation"}
return BuildCandidate(n, "qualification", "independent-evaluator", evidence, at)
}
func interventionFor(c self.DiagnosisClass) self.InterventionClass {
switch c {
case self.DiagnosisSkillFailure:
return self.InterventionSkillReview
case self.DiagnosisReflexFailure:
return self.InterventionReflexReview
case self.DiagnosisProviderFailure:
return self.InterventionProviderReview
case self.DiagnosisRoutingInefficiency:
return self.InterventionRoutingReview
case self.DiagnosisContextSelection, self.DiagnosisMemoryNotRetrieved:
return self.InterventionContextReview
}
return self.InterventionConfiguration
}
func uniqueRefs(refs []self.SemanticReference, max int) []self.SemanticReference {
seen := map[string]bool{}
out := make([]self.SemanticReference, 0, len(refs))
for _, r := range refs {
if !r.Valid() || seen[string(r.StableID)] {
continue
}
seen[string(r.StableID)] = true
out = append(out, r)
if len(out) == max {
break
}
}
sort.Slice(out, func(i, j int) bool { return string(out[i].StableID) < string(out[j].StableID) })
return out
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func ImprovementForReflexDivergence(principal string, reflexRef self.SemanticReference, evidence []self.SemanticReference, at int64) (self.ImprovementNeed, self.LearningLedgerEntry, error) {
s := self.LearningSignal{ID: "signal:reflex:" + string(reflexRef.StableID), PrincipalID: principal, Kind: self.SignalReflexDivergence, TaskPattern: "reflex", ScopeKey: "default", SubjectRef: reflexRef, EvidenceRefs: evidence, ObservedAt: at, Severity: 1}
d := AttributeFailure(s)
n, err := BuildImprovementNeed(s, d, "reflex")
if err != nil {
return self.ImprovementNeed{}, self.LearningLedgerEntry{}, err
}
ref := self.SemanticReference{OwnerNamespace: "self", SemanticKind: "improvement_need", StableID: ids.StableID(n.ID)}
l := self.LearningLedgerEntry{ID: self.LearningLedgerEntryID("ledger:" + string(reflexRef.StableID) + ":" + fmt.Sprint(at)), PrincipalID: principal, ImprovementNeedRef: &ref, ObservedProblem: n.ProblemSummary, EvidenceRefs: evidence, Diagnosis: string(d.Class), Hypothesis: d.Rationale, ProposedChangeRef: reflexRef, ExpectedImprovement: n.ExpectedBenefit, Decision: "PROPOSE_REVALIDATION", RecordedAt: at}
if err := l.Validate(); err != nil {
return self.ImprovementNeed{}, self.LearningLedgerEntry{}, err
}
return n, l, nil
}