# AI 工具失败画像复盘 —— 逐类机制根因与降失败

> 日期:2026-08-08 | 类型:失败画像复盘(实证 + 代码逻辑推断) | 关联:[待办 AC-5](../todo.md)
> 数据来源:`docs/05-代码审查/aichat历史会话实证诊断-2026-08-04.md` 实证 5(prod 库 6392 次工具执行)

## 概述

实证诊断给出 5 个高频失败工具画像:run_command(67)/ read_file(55)/ search_files(33)/ advance_task(31,状态机拒)/ patch_file(27)。本复盘逐类从「代码逻辑 + 实证」双视角找机制根因,并落地 2 项风险可控的机制降失败(advance_task 合法目标前置、patch_file 相近锚点提示),其余给建议待评估。

已有的失败兜底(L1 断路器 `agentic/mod.rs` count_recent_failures + 缓存去重 + approval retry guard)负责「止损」——连续同类失败熔断。本复盘关注「首次失败后让 LLM 自愈」——把失败原因与修正线索直接回灌,降低重试往返。

---

## 1. run_command(67 次,命令执行失败/超时)

### 失败模式
- 命令非零退出(测试失败/构建报错/git diff 无差异),返回 `succeeded=false`。
- 命令启动失败(找不到解释器/命令),execute_streaming 返 Err。
- 超时(交互式命令/死循环/大构建),超时 Err 包「命令执行超时」语义。
- 实证根因多为:LLM 按 Unix 习惯生成命令(PS5 不支持 `&&`)、路径未引用、命令本身错。

### 现状处理
`src-tauri/src/commands/ai/tools/file.rs` run_command handler:
- 超时拦截(file.rs:1099-1128):错误改写为「超时 + 勿盲目重试 + 需更长时限改 timeout_secs」。
- shell 适配提示(file.rs:1106-1113):Windows 下附 PowerShell 路径/`&&`/pwsh 提示。
- 输出完整性(file.rs:1131-1145):stdout/stderr 各截断 10KB(尾部保留),返回 exit_code/duration_ms/truncated。

### 根因
命令执行失败大部分是「LLM 生成命令与目标 shell 环境不符」或「命令语义本身失败(非 bug)」。机制已较好:错误信息已含 exit_code + stderr 截断 + shell 提示,超时与命令失败语义区分明确。

### 降失败建议
- 维持现状(信息已完整),可评估:run_command 失败且 stderr 为空时,附 `detect_environment` 探测结果(默认 shell / 可用解释器),进一步缩小 LLM 猜测空间。
- 状态机已有断路器兜底,不建议再加重。

---

## 2. read_file(55 次,路径错/授权)

### 失败模式
- 路径不存在:File::open 返 NotFound(file.rs:88-97)。
- 权限/解码失败:非 UTF-8 / 二进制 / 1MB 限制。

### 现状处理
file.rs:87-97 NotFound 分支已附引导:「建议用 list_directory 先查看目录下的实际文件列表」。二进制/超限/解码均有明确错误。

### 根因
路径不存在占多数,根因是 LLM 凭记忆猜路径(相对路径/大小写/文件名拼错)。现有「建议 list_directory」是通用引导,LLM 需额外一次往返列目录才能修正。

### 降失败建议
- **(推荐,未落地)** NotFound 时附加父目录下与目标文件名相似的文件列表(复用 patch_file 已实现的 `similar_line_fragments` 思路,改为文件名 Dice 匹配,取父目录 read_dir + top-3 相近名)。LLM 一次失败即可看到正确候选,无需再列目录。
- 授权失败(path_auth)已有授权申请机制,保持现状。

---

## 3. search_files(33 次,路径/参数)

### 失败模式
- 未传 path(返回引导提示,file.rs:1032-1034)。
- 路径非法/授权失败(resolve_workspace_path_with_allowed Err)。
- 路径正确但 pattern 无匹配(返回空 results,total=0,非错误)。

### 现状处理
file.rs:1022-1053:未传 path 有引导;无匹配返回空数组不报错。

### 根因
「路径/参数」失败多为 LLM 用未绑定的绝对路径或 pattern 过宽/过窄。无匹配返回空结果时,LLM 常反复换 pattern 盲探(与实证 1 重复探索叠加)。

### 降失败建议
- **total=0 时返回引导提示**(如「该目录下文件总数 / 列出前几项文件名」),让 LLM 判断是 pattern 错还是目录错,避免空结果盲探。
- 路径解析失败沿用现有授权机制。

---

