# 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` + 手动 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, // 归属 conv_id/agent_id,软隔离 + evict_hint: Option, // 预生成的淘汰提示(工具名+参数前 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`(容量上限 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 组)。