feat(urgency): 通用紧急度通道(Declarer/Level + 只抬不压折算)+ IoT 首个生产者 + 她自己的结论走结构化标记穿透安静时段
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

This commit is contained in:
AskaEth committed 2026-10-05 13:11:04 +08:00
1 parent 4a2d276881
commit f91a83ff81
14 files changed
+917 -15

No files matched your search

+41 -3
View File
@@ -4,6 +4,8 @@ import (
"regexp"
"strings"
"unicode"
"git.yeij.top/AskaEth/Cyrene_v2/internal/urgency"
)
// 自主思考被引导(context.go 的 guidancePrompt)以如下紧凑、
@@ -12,6 +14,7 @@ import (
//
// [我决定:push] (或 no_push)
// [渠道:web|im|kook|telegram|...|auto]
// [紧急:routine|warning|critical] (可省略;见 parseProactiveUrgency)
// <温暖的正文内容>
//
// 这些标记按纯文本解析,因此模型稍有不同地
@@ -25,6 +28,9 @@ var (
decideMarker = regexp.MustCompile(`(?i)\[\s*我决定\s*[::]\s*(no_push|push)\s*\]`)
// channelMarker 匹配决策行上的目标渠道词。
channelMarker = regexp.MustCompile(`(?i)\[\s*渠道\s*[::]\s*([a-z0-9_\-]+)\s*\]`)
// urgencyMarker 匹配她自报的紧急度档位词。它是**结构化**通道:只认固定枚举,
// 绝不从散文里解读意图(项目红线);解析严格(必须带括号)=失败安全。
urgencyMarker = regexp.MustCompile(`(?i)\[\s*紧急\s*[::]\s*(routine|warning|critical)\s*\]`)
// decidePrefixes 是「尝试了决策但无法解析」的标记形状,
// 这类轮次被视为含糊(失败安全)。它
// 刻意只含带括号形式,因此仅仅在散文中出现
@@ -36,8 +42,14 @@ var (
// 与全角括号形式处理,因此格式良好的决策永不泄漏。
stripDecideRe = regexp.MustCompile(`(?i)[【\[]\s*我决定\s*[::]\s*(no_push|push)\s*[】\]]`)
stripChannelRe = regexp.MustCompile(`(?i)[【\[]\s*渠道\s*[::]\s*[a-z0-9_\-]+\s*[】\]]`)
// stripUrgencyRe 删掉带括号的紧急度标记,档位值不限枚举:她写成枚举外的词时
// 解析侧不上抬(保守),面向用户的正文里也绝不能留着这段内部标记。
stripUrgencyRe = regexp.MustCompile(`(?i)[【\[]\s*紧急\s*[::]\s*[^】\]\s]+\s*[】\]]`)
bareDecideRe = regexp.MustCompile(`(?i)我决定\s*[::]\s*(no_push|push)`)
bareChannelRe = regexp.MustCompile(`(?i)渠道\s*[::]\s*[a-z0-9_\-]+`)
// bareUrgencyRe 只认枚举档位:散文里「紧急:」这类词很常见,
// 不该因为恰好紧跟一个英文词就被删掉。
bareUrgencyRe = regexp.MustCompile(`(?i)紧急\s*[::]\s*(routine|warning|critical)`)
)
// nonePhrases 是没有标记时表示「无可汇报」的结论。
@@ -61,6 +73,8 @@ var nonePhrases = []string{
// P2 之前的默认一致。
// - 渠道提示是 [渠道:...] 词,转小写;为空/auto 则保持
// 默认的活跃客户端优先、IM 降级路由。
// - 她自报的紧急度由 parseProactiveUrgency 单独恢复(固定枚举的
// 结构化标记,绝不从散文解读)。
// - 决策为推送但正文为空或仍读作「无」 → 不推送
// (失败安全),因此单独一个标记绝不会推出空消息。
func parseProactive(conclusion string) (message, channelHint string, push bool) {
@@ -83,6 +97,28 @@ func parseProactive(conclusion string) (message, channelHint string, push bool)
return message, channelHint, push
}
// parseProactiveUrgency 从结论的结构化尾段恢复她自报的紧急度 —— 这是通用紧急度通道
// 的一种生产者形式(「她自己」):主动思考轮可以读外围插件数据、调工具,因此她要能
// 声明「我认为这条紧急、要突破安静时段发出去」。
//
// 它只认固定枚举 [紧急:routine|warning|critical],绝不从散文/自由文本里解读意图
// (项目红线:判断要结构化)。没写、格式错乱或写了枚举外的档位一律得到零值
// LevelRoutine(即不上抬)—— 保守默认优于猜测,误判只可能少打扰、不会多打扰。
func parseProactiveUrgency(conclusion string) urgency.Level {
m := urgencyMarker.FindStringSubmatch(conclusion)
if m == nil {
return urgency.LevelRoutine
}
switch strings.ToLower(m[1]) {
case "critical":
return urgency.LevelCritical
case "warning":
return urgency.LevelWarning
default:
return urgency.LevelRoutine
}
}
// matchDecide 返回匹配到的决策词(已转小写)以及
// 是否存在格式良好的决策标记。
func matchDecide(text string) (string, bool) {
@@ -118,10 +154,10 @@ func matchChannel(text string) string {
return strings.ToLower(m[1])
}
// stripInternalMarkers 移除结构化的决策/渠道标记,因此
// stripInternalMarkers 移除结构化的决策/渠道/紧急度标记,因此
// 主动消息正文绝不会把它们带给用户。它处理预期的
// 带括号形式([我决定:push][渠道:kook])以及模型可能发出的漂移形式——
// 裸的 "我决定:push" / "渠道:kook"(无括号)与全角 【我决定:push】——然后
// 带括号形式([我决定:push][渠道:kook][紧急:critical])以及模型可能发出的漂移形式——
// 裸的 "我决定:push" / "渠道:kook" / "紧急:critical"(无括号)与全角 【我决定:push】——然后
// 丢弃因此变空的行并压缩剩余部分。它既在
// parseProactive 内部应用(使消息与记录干净),又作为
// buildProactiveMessage 里的最后一道守护,因此无论标记如何
@@ -133,8 +169,10 @@ func stripInternalMarkers(text string) string {
for _, ln := range lines {
ln = stripDecideRe.ReplaceAllString(ln, "")
ln = stripChannelRe.ReplaceAllString(ln, "")
ln = stripUrgencyRe.ReplaceAllString(ln, "")
ln = bareDecideRe.ReplaceAllString(ln, "")
ln = bareChannelRe.ReplaceAllString(ln, "")
ln = bareUrgencyRe.ReplaceAllString(ln, "")
ln = strings.TrimSpace(ln)
if ln == "" {
continue