## 4. advance_task(31 次,状态机拒绝非法跳态)✅ 已落地机制

### 失败模式
- 非法跳态(如 todo→done,跳过闸门)。
- 同态(如 in_progress→in_progress,空操作)。
- 终态无后继(done→xxx)。
- 任务不存在。

### 现状处理(已改进)
`crates/df-nodes/src/task_advance_node.rs:78-89` 状态机校验三类拒绝:
- 原:非法转换错误 `InvalidState{ current: "todo→done(非法状态转换)" }`,只含 from→to,不含「能去哪」。
- **改后**:错误附 `legal_targets(from)` 合法目标列表,如 `todo→done(非法状态转换), todo 的合法目标: in_progress/cancelled`;同态错误附 `相同状态 "in_progress",无需推进, in_progress 的合法目标: in_review/blocked/cancelled`;终态提示「是终态, 无合法后继」。

### 根因
LLM 不知道任务当前状态与合法跳转(任务清单/进度上下文缺失),只能猜 target_status,命中非法跳态。错误信息此前「只报错不指路」,LLM 仍靠猜重试。

### 降失败机制(本次落地)
- 新增 `task_state_machine::legal_targets(from)`(crates/df-nodes/src/task_state_machine.rs),遍历 ALL_STATES 过滤 can_transition,与状态机矩阵单一真相源对齐;新增 3 条单测锁定矩阵与性质。
- advance_task 状态机拒绝错误回灌合法目标列表,LLM 下次直接选对目标态,从根上消除「猜目标」往返。
- 该错误同时用于 IPC 路径(前端展示)与 DAG 节点,一处改多处受益,不改前端契约。

---

## 5. patch_file(27 次,精确匹配失败)✅ 已落地机制

### 失败模式
- 模式1 old_text 精确匹配失败(缩进/空格/内容略有差异)。
- 文件已被外部修改(hash 不匹配)。
- 模式互斥冲突/缺定位方式/二进制/1MB 限制。

### 现状处理(已改进)
`src-tauri/src/commands/ai/tools/file.rs` patch_file old_text 分支:
- 原:`"未找到目标文本，文件可能已被修改"`,无任何修正线索。
- **改后**:未匹配时调 `similar_line_fragments(content, old_text, 3)` 找出文件里与 old_text 最相近的 3 行(行号 + 内容,字符多重集 Dice ≥40%),附进错误信息。LLM 对照真实缩进/空格一次修正,不必盲猜重试。

### 根因
LLM 的 old_text 与文件实际内容有细微差异(缩进从 2 空格变 4、全角/半角、行尾差异),错误信息此前无相近片段,LLM 只能重读文件再猜。最难的其实是「不知道真实文本长什么样」。

### 降失败机制(本次落地)
- 新增 `similar_line_fragments` + Dice 相似度辅助(src-tauri/src/commands/ai/tools/file.rs,注册函数后),纯函数无外部依赖:
  - 探针取 old_text 首行前 120 字符(超长/多行 old_text 稳定)。
  - 按「首非空白字符相同」预筛行,字符多重集 Dice 系数评分,阈值 40% 防误导。
  - 1MB 文件全量扫描约 40ms(实测),仅失败路径触发,性能可接受。
- 新增 3 条单测(缩进漂移命中/无关文本空/多行 old_text 取首行)。devflow lib 测试二进制在 Windows 有既有加载失败(见文末),逻辑已用独立 Rust 程序验证通过。

---

## 机制降失败落地小结

| 项 | 落地 | 证据 |
|----|------|------|
| advance_task 合法目标前置 | 是 | task_state_machine.rs `legal_targets` + task_advance_node.rs:78-89 错误附加合法目标 |
| patch_file 相近锚点提示 | 是 | file.rs `similar_line_fragments` + old_text 未匹配分支附加相近片段 |
| run_command 信息完整性 | 维持现状(已含 exit_code+stderr+shell 提示) | file.rs:1109-1145 |
| read_file 相近文件名 | 建议待评估 | 复用 similar_line_fragments 思路,改文件名匹配 |
| search_files 空结果引导 | 建议待评估 | total=0 时附目录文件概览 |

## 说明

devflow lib 单元测试二进制在 Windows 存在既有加载失败(STATUS_ENTRYPOINT_NOT_FOUND,连未改动基线测试同样崩溃,与本次改动无关),本次 Rust 改动验证方式:cargo check -p devflow 通过 + df-nodes 全部单测通过 + similar_line_fragments 逻辑以独立 Rust 程序验证。
