192 lines
11 KiB
Go
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
|
|
}
|