Skip to content
dsh.fish
Bundle

dsh-failbook

失败账本 — DeepSeek Harness 工具失败记录、签名聚类与失败感知重试拦截(含 Web UI 面板)。Failure ledger for DeepSeek Harness: capture, cluster, persist and learn from failed tool calls, with a failure-aware retry guard and a Web UI panel.

Source
G1en-114
stars
2 stars
License
MIT
Updated
Updated 11 days ago

Readme

# 🧾 dsh-failbook · 失败账本

> 让 DeepSeek Harness 的 Agent **越用越少犯错**:记录每次工具调用失败,按失败原因自动聚类,持久化账本,并在同一错误反复出现时**拦住 Agent 的重试**——换参数犯同一个错也拦得住。

**DeepSeek Harness (DSH) 插件** · 宿主端(Cordis)+ 浏览器端(设置面板) · MIT · 零配置开箱即用

[English](#english) · [安装](#安装) · [配置](#配置) · [工作原理](#工作原理) · [对比](#与现有方案对比) · [Roadmap](#roadmap)

---

## 为什么做这个

Agent 干活时最烧钱、最烦人的场景:**同一个错误反复重试**。

- `command not found: pipx` —— 换个写法再来一遍,还是 127;
- 沙箱拒绝 —— 策略不给权限,硬重试 5 次;
- `npm ERR!` —— 依赖没装对,重装 N 遍。

官方 `repeat-tool-reminder` 只抓**参数完全相同的连续重复**,换参数犯同一个错就抓不住。`dsh-failbook` 按**失败签名**聚类:只要失败原因一样,不管参数怎么换,都记进同一个账本,攒够次数就注入提醒,告诉模型"别重试了,原因在这,建议这样做"。

## 功能

| 能力 | 说明 |
|---|---|
| 📥 自动记录 | 挂钩 `tools/post-execute`,捕获每次工具调用失败:结构化错误、非零退出码、沙箱拒绝、常见错误文本 |
| 🗂️ 签名聚类 | 同一失败原因(如 `bash:command-not-found`)自动归为一桶,不因参数不同而散落 |
| 💾 跨会话持久化 | 通过官方 `ctx.storageDomain` 存储,重启不丢;无该服务时自动降级为内存账本 |
| 🛑 失败感知重试拦截 | 同一签名近窗口内失败 ≥ N 次(默认 2 次)→ 注入一段建议性提醒,附失败原因和改法建议 |
| 🖥️ Web UI 面板 | 设置页「失败账本」:Top 失败签名表、次数/近窗口/最近时间、静音、单删/清空 |
| 🔇 静音与排除 | 误报桶一键静音;`excludeTools`/`patterns` 精细控制 |
| 🧮 保守检测 | 漏判 > 误判:只认确凿的失败标记,可选扩展错误模式 |

## 演示

```text
┌────────────────────────────────────────────────────────┐
│ 用户: 帮我跑一下 pipx 装好的工具                        │
│ 模型: bash pipx list        → ❌ command not found      │
│ 模型: bash pipx --version   → ❌ command not found      │
│ 模型: bash pipx install ... → ❌ command not found      │
│ 插件: ⚠️ [dsh-failbook] 该调用此前已失败 3 次            │
│       (command-not-found)。重试大概率仍会失败。          │
│       建议:确认命令已安装且 PATH 正确…                  │
│ 模型: 我先检查一下 PATH 和环境变量…                     │
└────────────────────────────────────────────────────────┘
```

## 安装

```bash
# 安装到你的 web 配置
dsh plugin --profile web add "github:G1en-114/dsh-failbook#main"
```

或手动编辑配置目录的 `cordis.patch.yml`:

```yaml
- insert:
    - id: failbook
      name: dsh-failbook
      config:
        enabled: true
```

重启 `dsh web` 后,打开 **设置 → 失败账本** 即可看到面板。

## 配置

| 配置项 | 默认 | 说明 |
|---|---|---|
| `enabled` | `true` | 总开关 |
| `retryGuardThreshold` | `2` | 近窗口内同一签名失败 ≥N 次触发提醒;`0` 关闭提醒(只记录) |
| `reminderCooldownSec` | `300` | 同一桶两次提醒的最小间隔(秒),防刷屏 |
| `reminderLocale` | `'zh'` | 提醒文案语言:`zh` / `en` |
| `exitFailureMin` | `2` | 退出码 ≥ 此值记为失败(`1` 常是 grep 无匹配等正常负结果) |
| `recentWindowSec` | `86400` | "近窗口"滚动窗口(秒) |
| `maxArgsChars` / `maxDetailChars` | `160` / `400` | 参数/输出预览截断长度 |
| `patterns` | `[]` | 额外错误模式,格式 `分类:正则`,如 `build-fail:BUILD FAILED` |
| `excludeTools` | `['todo_write']` | 不追踪的工具名(支持 `*` 通配) |
| `maxBuckets` | `1000` | 账本桶上限,超出按最近使用淘汰 |

示例:把退出码 1 也当失败,并拦截得更激进:

```yaml
- insert:
    - id: failbook
      name: dsh-failbook
      config:
        exitFailureMin: 1
        retryGuardThreshold: 1
        reminderLocale: 'en'
```

## 工作原理

```
工具调用 ──▶ tools/post-execute 瀑布
                │
                ▼
        detectFailure(exec, result)        结构化错误?沙箱拒绝?退出码?错误文本?
                │
                ▼
        签名 = tool:category                如 bash:command-not-found
                │
                ▼
        ledger.record() ──▶ ctx.storageDomain(跨会话持久化)
                │
                ▼
        recentCount ≥ 阈值 且 未静音 且 冷却期外?
                │
                ▼ 是
        createUserMessage 注入 additionalContexts
        (下一轮请求模型可见,0 额外 token 的提醒)
```

- **失败检测**:`result.isError`(结构化错误)→ 沙箱 `[sandbox: …]` 标记 → 保守错误文本(`command not found` / `ENOENT` / `EACCES` / `npm ERR!` / `fatal:` 等)→ 退出码兜底。取消(`ABORTED`)不计为失败。
- **提醒通道**:与官方 `repeat-tool-reminder` 相同的 `additionalContexts` 机制——不修改工具结果、不打断管线,模型下轮请求自然看到。
- **安全边界**:Web API 仅回环地址可访问;账本只存截断预览,完整命令输出永不离开宿主。

## 与现有方案对比

| | 官方 repeat-tool-reminder | dsh-fail-logger | **dsh-failbook** |
|---|---|---|---|
| 检测维度 | 参数完全相同的连续重复 | 失败记录 | 失败**签名**聚类(换参数也拦) |
| 持久化 | 内存 | skill 文件 | storageDomain 跨会话 |
| 重试拦截 | ✅(仅相同调用) | ❌ | ✅(签名 + 冷却 + 静音) |
| Web UI | ❌ | ❌ | ✅ 设置面板 |
| 跨会话统计 | ❌ | 部分 | ✅ 聚合/趋势 |

## Roadmap

- [x] 失败记录 + 签名聚类 + 持久化
- [x] 失败感知重试拦截(阈值/冷却/静音)
- [x] Web UI 面板(Top 签名、静音、清空)
- [ ] 会话开场简报:新会话注入本工作区 Top 失败模式
- [ ] 失败趋势图(按天/工具)
- [ ] 一键导出账本(JSON/CSV)

## 常见问题

**Q: 会不会误报?**
默认只认确凿标记:`[exit code: N]`、`[sandbox: …]` 是 DSH 自己生成的;文本模式全部带词边界且保守。误报桶可以直接在面板静音,或加进 `excludeTools`。

**Q: 提醒会不会刷屏 / 烧 token?**
每个桶默认 5 分钟冷却 + 阈值后才提醒,提醒文本有截断上限,且只在工具调用失败时产生。

**Q: headless 模式能用吗?**
能。没有 `storageDomain` 服务的配置会自动降级为进程内账本(重启清空),记录与拦截功能不变。

## 开发

```bash
npm install
npm run typecheck        # 类型检查
npm test                 # 构建 + 27 个单元测试
npm run test:integration # 真机集成验证(见下)
npm run build            # tsc(宿主) + esbuild(客户端 bundle)
```

## 集成验证(已在本仓库实测通过)

`scripts/integration-harness.mjs` 用真实 DSH 运行时(cordis + tools 管线 + storage-json + storage-domain)跑完整链路:

1. 注册一个固定返回 `[exit code: 2]` 的假工具;
2. 以 agent 身份连续执行 3 次 → 第 2 次起注入重试拦截提醒(含失败原因与建议);
3. 断言账本以签名 `fake_fail:exit:2` 落盘(count=3、remindersSent=2);
4. 读取存储介质文件验证**跨进程持久化**——与 `dsh web` 重启后 `/api/dsh-failbook/ledger` 读到的是同一份数据。

实测结果(`curl /api/dsh-failbook/ledger`,服务器重启后):

```json
{"buckets":[{"signature":"fake_fail:exit:2","category":"exit:2","count":3,
  "recentCount":3,"remindersSent":2,...}],"storage":"domain"}
```

仓库结构:

```
src/
├── index.ts        # 宿主入口:post-execute 观察者 + 路由注册
├── signature.ts    # 失败检测与签名提取
├── store.ts        # 账本(storageDomain / 内存双后端)
├── reminder.ts     # 提醒构造与注入
├── routes.ts       # /api/dsh-failbook/* 回环 API
├── shared.ts       # 宿主/客户端共享类型
└── client/         # 浏览器半:设置面板(React + fetch)
```

---

## English

**dsh-failbook** — a failure ledger for DeepSeek Harness: every failed tool call is recorded, clustered by *failure signature* (not by arguments), and persisted across sessions via `ctx.storageDomain`. When the same failure mode recurs past a threshold, the **failure-aware retry guard** injects a short advisory into the agent's next request — catching "same mistake, different arguments" loops that identical-args guards miss.

- **Install**: `dsh plugin --profile web add "github:<you>/dsh-failbook#main"`
- **UI**: Settings → 失败账本 (Failbook) — top failure signatures, counts, mute, clear
- **Detection**: structured errors, non-zero exits (default ≥2), sandbox denials, conservative error-text signatures; cancellations are never counted
- **Safety**: loopback-only API; only truncated previews are stored
- **License**: MIT

## License

MIT © 2026 dsh-failbook contributors

Install

dsh plugin --profile web add github:G1en-114/dsh-failbook

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source