- 无人值守长任务设计定稿,含效率与可观测补遗 - u-relay wire 协议规格与五项评估结论 - 新能力引入评估纪律五项清单 - todo 销账与狗粮短板分析登记
92 lines
5.4 KiB
Markdown
92 lines
5.4 KiB
Markdown
# 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 组)。
|