feat(resident): N3–N7 驻留式子 agent 全量落地(生命周期/双向投递/处理表/contextfull/e2e+压力)

设计:docs/zh/resident-subagent-design.md §6/§7/§8/§9/§10。

## N3 生命周期(resident.go)

- `SpawnResident`:主库**受限句柄** + 自己的 temp 实例(`LightMemory`)⇒ 子的轻量内核;
  划入 inputch(登记归属)、授权输出通道、注入任务提示词;为父登记 `child/<id>` 入站 inputch;
  建独立 `IOManager`(共享通道登记表);启动子。
- `DestroyResident`:停子内核、归还划入的 inputch(回到未分配)、丢弃 temp 目录、出登记表。
- `Stop()` → `StopResidents()`:**父退出必须销毁全部子、不留孤儿**(设计 §10 硬约束)。
- `Residents()` 登记表快照;`ResidentTable(id)` 父 pull 子的处理表(不打断)。

## N4 跨 agent 投递

- 子→父:`notify_parent` → 投进父的 `child/<id>` inputch,优先级 **L3**。
- 父→子:`SendToResident` → 投进子的 inputch,优先级 **L4**;
  `isKernelLevelSource` 泛化为"该 agent 的上级"(`AgentConfig.KernelSource`)⇒
  只有父能在子的阶梯上产生 L4(子内部一律 ≤L3)。
- 子的 contextfull → 父侧 `raiseKernelInterrupt`(内核级事件,带子标识,父侧 L4)。

## N5 inputch 处理表

- 子持有;`inputch_note` 主动写**优先**,轮末 `autoRecordInputch` 兜底 ⇒ 每轮必有记录。
- 压缩时清表(表记的是被压掉那段窗口的逐轮处理)。

## N6 contextfull(判据修正)

初版判据是"拼好的 `f.Msgs` 估算 > 90% 窗口",**结构上永不成立**:
`buildMessages` 拿到的 `budget.ContextTokens` 由 `targetUsage = 0.8 × 窗口` 推出,
时间线**在拼进消息之前就被预算裁过**,`f.Msgs` 封顶在 ~80% 窗口。
(初版测试用一个比系统提示词还小的窗口才勉强越线 —— 那等于什么都没测。)
现判据 = **未裁剪的积累上下文**(`a.context.Recent(0)`)超过窗口 90%:
它超过就说明下一轮必须丢事件,这正是"上下文满"。

三处置:`CompressResident`(保留语义:`TrimKeepRecent` 保留最近 N 条 + 清表)/
`ReclaimResident`(取消语义:`ExportTriples` 读 temp → 父选出要保留的 → `Commit` 进 main → 取消该子)/
`DestroyResident`(立刻销毁并移除)。

## 工具面

`resident_agents`(父,单工具多动作:list/create/send/inspect/compress/reclaim/destroy)、
`notify_parent` + `inputch_note`(子)。声明条件式:父才有前者,子才有后两者。

## 验收

`resident_test.go` 五项:生命周期与不留孤儿、双向投递(含"子的主动消息不得以 L4 出现")、
处理表(自动写 vs 主动写优先)、contextfull + 三处置、
**压力 8 子 × 12 轮(父→子 L4 与普通输入各半)+ 双向汇报 + 父退出清理**。
全仓 go test ./... 37 包 ok / 0 FAIL;`-race`(agent/memory/plugin)干净;
压力 `-race -count=3` 通过。
This commit is contained in:
JianFeeeee
2026-09-13 10:20:06 +08:00
parent 48cfa8fb6c
commit 2ebbdadcd6
11 changed files with 1215 additions and 14 deletions

View File

