文档: 设计定稿与评估纪律登记
- 无人值守长任务设计定稿,含效率与可观测补遗 - u-relay wire 协议规格与五项评估结论 - 新能力引入评估纪律五项清单 - todo 销账与狗粮短板分析登记
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# context_helpers.rs 拆分备勘(REFACTOR-260619-09 修订)· 设计(2026-10-04)
|
||||
|
||||
> 来源:todo.md「REFACTOR-260619-09 [P2 暂缓] — context.rs(1332)等 F-15 压缩链路稳定再拆」。
|
||||
> **勘误(2026-10-04 现场核实)**:原条目指向的 `crates/df-ai/src/context.rs`(1332 行)**已不存在**——context 已拆为目录模块 `context/{mod.rs(38.5KB), sanitize.rs(64.5KB), manager_tests.rs(51.8KB)}`。**原 REFACTOR-260619-09 事实完成,建议 todo 销账**。
|
||||
> 但拆分域出现**新超标 God 文件**:`crates/df-ai/src/context_helpers.rs` = **1733 行**(80KB,无测试分离),TokenEstimator/ContextConfig/工具结果压缩/分组/淘汰/公共工具混居。本设计以它为对象。
|
||||
> 同域现状参考:df-ai/src/ 下平级大文件尚有 coordinator.rs(68.7KB)、intent.rs(66.8KB)、anthropic_compat.rs(69.9KB)、openai_compat.rs(65.4KB)——均超 60KB,属后续拆分候选池,本期不扩scope。
|
||||
|
||||
## 1. 拆分对象画像
|
||||
|
||||
`context_helpers.rs` 1733 行,混合至少六类职责(已知锚点):
|
||||
- **Token 估算**:TokenEstimator 相关(行段待精确,拆时以 read_symbol 逐个锚定);
|
||||
- **上下文配置**:ContextConfig 及常量;
|
||||
- **工具结果压缩**:`:410 TOOL_RESULT_SUMMARIZE_BYTES=2048`、`:425 TOOL_RESULT_JSON_STR_FIELD_MAX=512`、`should_summarize_tool_result(:440)`、extract_key_info 系列(head/tail/array/char/json-str 五种截断);
|
||||
- **namespace 引用转发**:`:646-647 NAMESPACE_REF_PREFIX`(仅常量 re-export,薄);
|
||||
- **淘汰/分组逻辑**(eviction/grouping,与 context/mod.rs 的 ContextManager 呼应);
|
||||
- **杂项公共工具**(is_han `:396` 等纯函数)。
|
||||
|
||||
**前置动作(拆分前 0.5 天)**:用 read_symbol 对 1733 行做一次全骨架清点,产出「函数→职责块→行段」映射表落 `docs/05-代码审查/context_helpers骨架-2026-10-XX.md`——本设计给出块划分与迁移策略,具体行号以骨架表为准。
|
||||
|
||||
## 2. 目标模块划分(建议)
|
||||
|
||||
拆为 `context_helpers/` 目录(与 context/ 同款目录化先例,lib.rs 加 `pub mod` 不动外部路径——`df_ai::context_helpers::X` 引用形态不变):
|
||||
|
||||
| 子模块 | 职责 | 预计行数 | 风险 |
|
||||
|---|---|---|---|
|
||||
| `token.rs` | TokenEstimator + 估算纯函数 | ~250 | 低(纯函数,单测易迁) |
|
||||
| `config.rs` | ContextConfig + 阈值常量(含 TOOL_RESULT_* 六常量) | ~150 | 低 |
|
||||
| `tool_compress.rs` | should_summarize_tool_result + extract_key_info 系列 | ~350 | **中(压缩热点,殿后)** |
|
||||
| `eviction.rs` | 淘汰/分组 + namespace 常量转发 | ~400 | **中高(压缩链路高频改动区,殿后)** |
|
||||
| `misc.rs` | is_han 等纯工具(或就近下沉使用方) | ~100 | 低 |
|
||||
| `mod.rs` | re-export 保路径 + 少量真公共胶水 | ~50 | — |
|
||||
|
||||
**测试**:本文件测试与生产代码混居(无 #[cfg(test)] 分离迹象,拆时确认)——随成员走,`token.rs`/`tool_compress.rs` 各带自己的测试模块;若测试体量大,仿照 context/manager_tests.rs 惯例平级 `helpers_tests.rs`。
|
||||
|
||||
## 3. 迁移顺序(四批,压缩热点殿后)
|
||||
|
||||
```
|
||||
批1 低风险: config.rs + misc.rs → cargo test -p df-ai 全绿
|
||||
批2 低风险: token.rs (+其单测) → 同上
|
||||
批3 热点: tool_compress.rs → 同上 + 手测长会话压缩路径
|
||||
批4 热点: eviction.rs + mod.rs 收口 → 同上 + 手测 eviction 滚动摘要
|
||||
```
|
||||
|
||||
每批验收门:`cargo test -p df-ai`(基线 560+)+ `cargo check --workspace`;批3/4 加:起应用开长会话触发压缩与淘汰各一次,行为无回归。**每批独立提交**,冲突时可安全停在任意批。
|
||||
|
||||
## 4. re-export 兼容策略
|
||||
|
||||
`context_helpers/mod.rs` 对全部原 pub 项 `pub use` 转发——仓内 grep 显示引用方走 `df_ai::context_helpers::*` 与 `crate::context_helpers::*` 两种路径,目录化后**零调用方改动**。仅 `use df_ai::context_helpers` 整体 use(若有)需 mod.rs 全量 re-export 兜住。
|
||||
|
||||
## 5. 与原条目的关系
|
||||
|
||||
- todo 销账动作:REFACTOR-260619-09 原 context.rs 拆分 ✅ 完成;**新登记**本设计为 REFACTOR-261004-01(context_helpers.rs 1733 行拆分),优先级维持 P2 暂缓——**触发条件**:上下文管理批(无人值守 P0/P1)合入且稳定 ≥1 周后启动批1/2(低风险先行),批3/4 继续等压缩链路静默期;
|
||||
- context_helpers.rs 若在触发前因功能开发继续膨胀 >2000 行,优先把「工具结果压缩」块先行拆出(批3 提前),不整文件硬拆。
|
||||
|
||||
## 6. 工作量估算
|
||||
|
||||
骨架清点 0.5 天 + 批1/2 各 0.5 天 + 批3/4 各 1 天 ≈ 3.5 天(可拆散穿插,非连续占用)。
|
||||
|
||||
## 7. 拍板点(1 项)
|
||||
|
||||
- 触发时机:上下文管理批稳定后自动启动 vs 等用户指令?建议**等用户指令**(与 REFACTOR 暂缓惯例一致,本设计备勘即可)。
|
||||
@@ -1,6 +1,6 @@
|
||||
# devflow → u-relay → u-claw 集成可行性研究
|
||||
|
||||
> 2026-09-28 · 状态:可行性已定,待 devflow/u-claw 两侧细勘察补全
|
||||
> 2026-09-28 · 状态:可行性已定 ~~待 devflow/u-claw 两侧细勘察补全~~ ✅勘察已补全(2026-10-04,见 [uclaw两侧勘察补全-2026-10-04.md](./uclaw两侧勘察补全-2026-10-04.md)——多轮锚定定案+**协议漂移修正:现行 /ws/commander 角色,实施勿按本文档端点**)
|
||||
> 需求原话:「考虑 devflow -> u-relay -> u-claw, 让 devflow 集成 u-claw 能力」
|
||||
|
||||
## 一、定性结论
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# 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 组)。
|
||||
@@ -0,0 +1,254 @@
|
||||
# u-relay Wire 协议规格(改造蓝本)
|
||||
|
||||
> 版本:现行(第三代 agent/commander 命名,兼容层全拆)
|
||||
> 来源:`E:/wk-lab/u-relay/src/{relay.rs, broadcast.rs, conn.rs}` + `README.md`
|
||||
> 适用:df-tunnel / df-miniapp 升级对接
|
||||
|
||||
---
|
||||
|
||||
## 1. 拓扑与端点
|
||||
|
||||
```
|
||||
Commander(控制端) ──wss──> nginx(:443 TLS 终结) ──ws──> u-relay(:9180)
|
||||
↑
|
||||
Agent(执行端) ──wss 主动出站────────────────────────────┘
|
||||
(穿 NAT,无监听端口)
|
||||
```
|
||||
|
||||
| 角色 | 端点 | 典型实现 |
|
||||
|---|---|---|
|
||||
| **Agent**(执行端,事件真相源) | `GET /ws/agent` | df-tunnel(桌面端)、u-claw runner |
|
||||
| **Commander**(控制端,指令发起方) | `GET /ws/commander` | df-miniapp(小程序)、u-claw console、u-ppt |
|
||||
|
||||
**端点即身份**:端点决定期望的 `ClientKind`,与 Hello.kind 不一致直接拒。
|
||||
|
||||
---
|
||||
|
||||
## 2. 握手协议
|
||||
|
||||
### 2.1 客户端首帧(10s 内必发,否则断开)
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "agent" | "commander",
|
||||
"channel_id": "<非空, ≤128 字节, 无控制字符>",
|
||||
"token": "<U_RELAY_TOKEN 值>"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 约束 |
|
||||
|---|---|
|
||||
| `kind` | 必须与端点匹配:`/ws/agent` 端点仅收 `"agent"`;`/ws/commander` 端点仅收 `"commander"` |
|
||||
| `channel_id` | 配对绑定键;非空 + ≤128B + 无控制字符;同 channel_id 的 agent 与 commander 互为对端 |
|
||||
| `token` | 服务端常量时间比较(防时序侧信道) |
|
||||
|
||||
### 2.2 握手成功响应(relay 立即下发)
|
||||
|
||||
```json
|
||||
{ "kind": "control", "payload": { "control_kind": "hello_ack" } }
|
||||
```
|
||||
|
||||
**客户端判定**:收到首条非 error 消息即握手通过(不要求对端在线)。
|
||||
|
||||
### 2.3 握手失败(relay 发错误帧后立即 Close)
|
||||
|
||||
```json
|
||||
{ "kind": "control", "error": "<reason>" }
|
||||
```
|
||||
|
||||
| `error` 值 | 触发条件 |
|
||||
|---|---|
|
||||
| `handshake_failed` | 10s 未发 Hello / Hello JSON 解析失败 / 首帧非文本 / Hello 超 1 MiB |
|
||||
| `kind_mismatch` | Hello.kind 与端点不匹配(如 agent 端点收 commander) |
|
||||
| `invalid_channel_id` | channel_id 为空 / >128B / 含控制字符 |
|
||||
| `server_misconfigured` | 服务端 `U_RELAY_TOKEN` 未设置 |
|
||||
| `auth_failed` | token 不匹配 |
|
||||
|
||||
### 2.4 已下线的旧值(一律拒绝)
|
||||
|
||||
| 维度 | 现行 | 已下线(收到即拒) |
|
||||
|---|---|---|
|
||||
| 执行端端点 | `/ws/agent` | `/ws/runner` `/ws/device` `/ws/executor` |
|
||||
| 控制端端点 | `/ws/commander` | `/ws/console` `/ws/miniapp` `/ws/controller` |
|
||||
| Hello.kind 执行端 | `"agent"` | `"runner"` `"device"` `"executor"` |
|
||||
| Hello.kind 控制端 | `"commander"` | `"console"` `"miniapp"` `"controller"` |
|
||||
| 配对键字段 | `channel_id` | `device_id`(不再 alias) |
|
||||
| 错误码 | `invalid_channel_id` | `invalid_device_id` |
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据帧(握手通过后)
|
||||
|
||||
### 3.1 客户端 → relay(入站)
|
||||
|
||||
入站文本帧即业务 payload,relay **不解析**直接透传:
|
||||
|
||||
```json
|
||||
{ "任意 JSON" }
|
||||
```
|
||||
|
||||
非 JSON 文本会被包成 `{"payload": "<原字符串>"}`。
|
||||
|
||||
**特殊语义**:若 payload 顶层有 `control_kind: "ping"`,触发 relay 本地心跳响应(见 §4),不透传对端。
|
||||
|
||||
### 3.2 relay → 客户端(出站 BroadcastMessage)
|
||||
|
||||
```json
|
||||
{
|
||||
"channel_id": "chan-xxx",
|
||||
"kind": "event" | "command" | "control",
|
||||
"source": 12345,
|
||||
"from": "agent" | "commander",
|
||||
"payload": { "原始 JSON" },
|
||||
"ts": 1725000000000
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `channel_id` | 配对通道 |
|
||||
| `kind` | `event`=agent→commander;`command`=commander→agent;`control`=控制面 |
|
||||
| `source` | 来源 conn_id(u64,0=NIL 伪值,relay 自构造消息用) |
|
||||
| `from` | 来源客户端类型 |
|
||||
| `payload` | 原始 JSON 透传 |
|
||||
| `ts` | relay 收到入站帧的毫秒时间戳 |
|
||||
|
||||
### 3.3 路由规则(relay 内部,客户端无需关心但需理解)
|
||||
|
||||
| 消息 | 投递目标 |
|
||||
|---|---|
|
||||
| `Event`(agent 发) | 同 channel_id 的**所有 commander**(除来源自身) |
|
||||
| `Command`(commander 发) | 同 channel_id 的**所有 agent**(除来源自身) |
|
||||
| `Control` | 同 channel_id 的**所有连接**(除来源自身) |
|
||||
|
||||
**方向硬约束**(relay 路由层强制):
|
||||
- `Event` 永不投递给 agent
|
||||
- `Command` 永不投递给 commander
|
||||
|
||||
**多端登录**:同 channel_id 可并存多个 agent 和多个 commander,全投递(Vec 索引)。
|
||||
|
||||
### 3.4 帧限制
|
||||
|
||||
| 限制 | 值 |
|
||||
|---|---|
|
||||
| 入站文本帧 | **≤ 1 MiB**(含 Hello),超限直接断开 |
|
||||
| 二进制帧 | **忽略**(MVP 仅文本) |
|
||||
| WS Ping/Pong | 协议层自动处理(tungstenite),不进业务流 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 心跳协议
|
||||
|
||||
### 4.1 客户端发起
|
||||
|
||||
```json
|
||||
{ "control_kind": "ping" }
|
||||
```
|
||||
|
||||
**关键**:relay 收到后**本地直接回 pong**,不透传对端,也不依赖对端在线。
|
||||
|
||||
### 4.2 relay 响应
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "control",
|
||||
"from": "agent",
|
||||
"source": 0,
|
||||
"payload": { "control_kind": "pong" },
|
||||
"ts": 1725000000000
|
||||
}
|
||||
```
|
||||
|
||||
**字段细节**:
|
||||
- `source: 0` 是 `ConnId::NIL` 伪值 —— 若填接收方自己的 conn_id,路由防回环会跳过,pong 永远回不来
|
||||
- `from: "agent"` 是 relay 代理执行端身份 —— 让 commander 端识别为合法控制响应
|
||||
|
||||
### 4.3 客户端实现要点
|
||||
|
||||
| 端 | 用途 |
|
||||
|---|---|
|
||||
| **Commander** | 用 pong 更新 `lastPongTime`,看门狗超时则重连 |
|
||||
| **Agent**(跑桌面/移动网络) | 用 pong 重置读死线,死线超时走既有重连 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 部署配置
|
||||
|
||||
### 5.1 环境变量
|
||||
|
||||
| 变量 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| `U_RELAY_ADDR` | `0.0.0.0:8080` | 监听地址 |
|
||||
| `U_RELAY_TOKEN` | **无(必填)** | 配对 token,未设置启动即 exit 1 |
|
||||
| `RUST_LOG` | `info` | tracing env-filter |
|
||||
|
||||
### 5.2 nginx 反代参考
|
||||
|
||||
```nginx
|
||||
location /ws/ {
|
||||
proxy_pass http://127.0.0.1:9180;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 部署形态
|
||||
|
||||
- **musl 静态编译**:`x86_64-unknown-linux-musl` → 单二进制 ~3.6MB
|
||||
- **无 openssl/native-tls**:TLS 完全由 nginx 终结
|
||||
- **systemd 单元**:`/usr/local/bin/u-relay` + `/etc/u-relay.env`(权限 600)
|
||||
|
||||
---
|
||||
|
||||
## 6. 客户端实现 Checklist
|
||||
|
||||
### Agent(执行端,如 df-tunnel)
|
||||
- [ ] 连 `wss://host/ws/agent`
|
||||
- [ ] Hello `{kind:"agent", channel_id, token}`
|
||||
- [ ] 等首条非 error 帧判定握手通过
|
||||
- [ ] 心跳:周期发 `{"control_kind":"ping"}`,等 pong 重置读死线
|
||||
- [ ] 入站 `BroadcastMessage`:仅处理 `kind=="command"`(来自 commander 的指令)和 `kind=="control"`
|
||||
- [ ] 出站:业务事件 JSON 直接作为文本帧发送(relay 包成 `Event` 投递给 commander)
|
||||
|
||||
### Commander(控制端,如 df-miniapp)
|
||||
- [ ] 连 `wss://host/ws/commander`
|
||||
- [ ] Hello `{kind:"commander", channel_id, token}`
|
||||
- [ ] 等首条非 error 帧判定握手通过
|
||||
- [ ] 心跳:周期发 `{"control_kind":"ping"}`,等 pong 更新 `lastPongTime` 看门狗
|
||||
- [ ] 入站 `BroadcastMessage`:仅处理 `kind=="event"`(来自 agent 的事件)和 `kind=="control"`
|
||||
- [ ] 出站:业务指令 JSON 直接作为文本帧发送(relay 包成 `Command` 投递给 agent)
|
||||
|
||||
---
|
||||
|
||||
## 7. 离线语义
|
||||
|
||||
| 场景 | 行为 |
|
||||
|---|---|
|
||||
| 对端不在线时发消息 | relay **丢弃**(不缓存),debug 日志记录"广播无对端在线" |
|
||||
| 客户端判断对端在线 | 通过 `PresenceState`(relay 不主动推送,由客户端心跳或业务层判活) |
|
||||
| 重连 | 客户端指数退避自行实现(df-miniapp 已有 `reconnectBaseDelay`/`reconnectMaxDelay`) |
|
||||
|
||||
---
|
||||
|
||||
## 8. 安全说明
|
||||
|
||||
- **token 常量时间比较**:relay 端实现,防时序侧信道
|
||||
- **channel_id 校验**:格式防御,无 per-channel token 绑定(Phase3 留)
|
||||
- **风险**:单共享 token 下,持 token 者可声明任意 channel_id 冒充该通道 —— 生产部署需确保 token 不泄露
|
||||
|
||||
---
|
||||
|
||||
## 9. 与旧版 df-relay 协议差异速查
|
||||
|
||||
| 维度 | df-relay(已下线) | u-relay(现行) |
|
||||
|---|---|---|
|
||||
| 端点 | `/ws/device` `/ws/miniapp` | `/ws/agent` `/ws/commander` |
|
||||
| Hello.kind | `"device"` `"miniapp"` | `"agent"` `"commander"` |
|
||||
| 配对键 | `device_id` | `channel_id` |
|
||||
| 心跳 pong | 由对端回(路径长) | relay 本地回(路径短) |
|
||||
| 旧名兼容 | 多代别名 | **一律拒绝** |
|
||||
| 错误码 | `invalid_device_id` | `invalid_channel_id` |
|
||||
| 帧上限 | 无 | 1 MiB |
|
||||
| token env | `DF_RELAY_TOKEN` | `U_RELAY_TOKEN` |
|
||||
@@ -0,0 +1,75 @@
|
||||
# u-claw 两侧细勘察补全(多轮会话锚定 + 协议漂移)
|
||||
|
||||
> 2026-10-04 · 上游:[devflow集成u-claw-可行性研究-2026-09-28.md](./devflow集成u-claw-可行性研究-2026-09-28.md) 第六节待补槽位「u-claw 侧收尾:WS 渠道的多轮会话锚定细节(request_id↔chain 映射、会话持久化边界)」。
|
||||
> 勘察基线:u-claw HEAD 021a230(基线 d8dc003 后 18 提交)/ u-relay 现行源码 / u-ppt 参考实现未变。
|
||||
> 全部结论以源码为准(wsch.go 405 行全文 + chainpool.go + agentd.go + u-relay src)。
|
||||
|
||||
## 一、多轮会话锚定:channel 级单 chain 模型(结论定案)
|
||||
|
||||
**WS 渠道不是 per-request 会话,是 channel 级单 chain 粘性会话**:
|
||||
|
||||
```
|
||||
wsChannel (1:1)
|
||||
└─ chain = NewSessionChain("ws", cwd, statePath, ...) // wsch.go:113 run() 启动时建一次
|
||||
├─ TakeSummary() 每轮取摘要 → RenderWithSummary 注入下轮 prompt // process():224
|
||||
├─ HandleWithResult(prompt, progressWatcher) // 常驻 claude -p 子进程
|
||||
└─ 会话自动连续(各轮 session_id 相同,无需 --resume) // agentd.go:6 注释
|
||||
```
|
||||
|
||||
- **request_id 仅是单次指令标识**(配对进度/结果帧),不映射 chain——所有 request_id 共享同一 chain 与同一 claude 进程
|
||||
- **上下文维持**:常驻进程各轮 session_id 相同(agentd.go:6),链层另有摘要注入(`TakeSummary`→`RenderWithSummary`)双保险;崩溃/超时后 `ensureAlive` 自动重启并 `--resume` 上次会话 ID 恢复(agentd.go:154)
|
||||
- **持久化边界**:会话状态文件 `sessionStatePath("ws")`(exports.go:96,落 `~/.u-claw/sessions/`);会话 jsonl 落 claude 自己的项目目录(cwd 对应);**跨进程重启后上下文不丢**(--resume + 会话文件保留)
|
||||
- **devflow 侧含义**:接入后 devflow 与 u-claw 的对话天然是**一条连续会话**(同一 channel_id 下所有 ask_uclaw 调用共享上下文)——多轮追问无需传会话 ID,但也意味着 devflow 内不同 devflow 会话若共用 channel_id 会互相污染上下文(见 §4 设计含义 D-2)
|
||||
|
||||
## 二、协议漂移:可行性文档已过时(重要修正)
|
||||
|
||||
2026-09-28 可行性文档记载 `/ws/device` + `{kind,device_id,token}` hello——**已漂移**,e8113c5「中继握手协议对齐服务端」重定义了角色:
|
||||
|
||||
| 维度 | 可行性文档(9-28 记载) | 现行源码(10-04 实证) |
|
||||
|---|---|---|
|
||||
| 端点 | `/ws/device` / `/ws/miniapp` | **`/ws/agent`(执行端,u-claw 连此)** / **`/ws/commander`(控制端,devflow 连此)** |
|
||||
| hello | `{kind, device_id, token}` | **`{kind:"commander", channel_id, token}`**(commander);agent 端 `{kind:"agent", channel_id, token}` |
|
||||
| 帧路由 | Event→Miniapp / Command→Device | **Event(执行端发)→同 channel_id 全部 Commander;Command(控制端发)→同 channel_id 绑定的 Agent**(conn.rs:114, relay.rs:117-119) |
|
||||
| 心跳 | ping/pong(relay 本地回) | 同款仍在:30s ping / 90s 读死线 / 写死线 10s(wsch.go:45-50;miniapp 侧同款) |
|
||||
| 鉴权 | 全局共享 token(鉴权债) | **配对语义已改**:Commander 持 token 申请绑定 channel_id(broadcast.rs:138 `Pair{channel_id, token}`),Agent 端 token 校验;但 relay 侧仍是 env `U_RELAY_TOKEN` 单 token(relay.rs:9-10, main.rs:28 fail-fast),**per-channel token 未做**——鉴权债部分缩小 |
|
||||
|
||||
**接入实施时必须按本节现行协议实现,不能照抄 9-28 文档或 u-ppt relay.ts 原样**(u-ppt 用的还是旧 Miniapp/Device 语义,需确认 u-ppt 是否已随 e8113c5 升级,若未升级则 u-ppt relay.ts 仅作 TS 结构参考、协议字段按本节改)。
|
||||
|
||||
## 三、执行语义与时限(新增已核实事实)
|
||||
|
||||
- **串行 worker**:每 channel 一条串行 worker goroutine,reqCh 缓冲 4,满则**丢弃**由发起方重试(wsch.go:108-109)——devflow 侧需配超时重试与队列已满可感知的语义(当前协议无「已丢弃」回执,devflow 端只能在超时后重发)
|
||||
- **单条指令硬超时 15min**(`wsExecTimeout`,wsch.go:38):超时 AbortRunning 杀进程、回「处理超时」结果、链保 session ID 下条 --resume 续跑——**devflow aichat 工具的 client 侧超时须 >15min 或对齐处理**
|
||||
- **进度节流 2s**(progressWatcher,wsch.go:368):assistant text=阶段叙述+tool_use=当前动作,2s 节流回传——devflow 工具卡「流式」粒度即此(块级,非逐 token)
|
||||
- **空闲超时链回收**:chainPool idle 后整链回收(杀 claude 进程 ~500MB),来消息 --resume 重建冷启动 ~7s(chainpool.go:1-7)——**devflow 侧期望「空闲后首次调用慢」为正常**
|
||||
- **链池挤占语义**:槽满 LRU 挤最旧 idle 链,活跃链不挤;hardCap 全忙时拒绝建链返错(chainpool.go:100-128)
|
||||
|
||||
## 四、devflow 侧接入设计含义(增量到第五节)
|
||||
|
||||
- **D-1 工具卡流式**:ask_uclaw 工具卡可直接消费 agent_progress 帧(2s 块级)作流式显示,agent_result 全文为最终 tool result——零改造对齐 devflow 现有工具卡模式
|
||||
- **D-2 channel_id 策略**:单 chain 会话模型下,建议 devflow **per-conv 生成 channel_id**(如 `devflow-{conv_id}`)接入——每个 devflow 会话映射独立 u-claw chain,会话隔离天然成立;代价是 u-claw 侧 chainPool 槽位占用(max 上限与 LRU 挤占,见 chainpool.go),需确认 u-claw 侧 max 配置余量(当前 max 值未在本次勘察范围,实施前核 `~/.u-claw/u-claw.yml`)
|
||||
- **D-3 超时对齐**:devflow ask_uclaw 客户端超时建议 16min(>15min 硬上限),超时即回失败;进度 2s 节流下客户端 watchdog 应容忍 30s+ 无 progress(u-claw 长任务中间可能无 assistant 输出)
|
||||
- **D-4 队列满丢弃无回执**:reqCh 满时静默丢——devflow 侧在 15/16min 超时后重发(最多 2 次),并把「u-claw 渠道忙」文案明确给用户
|
||||
- **D-5 offline 语义**:relay 无离线缓存(9-28 已载),叠加本节串行+丢弃语义:**devflow 发送前可先发探活 ping**(30s 内无 pong 视为渠道不可达),不可达直接报「u-claw 离线」不进队列
|
||||
|
||||
## 五、接入实施批清单(更新版,覆盖 9-28 第五节)
|
||||
|
||||
1. **协议实现(Rust 客户端)**:连 `/ws/commander`,hello `{kind:"commander", channel_id: devflow-{conv_id}, token}`,按本节协议处理 Event 帧(agent_progress/agent_result 按 request_id 配对)——**不照抄 u-ppt relay.ts 字段**,仅参考结构
|
||||
2. **工具注册**:`ask_uclaw(prompt)` → declare_tool!(Low/只读? 不,执行类 Medium),progress 帧接 ToolCard 流式,result 全文为 tool result
|
||||
3. **配置页**:relay URL / channel 前缀 / token(keyring)——默认关
|
||||
4. **超时与重试**:客户端 16min 超时 + 2 次重试 + 发送前探活 ping
|
||||
5. **可选 phase2**:消息渠道形态(u-claw 代言 aichat)/ 反向 MCP——维持 9-28 形态 2/3 描述
|
||||
6. ~~**u-ppt 核对**(实施前置小步)~~ ✅2026-10-04 已核:u-ppt relay.ts 65bae53(2026-10-01)已随 e8113c5 升级 commander/channel_id 协议——端点自动补全 `/ws/commander`(:39)/hello `{kind:'commander',channel_id,token}`(:135)/10s 握手看门狗+hello_ack(:127)/30s ping+15s pong(:33)/指数退避 1s→30s(:36)/agent_request|progress|result 按 request_id 分发(:158) 六项与 u-relay 现行源码一致。**relay.ts 可直接作参考实现**(去 deck/SEP 专有逻辑),devflow 接入是 commander 协议第二个实现,非首个
|
||||
|
||||
## 六、基线更新
|
||||
|
||||
```
|
||||
基线 HEAD(2026-10-04):
|
||||
devflow 9216d4c u-relay e8113c5后现行(见 src) u-claw 021a230 u-ppt 待核
|
||||
漂移检查(下次):u-claw git log --oneline 021a230..HEAD;u-relay per-channel token 是否落地
|
||||
```
|
||||
|
||||
## 关联
|
||||
|
||||
- 上游:`devflow集成u-claw-可行性研究-2026-09-28.md`(本文件填其第六节末槽位)
|
||||
- u-claw 源:`wsch.go`(WS 渠道全文 405 行)/ `internal/core/chainpool.go`(链池)/ `internal/core/agentd.go`(常驻 claude + --resume)
|
||||
- u-relay 源:`src/relay.rs`(端点+握手)/ `src/conn.rs`(配对路由)/ `src/broadcast.rs`(Pair 协议)
|
||||
@@ -0,0 +1,97 @@
|
||||
# 子代理完成卡双轨「已消化」降级 · 设计(2026-10-04)
|
||||
|
||||
> ✅ 拍板通过(2026-10-04,2/2 按建议:D1 自动折叠默认开/D2 trace 随 namespace 批;见 [拍板集](../滚动规范/拍板集-2026-10-04.md))
|
||||
|
||||
> 来源:todo.md 子代理深查「P2 完成卡(前端全文)与父上下文(压缩)双轨无『已消化』降级机制[需设计]」。
|
||||
> 勘察结论(2026-10-04 现场核实,以源码为准,**修正深查一处过时结论**):
|
||||
> - 完成卡前端已非「重启蒸发」:`aiShared.ts:677` 已有按父 conv 经 `ai_sub_agent_list` 反查重建完成卡(轨迹存在即视为 completed),重启回补链路已通。剩余缺口即「已消化」降级。
|
||||
> - 双轨现状:前端卡持 summary 全文(`AgentFinishedCard.summary`,卡面 160 字截断展示);父上下文中报告经 namespace 引用+`context_helpers.rs` 视图压缩(`TOOL_RESULT_SUMMARIZE_BYTES=2048` / `TOOL_RESULT_JSON_STR_FIELD_MAX=512`),**父 LLM 后续轮次只能看到 ≤512 字符折叠摘要**。
|
||||
> - 事件链:`sub_agent.rs:495` 统一出口 emit `AiAgentFinished{status,summary,rounds,tokens,rejected_tools,stop_kind}`(挂父会话)+ 整轨迹落库(V44 origin='sub_agent')。
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
「双轨无降级」的三层含义:
|
||||
1. **信息层**:父 LLM 与前端卡各持一份(上下文折叠摘要 vs 前端全文),两者无互认,用户在卡上看到的细节 LLM 答不上;
|
||||
2. **生命周期层**:卡永久挂在消息流(直到会话删除),无「已读/已消化」状态;多轮追问后卡仍以初始完整态占据视觉焦点;
|
||||
3. **一致性层**:父 LLM 被追问报告细节时若折叠摘要不够,唯一出路是重新 `ai_sub_agent_list` 反查轨迹(LLM 未必想到/舍得花 token)。
|
||||
|
||||
## 2. 设计目标
|
||||
|
||||
1. 卡可标记「已消化」,视觉降级(折叠/淡化)但**可展开复原**,信息不丢;
|
||||
2. 父 LLM 追问细节时有**结构化自助通道**(不用重跑子代理);
|
||||
3. 「已消化」状态**持久化**(重启/切会话后保留);
|
||||
4. 零后端 schema 变更(复用现有落库轨迹)。
|
||||
|
||||
非目标:不做卡全文注入上下文(会反向膨胀 prompt);不做自动消化判定(何时算「消化」留人工,v2 可议 LLM 显式 ack)。
|
||||
|
||||
## 3. 方案
|
||||
|
||||
### 3.1 「已消化」状态与持久化
|
||||
|
||||
前端 `AgentFinishedCard` 增字段:
|
||||
```
|
||||
+ digested?: boolean // 默认 false
|
||||
```
|
||||
|
||||
持久化载体:**复用 KV 存储**(app 已有 settings/KV 表),key = `agentCardDigested:{parentConvId}` → `string[]`(agentId 列表,JSON)。切换/挂载回补时(aiShared.ts:677 重建路径)读 KV 合并标记。不落 SQLite 新列/新表——卡本身是派生态(由轨迹重建),消化标记是用户偏好,KV 语义足够。
|
||||
|
||||
### 3.2 视觉降级交互
|
||||
|
||||
```
|
||||
未消化卡: 现状(标题行+status 徽章+160 字摘要+展开全文)
|
||||
已消化卡: 标题行单行化(✓ 已消化 · summary 首 40 字 · status 徽章淡化),点击展开复原全文
|
||||
手动标记: 卡右上角「标记已消化」动作(hover 显现),支持批量(同会话多卡)
|
||||
自动辅助: 同会话出现新完成卡时,旧未消化卡自动折叠为单行(不标已消化,仅视觉收纳,复用 ToolCardList 分组折叠的 touched 语义——用户手动展开过则不自动收)
|
||||
```
|
||||
|
||||
### 3.3 父 LLM 自助通道(关键补强)
|
||||
|
||||
现状 LLM 有 `ai_sub_agent_list`(列轨迹元数据)但**没有读单条轨迹全文的工具**(前端卡经 IPC `ai_sub_agent_list` 拉的是轨迹列表,LLM 侧工具面无对应读全文入口)。
|
||||
|
||||
新增 LLM 工具(注册进 tool_registry,risk=Low 只读):
|
||||
```
|
||||
ai_sub_agent_trace(conversation_id, agent_id) -> { summary, status, rounds, tokens, messages_tail? }
|
||||
```
|
||||
- 返回该子代理轨迹的关键内容(summary 全文 + 末 N 条消息摘要),**只读、走现有 V44 落库数据**,零新存储;
|
||||
- system prompt 子代理指引段补一句:「追问子代理产出细节时,先调 ai_sub_agent_trace 而非重新 spawn」;
|
||||
- 这同时是 namespace 淘汰治理( sister 设计文档)中「报告蒸发死路」的兜底——即使 namespace 条目被淘,轨迹在 DB,工具可读。
|
||||
|
||||
### 3.4 「已消化」与上下文压缩的联动(可选增强,默认关)
|
||||
|
||||
`AgentFinishedCard.digested=true` 时,前端在后续轮次注入的用户消息尾部不加任何内容(保持零侵入);该标记仅影响 UI 与用户心智。**不**把 digested 同步回后端压缩策略(避免跨端状态同步复杂度)。
|
||||
|
||||
## 4. 改动面清单
|
||||
|
||||
| 层 | 文件 | 改动 |
|
||||
|---|---|---|
|
||||
| 前端 | `src/composables/ai/aiShared.ts` | AgentFinishedCard+digested;重建路径读 KV 合并 |
|
||||
| 前端 | 完成卡渲染组件(MessageList 内 SubAgentCard 或同域) | 降级态渲染+标记动作+批量 |
|
||||
| 前端 | KV 读写封装(复用现有 settings composable) | get/set `agentCardDigested:{convId}` |
|
||||
| 后端 | `src-tauri/src/commands/ai/agentic/sub_agent.rs` | 暴露 ai_sub_agent_trace IPC(读库组装) |
|
||||
| 后端 | `tool_registry.rs` | 注册 ai_sub_agent_trace(Low/只读白名单) |
|
||||
| 提示词 | system prompt 子代理指引段 | 追问先 trace 不重跑 |
|
||||
|
||||
## 5. 风险与对策
|
||||
|
||||
| 风险 | 对策 |
|
||||
|---|---|
|
||||
| KV 标记与轨迹删除不同步(轨迹删了 KV 残留) | 重建路径只对「轨迹存在的卡」应用标记,孤儿 key 自然失效,无需清理任务 |
|
||||
| trace 工具被滥用(LLM 反复查大轨迹) | 返回体加尾部分页/上限(messages_tail ≤ 20 条),复用 T-05 工具结果截断口径 |
|
||||
| 卡交互改动碰 MessageList 渲染热点 | 卡是独立组件,降级态纯展示层,vue-tsc+手测三态(未消化/已消化/展开) |
|
||||
|
||||
## 6. 验收
|
||||
|
||||
1. 标记已消化 → 卡单行化 → 重启应用后仍单行(KV 持久);
|
||||
2. 展开已消化卡 → 全文复原;
|
||||
3. LLM 调 ai_sub_agent_trace 能返回指定子代理 summary 全文;
|
||||
4. 未做任何标记的会话,渲染与现状逐像素一致(回归门);
|
||||
5. cargo test(新增 trace IPC 组装单测)+ vue-tsc 0 error。
|
||||
|
||||
## 7. 拍板点(2 项)
|
||||
|
||||
- **拍板1**:旧卡自动折叠(§3.2)默认开还是关?建议开( touched 语义保护手动展开)。
|
||||
- **拍板2**:ai_sub_agent_trace 本期做还是随 namespace 治理批一起做?建议**随 namespace 批**(同域文件+互为兜底)。
|
||||
|
||||
## 8. 工作量估算
|
||||
|
||||
约 1-1.5 天:digested+KV(2h)+ 卡降级渲染(3h)+ trace IPC+注册(3h)+ 提示词(0.5h)+ 测试(2h)。
|
||||
@@ -0,0 +1,318 @@
|
||||
# 无人值守长任务 — 机制设计
|
||||
|
||||
> 日期: 2026-10-04 | 状态: ~~设计稿(待拍板)~~ ✅拍板通过(2026-10-04,13/13 按建议;全量拍板记录见 [拍板集-2026-10-04.md](./滚动规范/拍板集-2026-10-04.md)) | 勘察: 只读实测锚点
|
||||
> 拍板背景: 产品方向定为「任务规划一次性确定边界后,无人值守、自主无干预、稳定完成长任务」;S2-B 真审批通道延后不做。
|
||||
> 设计目标(用户原话转译): ①长任务中途零打断(不弹审批、不等人) ②发起时一次拍板授权范围,存续期内 AI 全自主 ③无人盯着也能信任——失败自愈、进度可观测、异常安全停靠、完成/异常有通知。
|
||||
|
||||
---
|
||||
|
||||
## 一、现状勘察(锚点实测)
|
||||
|
||||
### 1.1 打断点全景与无人值守处置
|
||||
|
||||
| # | 打断点 | 现状机制(锚点) | 现行为 | 无人值守处置 |
|
||||
|---|--------|---------------|--------|-------------|
|
||||
| 1 | 风险审批挂起 | `audit/approval.rs` handle_approval_tool → insert_pending_approval;auto_exec_mode 三档(low/medium/all) + patch_file 全档放行特例 + 会话信任(session_trust,`mod.rs:542` TrustKey) | Medium/High 挂 loop,弹卡 + 系统通知,默认 15 分钟超时自动取消(`state.rs:240` approval_timeout_minutes) | **边界内放行**:风险≤任务上限 ∧ 非破坏性命令 → 直接执行(decided_by=boundary_auto);边界外不弹卡,拒绝 tool_result + 记待办 |
|
||||
| 2 | 路径授权挂起 | `audit/path_auth.rs` check_file_tool_auth → NeedsAuth → ApprovalKind::Path + emit AiDirAuthRequired | 挂 loop 等三档授权(once/session/always,`state/allowed_dirs.rs`) | **边界内放行**:路径命中任务白名单 → Authorized;白名单外 → 不弹卡,结构化拒绝(L1 可改提示) |
|
||||
| 3 | 黑名单硬拒 | 同上 Denied 分支 + validate_path 黑名单;已有先例:all 档保黑名单(audit/mod.rs:667),bypass 开关才连黑名单放行(PathBypass::All),子代理 KeepBlacklist | 就地拒绝,loop 继续 | **永不放行**(安全底线,同 all 档/子代理先例);拒绝 envelope 提示换路径 |
|
||||
| 4 | 破坏性命令 | `mod.rs:603` is_destructive_command(rm/del/format/... 首 token 精配) | 信任放行门控,命中走正常审批 | **永不放行**;边界内 run_command 也过此闸 |
|
||||
| 5 | LLM 流错误停 | `agentic/mod.rs` RoundOutcome::Fatal(候选 4xx 非429 或全 candidate 耗尽)→ return;流前 InitFailed Retryable 已有退避重试+candidate 链切换(retry.rs 同源分类) | Fatal 直接终轮 | **降级为安全停靠**(见 4.2):非正常终局统一停靠出口,不再裸 return |
|
||||
| 6 | 工具执行失败 | Low 失败 = 失败 envelope 进 tool_result,loop 继续(AR-6);外层超时 60s/run_command clamp/spawn_agent 挂钟+60 | 错误交 LLM 自纠 | **可降级继续**:已有,不改;唯一化入停靠判定(连续失败见 4.1) |
|
||||
| 7 | 断路器求助 | L1 失败断路器(THRESHOLD=3/WINDOW=20)+ G2 探索熔断(3 次) → emit 求助,等用户 | 挂起等介入 | **安全停靠**:触发即停靠(保留熔断,语义从「等」改「存档+通知」) |
|
||||
| 8 | 达最大轮次 | agentic/mod.rs:1150 已改自动收尾(incomplete=true)+ NeedAction 通知 | 自动终止 | **已是停靠雏形**,补停靠说明+待办清单,预算可由任务边界放大 |
|
||||
|
||||
结论:打断点 1/2 是无人值守的**主要敌人**(挂起等输入);3/4 是**安全线**(保留);5-8 已有自动终局雏形,统一成语义即可。
|
||||
|
||||
### 1.2 既有边界件盘点(可直接复用为任务级边界底座)
|
||||
|
||||
| 件 | 锚点 | 复用方式 |
|
||||
|----|------|---------|
|
||||
| spawn_agent 白名单钳制 | `agentic/sub_agent.rs:260` clamp_tool_whitelist(tools ∩ Low ∖ spawn_agent)+ 执行期双闸(audit/mod.rs:172 sub_agent_tool_clamped) | 判定结构参照;**子代理不放宽**(fail-closed 保持,见拍板点 6) |
|
||||
| 会话级授权目录 | `state/allowed_dirs.rs` AllowedDirs{persistent/session/once} | 任务白名单独立于三档(任务级,不写用户 persistent 白名单——一次性授权不污染全局);判定函数复用 check_path_authorization 的规范化/黑名单逻辑 |
|
||||
| 三重熔断 | `sub_agent.rs:301` round_budget_stop(轮次/200k token/900s 挂钟) | 结构直接抄给任务级预算(参数独立可配,主会话量级上限实测后定) |
|
||||
| 看门狗/心跳 | execute_with_heartbeat 30s AiHeartbeat + 前端 130s watchdog + stall_detector check_retry_exhausted | 已覆盖长工具误杀与流断连,零改动 |
|
||||
| fail-closed 拒绝协议 | deny_sub_agent_tool 结构化 envelope(denial_layer 四层)+ is_sub_agent_deny_envelope 解析 | 边界外拒绝**照抄此协议**(新 denial_layer=boundary-exceeded),LLM 可自纠、报告可申报缺口 |
|
||||
| 审批恢复/落库 | pending_approvals 序列化 + restore_pending_approvals 启动恢复;auto checkpoint(save_conversation 每 20 条消息 <1MB 快照入 ai_conversation_checkpoints) | 断点续跑底座(P2) |
|
||||
| 全局全自动先例 | df-ai-auto-execute-mode=all + df-ai-path-auth-bypass | 即「完全信任」档的存量实现(L3 远期档,见五);本设计不重造 |
|
||||
|
||||
### 1.3 稳定性现状
|
||||
|
||||
- 重试面: retry.rs 指数退避(1s→2s→4s+jitter,30s 预算,Retryable/Fatal 分类)覆盖 complete() 与流前;candidate 链自动切 provider。**工具级无自动重试**——被 detect_retry_count/去重缓存刻意压制(防死循环),维持。
|
||||
- 进度可观测: AiToolCallStarted/Completed 全事件流 + AiHeartbeat + 审计表 + checkpoint。缺「任务级」聚合视图(轮次/预算余量)。
|
||||
- 通知: `notify.rs` 三类(Completed/Failed/NeedAction),主窗聚焦跳过 + 5s 节流合并 + 失败静默。P3-6 基建完整,停靠/完成通知直接挂现有出口。
|
||||
- 断点续跑: checkpoint 表有快照、审批有启动恢复,但 **loop 本身重启后不自动续跑**,需人工。
|
||||
|
||||
---
|
||||
|
||||
## 二、任务级授权边界模型
|
||||
|
||||
### 2.1 数据结构(挂会话,不建新表——宁少勿预留)
|
||||
|
||||
```rust
|
||||
// commands/ai/boundary.rs(新文件,纯结构+判定函数+单测)
|
||||
pub struct TaskBoundary {
|
||||
pub risk_ceiling: RiskLevel, // 上限 Low|Medium;High 不经此通道(拍板点 1)
|
||||
pub allowed_dirs: Vec<PathBuf>, // 任务目录白名单(canonicalize 规范形态,含项目根)
|
||||
pub forbid_destructive: bool, // 恒 true,字段留作显式声明(UI 展示用),判定不读
|
||||
pub budget: TaskBudget { max_rounds: u32, max_tokens: u64, wall_secs: u64 }, // 缺省继承现网常量
|
||||
}
|
||||
```
|
||||
|
||||
存储与生命周期:
|
||||
- 内存: per_conv 会话条目新增 `boundary: Option<TaskBoundary>`(会话级单真相源先例,`mod.rs:621`);
|
||||
- 落库: ai_conversations 新列 `boundary_json`(迁移一个 ALTER TABLE,save_conversation 随现有 meta 链路写,启动 load 还原);
|
||||
- 生命周期 = 会话存续: 发起时一次声明 → 整个任务存续生效 → 会话删除级联消失。不做中途改边界(改边界=新拍板,UI 提示重开任务),防「边跑边扩权」;
|
||||
- 与全局档正交: 有 boundary 的会话 boundary 优先;无 boundary = 现状逐次审批,**渐进增强零倒退**。
|
||||
|
||||
### 2.2 UI 形态(发起时一次性配置卡)
|
||||
|
||||
参照 spawn_agent 参数卡(审批卡 approval_hint 模板,`tools/spawn_agent.rs:172`)+ 设置页范式:
|
||||
- 入口: 新任务发起对话框(或首条消息发送时)展开「无人值守边界」折叠卡;非必填——不展开 = 现状审批模式;
|
||||
- 字段四项: 风险上限(Low/medium 单选,默认 Low)/ 目录白名单(复用设置页 allowed_dirs 目录选择交互,默认预填当前项目绑定目录)/ 预算(轮次/token/挂钟,默认值预填,高级折叠)/ 确认按钮文案「一次授权,任务期内不再打扰」;
|
||||
- 展示态: 任务进行中侧栏会话条目挂边界徽标,点开只读回显(禁中途改)。
|
||||
|
||||
### 2.3 IPC 面
|
||||
|
||||
`ai_set_task_boundary(conv_id, boundary_json)` 发起时调用;`ai_get_task_boundary(conv_id)` 回显。删除/停止会话既有级联不动(边界随会话条目同亡,无需新清理点)。
|
||||
|
||||
---
|
||||
|
||||
## 三、边界内审批旁路(risk 分流改造)
|
||||
|
||||
### 3.1 判定链插桩(单点,两处)
|
||||
|
||||
现判定链: path_auth 预校验(path_auth.rs)→ classify_risk_and_auto(approval.rs:33)→ trust → pending。改造在 `process_tool_calls`(audit/mod.rs:604)读取 auto_exec_mode 处并列读取 boundary 快照,然后:
|
||||
|
||||
```
|
||||
对每条工具调用(有 boundary 会话):
|
||||
① 黑名单命中 → Denied(现状,永不放行)
|
||||
② 文件工具: 路径 ∈ boundary.allowed_dirs → Authorized;
|
||||
路径 ∉ 白名单 → 拒绝 envelope{denial_layer:"boundary-dirs"},不弹卡不挂起,
|
||||
LLM 换路径或写入报告未尽事项(fail-closed 同构)
|
||||
③ 风险: risk ≤ risk_ceiling ∧ (run_command → !is_destructive_command)
|
||||
→ 归入 low_risk 并行执行向量(复用既有执行+审计链),
|
||||
decided_by = "boundary_auto"(risk 真实等级透传,F-#97 先例防审计失真)
|
||||
④ 超上限(含 High、破坏性命令) → 拒绝 envelope{denial_layer:"boundary-risk"},
|
||||
记入会话待办清单(4.3),不 insert pending
|
||||
```
|
||||
|
||||
### 3.2 兼容性(不倒退保证)
|
||||
|
||||
- 无 boundary 声明: 上述分支全部跳过,auto_exec_mode/trust/审批/路径授权全链路现状,零行为变更;
|
||||
- ai_approve / ai_authorize_dir IPC 不动: 有边界会话不产生新 pending(黑名单本来也不产生),审批 UI 自然不触发;
|
||||
- 审批超时定时器(15 分钟)对边界会话天然不触发(无新挂起),零改动;
|
||||
- PathBypass 枚举不加新形态: 边界会话白名单判定在 check_file_tool_auth **调用前**完成(boundary 命中即 Authorized,bypass 参数传 Off 保黑名单),避免与 bypass 开关语义纠缠;
|
||||
- 审计: boundary_auto / boundary-dirs / boundary-risk 三种 decided_by/denial_layer 全留痕,无人值守事后可全量追溯。
|
||||
|
||||
---
|
||||
|
||||
## 四、无人值守稳定性机制
|
||||
|
||||
### 4.1 失败分级自愈(增量最小化)
|
||||
|
||||
| 级别 | 判定 | 处置 | 增量 |
|
||||
|------|------|------|------|
|
||||
| 瞬态可重试 | 网络超时/5xx/429(复用 retry::is_status_retryable) | 已有: 退避重试+candidate 链,零改动 | 无 |
|
||||
| 工具级失败 | 失败 envelope | 已有: 交 LLM 自纠(loop 继续),死循环已被去重缓存+retry guard 治理 | 无 |
|
||||
| 探索停滞 | L1/G2 断路器触发 | 改: 熔断求助出口 → 安全停靠(4.2) | 小 |
|
||||
| 不可恢复 | Fatal 全 candidate 耗尽 / 边界预算耗尽 / 连续 N 轮零产出(阈值实测后定) | 统一安全停靠出口 | 中 |
|
||||
|
||||
不新造工具级自动重试层——既有 envelope+熔断已覆盖,自动重试同参数正是 detect_retry_count 刻意压制的行为(反馈_no_guess_api 同源: 盲重试制造风暴)。
|
||||
|
||||
### 4.2 安全停靠语义(不是死等)
|
||||
|
||||
现网「非正常终局」五路出口(Fatal/Stale/MidStream/熔断/达轮次)行为不一。统一停靠出口:
|
||||
|
||||
1. 保存现场: 既有 teardown_round 落库链路不变;
|
||||
2. 会话尾部追加停靠说明(system 提示块): 停止原因 / 已完成什么(取本轮 assistant 文本) / **待办清单**(边界外被拒操作 + 未完成事项,数据源=3.1④ 的拒绝记录 + 熔断采样);
|
||||
3. emit `AiTaskParked { conv_id, reason, pending_items }` 新事件(前端操作卡: 继续恢复/停止归档,复用 pendingMaxRounds 操作卡骨架——lib.rs:435 既有续/停 IPC);
|
||||
4. notify NeedAction 系统通知(托盘场景补位,复用 P3-6 全套);
|
||||
5. **不保持 generating=true**: guard 复位(达轮次出口已有先例),用户回来点「继续」走 ai_continue_loop(start_iter 语义沿用: 预算耗尽重计=用户再授权)。
|
||||
|
||||
### 4.3 进度可观测
|
||||
|
||||
- 已有零改动: 工具事件流 + 心跳 + 审计表即事实源;
|
||||
- 增量: 每 N 轮(建议 5,实测后定)emit `AiTaskProgress { conv_id, round, budget, tokens_used, pending_items_count }`;前端会话头部挂轻量进度条(轮次/预算维度),长任务静默期(如 spawn_agent 子代理长跑)用户可判断「在干活还是死了」。不落新表,事件即逝。
|
||||
|
||||
### 4.4 完成与异常通知
|
||||
|
||||
复用 notify.rs 现有三类出口,无需新通知通道;增量仅两处挂点: 停靠出口(4.2)发 NeedAction、边界会话正常收敛发 Completed(emit_ai_completed_once 已挂,核对边界会话不因 incomplete 路径漏发即可)。
|
||||
|
||||
### 4.5 断点续跑(P2,进程重启后)
|
||||
|
||||
- 现状: checkpoint 快照已有、pending_approvals 有启动恢复,loop 不自动续;
|
||||
- 设计: ai_conversations 新列 `loop_state`(running/parked/finished),边界会话 loop 生命周期同步落库;启动时检测「loop_state=running 但进程重启」→ **不自动续跑**(本地桌面场景,重启即用户在场,自动续跑反而惊吓),侧栏该会话标「任务中断可恢复」+ 一键恢复按钮;
|
||||
- V21 迁移教训直接套用(docs/todo.md:860: 全局 COUNT 幂等窗口,中途崩溃→永久残缺被版本号掩盖): 恢复判据必须**按会话逐条落进度标记**(loop_state per-conv),禁止依赖全局内存态或推算;快照恢复走既有 checkpoint 表,恢复粒度=消息级(本轮已落库消息不重放,ai_messages 增量基线既有机制天然支持)。
|
||||
|
||||
---
|
||||
|
||||
## 五、信任分级(拍板项)
|
||||
|
||||
| 档 | 语义 | 载体 | 状态 |
|
||||
|----|------|------|------|
|
||||
| L1 逐次审批 | 现状: 三档 auto + 审批卡 + 路径授权弹窗 | df-ai-auto-execute-mode | 现网,不动 |
|
||||
| L2 任务边界自主 | 本设计主体: 发起时一次拍板,边界内零打断,黑名单/破坏性命令永不放行 | TaskBoundary(会话级) | 本设计 P0 |
|
||||
| L3 完全信任 | 等价 skip-permissions: 全档自动+连黑名单也放行 | 存量组合: auto=all + path-auth-bypass=on | 远期可选,UI 不与 L2 并列推荐,标注「含敏感目录」风险;不新造代码 |
|
||||
|
||||
分级关系: L2 是 L1 之上的会话级增强,L3 是全局级现状开关的组合,三层互不替代。
|
||||
|
||||
---
|
||||
|
||||
## 六、分期实施建议
|
||||
|
||||
### P0 边界模型 + 旁路主干(可独立交付验收)
|
||||
- 改动面: 新增 `commands/ai/boundary.rs`(结构+判定+单测)、`commands/ai/mod.rs`(per_conv 字段+IPC 注册)、`conversation.rs`(boundary_json 落库列)、`audit/mod.rs`(判定链插桩:auto_exec_mode 读取处)、`audit/path_auth.rs`(boundary 白名单判定前置)、`audit/approval.rs`(boundary_auto 分流)、迁移一条(boundary_json 列)、前端任务发起配置卡 + useAiConversations 透传;
|
||||
- 验收: 边界会话 Medium 工具/白名单路径零弹卡直接执行+审计 boundary_auto;黑名单/破坏性命令/超上限三项被拒且不弹卡;无边界会话全链路行为与改造前逐字节一致(既有测试全绿);
|
||||
- 冲突面: audit/mod.rs、approval.rs、path_auth.rs、conversation.rs、chat.rs、state.rs **全部在当前在飞批次工作区改动中**(git status 30+ 文件,befix2/dx1/dx2 三代理在改)——P0 必须等在飞批次合入后开工,或以 worktree 零交集摘出纯新增文件(boundary.rs+前端卡)先行、插桩点后置。串行,禁双头互踩(uswitch-403 同款事故预防)。
|
||||
|
||||
### P1 自愈 + 通知(依赖 P0 边界字段)
|
||||
- 改动面: `agentic/mod.rs`(五路非正常终局统一停靠出口)、`stall_detector.rs`(熔断求助→停靠)、`notify.rs` 挂点、event_bus 新事件 AiTaskParked/AiTaskProgress、前端操作卡+进度条;
|
||||
- 验收: 构造 Fatal/熔断/预算耗尽三种场景 → 各自产停靠说明+待办清单+托盘通知,generating 复位,点继续可续跑;
|
||||
- 冲突面: agentic/mod.rs 在飞改动中,同 P0 策略。
|
||||
|
||||
### P2 断点续跑 + 信任分级(依赖 P1 停靠语义)
|
||||
- 改动面: `conversation.rs`(loop_state 列+落库点)、`lib.rs`(启动扫描)、侧栏恢复入口;信任分级 UI 是否进设置页等拍板点 7;
|
||||
- 验收: 边界会话长任务跑一半杀进程重启 → 侧栏标「可恢复」→ 一键恢复从 checkpoint 续跑不重放已落库轮次;
|
||||
- 冲突面: lib.rs 启动段低频改动,冲突小。
|
||||
|
||||
---
|
||||
|
||||
## 拍板点清单
|
||||
|
||||
| # | 决策 | 选项 | 建议 |
|
||||
|---|------|------|------|
|
||||
| 1 | risk_ceiling 是否开放 High | A 封顶 Medium;B 开放 High 但破坏性命令仍拒 | **A**。High(run_command 等)无人看过命令串风险不可控;确需 High 归 L3 完全信任档 |
|
||||
| 2 | 边界存储形态 | A 会话级 JSON 列;B 独立任务表 | **A**。任务与会话 1:1,独立表是过度设计;字段随会话同亡零清理成本 |
|
||||
| 3 | 边界外动作处置 | A 拒绝+记待办随停靠报告;B 拒绝即停靠;C 弹卡(违背无人值守) | **A**。fail-closed 同构子代理,LLM 有自纠空间,缺口随停靠说明透出 |
|
||||
| 4 | 进度事件频率 | A 每 5 轮;B 每轮;C 不做 | **A**。B 噪音,C 牺牲静默期可观测;具体值实测后定 |
|
||||
| 5 | 重启后自动续跑 | A 不自动,标记+一键恢复;B 自动续跑 | **A**。本地桌面重启即用户在场,自动续跑惊吓且 V21 教训表明恢复判据须可人工确认 |
|
||||
| 6 | 边界会话内子代理是否放宽 | A 维持 Low-only fail-closed;B 继承父边界 | **A**。子代理无监督面,放宽破坏隔离语义;Medium 需求由父会话边界内直接执行 |
|
||||
| 7 | L3 完全信任是否进设置页 UI | A 不做 UI,文档说明存量开关组合;B 设置页显式三档 | **A(P2 再议 B)**。存量开关已可达,先验证 L2 使用率 |
|
||||
| 8 | 中途改边界 | A 全不可改;B risk/目录不可改,预算可由用户中途调 | **B(补遗后修订)**。一次性拍板约束的是「AI 不回头问用户」,不是「用户不能动自己的任务」;risk 上限与目录白名单一次性不可改(安全边界),预算字段开放用户中途收紧/放宽(经 #12);**纠偏注入(7.2)≠改边界**——只注入指令文本,边界字段零触碰 |
|
||||
|
||||
---
|
||||
|
||||
## 补遗(2026-10-04 二次拍板追加,与主体章节并列生效)
|
||||
|
||||
> 追加两维:效率(任务完成得快、tokens 高效)与可观测/可控。不重写主体章节,冲突处以本补遗为准。
|
||||
|
||||
## 七、效率维度
|
||||
|
||||
### 7.1 token 效率(复用为主,增量小)
|
||||
|
||||
| 设计点 | 既有件(复用) | 无人值守增量 |
|
||||
|--------|--------------|-------------|
|
||||
| 上下文膨胀控制 | ContextManager::build_for_request 预算裁剪;namespace_store 大工具结果进 namespace 主队列留引用(audit/mod.rs 两处 T2);TOOL_RESULT_COMPRESS_ENABLED 工具结果压缩;TOPIC_MARKER 话题标记;自动 checkpoint | 长任务场景将「工具结果冗余」放大,既有链路已按预算自动收敛,**零新增**;视图裁剪滚动摘要(派工决策单 B2 待做)落地后收益直接作用于长任务,不另立项 |
|
||||
| 重复调用抑制 | 只读工具跨轮缓存+轮内去重(audit/mod.rs:976+,cache_hit_warning 递增威慑);High 去重缓存;detect_retry_count 断重试循环;G2 探索熔断(同签名重复派发检测) | **自动降级**:缓存命中计数达阈值(现警告头已有,阈值实测后定)时自动插入一条 system 降级指令(「同参调用已 N 次,必须换策略或写入报告」),把「威慑」升级为「强制转向」;G2 熔断在无人值守下直接走安全停靠(4.1 已定),不再等用户 |
|
||||
| 任务级模型分级 | router ModelTier(Code/Debug→Heavy/Chat→Fast,agentic/mod.rs:1617 tier tiebreak);resolve_model_with_override;spawn_agent 的 model 参数(子代理可指定模型) | 边界配置卡加可选「子代理模型」高级项;**默认策略: 子代理派发缺省走轻量模型**(重活主循环继承会话模型,机械探索活子代理小模型),路由复用 ModelTier 既有分档,零新机制(#10) |
|
||||
| prompt 瘦身/任务书复用 | spawn_agent 任务书自包含语义(子代理看不到父对话,不重复注入父轨迹);GOAL_PIN 钉目标防逐轮重述(GOAL_INJECT_BANNER);SKILL_LIST 描述截断 80 字 | 零新增——「任务书复用而非重注入」正是子代理架构的既有形态;长任务多轮目标防漂移由 GOAL_PIN 覆盖 |
|
||||
|
||||
### 7.2 速度设计点
|
||||
|
||||
| 设计点 | 机制 | 与既有的关系 |
|
||||
|--------|------|-------------|
|
||||
| 并行工具批最大化 | 旁路后 Medium 工具归入 low_risk 向量走 join_all 并行(§3.1③) | **本设计免费提速点**:现网 spawn_agent(Medium)逐个走审批串行挂起;边界会话内 LLM 一轮发多个 spawn_agent → join_all 天然并行,上限 MAX_TOOL_CALLS_PER_ROUND=8。**P0 零代码副产品,须在验收清单标注** |
|
||||
| 子代理并行编排 | 同上——并行编排不需要新调度器,是「旁路 × join_all」的组合效应 | 零新增;子代理各自独立预算熔断(三重熔断互不挤占),父侧只等 join_all 汇合 |
|
||||
| 空轮/确认轮消除 | 无人值守任务书模板追加一条纪律:「不向用户提问;信息不足按明确假设继续,假设写入报告」;纯文本收敛轮(无 tool_calls)即终态,不存在「等确认轮」 | 复用 PURE_GREETING_TOOL_GATE 与 G2 熔断兜底异常空转;确认类行为在无人值守下=fail-closed 反面(挂起等输入),任务书纪律+边界语义双重压制,P0 随配置卡文案交付 |
|
||||
|
||||
### 7.3 效率度量口径(为「快」提供数据)
|
||||
|
||||
- **任务耗时** = 首条 user 消息 → 终态事件(AiTaskParked / 正常收敛)的挂钟差;落库复用 ai_conversations.created_at + 终态时间戳(补 parked_at 小列或复用 updated_at,P0 定);
|
||||
- **token 花费** = 会话累计 prompt/completion 列(已有,save_conversation 落库)+ **子代理单列**(SubAgentOutcome rounds/tokens 已经 AiAgentFinished 透出;子代理出口整轨迹落库但 token 不进父 TokenAccumulator,故口径必须「父会话 / 子代理 / 合计」三段呈现,禁混算,#11);
|
||||
- **每轮均摊** = 合计 token / 轮次(趋势指标,识别哪段任务烧钱);不做成本换算(密钥价目不进引擎,数据不出门边界)。
|
||||
|
||||
**分期归属**: 并行编排/确认轮消除/度量口径落库=P0(大部分零代码或小列);缓存降级注入+子代理默认小模型=P1;度量面板汇总视图=P2(随可观测面板同批)。
|
||||
|
||||
## 八、可观测/可控维度
|
||||
|
||||
### 8.1 可观测(10 秒三问:跑到哪/花多少/剩多少)
|
||||
|
||||
| 呈面 | 数据源(复用) | 新增量 |
|
||||
|------|--------------|--------|
|
||||
| 当前进度呈面(阶段/当前动作/轮次与 token/预算剩余) | 当前动作=最近一条 AiToolCallStarted 的 tool+GoalMeta 摘要(goal_of 已有,如 run_command→「执行 xxx」);轮次/token=AiTaskProgress 事件(§4.3 已设计) | 会话头部常驻任务面板(#12):一行「阶段·当前动作」+进度条(轮次/预算)+token 累计;打开界面即答三问,不依赖滚动消息流 |
|
||||
| 子代理树状态一览 | SubAgentRegistry(parent→children 内存注册表,已有)+ AiAgentFinished(每子代理终态 rounds/tokens/rejected 已透出) | 树 UI 为纯前端消费:运行中子代理=spawn 事件实时挂节点,完成=Finished 回填终态;父会话面板内嵌一行树,不建独立页面 |
|
||||
| 花费实时累计 | TokenAccumulator 快照 + usage_delta_since 增量口径(均已有) | AiTaskProgress 携带 tokens_used 即可,零新事件 |
|
||||
|
||||
### 8.2 可控(逐项标注复用/新建)
|
||||
|
||||
| 操作 | 机制 | 复用 or 新建 |
|
||||
|------|------|-------------|
|
||||
| 中途停止 | ai_chat_stop → stop_flag 置位 + cascade_stop_children(Stop form)级联子代理 | **纯复用**,零改动;边界会话停止=任务终止,停靠说明照发(4.2) |
|
||||
| 暂停/恢复 | 「停止 + 一键恢复」组合即暂停:stop 保现场落库,恢复走 P2 断点续跑入口(ai_continue_loop, checkpoint 粒度) | **复用组合,不新造「暂停态」**(宁少勿预留——独立暂停态需新增 loop 挂起检查点,收益不抵复杂度);P2 交付 |
|
||||
| 中途纠偏(向运行中任务注入指令) | ai_chat_force_send 既有通道:打断当前轮 + 注入新 user 指令 + F1 并发 epoch 新 loop 接管(旧 loop 让位,epoch 机制已有)+ 级联 Stop form 子代理 | **纯复用**;语义定义:纠偏只注入指令文本,边界字段零触碰(**纠偏≠改边界**,与拍板点 8 对齐);P0 即可用,任务面板加「插话」入口即可(前端小改) |
|
||||
| 接管转人工 | = 任务终止的单向形态:用户纠偏注入「停下我来」或点停止 → 移除 boundary 标记,会话回落 L1 逐次审批模式继续对话 | **复用 stop + boundary 摘除**;单向降级(任务态→普通态)不是边界修改,不违反拍板点 8;P1 随停靠操作卡交付「转人工」按钮 |
|
||||
| 预算中途调整 | IPC ai_update_task_boundary 仅开放 budget 三字段(轮次/token/挂钟),risk/目录拒改 | **新建一个窄 IPC**(P1);用户主动调整合规(拍板点 8 修订后口径),UI 在任务面板预算条旁放开编辑 |
|
||||
| 与看门狗/级联取消的关系 | 纠偏/接管经 epoch 接管不与前端 watchdog 冲突(新 loop 自带 guard+heartbeat);级联取消(Stop/Delete 双形态)覆盖停止与删除两路 | 既有机制全覆盖,零新增 |
|
||||
|
||||
**分期归属**: 中途停止/纠偏注入=P0(复用件,仅前端入口);任务面板+子代理树+预算调整+接管=P1;暂停/恢复=P2(与断点续跑同批,共享恢复入口)。
|
||||
|
||||
## 补遗新增拍板点(续编)
|
||||
|
||||
| # | 决策 | 选项 | 建议 |
|
||||
|---|------|------|------|
|
||||
| 9 | 缓存命中的自动降级策略 | A 命中超阈值自动插 system 强制转向指令;B 维持警告头威慑 | **A**。无人值守无人读警告,机制必须自己踩刹车;阈值实测后定 |
|
||||
| 10 | 任务级模型分级默认 | A 边界卡显式选;B 子代理默认轻量模型+主循环继承会话模型,卡内高级项可覆盖;C 不做 | **B**。自动分级零配置收益最大,显式选择作兜底;轻量模型具体指认实测后定 |
|
||||
| 11 | token 度量口径 | A 父会话/子代理分列+合计;B 只算父会话 | **A**。子代理 token 不进父 TokenAccumulator,混算会严重低估;合计是「任务花费」的正口径 |
|
||||
| 12 | 任务面板形态 | A 会话头部常驻面板(进度+花费+预算+插话+树);B 仅侧栏徽标点开看 | **A**。「10 秒三问」要求免点击可达;徽标作为托盘化场景补充 |
|
||||
| 13 | 预算中途调整方向 | A 双向(收紧/放宽)都允许;B 只许收紧 | **A**。用户主动放宽=追加授权,与「一次性拍板」不冲突(拍板约束 AI 不约束用户) |
|
||||
|
||||
## 九、效率补遗二:u-claw 实战思想迁移(2026-10-04,历史会话汲取)
|
||||
|
||||
> 来源:`~/.u-claw/willcard/decisions.jsonl`(298 条决策,效率相关 60+ 条实证)+ devflow 知识库。
|
||||
> 基调(用户原话):「从我历史使用 u-claw 的历史会话、我主张的思想中汲取知识辅助设计和决策;无人值守同时也要确保极高效率任务推进能力」。
|
||||
> 以下思想在 u-claw 侧均经生产实证,迁移 devflow 时逐条标注落点与分期。知识库已沉淀两条 workflow_optimization(f6cd9de8 派工契约/c2c0a444 A/B 层纪律)。
|
||||
|
||||
### 9.1 派工契约:颗粒约束+验收前置+首轮任务图(u-claw 09-25/09-30 拍板)
|
||||
|
||||
- **废除时长预估**:不要求「预计 N 分钟」——u-claw 实证时长预估无锚定价值,超时即杀导致活任务被误杀;
|
||||
- **替代契约**:①任务书必含**验收命令**(一轮内自证完成并带回验收证据) ②**超一轮未完成=派工颗粒过大**,主控拆批重派,不盲等 ③杀进程仅作卡死/跑偏兜底;
|
||||
- **首轮任务图**(09-30 第 7 条):整包委托语义令下,round-0 **必须产出任务分解+派工清单**再动手,禁主控先亲手做首个任务(亲手兜底仅 1-2 行 typo 级);
|
||||
- **devflow 落点**:无人值守任务书模板加「round-0 规划纪律」段(P0 文案);spawn_agent 超轮触发拆批提示(而非熔断即弃);子代理任务书 acceptance 对齐(批 2 指引真空项同源改造)。
|
||||
|
||||
### 9.2 A/B 层 prompt 纪律:预算行进 user 尾部不毁缓存(u-claw 08-31)
|
||||
|
||||
- system 层(A):编排纪律五判据(耗时意识/试错预算/委派契约/超时自查/并行合并)——稳定段进 cache_control;
|
||||
- **每轮 user 尾部(B):任务预算行**(当前阶段/剩余轮次/剩余 token)——user 消息注入不动 system 前缀,prompt cache 零失效(u-claw 实证口径);
|
||||
- **devflow 落点**:对齐已拍板的 AC-EFF-C0-2(稳定段/易变段分离)——预算行作为易变段追加到消息流尾部,AiTaskProgress 的前端展示与后端注入同源数据;**P1 实施**(与 A9 缓存降级同批)。
|
||||
|
||||
### 9.3 换代水位 70% 主动切(u-claw 09-03)
|
||||
|
||||
- rollover 判据=窗口×70%(如 1M→700K),**不等硬爆**;水位取值=最后一条 assistant 行 usage(真实窗口口径,非估算);
|
||||
- **devflow 落点**:无人值守长任务压缩触发阈值联动此口径(maybe_auto_compress 判据核对,P1 顺带);devflow 主循环已有压缩链,补「长任务下水位提前」参数化。
|
||||
|
||||
### 9.4 文件互斥分区并行(u-claw 09-29-6)
|
||||
|
||||
- 多代理并行按**文件清单显式隔离**,各自批次文件集无交集才能并行写;
|
||||
- **devflow 落点**:spawn_agent 派工时若子代理任务书含明确目标文件集,主循环日志登记防重叠(软约束:提示词级「不要改本任务书之外的文件」已有,补主控侧登记);硬锁在 namespace owner 隔离(已设计)同批考虑。
|
||||
|
||||
### 9.5 卡片活性与聚合回填(u-claw 09-06 三连拍板)
|
||||
|
||||
- 后台执行被否决:**用户以为任务丢失**;卡片须延续活性+回填结果+多任务聚合展示;
|
||||
- **devflow 落点**:对齐 A12 任务面板+AiAgentFinished 完成卡(已设计);面板补「子代理状态/任务清单/等待原因/危险动作告警」四件套语义(u-claw 08-29 processing 卡四件套实证有效),P1 面板实施时并入。
|
||||
|
||||
### 9.6 失败按码分级冷却(u-claw 09-13)
|
||||
|
||||
- 402/401(确定性失败)冷却 10min,其余(瞬态)30s——**确定性失败不烧重试预算**;
|
||||
- **devflow 落点**:对齐已修的「Retryable/Fatal 分层」(10-02 批次2),补冷却时长分级(401/403/404 系 10min,超时/网络 30s);retry.rs 已有骨架,P2 效率批接线。
|
||||
|
||||
### 9.7 高置信自主推进决策法(u-claw 10-02)
|
||||
|
||||
- 决策项先查历史指令(will-card+memory+decisions.jsonl)辅助直裁;实在不决记录挂起,**自主连续推进**;
|
||||
- **devflow 落点**:已对齐——拍板集+知识库检索+挂起登记(todo 遗留),无需新增;持续把用户长期偏好(如「决策影响边界简洁呈现」「列表禁表格」)沉淀进知识库,供 AI 直裁。
|
||||
|
||||
### 9.8 汇报与决策呈现纪律(u-claw 09-28)
|
||||
|
||||
- 决策须给**足够且简洁易读的影响边界**(降低决策心智投入);默认列表禁表格(宽列截断看不见内容);
|
||||
- **devflow 落点**:停靠卡/NeedAction 通知文案规范(A12 面板实施时并入:一行定性结论+决策级技术事实+影响边界,列表化呈现)。
|
||||
|
||||
### 分期并入
|
||||
|
||||
| 思想 | 分期 |
|
||||
|---|---|
|
||||
| 9.1 round-0 任务图+验收契约 | **P0 文案**(任务书模板,P0 收尾即生效) |
|
||||
| 9.2 预算行 user 尾注入 | P1(与 A9 同批) |
|
||||
| 9.3 水位 70% 联动 | P1(压缩链顺带) |
|
||||
| 9.4 文件互斥登记 | P1(子代理批同做) |
|
||||
| 9.5 面板四件套 | P1(A12 面板) |
|
||||
| 9.6 冷却分级 | P2(效率批) |
|
||||
| 9.7/9.8 | 已对齐/随 P1 文案 |
|
||||
@@ -0,0 +1,90 @@
|
||||
# 跨会话召回最小闭环 · 设计(2026-10-04)
|
||||
|
||||
> ✅ 拍板通过(2026-10-04,3/3 按建议:B1 mention 唯一才启用/B2 软排序/B3 新增 scoped 方法;见 [拍板集](../滚动规范/拍板集-2026-10-04.md))
|
||||
|
||||
> 来源:todo.md「P1 跨会话召回最小闭环(项目级记忆+会话结束提炼进知识库,source_project 已有字段未用)」。
|
||||
> 勘察结论(2026-10-04 现场核实,以源码为准):
|
||||
> - 会话内自动提炼链已存在:`knowledge_inject.rs` `extract_knowledge_from_conversation`(user/assistant 过滤+`KNOWLEDGE_EXTRACT_WINDOW` 窗口+JSON 提炼+candidate 批量插入)+ `EXTRACT_WATERMARKS` 水位防重 + `conv(cid).knowledge_extracted` 哨兵 + `trigger_extraction_now` 手动强制。
|
||||
> - `source_project` 字段在**主仓存储层零使用**:仅 `idea_repo.rs` 列定义/INSERT/UPDATE 贯通,`idea_repo.rs:949` 注释自证「source_project/source_ref 等元字段未被使用却仍随每行读出」。检索侧 `search_keywords`(idea_repo.rs:699)无项目过滤。
|
||||
> - 注入点:`knowledge_inject.rs:622 inject_knowledge_into_prompt`(`search_keywords(keywords, include_candidates, limit)`,无项目维度)。
|
||||
> - 会话→项目关联:`ai_conversations` 表**无 project_id 列**(V4 仅补 archived、V33 pending_approvals、V44 origin、V45 boundary_json);项目归属当前只能靠会话内 mention/`boundary_json`/`@项目` enrichment 间接推断。
|
||||
> - candidate→published 机制已通(自动晋升 + knowledge_promote 工具 + V43 存量迁移 854 条),**跨会话可见性**只欠「published 参与检索注入」+「项目维度过滤」两环。
|
||||
|
||||
## 1. 目标与非目标
|
||||
|
||||
**目标(最小闭环)**:
|
||||
1. 会话结束(AiCompleted 收敛点)自动触发提炼时,把当前会话**归属项目**写入产出知识的 `source_project`;
|
||||
2. 知识注入(`inject_knowledge_into_prompt`)按**当前项目优先**:同项目知识加权/前置,跨项目知识仍可召回但不淹没;
|
||||
3. 形成闭环:会话 A 的踩坑 → candidate → 晋升 published → 会话 B(同项目)注入时优先命中。
|
||||
|
||||
**非目标(本期不做)**:
|
||||
- 不做会话↔项目的强绑定 schema 变更(不加 `ai_conversations.project_id` 列)——归属用**弱推断**(见 §3.1),避免迁移风险;
|
||||
- 不动 candidate 晋升策略(C3 已拍板项);
|
||||
- 不做向量检索(embedding 现状 1033 条全 NULL,另线);
|
||||
- 不做 UI 变更(Knowledge 页项目筛选留后续)。
|
||||
|
||||
## 2. 核心取舍:项目归属从哪来
|
||||
|
||||
会话本身不存 project_id。归属来源三选,按可靠性排序:
|
||||
|
||||
| 来源 | 可靠性 | 实现成本 | 结论 |
|
||||
|---|---|---|---|
|
||||
| a. `boundary_json`(V45 无人值守边界)含项目路径 | 高(显式绑定) | 低(已落库) | ✅ 首选:有则用 |
|
||||
| b. 会话消息中的 `@项目` enrichment / mention 记录 | 中(用户显式引用过) | 中(解析消息) | ✅ 次选:回退 |
|
||||
| c. `ai_conversations` 加 project_id 列 | 高但侵入 schema | 高(迁移+全链) | ❌ 本期不做 |
|
||||
|
||||
**设计决策 D1(建议拍板)**:归属采用 a→b 两级弱推断,都不命中则 `source_project = NULL`(全局知识,不过滤)。这样零 schema 变更,且与「无人值守长任务」边界模型(boundary 已带项目上下文)天然对齐。
|
||||
|
||||
**提炼函数签名扩展**(knowledge_inject.rs 内,不新建文件):
|
||||
|
||||
```
|
||||
async fn resolve_source_project(db, conv_id) -> Option<String>
|
||||
// 1. 读 ai_conversations.boundary_json → 解析 project_path → 反查 projects 表 path 匹配 → id
|
||||
// 2. 回退:读最近 N 条消息找 "[项目:xxx]" enrichment 标记 → 反查 → id
|
||||
// 3. 皆无 → None
|
||||
```
|
||||
|
||||
候选插入处(`extract_knowledge_from_conversation` 内 INSERT 前)把 `source_project = resolve_source_project(...)` 赋上。
|
||||
|
||||
## 3. 注入侧:项目维度过滤
|
||||
|
||||
**设计决策 D2**:注入不做硬过滤(`WHERE source_project = ?` 会把同问题跨项目通用知识挡死),改**软排序**——SQL 层 `search_keywords` 增加可选 `project_id` 参数:
|
||||
|
||||
```
|
||||
ORDER BY (CASE WHEN source_project = ?1 THEN 0 ELSE 1 END), reuse_count DESC, ...
|
||||
```
|
||||
|
||||
- 同项目知识排前(limit 截断时优先保留);
|
||||
- `source_project IS NULL`(全局知识)视为第二档但优于异项目:`(CASE WHEN source_project = ?1 THEN 0 WHEN source_project IS NULL THEN 1 ELSE 2 END)`;
|
||||
- `inject_knowledge_into_prompt` 调用前同样走 `resolve_source_project`(此时是当前活跃会话)取 project_id 传入;
|
||||
- `knowledge_search` 工具(LLM 显式调用)暂不加项目参数——LLM 不知道当前 project_id,靠注入侧自动带。
|
||||
|
||||
**接口改动面**:
|
||||
- `df-storage/crud/idea_repo.rs`:`search_keywords(&keywords, include_candidates, limit)` → 增参 `project_id: Option<&str>`(或新方法 `search_keywords_scoped`,保旧签名给 MCP 等外部调用方,内部转调);
|
||||
- `knowledge_inject.rs` 两个调用点(:423 检索、:622 注入前置);
|
||||
- `knowledge.rs` 工具(tools/knowledge.rs)不动。
|
||||
|
||||
## 4. 闭环验证与防劣化
|
||||
|
||||
| 风险 | 对策 |
|
||||
|---|---|
|
||||
| 提炼重复(每会话产相似 candidate 洪水) | 已有水位+哨兵;新增:插入前按 `title` 相似度(`search_keywords` 查同 title 命中即跳过或 `reuse_count+1`)——复用现有检索,零新机制 |
|
||||
| 弱推断错误归属(张冠李戴) | 只在高置信(boundary 显式路径)时写 source_project;mention 回退可拍板是否启用(D1-b) |
|
||||
| 注入 token 膨胀 | limit 不变,软排序只改顺序不增量 |
|
||||
| 存量 967 条 candidate/907 published 无 source_project | 不回填(NULL=全局,行为正确);增量自然带项目 |
|
||||
|
||||
**验收**:
|
||||
1. 新会话提及某项目 → 完成后提炼的 candidate `source_project` 非空且指向该项目;
|
||||
2. 同项目新会话注入时该知识(晋升后)排在异项目知识前;
|
||||
3. 无项目会话(source_project=NULL)注入行为与现状逐字一致(回归门);
|
||||
4. `cargo test -p df-storage -p df-ai` 全绿 + search_keywords 新旧签名编译兼容。
|
||||
|
||||
## 5. 拍板点(3 项)
|
||||
|
||||
- **拍板1(D1-b)**:mention 回退推断是否启用?建议启用但仅当会话中项目 mention ≥1 次且唯一(多项目 mention 不猜,置 NULL)。
|
||||
- **拍板2(D2)**:注入软排序 vs 硬过滤?建议软排序(理由见 §3)。
|
||||
- **拍板3**:`search_keywords` 改签名(breaking,内部 crate)vs 新增 scoped 方法?建议**新方法**(df-mcp 等外部链路零影响)。
|
||||
|
||||
## 6. 工作量估算
|
||||
|
||||
约 1 天:resolve_source_project(2h)+ search_keywords_scoped + 两调用点接线(3h)+ 去重跳过(1h)+ 单测(2h)。
|
||||
Reference in New Issue
Block a user