Files
Cyrene_v2/internal/runtime/memory/preinsert_config.go
T
AskaEth 1b4d0a552f
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(memory): T1 灰区模型判定 —— D3 入库前审查补上 LLM 那一半
主人 2026-10-07 裁定「可以做 T1」。此前 D3 只有纯规则 T0,凡灰区(相似但
互不包含)一律保守 create,真机上「同一事实换措辞」类重复结构性治不住
(清完 92 条字面级重复后,active 里含「喵喵七号」的仍有 101 条)。

- 插入点:T0 判定内核只多吐一份结构化 Gray(惰性收集,恒零模型调用);
  T1 挂在异步审查层 Review,同步入库闸不碰它 ⇒ 写入路径零模型调用是
  **数据流结构**而不是承诺
- 只许回 merge / modify / keep + 一句理由;坏 JSON、非法枚举、编号越界
  一律回落 create(绝不猜、绝不挑最近的一条)
- 禁产新内容:模型只给「动作 + 候选编号 + 一句理由」,正文一律取自库里
  既有的两行 ⇒ T1 结构性地不可能把幻觉写进记忆库
- 不越硬门与红线:灰区已滤掉跨角色/作用域/项目/只读来源与时效/数值/冲突/
  低置信;「规则否决一票否决」不可走样,T1 不提供翻案入口
- 有界:每次审查至多一次调用(灰区打包,上限 8 条),子超时 8s 且不超过
  审查总预算;失败只改 reason、决定恒 create、零 UPDATE
- 开关 t1Judge(放 $DATA_DIR/config/memory-tidy.json,零新 env 键),默认开;
  模型出口用闭包晚绑定既有模型栈(零新客户端、零新依赖)
- 留痕 reason 结构化(t1_merge / t1_modify / t1_keep / t1_reply_invalid /
  t1_unavailable),rule_version d3.1 → d3.2;模型的自由文本理由一律丢弃
- preinsert_config.go 为纯搬移(为守 500 行上限,无新逻辑)
2026-10-07 14:10:33 +08:00

