feat(memory): sensitive 标记 + 提取器敏感判据,私事退出受限车道召回
M63(M60 审计 P0-1/P0-2)。作用域 ≠ 敏感度:`scope='user'` 是共享域,里 面却装着住址(花溪 64 条 active)/私称(叶酱 63)/作息(早睡 38),于是受限车 道(群聊/陌生人)把共享域整锅注入上下文 —— 2026-10-29 群 531082168 的住址泄漏 即此路径。本片给 memory_items 加 `sensitive` 列,把「这条归谁」(scope)与 「这条能不能外说」(sensitive)拆成两根正交的轴。 - 迁移 0028:加列(INTEGER NOT NULL DEFAULT 0)+ **同迁移幂等回填**(精确短语 白名单 花溪/叶酱/早睡,`AND sensitive=0`,不改 updated_at),down 只撤结构; memory 包内 `ensureSensitiveSchema` 同形前滚(任意 *sql.DB 自举)。 - 受限判据:新增 `MemoryItem.SharedVisible()` = 共享域 且 !sensitive,成为 restricted 侧唯一起点;`loop.FilterSharedVisible` 与复用它的 knowledge 工具 自动跟随。主人车道不过这道判据,照常读到全部记忆(sensitive 不是权限系统)。 - 提取器:prompt 追加 sensitive 位(住址/私人称呼/家人/健康/作息与行程 → true, 口味/兴趣/项目约定 → false,含正反例);产出侧与落库侧共用 `resolveSensitive`(模型的类目判断 ∨ 已实证短语白名单),只加标不撤标。 - 回填入口 `BackfillSensitive(ctx, db, dryRun)`:幂等、可 dry-run、返回候选池 前后规模与逐短语计数(生产快照实测 3116 → 2948,标 725 行)。 - 读路径并到 15 列(scanItem + 7 处 SELECT);store.go 拆分 store_insert.go 守住 500 行上限。 验收:go build/vet/test 全绿(memory + loop 单包复跑绿);删实现三处变异分别打红 (sensitiveColumn 恒 0 / SharedVisible 忽略 Sensitive / Extractor 跳过白名单); 生产库快照上跑 0028 后 Go 侧回填 marked=0(迁移 SQL 与 privateFacts 等价)。
This commit is contained in:
1 parent
a8bc2b4470
commit
44fbd29836
22 files changed
+1360
-102
No files matched your search
@@ -8,23 +8,24 @@ import (
|
||||
)
|
||||
|
||||
// FilterSharedVisible 返回群聊/陌生人(受限环境)可见的记忆子集:按**作用域**
|
||||
// 只保留共享域(用户习惯 ScopeUser 与项目 ScopeProject,人人可读),
|
||||
// 并剔除 L4 意图记录(capacity.RecordIntent 的回写格式,内容是主人逐句
|
||||
// 原话的机器可读形态 —— 虽落在共享域以便 L4 闭环学习,但只该暴露
|
||||
// 「不敏感的习惯结论」,不该暴露主人原话)。角色日常(ScopePersona)
|
||||
// 是主人私有的敏感项,不在可见域内(design/03 §3/§5「不泄露主人敏感信息」)。
|
||||
// 只保留共享域(用户习惯 ScopeUser 与项目 ScopeProject,人人可读),剔除主人
|
||||
// 私有的角色日常(ScopePersona);再按**敏感度**剔除敏感标记为真的条目
|
||||
// (MemoryItem.SharedVisible,迁移 0028);并剔除 L4 意图记录
|
||||
// (capacity.RecordIntent 的回写格式,内容是主人逐句原话的机器可读形态 —— 虽落在
|
||||
// 共享域以便 L4 闭环学习,但只该暴露「不敏感的习惯结论」,不该暴露主人原话)。
|
||||
//
|
||||
// 它是**唯一**的隔离判据:循环的记忆注入(retrieveMemoryBlock)与模型
|
||||
// 可达的 knowledge 检索工具都调用它。knowledge 若自造一套过滤(或不过滤),
|
||||
// 一旦管理面板把它放行给群聊/陌生人,就会返回角色日常与主人原话
|
||||
// (审计 2026-10-05:knowledge 绕过群聊/陌生人召回过滤,P0 红线)。
|
||||
//
|
||||
// ⚠️ 它按作用域分档,**不按内容分档**:过了这道过滤不等于内容不敏感
|
||||
// (见 sharedScope 的说明与审计 2026-10-30 §6 R1)。
|
||||
// 🔴 **它只决定「受限车道看不看得见」**:主人车道(`!env.Restricted()`)根本不过
|
||||
// 这道过滤,照常读到全部记忆(含 sensitive=1)。sensitive 是数据层标记,不是
|
||||
// 权限系统(切片 M63、审计 2026-10-30 §7 P0-1)。
|
||||
func FilterSharedVisible(results []memory.Result) []memory.Result {
|
||||
visible := make([]memory.Result, 0, len(results))
|
||||
for _, r := range results {
|
||||
if !sharedScope(r.Item.Scope) {
|
||||
if !r.Item.SharedVisible() {
|
||||
continue
|
||||
}
|
||||
if capacity.IsIntentRecord(r.Item.Content) {
|
||||
@@ -35,23 +36,6 @@ func FilterSharedVisible(results []memory.Result) []memory.Result {
|
||||
return visible
|
||||
}
|
||||
|
||||
// sharedScope 报告 scope 是否为群聊/陌生人可查的共享域:用户习惯
|
||||
// (user,全局共享)与项目记忆(project,人人可读)。角色日常(persona)
|
||||
// 是主人私有的敏感项,不在其列(design/03 §3/§5)。
|
||||
//
|
||||
// 🔴 **作用域 ≠ 敏感度**(审计 2026-10-30 §6 R1 纠正了本注释此前的说法:
|
||||
// 原文写「敏感度划分即作用域本身」,那个前提是错的)。生效裁决只按作用域
|
||||
// 分档,而 `user` 域里事实上装着住址、私称、作息这类私事(data/cyrene.db
|
||||
// 实测:花溪 64 条 / 叶酱 63 条 active),它们会随本判据进入受限车道的召回
|
||||
// 候选。因此「过了 FilterSharedVisible 就一定不敏感」**不成立**:群聊/陌生人
|
||||
// 车道还有一道呈现层防线 —— 公域呈现块与发言归属口径
|
||||
// (channelbridge 的 publicPresencePrompt、loop/attribution.go)明令
|
||||
// 即使读到主人私事也不得外说。按内容逐条判敏感(sensitive 列 + 提取器判据)
|
||||
// 是 MEMSENS 切片的事,届时本判据才会带上内容维度。
|
||||
func sharedScope(s memory.Scope) bool {
|
||||
return s == memory.ScopeUser || s == memory.ScopeProject
|
||||
}
|
||||
|
||||
// formatMemoryBlock 由检索结果构建前导系统块。非空
|
||||
// marker 作为该块的前缀,使隔离的群聊读取可与
|
||||
// 主人的完整读取区分开。
|
||||
|
||||
Reference in new issue
Block a user