refactor(memory): M45 裁决 D —— temporal 位撤出判定,只保留观测
按 r1 复审裁决 D:这一位先只做观测,`resolveRetention` 不许读它。理由(实测):
无条件 one_off ⇒ temp 会当天删 21 条长期约定;只放开 N6 位置判据能翻的 13 条
逐条是约定、而复审点名的三条目标行 0/3 救得了。在「模型真实标签分布」这份证据
缺位时不该做 7 天后物理删的不可逆决定。
撤接线(结论集逐字回到 M45 之前):
- retention.go:resolveRetention 不再读 item.Temporal;与之对比 dev base
(af61b8c)本文件**只有注释差异**,规则与判定入口逐字未变。
- extract_retention.go:judgeExtractionRetention 签名回退单参;删除
judgeRetentionTimepointWide 与 n6_timepoint_wide 规则码。与 dev base 对比
**同样只有注释差异** ⇒ 「结论集 = 不含 temporal 的那个集合」是结构性的,
不是靠调用点记得别传。
- 连带取消初版的 durable / recurring ⇒ long(零影响必须是双向的)。
保留观测面(一律未动):迁移 0027 的列、自举前滚、提取器的 temporal 产出、
prompt 枚举、闭集与 normalizeTemporal、store_temporal.go 的校验/编码。
另外把 prompt 里「标成 one_off 会让它进临时层、到期消失」这句**撤接线后已是假话**
改成中性标注说明 —— 否则会诱导模型避开 one_off,观测数据直接被污染。
测试:
- TestTemporalBitDoesNotAffectRetention —— 4 类语料 × 5 个取值,(档位,规则码)
与未分型逐字相同(核心正断言);
- TestTemporalOneOffDoesNotMakeAnythingTemporary / ...DoesNotRescueRuleHitRows
—— 零影响的双向点名;
- TestTemporalIsStillRecorded —— one_off 的约定行仍落 temporal='one_off' 而档位
long、expires 空(观测在、判定不动);
- TestTemporalDoesNotTouchExplicitTierOrRestrictedSource —— 人工入口与受限来源不变。
变异实测:把 `if item.Temporal == TemporalOneOff { return temp }` 插回
resolveRetention ⇒ 上述四条用例全红(FAIL 输出已贴给塔),恢复后全绿。
IngestRuleVersion 保持 d3.5,注释改准为「记录 temporal 信号,但不参与判定」
(留痕面变了:库里多了「模型当时标了什么」这一维,回退会让新旧留痕混在一起)。
P2 三条保留:store.go 500 行(拆 store_temporal.go)、temporal.go 头注('' 的口径
+ 两次被否的接线方案 + 接线前必须看的那两条统计 SQL)、报告语境段。
回归:go build ./... + go vet ./... 全绿;memory/db 包测试全绿;memory -race
38.2s 全绿。
This commit is contained in:
1 parent
3b9ae3f918
commit
c7c0294a16
11 files changed
+280
-551
No files matched your search
@@ -32,11 +32,12 @@ import (
|
||||
//
|
||||
// 🆕 2026-10-27(切片 M45):prompt 里追加**时间性位** `temporal`(四个枚举值)。
|
||||
// 它**不是**「又一段靠自然语言描述判据」的提示词加固:模型只负责**标注**一个值为
|
||||
// 四个枚举之一,**判定**(one_off ⇒ temp / durable ⇒ long / unknown ⇒ 走旧规则)
|
||||
// 是 temporal.go 里的一段 Go 闭集 switch,落库是 `temporal` 列(迁移 0027)——
|
||||
// 因此 AGENTS §9「判据靠元数据,不靠提示词」在这里是**结构性成立**的:即便模型
|
||||
// 完全不听这段说明,最坏结果也只是它一律答 unknown(= 回到本片之前的旧规则),
|
||||
// 不会产生任何新的误判路径。
|
||||
// 四个枚举之一,落库是 `temporal` 列(迁移 0027)。
|
||||
//
|
||||
// 🔴 **这一位目前不参与判定**(r1 复审裁决 D):`resolveRetention` 不读它,判定的
|
||||
// 结论集与没有它逐字相同 —— 保留它只为积累「模型到底怎么标」的证据(temporal.go
|
||||
// 头注记着两次被生产重放否掉的接线方案与接线前必须看的那份分布)。因此这段 prompt
|
||||
// 的**唯一**作用是让观测面有数据;即便模型完全不听,判定的行为与今天一致。
|
||||
const extractSystemPrompt = `你是记忆提取器。从对话中提取**值得长期记住**的记忆项。
|
||||
|
||||
只保留这些(正向清单):
|
||||
@@ -59,20 +60,18 @@ const extractSystemPrompt = `你是记忆提取器。从对话中提取**值得
|
||||
|
||||
每条还要标一个**时间性位** "temporal",只能取下面四个英文值之一:
|
||||
- "one_off":这条说的是**某一次**发生的事(今天 / 昨天 / 这次 / 某个具体日子),
|
||||
即使你已经把它写成「用户……」这样的陈述句 —— 一次性事件标成 durable 会让它
|
||||
永远留着,标成 one_off 会让它进临时层、到期自动消失,所以只有确实只发生过
|
||||
一次时才标它;
|
||||
即使你已经把它写成「用户……」这样的陈述句;
|
||||
- "recurring":说的是**反复发生**的规律(每天 / 每周 / 每次都…);
|
||||
- "durable":说的是**长期成立**的事实 / 偏好 / 约定 / 称呼 / 身份;
|
||||
- "unknown":拿不准,或者上面三个都不贴切。
|
||||
|
||||
temporal 只许填这四个英文值之一(不要换成中文、不要自造新值);拿不准就填
|
||||
"unknown" —— 标错方向的代价不对称:标成 one_off 会让本该留着的记忆被提前删掉。
|
||||
"unknown"。这个标记会被记录下来供以后核对用途,如实标注即可,不要为了省字省掉它。
|
||||
|
||||
只输出一个 JSON 数组,不要任何其他文字。每个元素形如:
|
||||
{"content":"记忆内容","scope":"user|persona|project","kind":"relationship|world_event","temporal":"one_off|recurring|durable|unknown","tags":["标签"],"confidence":0.9,"project_id":"可选项目id"}
|
||||
去噪去重:只保留值得长期记住的,忽略寒暄与临时信息;确实发生过的**一次性事件**
|
||||
如果值得记,也照实提取,但必须把 temporal 标成 one_off(它会进临时层)。`
|
||||
如果值得记,也照实提取,并把 temporal 标成 one_off。`
|
||||
|
||||
// llmCandidate 是提取器要求 LLM 输出的线上形状:每条候选
|
||||
// 记忆一个 JSON 对象。模型未给出合法值时,Scope/Kind 取提取器配置的
|
||||
|
||||
Reference in new issue
Block a user