🧾 dsh-failbook · 失败账本
让 DeepSeek Harness 的 Agent 越用越少犯错:记录每次工具调用失败,按失败原因自动聚类,持久化账本,并在同一错误反复出现时拦住 Agent 的重试——换参数犯同一个错也拦得住。
DeepSeek Harness (DSH) 插件 · 宿主端(Cordis)+ 浏览器端(设置面板) · MIT · 零配置开箱即用
English · 安装 · 配置 · 工作原理 · 对比 · 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 精细控制 |
| 🧮 保守检测 | 漏判 > 误判:只认确凿的失败标记,可选扩展错误模式 |
演示
┌────────────────────────────────────────────────────────┐
│ 用户: 帮我跑一下 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 和环境变量… │
└────────────────────────────────────────────────────────┘
安装
# 安装到你的 web 配置
dsh plugin --profile web add "github:G1en-114/dsh-failbook#main"
或手动编辑配置目录的 cordis.patch.yml:
- 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 也当失败,并拦截得更激进:
- 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
- 失败记录 + 签名聚类 + 持久化
- 失败感知重试拦截(阈值/冷却/静音)
- Web UI 面板(Top 签名、静音、清空)
- 会话开场简报:新会话注入本工作区 Top 失败模式
- 失败趋势图(按天/工具)
- 一键导出账本(JSON/CSV)
常见问题
Q: 会不会误报?
默认只认确凿标记:[exit code: N]、[sandbox: …] 是 DSH 自己生成的;文本模式全部带词边界且保守。误报桶可以直接在面板静音,或加进 excludeTools。
Q: 提醒会不会刷屏 / 烧 token? 每个桶默认 5 分钟冷却 + 阈值后才提醒,提醒文本有截断上限,且只在工具调用失败时产生。
Q: headless 模式能用吗?
能。没有 storageDomain 服务的配置会自动降级为进程内账本(重启清空),记录与拦截功能不变。
开发
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)跑完整链路:
- 注册一个固定返回
[exit code: 2]的假工具; - 以 agent 身份连续执行 3 次 → 第 2 次起注入重试拦截提醒(含失败原因与建议);
- 断言账本以签名
fake_fail:exit:2落盘(count=3、remindersSent=2); - 读取存储介质文件验证跨进程持久化——与
dsh web重启后/api/dsh-failbook/ledger读到的是同一份数据。
实测结果(curl /api/dsh-failbook/ledger,服务器重启后):
{"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
No comments yet. Be the first to write one.