feat(persona,capacity): persona awakening 档位真生效 + 当前主角色已填(裁决 14.4.3)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- loader 新增可选 awakening 块(default_state/overrides{min_confidence,force_state}),载入期校验 + 归一化,
  非法值显式报错(拒绝「写了却静默无效」);缺失 = nil = 无该档位,不补默认
- 组合根把当前主角色档案的档位填进 capacity.Config.Awakening(未知档位名告警后按无档位处理),
  真的参与 Decide → applyAwakening(MinConfidence 优先于全局 0.6;force_state 钉死态)
- 当前主角色 cyrene.yaml 填 default_state=静息 + min_confidence=0.7(设计 §3 陪伴型取静息;
  0.7 压掉 L2 趋势(0.66)误唤醒、仍低于 L1 强信号(0.9),温柔陪伴不误打扰)
- 复核:build/vet 绿 + persona/capacity/cmd 包测试绿;端到端 4 例锁定「同回合有无档位判定相反」;
  变异两轮(yaml tag 改错 → 8 处红;解析但不接线 → 端到端红)均已还原
- 已知盲区(另派):per-role 注入未接线(loop/request.go 的 Input.Awakening);
  面板整块替换 persona 会抹掉 awakening 块(需 applyAwakeningEdit)
This commit is contained in:
AskaEth committed 2026-10-04 12:49:40 +08:00
1 parent 38b4527949
commit 71fefe5fc3
7 files changed
+602 -1

No files matched your search

+116
View File
@@ -0,0 +1,116 @@
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
}