diff --git a/internal/runtime/memory/extract_retention.go b/internal/runtime/memory/extract_retention.go index ac4aaa5..23b751b 100644 --- a/internal/runtime/memory/extract_retention.go +++ b/internal/runtime/memory/extract_retention.go @@ -150,7 +150,9 @@ func judgeRetentionChatter(text string) retentionRule { // **为什么它在结构上不可能把带实体的句子判成 defer**(不是"概率很低",是结构性): // // 1. 匹配要求**整句被消费干净**(`retentionEmotionFormRest` 返回空余部)。任何 -// 一个字符只要不在封闭表里,就会留在余部 ⇒ 判不出空 ⇒ **放行**。 +// 一个**非标点 / 非空白**的字符只要不在词表里,就会留在余部 ⇒ 判不出空 ⇒ +// **放行**。(**标点与空白是被容忍的**:它们由 `onlyRetentionPunctuation` 在收尾 +// 时清掉,所以 `心情不错!` 实测 `rest=""` ⇒ 仍判 defer。) // // 2. **位置锁定 + 核心之后只允许四类修饰** —— 这才是真正保安全的一条。 // (早先这里写的是「封闭表里没有承载内容的词」,**那句被 reviewer 证伪** @@ -158,12 +160,16 @@ func judgeRetentionChatter(text string) retentionRule { // `压力大` 是动宾,而主语表里的 `主人`/`我`/`他` 本身就是指人实体。) // // 真正的机制是**位置**而不是"表里没有实词": -// - 主语 / 时间词**只允许出现在句首**、各至多一次 ⇒ `主人`/`我`/`你`/`他`/`今天` +// - **位置锁定**(按实现写):主语只在**位置 0** 被消费一次;时间词只在**紧随其后** +// 被消费一次(无主语时它就是首词)。两者都**没有**「后面还能再出现」的缝 —— +// `主人今天好累` 匹配(`rest=""`),而 `今天主人好累` 连核心都认不出 +// (`rest="今天主人好累"`, `saw=false` ⇒ 放行)。⇒ `主人`/`我`/`你`/`他`/`今天` // 这些**指人 / 指时的实体在文法里永远不可能落到宾语位置**(上一版正是死在 // 这里:`会想我` 的 `我` 被当成主语剥掉了); // - 核心之后只允许「程度 / 否定 / 情绪 / 语气」四类 token,而它们**只允许紧跟 // 在情绪核心之后** —— 核心本身必须先被消费掉,因此那一段在语义上只能是 -// **情绪的修饰**(「心情**不错**」「**不**开心」「生气**得很**」), +// **情绪的修饰**(「心情**不错**」「**不**开心」「心情**很好**」,三条均实测 +// `rest=""`;反例「生气**得很**」的 `得` 不在表里 ⇒ `rest="得很"` ⇒ 放行), // 装不下一个宾语短语。核心之后一旦出现四类之外的东西(`想`/`会`/`时`/`和`/ // `听音乐`/`妈妈`),**必然消费不掉** ⇒ 余部非空 ⇒ 放行。 // @@ -408,7 +414,9 @@ func retentionEmotionSuffixTokens() []string { return tokens } -// retentionEmotionTimeWords 是**只允许出现在句首**的时间词(「今天好累」)。 +// retentionEmotionTimeWords 是**只允许在「主语之后」出现一次**的时间词:无主语时 +// 它就是首词(「今天好累」),有主语时紧跟主语(「主人今天好累」)—— 之外的位置一律 +// 不匹配(`今天主人好累` 实测 `rest="今天主人好累"`, `saw=false` ⇒ 放行)。 // 它们让一句情绪陈述显形为「今天怎么样」,本身不含实体。 func retentionEmotionTimeWords() []string { return []string{