package memory import "fmt" // 本文件是入库前审查(design/23 §15.1 D3)的**观测面**:进程内计数与审计出口。 // 它从 preinsert.go 拆出来只为守 AGENTS §10 的文件行数上限(≤500 行)与 // 「模块按职责拆源文件」那条规范 —— 口径逐字未变。 // // 为什么要**分列**闸与异步队列的计数:同步入库闸(写路径上当场拍板)与异步 // 审查队列(后台补审)是两件事,把它们加成一个数会让「闸拦下了多少重复」与 // 「事后补审了多少」不可分辨。分列之后,面板/日志能分别读到两条路径的量。 // ReviewStats 是审查的观测计数(进程内累计,供日志/测试断言)。 type ReviewStats struct { Reviewed int Skipped int Merged int Modified int Created int Dropped int Failed int // GateChecked 是**同步入库闸**实际做了一次判定的条数(跳过闸的条目不计)。 GateChecked int // GateAbsorbed 是闸当场拦下并落成 `merged` 的条数(真重复,含 modify 的 // 「旧行被吸收」)。 GateAbsorbed int // GateFailed 是闸因预算/候选源不可用而 fail-open 的次数(照常插入 + 留痕)。 GateFailed int // T1Called 是 T1 实际发出过**模型调用**的次数。每次审查至多 +1 // (defaultT1MaxGray 只是把多条灰区候选打包进同一次 prompt,不多发请求)。 T1Called int // T1Decided 是 T1 真的定下结论(merge/modify/keep)的次数。它与 T1Called // 的差值就是「调了但没用上」(非法回复 / 超时 / 报错回落 create)—— // 这个差值本身是可观测信号,因此两个数分开计。 T1Decided int } // bump 在互斥下修改计数(工作协程、手动 Review 与同步闸都可能调用)。 func (r *PreInsertReviewer) bump(apply func(*ReviewStats)) { r.statsMu.Lock() defer r.statsMu.Unlock() apply(&r.stats) } // recordDecision 累计观测计数(含降级计数,便于运维判读)。 func (r *PreInsertReviewer) recordDecision(rev IngestReview) { r.bump(func(s *ReviewStats) { s.Reviewed++ switch rev.Decision { case IngestSkip: s.Skipped++ case IngestMerge: s.Merged++ case IngestModify: s.Modified++ default: s.Created++ } }) } // auditWith 写一条审计。verb 让「同步闸当场拦下的」与「异步审查事后合并的」 // 在审计链上可分(前者 memory.ingest.gate,后者 memory.review.ingest)。 // 它**只带决定与 id,绝不带任何记忆正文**。 func (r *PreInsertReviewer) auditWith(verb string, rev IngestReview) { result := fmt.Sprintf("decision=%s reason=%s item=%s target=%s degraded=%t score=%.3f", rev.Decision, rev.Reason, rev.ItemID, rev.TargetID, rev.Degraded, rev.Score) if r.deps.Audit == nil { r.log.Debug("memory ingest review audit (no sink configured)", "what", verb, "result", result) return } r.deps.Audit(reviewAuditWho, verb, result) } // reviewStatsLabel 把计数渲染成一条稳定日志串。 func reviewStatsLabel(s ReviewStats) string { return fmt.Sprintf("reviewed=%d skip=%d merge=%d modify=%d create=%d dropped=%d failed=%d "+ "gate_checked=%d gate_absorbed=%d gate_failed=%d t1_called=%d t1_decided=%d", s.Reviewed, s.Skipped, s.Merged, s.Modified, s.Created, s.Dropped, s.Failed, s.GateChecked, s.GateAbsorbed, s.GateFailed, s.T1Called, s.T1Decided) }