feat(lightkernel): N2c —— 轻量内核 profile(窄接口 GraphMemory + nil 即禁用整理面)

按用户指出的关键点(a.memory 多数使用点属"主 agent 整理记忆"与"记忆整理流水线",
子不该有那些路径)实现,方案见设计 §16.0。

## 窄接口:只有 Recall + Commit

新增 `GraphMemory` 接口(memoryface.go)—— 按调用方实测分类后,真正"根与子都要"的只有这两个:
- `Recall`:上下文检索 / memory_recall
- `Commit`:自动写入路径的图部分 / memory_commit

新增 `Agent.graph`(共同面)与 `Agent.graphMem()` 访问器:
- `graph` 显式为 nil 时**回落**到 `memory` ⇒ 既有"只用 Agent 字面量设 memory"的测试无需改动
  (原本会出现"必须同时设两个字段"的脚坑,实测踩到后去掉了)
- 轻量内核:`graph = *memory.LightMemory`,`memory = nil`

## nil 即禁用:整理面自动消失,不需要受限包装

子的 `a.memory == nil` ⇒ 既有的 22 处 `if a.memory != nil` 关卡自动禁掉全部整理面:
- 记忆整理流水线(distill.go 的 archive/review/merge 循环)
- 记忆块 + 媒体桥(graphmedia.go / medialoop.go)
- 记忆整理工具(memory_merge / memory_delete_entity / memory_block_merge /
  memory_purge / memory_edit / memory_introspect)—— 它们本就在 `if a.memory != nil` 块内

唯一拆开的一处是自动写入 `commitTriplesWithMedia`:
图部分走 `graphMem().Commit`(父落 main、子落 temp),块/媒体部分仍由 `a.memory != nil` 守卫。
`executeMemoryTool` 的读路径改走 `graphMem().Recall`;整理类 case 加 `requireFull()` 闸门,
被直调时明确报"本 agent 是轻量内核:记忆整理不可用",不静默降级。

## 验收(3 项新测试)

- `TestLightProfile_MemoryFaceWiring`:整理面为 nil、写入只落 temp(主库无子痕迹)、
  读是并集(主库实体 + temp 实体都看得到)
- `TestLightProfile_OrganizeToolsAbsentAndRefused`:整理类工具**不进工具表**;
  即便被直调也明确报"轻量内核不支持"
- `TestFullProfile_KeepsOrganizeFace`:对照,根 agent 仍保留整理面与整理工具

全仓 go test ./... 37 包 ok / 0 FAIL;-race(agent/memory)干净;gofmt 干净。
This commit is contained in:
JianFeeeee
2026-09-13 10:02:02 +08:00
parent 93aa942e1e
commit 48cfa8fb6c
6 changed files with 288 additions and 17 deletions

View File

@ -37,12 +37,15 @@ type Agent struct {
providerManager *agentAPI.ProviderManager
io *agentIO.IOManager
memory *memory.GraphDB
indexer *memory.Indexer
tracker *tracker.Tracker
context *RelevanceContext
systemPrompt string
ctx context.Context
cancel context.CancelFunc
// graph 是本 agent 的**图记忆共同面**(根 = 同一个 GraphDB;驻留子 = LightMemory)。
// 整理面仍走 memory 字段(子为 nil ⇒ 既有的 nil 关卡自动禁用整理面)。
graph GraphMemory
indexer *memory.Indexer
tracker *tracker.Tracker
context *RelevanceContext
systemPrompt string
ctx context.Context
cancel context.CancelFunc
// 文档记忆(第二层)
docStore *document.Store
@ -192,8 +195,13 @@ type AgentConfig struct {
ProviderManager *agentAPI.ProviderManager
IO *agentIO.IOManager
Memory *memory.GraphDB
Indexer *memory.Indexer
Tracker *tracker.Tracker
// LightMemory 是**轻量内核**的图记忆装配(驻留子用;读 temp∪main,只写 temp)。
//
// 给了它就意味着这是轻量内核:`Memory` 必须为 nil,
// 于是记忆整理面(块/媒体/流水线/整理工具)全部不可达(见 memoryface.go)。
LightMemory *memory.LightMemory
Indexer *memory.Indexer
Tracker *tracker.Tracker
DocStore *document.Store
Knowledge *knowledge.Store
@ -291,6 +299,7 @@ func New(cfg AgentConfig) *Agent {
providerManager: cfg.ProviderManager,
io: cfg.IO,
memory: cfg.Memory,
graph: graphMemoryOf(cfg),
indexer: cfg.Indexer,
tracker: cfg.Tracker,
context: rc,
@ -351,6 +360,36 @@ func (a *Agent) Stop() {
a.cancel()
}
// graphMemoryOf 决定本 agent 的图记忆共同面实现。
//
// - 轻量内核(给了 LightMemory):用 LightMemory,**整理面保持 nil**;
// - 完整内核:直接用主图库(*memory.GraphDB 天然满足 GraphMemory)。
func graphMemoryOf(cfg AgentConfig) GraphMemory {
if cfg.LightMemory != nil {
return cfg.LightMemory
}
if cfg.Memory == nil {
return nil
}
return cfg.Memory
}
// graphMem 返回本 agent 的图记忆**共同面**。
//
// `graph` 显式为 nil 时回落到 `memory` —— 这样"只设 memory 的构造"
// (大量既有测试直接用 Agent 字面量)照常工作,不需要同时维护两个字段。
// 轻量内核则显式设 graph=LightMemory 且 memory=nil:共同面走 LightMemory,
// 整理面因 memory==nil 而全部不可达。
func (a *Agent) graphMem() GraphMemory {
if a.graph != nil {
return a.graph
}
if a.memory == nil {
return nil
}
return a.memory
}
func (a *Agent) ID() types.AgentID { return a.id }
// IsOutputAllowed 报告某个输出通道是否被授权给本 agent。