feat(sdk+memory): 补 ScenePolicy 声明项,让通道能退出场面识别

缺口(R6):ChannelDef 的记忆声明已有三件套——NoMemory 管「进不进
记忆计算」、ContextPolicy 管「裁不裁上下文」、RecallPolicy 管「召不召回
记忆」,唯独没有「这条输入算不算一场戏的一部分」。现状是无条件参与:
situationFeaturesFor 里只要 evt.Source != "" 就产出一个 chan 特征,没有
可关的开关 ⇒ chan:system / chan:kernel / chan:timer 这类纯内部信噪通道
也在撑场面,每次触发都让不相干的场景长出来或变强,召回时又会把
「内核在跑定时器」当成「用户在这类场景下说过的话」取回。

穷举确认不是查漏:go.mod replace 指向 third_party/homeagent-sdk,
plugin.go 中 scene 出现 0 次,SDK 自身 git 历史 -S'Scene' -- sdk/ 为空。

SDK(纯追加,老插件行为逐字节不变):
- 常量 ScenePolicyAuto / ScenePolicyNone + ValidScenePolicy,形状与
  ContextPolicy / RecallPolicy 完全一致
- ChannelDef.ScenePolicy 与 InjectOptions.ScenePolicy,均带 omitempty
- 默认取 auto(参与)而非 none:场景只附加检索路、不改记忆本体,
  默认关会让存量通道突然失去召回;「关」是少数意图。与 ContextPolicy
  刻意相反(同为破坏性操作,那里是默认关)。

内核:
- applyInjectOpts 搬运 scene_policy(与另外三个标志位同面)
- sceneSuppressed 完全照 recallDeclared 的形状:注入点 payload >
  通道定义 > 默认。none 时连时段(part)特征都不产,也不派生场景键
  (只停指纹采集而留声明路,等于给这个口子开后门)
- situationFeaturesFor / sceneKeysFor 由包级函数改为 Agent 方法
  (需要 a.io 查通道定义),23 个调用点同步

判据:scenepolicy_test.go 7 例,改前编译期红(undefined:
pubsdk.ScenePolicyAuto),改后全绿。其中两例专门护住「未声明时行为
逐字节不变」,是纯追加承诺的护栏。

记忆 8 包 + agent/core 全绿,8 包齐全、无 FAIL/panic/race。

存量通道标注待定:kernel/timer/offload-*/child/* 是纯 0-refs 信噪,
可直接标 none;但 mc:system(12 refs) 与 system(3 refs) 带真实记忆,
性质不明,不擅自标。
This commit is contained in:
JianFeeeee
2026-09-26 20:09:17 +08:00
parent c7d1aa06d2
commit be9c1dc5ac
8 changed files with 241 additions and 18 deletions

View File

@ -74,6 +74,36 @@ func ValidRecallPolicy(policy string) bool {
return false
}
// 场面策略:决定一次输入是否参与**场面识别**(场景式记忆)。
//
// 与前两项再正交一轴:NoMemory 管「进不进记忆计算」、ContextPolicy 管
// 「裁不裁上下文」、RecallPolicy 管「召不召回记忆」,本项管的是
// 「这条输入算不算一场戏的一部分」——它决定输入会不会产出现场指纹
// (通道/对话对象/工具/话题/时段),进而决定会不会长出、命中、写入场景。
//
// 默认(空串或 ScenePolicyAuto)**参与**,保持既有行为:场景式记忆自
// v1.3 落地起就对所有通道无条件生效,没有开关。不默认关有两个原因:
// 1. 场景只**附加**现有记忆的检索路,不改记忆本体,默认关会让存量
// 通道突然失去场景召回;
// 2. 「关」是少数意图(内部信噪通道),少数意图不该是默认——
// 与 ContextPolicy 刻意相反(同为破坏性操作,那里是默认关)。
//
// 该关的典型是纯内部通道:system(内核自循环)、kernel、timer、healthcheck。
// 它们每次触发都在撑一个「场面」,会把不相干的交互聚到一起。
const (
ScenePolicyAuto = "auto"
ScenePolicyNone = "none"
)
// ValidScenePolicy 校验场面策略取值;空串等价于 ScenePolicyAuto。
func ValidScenePolicy(policy string) bool {
switch policy {
case "", ScenePolicyAuto, ScenePolicyNone:
return true
}
return false
}
// InjectOptions 声明一次注入行为在记忆层与上下文层的表现。
//
// 零值 = 记入记忆 + 不裁剪上下文,与历史行为(三参数注入方法)完全一致,
@ -102,7 +132,11 @@ type InjectOptions struct {
// 空串 = 默认(输入/注入 auto,即保持既有「每条输入都召回」的行为);
// RecallPolicyNone 显式关闭(如中断通知的 meta 文本不该据它召回)。
RecallPolicy string
CleanerName string
// ScenePolicy 声明此次注入是否参与场面识别(场景式记忆)。
// 空串 = 默认参与(保持既有行为);ScenePolicyNone 显式关闭,
// 适用于不产生任何场面指纹的纯内部信号(心跳、自循环、内部状态)。
ScenePolicy string
CleanerName string
// Priority 声明**中断注入**的优先级(仅 InjectInterrupt* 有意义)。
//
@ -132,6 +166,7 @@ const (
// Cleaner: 计算层过滤函数,不改原文;仅在向量化/jieba/蒸馏/存档提取关键词时调用
// ContextPolicy: 此通道的输入到达后是否据此裁剪上下文,默认 none(不裁剪)
// RecallPolicy: 此通道的输入到达后是否据此召回相关记忆,默认 auto(召回)
// ScenePolicy: 此通道的输入到达后是否参与场面识别(场景式记忆),默认 auto(参与)
//
// JSON tag 是必需的:通道定义要跨进程传给内核,而 Cleaner 是函数(必须忽略)。
// 没有 tag 时既无法整体 marshal(func 不支持),又会诱使调用方手写字段白名单——
@ -142,6 +177,8 @@ type ChannelDef struct {
ContextPolicy string `json:"context_policy,omitempty"`
// RecallPolicy 见 InjectOptions.RecallPolicy;空串等价 auto(保持既有行为)。
RecallPolicy string `json:"recall_policy,omitempty"`
// ScenePolicy 见 InjectOptions.ScenePolicy;空串等价 auto(保持既有行为)。
ScenePolicy string `json:"scene_policy,omitempty"`
}
// StageContext provides context for stage handlers.