103 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package memory
import "time"
// 本文件是 D3「入库前审查」(design/23 §15.1 D3)的**调优面**:默认值常量、
// ReviewConfig 的旋钮与它们的零值归一化。它从 preinsert.go 拆出来只为守
// AGENTS §10 的文件行数上限(≤500 行)与「模块按职责拆源文件」那条规范 ——
// 口径逐字未变。
//
// 边界:本文件只管「旋钮长什么样 + 零值取什么默认值」,不认识服务本体
// (PreInsertReviewer 的队列/工作协程在 preinsert.go),也不认识任何开关的
// 运维来源(env 与 `$DATA_DIR/config/memory-tidy.json` 都在组合根)。
// 默认值与审计口径。运算路径上不出现裸魔法数字(AGENTS §10)。
const (
// defaultReviewQueueSize 是有界审查队列长度:审查是「尽力而为」的旁路,
// 队列满时宁可少审一条(记忆早已落库),也绝不阻塞写入方。
defaultReviewQueueSize = 256
// defaultReviewScanLimit 是每次审查在同一分区内最多比对的既有条目数。
defaultReviewScanLimit = 200
// defaultReviewTimeout 是单条审查的墙钟上限。
defaultReviewTimeout = 20 * time.Second
// reviewAuditWho / reviewAuditVerb 是审查留痕的审计口径:它是**系统任务**,
// 不是任何角色(design §4.1 C 的同一理由)。
reviewAuditWho = "system/memoryreview"
reviewAuditVerb = "memory.review.ingest"
)
// ReviewConfig 携带入库前审查的调优旋钮。零值经 normalize 选取安全默认值。
type ReviewConfig struct {
// Enabled 是总开关。关闭时组合根不建、不接、不跑 —— 写入路径行为逐字
// 回到本次改动之前。
Enabled bool
// QueueSize 是有界队列长度(≤0 用默认)。
QueueSize int
// ScanLimit 是单次审查的候选上限(≤0 用默认)。
ScanLimit int
// Timeout 是单条审查的墙钟上限(≤0 用默认)。
Timeout time.Duration
// Threshold 是候选召回的余弦门,**复用记忆整理的既有阈值**
// (config.MemoryTidy.RecallThreshold,design §13 开放点 2 = 0.80):
// 入库前审查与夜间整理必须用同一把尺子,否则同一对条目会有两个结论。
// 非正用设计稿默认值。
Threshold float64
// DisableIngestGate 关闭**同步入库闸**(任务书规格 3 的开关)。
//
// 它是**反向**字段,且这是刻意的:bool 的零值必须落在「默认行为」上 ——
// 同步闸本机制的默认取向(主人 2026-10-07「把记忆整理功能接到昔涟和记忆库
// 中间做审查,由整理功能进行入库」),若写成正向的 EnableIngestGate,任何没
// 显式配过的地方(含测试与组合根构造的一半配置)都会被读成「关掉」,与默认
// 相反。要一键回到「只插不审」(= 本次改动之前的异步行为),写
// `$DATA_DIR/config/memory-tidy.json` 的 `{"ingestGate": false}`(**零新
// env 键**:`.env` 已冻结,design/13 §2.1)。
DisableIngestGate bool
// GateBudget 是同步入库闸的**总墙钟预算**(任务书规格 1:写入路径不许变慢)。
// 非正用代码默认值 defaultReviewGateBudget。超预算一律 fail-open(照常插入 +
// 留痕),绝不静默丢。
GateBudget time.Duration
// GateScanLimit 是同步入库闸的候选扫描上限。它比异步队列更紧(后者
// ScanLimit 默认 200),因为它在**写入路径**上:非正用代码默认值
// defaultReviewGateScanLimit,且最终还会被 cfg.ScanLimit 收紧取小。
GateScanLimit int
// DisableT1 关闭 **T1 灰区模型判定**(design/23 §15.1 D3-2:灰区调一次轻量
// 模型,只许回 merge/modify/keep + 理由、禁产新内容)。
//
// 它是**反向**字段,理由与 DisableIngestGate 逐字相同:bool 的零值必须落在
// 「默认行为」上,而 T1 的代码默认值是**开**(主人 2026-10-07:要的就是「真的
// 走记忆管理会话」,T0 仍是第一道零成本闸)。若写成正向的 EnableT1,任何没
// 显式配过的地方(含测试与组合根构造的一半配置)都会读成「关掉」,与默认相反。
// 要一键回到纯规则(零模型调用),写 `$DATA_DIR/config/memory-tidy.json` 的
// `{"t1Judge": false}`(**零新 env 键**:`.env` 已冻结,design/13 §2.1)。
DisableT1 bool
// T1Timeout 是**单次** T1 模型调用的墙钟上限。非正用代码默认值
// defaultT1Timeout;最终还会被 cfg.Timeout 收紧取小(见 t1Timeout)。
T1Timeout time.Duration
}
// normalizeReviewConfig 用设计稿默认值填充零值字段。
func normalizeReviewConfig(cfg ReviewConfig) ReviewConfig {
if cfg.QueueSize <= 0 {
cfg.QueueSize = defaultReviewQueueSize
}
if cfg.ScanLimit <= 0 {
cfg.ScanLimit = defaultReviewScanLimit
}
if cfg.Timeout <= 0 {
cfg.Timeout = defaultReviewTimeout
}
if cfg.Threshold <= 0 {
cfg.Threshold = defaultTidyRecallThreshold
}
if cfg.GateBudget <= 0 {
cfg.GateBudget = defaultReviewGateBudget
}
if cfg.GateScanLimit <= 0 {
cfg.GateScanLimit = defaultReviewGateScanLimit
}
if cfg.T1Timeout <= 0 {
cfg.T1Timeout = defaultT1Timeout
}
return cfg
}