fix(memory,loop): 记忆作用域读写成对 + source_conversation 结构化(裁决 1.9/04.4.2/04.4.3)
- 04.4.2/1.9 读侧:Retrieve 候选集并集 = 本人 persona 域 ∪ user 共享域 ∪ project 共享域(他角色 persona 域仍排除),群聊/陌生人取受限收窄
- 写侧:群聊/陌生人整段跳过记忆提取(可查不可写),persona 域仅属主可写
- 04.4.3:source_conversation 不再由 LLM 自由生成(prompt 不索要、候选结构不含该键),落库时无条件盖真实 {session_id,message_id},编解码集中在 memory/source.go
- 新增跨角色种子用例(原用例即使关掉并集也会通过)+ 成对用例「同群聊上下文:读得到共享域、写不进 store」
- 同步 store.go/retrieve.go 文档注释,与 design/03 §3/§4 口径一致
This commit is contained in:
1 parent
af8591b78a
commit
0ce498754a
11 files changed
+428
-60
No files matched your search
@@ -31,10 +31,11 @@ type asyncExtractor interface {
|
||||
// 它原样返回输入消息。
|
||||
//
|
||||
// ctx 上的会话环境为检索设门:owner 全权限环境保留完整
|
||||
// 召回(行为不变);受限环境(群聊 / 陌生人,
|
||||
// design/02 §3, design/11 §2)是只读隔离 —— 只注入
|
||||
// 非主人私有的项目记忆,使主人敏感记忆不泄露给群聊或
|
||||
// 陌生人。
|
||||
// 召回(行为不变;候选集已并入 user/project 共享域,
|
||||
// design/03 §4);受限环境(群聊 / 陌生人,
|
||||
// design/02 §3, design/11 §2, design/03 §5)是只读隔离 ——
|
||||
// 只注入非敏感的共享记忆(用户习惯 + 项目),主人私有的
|
||||
// 角色日常记忆绝不泄露给群聊或陌生人。
|
||||
func (l *Loop) injectMemory(ctx context.Context, messages []model.Message) []model.Message {
|
||||
block := l.memoryBlock(ctx, messages)
|
||||
if block == "" {
|
||||
@@ -66,10 +67,14 @@ func (l *Loop) memoryBlock(ctx context.Context, messages []model.Message) string
|
||||
}
|
||||
|
||||
// retrieveMemoryBlock 返回 env 与该 turn 角色的记忆注入块。
|
||||
// owner 全权限环境保持既有的 Inject 行为。受限
|
||||
// 环境(群聊 / 陌生人)只注入项目作用域记忆(人人可读,
|
||||
// 非主人私有),并把该块标记为群聊隔离,使调用方能区分
|
||||
// 隔离读取与主人的完整读取。
|
||||
// owner 全权限环境保持既有的 Inject 行为(候选集已并入
|
||||
// user/project 共享域)。受限环境(群聊 / 陌生人)只注入
|
||||
// 非敏感的共享域记忆 —— 用户习惯(ScopeUser)与项目记忆
|
||||
// (ScopeProject,人人可读)—— 并把该块标记为群聊隔离,
|
||||
// 使调用方能区分隔离读取与主人的完整读取;主人私有的
|
||||
// 角色日常记忆(ScopePersona)属敏感项,绝不注入
|
||||
// (design/03 §5「不泄露主人敏感信息」+ 主人 2026-10-03
|
||||
// 裁决「用户习惯记忆与不敏感数据:可查、不可写」)。
|
||||
func (l *Loop) retrieveMemoryBlock(ctx context.Context, env SessionEnv, personaID, query string) (string, error) {
|
||||
if !env.Restricted() {
|
||||
return l.retriever.Inject(ctx, personaID, query, 0)
|
||||
@@ -80,7 +85,7 @@ func (l *Loop) retrieveMemoryBlock(ctx context.Context, env SessionEnv, personaI
|
||||
}
|
||||
shared := make([]memory.Result, 0, len(results))
|
||||
for _, r := range results {
|
||||
if r.Item.Scope == memory.ScopeProject {
|
||||
if sharedScope(r.Item.Scope) {
|
||||
shared = append(shared, r)
|
||||
}
|
||||
}
|
||||
@@ -90,6 +95,14 @@ func (l *Loop) retrieveMemoryBlock(ctx context.Context, env SessionEnv, personaI
|
||||
return formatMemoryBlock(shared, "群聊隔离"), nil
|
||||
}
|
||||
|
||||
// sharedScope 报告 scope 是否为群聊/陌生人可查的非敏感共享域:
|
||||
// 用户习惯(user,全局共享)与项目记忆(project,人人可读)。
|
||||
// 角色日常(persona)是主人私有的敏感项,不在其列
|
||||
// (design/03 §3/§5 的敏感度划分即作用域本身)。
|
||||
func sharedScope(s memory.Scope) bool {
|
||||
return s == memory.ScopeUser || s == memory.ScopeProject
|
||||
}
|
||||
|
||||
// formatMemoryBlock 由检索结果构建前导系统块。非空
|
||||
// marker 作为该块的前缀,使隔离的群聊读取可与
|
||||
// 主人的完整读取区分开。
|
||||
@@ -257,14 +270,22 @@ func (l *Loop) extractMemoryAsync(ctx context.Context, messages []model.Message,
|
||||
// extractor 刻意不感知角色(它不再持有一个在构造期
|
||||
// 内嵌的人格,cmd/cyrene/extractor.go),否则多角色
|
||||
// 进程会把每条记忆都归到一个角色下。角色为空(完全未配置
|
||||
// persona)时条目保持 extractor 产出时的样子。store
|
||||
// persona)时条目保持 extractor 产出时的样子。
|
||||
//
|
||||
// 溯源同样在此盖真实 id(裁决 04.4.3,design/03 §2):
|
||||
// source_conversation 由本持久化点从运行上下文取 Run 的
|
||||
// session id 与 gateway 挂载的 turn id 结构化编码
|
||||
// (memory.FormatSourceConversation),无条件覆盖 extractor
|
||||
// 产出值 —— 模型自由文本绝无机会落库为来源。store
|
||||
// 失败会被记录日志,绝不致命,因此一条被拒的条目无法中止一个 turn。
|
||||
func (l *Loop) persistMemories(ctx context.Context, items []memory.MemoryItem, step int) {
|
||||
personaID := l.personaFor(ctx)
|
||||
source := memory.FormatSourceConversation(loopSession(ctx), loopTurn(ctx))
|
||||
for _, it := range items {
|
||||
if personaID != "" {
|
||||
it.PersonaID = personaID
|
||||
}
|
||||
it.SourceConversation = source
|
||||
if _, err := l.memory.Add(ctx, it); err != nil {
|
||||
l.log.Warn("memory store", "step", step, "error", err)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user