@ -456,6 +456,16 @@ type GraphMemory interface {
- 轻量 profile 另外不接线的装配doc 记忆 / context 动态上下文(`pruneOnInput` 等)/
蒸馏 / 归档 / 关系复审 / 实体合并定时器 / consolidation / 人格门禁。
### 16.0.1 工具面(已实现)
| 工具 | 谁用 | 作用 |
|---|---|---|
| `resident_agents` | 父 | **单工具多动作**`list` / `create`(划入 inputch + 授权输出通道 + 注入任务提示词)/ `send`(对子 = L4/ `inspect`pull 处理表,不打断)/ `compress`(保留)/ `reclaim`(取消 + 合入)/ `destroy` |
| `notify_parent` | 子 | 主动汇报(父侧 = **L3 中断** |
| `inputch_note` | 子 | 主动写本轮 inputch 处理信息(写了就不自动写) |
> 声明是条件式的:父(`parentID == ""`)才有 `resident_agents`;子才有 `notify_parent` / `inputch_note`。
### 16.1 N2 的记忆面清单(现状)
`internal/memory/` 下需要加 space 维度的面:
@ -550,11 +560,11 @@ type GraphMemory interface {
| — | 其余记忆面doc 记忆 / 动态上下文 / 知识库 / 文本 / 媒体 / 社交):**v1 不加 space**(子不可达) | 由 S13/S14 隐含 |
| **N2c** | ~~Agent 级 profile~~ **已完成**`GraphMemory` 窄接口Recall/Commit+ `a.graph` 共同面;子 `a.memory = nil` ⇒ 22 处既有关卡自动禁用整理面 | S13S15 ✅ |
| **N2d** | ~~晋升与丢弃~~ **数据面已完成**`GraphDB.ExportTriples` + 复用 `Commit` 合入(父选哪几条);`LightMemory.Close()` 丢弃 temp | S11 数据面 ✅ |
| **N3** | **驻留子生命周期**:创建 / 销毁 / 登记表 / 父退出清理 | S12、S16 |
| **N4** | **跨 agent 投递**:子→父 L3、父→子 L4+ `isKernelLevelSource` 分层 | S4、S6、S17 |
| **N5** | **inputch 处理表**:主动写入工具 + 自动写兜底 + 生命周期 | S8、S9 |
| **N6** | **contextfull**:检测 + L4 通知(带子标识)+ 三处置(压缩/回收/销毁) | S5、S10、S11 |
| **N7** | **e2e + 压力**:父子互中断、contextfull 处置矩阵、多子并发、父退出清理 | S1S18 全绿;`-race` 干净 |
| **N3** | ~~驻留子生命周期~~ **已完成**`SpawnResident` / `DestroyResident` / `Residents()`登记表/ `Stop()` 内 `StopResidents()`(父退出不留孤儿)/ 归还划入的 inputch / 丢弃 temp 目录 | S12、S16 |
| **N4** | ~~跨 agent 投递~~ **已完成**:子→父 `notify_parent`L3投父的 `child/<id>` inputch父→子 `SendToResident`L4`KernelSource` 分层使父在子的阶梯上是唯一 L4 来源);子的 contextfull 经 `raiseKernelInterrupt` 以 L4 上报父 | S4、S6、S17、S19、S20 ✅ |
| **N5** | ~~inputch 处理表~~ **已完成**`inputch_note`(主动写优先)+ `autoRecordInputch`(轮末兜底)+ `CompressResident` 清表 + 父 `ResidentTable(id)` pull 查看 | S8、S9 |
| **N6** | ~~contextfull~~ **已完成**:判据 = **未裁剪的积累上下文**超过窗口 90%(不能用拼好的 `f.Msgs`——它被 token 预算封在 ~80% 窗口内,是永不成立的判据);通知 = 父侧 L4`child/<id>`);三处置 = `CompressResident`(保留:`TrimKeepRecent` + 清表)/ `ReclaimResident`(取消:`ExportTriples` 选出后 `Commit` 进 main/ `DestroyResident` | S5、S10、S11 |
| **N7** | ~~e2e + 压力~~ **已完成**`resident_test.go` 五项(生命周期/双向投递/处理表/contextfull 处置/8 子×12 轮压力 + 双向汇报),`-race -count=3` 干净 | S1S20 覆盖 ✅ |
每步收尾命令: