Files
DevFlow/docs/02-架构设计/专项设计/namespace淘汰治理-设计-2026-10-04.md
T
lxy b994cf2c54 文档: 设计定稿与评估纪律登记
- 无人值守长任务设计定稿,含效率与可观测补遗
- u-relay wire 协议规格与五项评估结论
- 新能力引入评估纪律五项清单
- todo 销账与狗粮短板分析登记
2026-10-04 16:27:32 +08:00

92 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Namespace 淘汰互踩治理 · 设计(2026-10-04)
> ✅ 拍板通过(2026-10-04,2/2 按建议:C1 pin 50% 上限/C2 本期仅报告 pin;见 [拍板集](../滚动规范/拍板集-2026-10-04.md))
> 来源:todo.md 子代理深查「P2 namespace 10MB 全局 LRU 淘汰:报告蒸发为『重新调用』死路+多代理共池互踩[需设计]」。
> 勘察结论(2026-10-04 现场核实,以 `crates/df-ai/src/namespace_store.rs` 全文为准):
> - `NamespaceStore` = 纯内存 `HashMap<key, NamespaceEntry>` + 手动 LRU(`evict_if_needed`),默认 10MB(`:85 default()`),单条超容量拒绝入库(`:108`,返回不可解析引用→展开点落 `EVICTED_PLACEHOLDER`)。
> - 阈值:`NAMESPACE_BYTE_THRESHOLD=2048` / `NAMESPACE_LINE_THRESHOLD=50`,`ALWAYS_LARGE_TOOLS=[read_file, list_directory, grep, diff_files]`。
> - **无淘汰回调/通知**:淘汰静默发生,父 LLM 只在下次展开引用时经 `EVICTED_PLACEHOLDER`(`:17`)得知「已淘汰,如需请重新调用」。
> - 展开点:`agentic/context_lifecycle.rs:145-155`(压缩路径)、`agentic/mod.rs:1632`(主循环),均 `is_namespace_ref` 检查。
> - 作用域:store 实例按会话生命周期持有(`ContextManager` 或 agentic loop 内),**子代理 loop 复用同一全局/会话 store**——互踩根源。
## 1. 失败模式拆解
| 模式 | 现状 | 后果 |
|---|---|---|
| F1 报告蒸发死路 | 子代理 A 报告进 namespace → 池满淘汰 → 父 LLM 展开得 EVICTED_PLACEHOLDER → 指引「重新调用对应工具」 | 「重新调用」= 重跑子代理(spawn 新一轮),成本爆炸且非确定性 |
| F2 多代理互踩 | 同会话并发/串行多子代理共用一个 10MB 池,A 的大结果把 B 的挤掉 | 无归属概念,淘汰不看 owner |
| F3 跨会话残留 | 引用路径含 args_hash 不含会话维度,会话切换后引用可能指向别会话条目(或已蒸发) | 语义错乱(EVICTED_PLACEHOLDER 兜底但误导) |
## 2. 设计目标
1. **报告类内容不可淘汰**(pin 机制):子代理报告、用户显式产出的长文标记 pin,LRU 跳过;
2. **淘汰可感知**:淘汰发生时记录「淘汰清单」,下次展开引用时给出**结构化**提示(含原工具名+参数摘要+淘汰时间),而非裸 EVICTED_PLACEHOLDER;
3. **隔离互踩**:store 增 owner 维度(conv_id 或子代理 id),淘汰优先跨 owner 不回收他域(软隔离,不引入硬配额复杂度);
4. **零 schema 变更**:全部内存态增强,不动 DB。
非目标:不做磁盘 spill(超容量落盘另线);不改阈值(2048B/50 行实测合理);不动 ALWAYS_LARGE_TOOLS 名单。
## 3. 方案
### 3.1 NamespaceEntry 扩展
```
pub struct NamespaceEntry {
key, tool_name, content, created_at, access_count, // 现有
+ pinned: bool, // pin 标记,LRU 跳过
+ owner: Option<String>, // 归属 conv_id/agent_id,软隔离
+ evict_hint: Option<String>, // 预生成的淘汰提示(工具名+参数前 80 字),淘汰时留档
}
```
### 3.2 pin API
```
impl NamespaceStore {
+ pub fn store_pinned(&mut self, tool_name, content, owner) -> String // 子代理报告/关键产出走此
+ pub fn pin(&mut self, key) // 事后 pin
+ pub fn unpin(&mut self, key)
}
```
调用点:`sub_agent.rs` 统一出口(:495 emit AiAgentFinished 处)把报告 `store_pinned`;`run_command`/`generate_image` 等大产出可选 pin(后续)。
### 3.3 淘汰清单(EvictionLog)
store 内加 `VecDeque<EvictionRecord>`(容量上限 50 条):
```
struct EvictionRecord { key, tool_name, hint(前80字), evicted_at, owner }
```
`evict_if_needed` 淘汰时 push。展开点 `read_only()=None` 时:
1. 查 EvictionLog 命中 → 返回富提示:`[结果已淘汰] 原为 read_file(src/main.rs 前 80 字…) 于 3 分钟前因容量淘汰。可重新调用 read_file 取回。`
2. 未命中(跨会话残留/未知)→ 现 EVICTED_PLACEHOLDER 不变。
### 3.4 owner 软隔离
`evict_if_needed` 淘汰顺序改为:**先淘无 owner 且未 pin 的最旧 → 再淘他 owner(非当前活跃 conv)未 pin → 最后当前 owner 未 pin**。pin 永不淘(除非 pin 总量超 50% 容量,此时最旧 pin 降级可淘——防 pin 滥用撑死池)。
### 3.5 store 传递 owner
`store(tool_name, content)` → `store(tool_name, content, owner: Option<&str>)`。调用方在 agentic loop / sub_agent 处已知 conv_id,传入即可。旧两参签名内部转调 `store(.., None)` 兼容。
## 4. 风险与对策
| 风险 | 对策 |
|---|---|
| pin 滥用撑死池 | §3.4 降级规则 + pin 总量监控日志 |
| EvictionLog 内存增长 | 50 条上限 VecDeque,单条 ~200B,可忽略 |
| owner 传错 | 软隔离只影响淘汰顺序,不错即退回现状行为,无正确性风险 |
| 兼容 | 旧 store() 两参保留转调;EVICTED_PLACEHOLDER 文案仅在 Log 命中时升级,未命中原样 |
## 5. 拍板点(2 项)
- **拍板1**:pin 是否上限保护(pin 总量超 50% 降级最旧 pin)?建议加(防失控)。
- **拍板2**:报告外的哪些工具产出默认 pin?建议本期**仅子代理报告**,其余观察后再加。
## 6. 工作量估算
约 0.5-1 天:entry 扩展+pin API(2h)+ EvictionLog(1.5h)+ owner 软隔离(1.5h)+ 两展开点富提示(1h)+ 单测(2h,现有 6 个 namespace 测试全保留+新增 pin/淘汰清单/隔离顺序 3 组)。