package loader import ( "errors" "fmt" "math" "strings" ) // 唤醒档位(design/14 §3,裁决 14.4.3)的规范态名,与能力决策层 // capacity.State 的三个态同名。persona YAML 里既可按设计原文写中文档位名 // (静息/唤醒/全能),也可写这里的规范态名;载入期一律归一化为规范态名, // 组合根据此直接映射到 capacity.Awakening,不做二次猜测。 const ( // AwakeningStateQuiet 是静息档(静·陪伴):保守落点,窄工具面。 AwakeningStateQuiet = "quiet" // AwakeningStateAwake 是唤醒档(醒·办事):识别到真实任务时唤醒工具。 AwakeningStateAwake = "awake" // AwakeningStateFull 是全能档(全·复杂任务):完整能力窗口。 AwakeningStateFull = "full" ) // awakeningStateLabels 是设计文档写法的中文档位名到规范态名的映射。 func awakeningStateLabels() map[string]string { return map[string]string{ "静息": AwakeningStateQuiet, "唤醒": AwakeningStateAwake, "全能": AwakeningStateFull, } } // ErrInvalidAwakening 由 awakening 块的校验错误包裹,使调用方能识别 // 「块写了、但档位名或阈值不合法」,而不是被静默忽略 // (design/01 §2.1 红线:不允许静默无效的配置)。 var ErrInvalidAwakening = errors.New("persona: invalid awakening config") // AwakeningParams 是人格可选的 `awakening` 块(design/14 §3):人格倾向 // (default_state)加上对意图感知结果的覆盖规则(overrides)。它只描述 // 「唤醒倾向」这一档位,**不构成能力边界**——能力始终全局全能。 // // 语义:**块缺席 = 无覆盖**,不是配置错误(裁决 14.4.3「缺席就是没有」)。 // 因此整块与子块都用指针:nil 明确表示「未声明」,本层**绝不**补任何默认档位, // 没写的角色就由决策层走全局默认(静息倾向 + 全局阈值)。 type AwakeningParams struct { // DefaultState 是人格倾向落点(default_state):意图各层无定论或低置信时 // 落到该态。空串 = 未声明该档位。 DefaultState string `yaml:"default_state,omitempty"` // Overrides 是覆盖规则(overrides);整块缺席时为 nil。 Overrides *AwakeningOverrides `yaml:"overrides,omitempty"` } // AwakeningOverrides 是 `awakening.overrides` 块的字段(design/14 §3)。 type AwakeningOverrides struct { // MinConfidence 是人格级唤醒阈值(min_confidence):正值优先于全局 // CAPACITY_MIN_CONFIDENCE;低于阈值强制回落 default_state(保守、不打扰)。 // 零值 = 未声明(沿用全局阈值)。 MinConfidence float64 `yaml:"min_confidence,omitempty"` // ForceState 设定即无条件钉死状态(force_state),无视意图感知输出。 // 空串 = 未声明。 ForceState string `yaml:"force_state,omitempty"` } // ValidateAwakening 校验并**就地归一化**该人格的 awakening 块。非法档位名或 // 越界阈值返回 %w 包裹的 ErrInvalidAwakening(明确报错,绝不静默忽略); // 块缺席(nil)返回 nil——缺席就是没有该档位,不是错误。归一化后 // DefaultState / Overrides.ForceState 一律是 AwakeningStateQuiet/Awake/Full, // 组合根可直接映射成决策层的态。 func (p *Persona) ValidateAwakening() error { if p == nil || p.Awakening == nil { return nil } state, err := normalizeAwakeningState(p.Awakening.DefaultState, "default_state") if err != nil { return err } p.Awakening.DefaultState = state if p.Awakening.Overrides == nil { return nil } force, err := normalizeAwakeningState(p.Awakening.Overrides.ForceState, "overrides.force_state") if err != nil { return err } p.Awakening.Overrides.ForceState = force if err := validateAwakeningConfidence(p.Awakening.Overrides.MinConfidence); err != nil { return err } return nil } // normalizeAwakeningState 把档位字面量归一化为规范态名。空串(未声明)原样返回; // 非法值返回 %w 包裹的 ErrInvalidAwakening,错误信息点名出错字段与可选值。 func normalizeAwakeningState(raw, field string) (string, error) { trimmed := strings.TrimSpace(raw) switch strings.ToLower(trimmed) { case "": return "", nil case AwakeningStateQuiet, AwakeningStateAwake, AwakeningStateFull: return strings.ToLower(trimmed), nil } if canonical, ok := awakeningStateLabels()[trimmed]; ok { return canonical, nil } return "", fmt.Errorf("%w: %s %q (want %s|%s|%s 或 静息|唤醒|全能)", ErrInvalidAwakening, field, raw, AwakeningStateQuiet, AwakeningStateAwake, AwakeningStateFull) } // validateAwakeningConfidence 校验人格级阈值:它是一次判定的置信度,取值域为 // [0,1];负数、超过 1 或 NaN 都判为非法——越界阈值会让「是否唤醒」变成无意义的 // 恒定值,宁可显式报错也不静默生效。 func validateAwakeningConfidence(v float64) error { if math.IsNaN(v) || v < 0 || v > 1 { return fmt.Errorf("%w: overrides.min_confidence %v (want 0..1)", ErrInvalidAwakening, v) } return nil }