# dsh-errkb · 设计说明书(待审查) | 项 | 值 | |---|---| | 文档状态 | **待审查** —— 设计已定稿;P1(T01–T04)已实施并合并,§17 八问仍待回答 | | 版本 | v0.1-draft | | 日期 | 2026-09-22 | | 对象 | DeepSeek Harness(dsh)插件:报错回收再利用(Error Knowledge Base) | | 落点 | ``(拟作为插件包根) | | 本机 dsh | `0.1.5-rc.2` / cordis `4.0.2` / dsh-tools `0.1.5-rc.2` / schemastery `3.18.2` | **怎么读这份文档**:§1–§16 是我定稿的设计,§17 是需要你填空/拍板的清单,§18 是超出你原始描述的想法。你直接在 §17 和 §19 里写批注即可,我按你的批注改设计,改完再问你要不要放行 P1。 **变更记录** | 日期 | 版本 | 变更 | |---|---|---| | 2026-09-22 | v0.1-draft | 首版:依据本机 v0.1.5-rc.2 实际接口定稿 | | 2026-09-27 | v0.1-draft | P1(T01–T04)实施完成并合并:包可安装、可构建、可过类型检查与 lint,CI 五步全绿。实施中实测到两处与本文不一致,记录于此待同步:① 本机 dsh / dsh-tools / dsh-llm 实为 `0.1.5-rc.3`(§2 表仍记 rc.2);② §11 的 peer 写法 `^4.0.2` / `^3.18.2` 在实施中改成精确钉死 `4.0.2` / `3.18.2` —— 整个栈有 15 个包要求精确 `4.0.2`,caret 会把 `4.0.4` 和第二份 schemastery 拉进依赖树 | --- ## 0. 动作边界(重要) 本文件是**本轮唯一交付物**。在你补齐 §17 并明确说"开始"之前,我不会: - 建 `package.json` / `src/` / 测试; - 执行 `pnpm install` / `dsh plugin add`; - 修改 `~/.dsh` 下任何文件; - 重启或触碰你正在运行的 `dsh web`。 --- ## 1. 目标与成功标准 **目标**:DeepSeek 模型一次运行中遇到的报错,被自动收敛为「文档里一个带独立编号的条目」;下次遇到同类报错时,插件在**模型开始诊断之前**就把已记录的解法塞给它,省掉重复推理的 token;没见过的报错才新增编号。 **可验证的成功标准** | # | 标准 | 验证方式 | |---|---|---| | S1 | 报错自动入库并获得唯一编号(`E-0001` 起) | 制造一次失败工具调用 → `errors\ERRORS.md` 出现新编号块 | | S2 | 同种报错**不重复编号**(路径/行号/PID/时间戳变化也算同种) | 换临时目录、换行号重跑 → 命中计数 +1,编号不变 | | S3 | 命中时向模型注入 ≤120 token 的已知解法通知 | 会话里出现 `plugin` 来源的 notice 行,模型不再重新诊断 | | S4 | 新报错在解决后被写入「解法」字段 | 模型调用 `err_record`,或收到一次性提示后补写 | | S5 | 插件自身的任何失败都**不会**中断回合或影响重试 | 单测注入异常 → 监听器吞掉,`agent/request-error` 仍返回下游结果 | | S6 | 文档可被人和模型双向编辑,机器索引可重建 | 手工改 `ERRORS.md` 的解法 → 命中时读到改后文本 | | S7 | 跨设备安全:文档随插件目录走,无绝对路径、无密钥 | 全文检索 `D:\`、`sk-`、`Bearer` 均为 0 命中 | --- ## 2. 事实基础(已在本机核实,非推测) | 事实 | 出处 | |---|---| | 插件形态:`export const name` / `inject` / `Config`(schemastery)+ `apply(ctx, config)` | 本机范本插件 `dsh-note-main\src\index.ts`、`dsh-repeat-tool-reminder\lib\index.js` | | LLM 请求失败扩展点 `agent/request-error`(waterfall):`{agent,turn,step,provider,failure,retryPolicy,signal}` → `{kind:'retry'} \| undefined` | `@deepseek-ai/dsh-agent\lib\types\runtime-types.d.ts:357`、`dsh-agent-loop\lib\index.js:1088` | | 回合级任意错误 `agent/error`(emit):`{agent,turn,step,error}` | 同上 `:411` | | 工具结果最终快照 `tools/result`(emit,已冻结、异常被隔离) | `dsh-tools\lib\types\index.d.ts:83` | | 可注入模型上下文的 `tools/post-execute`(waterfall)→ `additionalContexts` | 同上 `:61`、`:432` | | 下一步前的 `agent/pre-step`(waterfall)→ `{kind:'enter', messages}` | `runtime-types.d.ts:92,313` | | 开场注入 `agent/session-start` + `agent.inject(message)` | 同上 `:298`、`:209` | | 静态提示词段 `ctx.systemPrompt.section({name,order,text})` | `dsh-system-prompt\README.zh.md:59` | | 插件消息来源形状 `{kind:'plugin', plugin, form:'notice', summary}`,summary 需 `boundContextSummary` 截断 | `dsh-llm\lib\types\message.d.ts:98,110`;`createUserMessage` 由 `@deepseek-ai/dsh-llm` 导出 | | 工具定义 `defineTool({name,description,parameters,output,execute})` + `ctx.tools.register()` | `dsh-tools\lib\types\index.d.ts:106`、`dsh-note-main\src\note-tools.ts:64` | | 安装机制:`dsh plugin --profile web add .` 转发 pnpm;声明了 `dsh.bundle.patch` 的包会**自动并入** `dsh.profile.bundles` | `dsh\lib\plugin-Ddi42qoW.js:46-101`、`dsh\README.zh.md:18` | | 本机版本:dsh `0.1.5-rc.2`、cordis `4.0.2`、dsh-tools `0.1.5-rc.2`、schemastery `3.18.2` | `~/.dsh/profiles/node_modules/@deepseek-ai/*/package.json` | | `dsh-llm-retry` 拥有重试所有权,且每次重试都会再次计费 | `dsh-llm-retry\README.zh.md:12,119` | **关键推论**:`agent/request-error` 的监听器必须 `await next()` 并原样返回,**不能**抢恢复权——否则会和 `dsh-llm-retry` 的正确性契约打架。我们只观察、不接管。 --- ## 3. 总体架构与数据流 ``` ┌──────────────── 采集层(只读观察,永不抛错)────────────────┐ agent/request-error ──┤ LLM 失败:code=RATE_LIMIT/SERVER/AUTH/CONTEXT_OVERFLOW… │ agent/error ──┤ 回合级异常(工具/插件抛错) │ tools/result ──┤ 工具失败 isError + info.code │ (内容嗅探) ──┤ [exit code: N] 非 0 的命令失败(可选) │ └───────────────────────┬──────────────────────────────────┘ ▼ ┌───────────────────────────────┐ │ 指纹 fingerprint(规范+哈希) │ │ 匹配 match(精确 → 模糊) │ └───────┬───────────────┬───────┘ 命中 │ │ 未命中 ▼ ▼ ┌──────────────────────┐ ┌──────────────────────────┐ │ 注入层(省 token 的 │ │ 编号分配 + 追加条目 │ │ 收益所在,1 条 ≤120t)│ │ ERRORS.md 新块 + 派生索引 │ └──────────────────────┘ └──────────────────────────┘ ▲ ▲ │ │ ┌────────────┴───────────────┴──────────────┐ │ 模型/用户写解法:err_record(唯一入口) │ │ 解决检测 → 一次性提示 → 状态转 fixed │ └───────────────────────────────────────────┘ ``` **权威规则(防漂移)**:`ERRORS.md` 是**唯一真源**;`errors.index.json` 是由它派生的缓存(可删、可重建);本机计数在 `state.json`(可丢)。三者永不互写对方的事实。 --- ## 4. 存储设计(放在插件自己的文件夹下) ### 4.1 目录解析优先级 1. 配置 `kbDir`(相对路径 → 相对**插件包根**;绝对路径 → 原样)——设备本地差异只写在这里。 2. 未配置时:插件包根可写且**不在 `node_modules` 内** → `<插件根>\errors\`。 3. 否则(npm 全局安装、目录只读)→ `$DSH_HOME\errkb\`。 启动时用一行日志打印**解析后的真实路径**(可诊断、可预期)。 > 开发期是 `link:` 安装,所以落点就是 `报错的回收再利用\errors\`,随 GitHub 走;其他设备 clone 到任何盘符,路径自动跟着走,**文档里不出现任何绝对路径**。 ### 4.2 目录布局 ``` / ├── ERRORS.md # 唯一权威文档:编号条目,追加式(人也读、模型也读) ├── ERRORS.archive.md # 超出 maxEntries 后归档的老条目(只追加) ├── errors.index.json # 派生缓存:指纹→编号,损坏即从 ERRORS.md 重建 ├── state.json # 本机状态:命中数、lastSeen、nextId、环境指纹(建议 gitignore) ├── .machine.json # 设备标识 deviceSlug(如 DESKTOP-A),用于条目里的「设备」字段 └── .lock # 写锁(瞬时文件,写完即删) ``` ### 4.3 GitHub 多设备合并策略 | 数据 | 存放 | 冲突处理 | |---|---|---| | 条目正文与解法 | `ERRORS.md`,**只追加、块自包含、从不回改旧块** | 两台设备各追加到 EOF,git 绝大多数情况干净合并;真冲突时是纯追加冲突,人手可解 | | 命中计数、lastSeen | `state.json`(**本机,不进 git**) | 永不冲突;跨设备数字不必一致,知识一致就够 | | 编号 | 写入时取 `max(现有编号)+1`;合并后发现重复 | **已写入的编号永不改**;较晚 timestamp 的重复者改号为 `max+1`,块内留 `改号自 E-0007` 说明 | | 索引 | `errors.index.json` | 建议 gitignore;缺失自动重建,零成本 | ### 4.4 脱敏(强制,因为文档要上 GitHub) 入库前对**所有**文本做替换: - `sk-*`、`Bearer *`、`api[_-]?key=*`、`token=*`、`authorization:` 头; - 长 base64 串、32 位以上十六进制串、邮箱、原始 `requestId`。 配置 `share: 'public' | 'private'`(默认 `public`): - `public`:额外把绝对路径压成 ``,样本 500 字符封顶; - `private`:保留项目内相对路径便于自查。 **未脱敏的原文永不落盘**(只在内存里参与指纹计算)。 --- ## 5. 指纹与匹配算法("同一种报错"的判定,全篇最关键) ### 5.1 规范化 `normalize(raw)` 1. 去 ANSI 转义、去 `\r`、折叠空白; 2. 时间戳 / ISO 日期 / `耗时 123ms` / `PID 1234` / `port 3000` / `line 42:17` / UUID / 64 位哈希 / 临时目录名 → 统一占位符(` `); 3. 绝对路径(`X:\...`、`\\?\...`、`/Users/...`、`/home/...`)→ ``,但**保留最后一段文件名**(形如 `\package.json`,保住区分度); 4. 转小写、去首尾标点。 ### 5.2 指纹 ``` signature = sha256(category + '\u0000' + normalize(message)).slice(0, 12) // 例:3f2a1c9d0b71 ``` 只用 `message`,**不用堆栈**:堆栈行号噪声最大,且 DSH 的 `LlmFailure` 本身就只给规范化后的 `message + code`。 ### 5.3 匹配顺序(全部本地计算,零模型成本) 1. **精确**:`signature` 命中索引 → 命中。 2. **模糊**:规范化 token 集合的 Jaccard 相似度 `≥ fuzzyThreshold`(默认 `0.72`)→ **近似命中**(沿用原编号、计数 +1,并把本次样本追加为该条目的「变体样本」,最多保留 3 条)。近似命中在注入文本里标注「近似匹配,请先确认」。 3. **同种 code 兜底**:`category` 相同、code 相同、且两条 message 都 < 40 字符 → 视为同种(覆盖 `NO_ADAPTER` 这类短消息)。 4. 都不中 → **分配新编号**。 **误判兜底**:条目支持 `状态: wontfix` 与 `误判: true`;`err_record(id, status:'wontfix')` 后该条目不再自动注入(但仍计数),避免一条坏记录反复污染上下文。 --- ## 6. 采集层:4 个钩子 + 分类矩阵 | 来源 | 钩子 | 模式 | 动作 | |---|---|---|---| | LLM API 失败 | `agent/request-error` | waterfall | `const r = await next(); return r;`,前后各记一次。**绝不改返回值** | | 回合级异常 | `agent/error` | emit | 记录 `error.message/code/name`(`unknown` → 安全字符串化) | | 工具失败 | `tools/result` | emit | `result.isError` → 记录 `exec.name` + `result.error.message` + `result.error.info.code` | | 命令非零退出 | `tools/result` 内容嗅探 | emit | 结果文本匹配 `\[exit code: (\d+)\]` 且 N≠0 → 记为 `command-exit`(配置 `captureExitCodes`) | ### 分类与「是否值得占用一个编号」(保证文档高信噪比) | 分类 | code 例 | 立即编号? | |---|---|---| | `command-exit` / `tool` | EPERM、ENOENT、语法错 | ✅ 立即 | | `llm:<永久性>` | `AUTH`、`QUOTA`、`INVALID_REQUEST`、`CONTEXT_OVERFLOW`、`NO_ADAPTER`、`UNKNOWN` | ✅ 立即(解法通常是配置改动,复用价值最高) | | `llm:<瞬时性>` | `RATE_LIMIT`、`SERVER`、`TIMEOUT`、`TRANSPORT`、`EMPTY_RESPONSE` | ❌ 只计数;同一会话内累计 ≥ `transientThreshold`(默认 5)才升级为编号条目 | 理由:瞬时限流刷满文档会把命中率稀释成噪声,让模型每次读到无用通知——**这是本设计里唯一会让 token 变成负收益的坑**。 --- ## 7. 注入层:省 token 真正发生的地方 | 场景 | 注入点 | 内容 | |---|---|---| | 工具/命令失败后(同一回合下一步) | `tools/post-execute` 返回里的 `additionalContexts` | 命中:`[errkb] E-0007 已知(命中 5 次)|成因:…|解法:… 直接照做,不要重新诊断。`;未命中:`已记录为 E-0011(暂无解法)。` | | LLM 失败(回合已死,注不进去) | 下一次 `agent/pre-step` 的 `{kind:'enter', messages}` | 把上一步失败对应条目的通知追加进这一步的入场消息 | | 会话开场 | `agent/session-start` → `agent.inject()` | `sessionDigest` 控制:`off` / `counts`(默认,一行"错误库共 37 条,命中即注入")/ `index`(前 N 条标题,上限 10 条) | | 常驻行为约定 | `ctx.systemPrompt.section({name:'plugin:errkb', order:10400, text})` | 约 50 token 固定文本:报错先查库、新报错调 `err_record` 记录解法 | **硬上限(防注入反噬)** - 每条通知 ≤ 120 token(正文 400 字符封顶,source 的 `summary` 用 `boundContextSummary` 截到 ≤120 字符); - **每步最多 1 条**通知;每回合最多 3 条;同一编号在同一会话内最多提示 2 次; - `状态: fixed` 的条目提示 1 次即静默。 **成本模型(写进 README,让你能自己判断划不划算)** - 命中一次 ≈ 80 token 注入,替换掉一次「模型自己重新诊断」(通常 800–3000 token 的思考 + 失败试错); - 会话级固定成本 ≈ system-prompt 段 50 token/请求(可关)+ `counts` 摘要 40 token/会话; - **净收益为正的门槛:命中率 ≳ 5%**。`err_stats` 本地输出命中/注入/估算节省,不花一分钱模型费用。 --- ## 8. 编号与文档格式 编号:`E-0001` 起,`idPrefix`(默认 `E-`)+ `idWidth`(默认 4)。**只增不改**。 `ERRORS.md` 单条目样例(HTML 注释承载机器字段 → 渲染后干净,且手工编辑安全): ````md ## E-0007 · [tool:pwsh] EPERM: operation not permitted, rename - 指纹: `3f2a1c9d0b71` - 分类: `tool / pwsh` - 首次: 2026-09-14 09:12 · 最近: 2026-09-14 15:40 · 命中: 5 - 触发: `pnpm install` 在中文路径下写 `node_modules` 时被占用 - 原始信息: ```text EPERM: operation not permitted, rename '\node_modules\.pnpm\' ``` - 解法: 关闭占用该目录的编辑器/杀软实时扫描后重跑 `pnpm install`;仍失败则改用 `pnpm install --config.node-linker=hoisted`。 - 状态: `fixed` - 备注: ```` **解析规则**:以 `^## (E-\d+) ·` 切块;块内 `` 提供机器字段;`- 解法:` 取到下一个 `- ` 字段为止。**你对文档的手工修改优先于索引**(索引只是缓存),所以你在任意 md 阅读器里补写解法,插件立刻就能用上。 --- ## 9. 工具 API(模型与人都能调) | 工具 | 参数 | 返回 | 用途 | |---|---|---|---| | `err_lookup` | `query`(原文/指纹/编号)、`full?` | 命中条目(编号/分类/命中数/解法);未命中返回 `null` + 最接近的 3 条 | 主动查库;`full:true` 才返回原始信息,省 token | | `err_record` | `id?`、`message?`、`fix?`、`status?`、`note?`、`category?` | 编号 + 是否新建 | 写解法/改状态(`fixed`/`wontfix`/`open`);`id` 与 `message` 二选一 | | `err_list` | `cat?`、`status?`、`limit?`(默认 20) | 编号 + 标题 + 命中数 | 看清单,不返回全文 | | `err_forget` | `id`、`reason?` | 是否删除 | 删误判条目(移入 `ERRORS.archive.md` 留痕,不真删) | | `err_stats` | `scope?`(`session`/`all`) | 条数、命中率、估算节省 token、解析出的库路径 | 给你看的账本 | 输出声明统一走 `output.schema` + `render`(与 `dsh-note` 同款),保证模型侧内容是受控纯文本。 --- ## 10. 配置 schema(`Config = z.object({...})`,写在 profile patch 里) | 字段 | 默认 | 说明 | |---|---|---| | `kbDir` | `''` | 空 = 自动(§4.1)。相对路径基于插件包根 | | `capture` | `['tool','command','llm','agent']` | 采集开关,逐个可关 | | `captureExitCodes` | `true` | 命令非零退出是否入库 | | `transientThreshold` | `5` | 瞬时 LLM 错误升级为条目的阈值 | | `fuzzyThreshold` | `0.72` | 模糊匹配阈值(0.5–1.0) | | `captureFix` | `'prompt-once'` | `prompt-once` / `off`:新条目解决后是否一次性提示模型补写解法 | | `inject` | `'hit-only'` | `hit-only` / `always` / `off` | | `sessionDigest` | `'counts'` | `off` / `counts` / `index` | | `systemPromptHint` | `true` | 是否注入 50 token 的行为约定段 | | `providers` | `['*']` | 限定只记录哪些 provider(可填 `deepseek-official`) | | `share` | `'public'` | 脱敏强度(§4.4) | | `maxEntries` | `200` | 超出归档到 `ERRORS.archive.md` | | `maxSampleChars` | `500` | 原始信息封顶 | | `exportDir` | `''` | 可选的**设备本地**导出目录(如某台机器上的 Obsidian 路径);空 = 不导出 | --- ## 11. 插件工程结构(照本机已验证的范本插件) ``` \ ├── package.json # name: dsh-errkb · main: lib/index.js · dsh.bundle.patch ├── cordis.patch.yml # - insert: [{ id: err-kb, name: 'dsh-errkb', config: {...} }] ├── tsdown.config.ts # esm/node/es2022,neverBundle: cordis + dsh-tools + dsh-llm ├── vitest.config.ts # 覆盖率门槛(statements/lines ≥ 99) ├── tsconfig.json # strict, noEmit, moduleResolution: bundler ├── src/ │ ├── index.ts # 插件入口:name/inject/Config/apply,注册 5 个工具 + 4 个监听器 │ ├── paths.ts # §4.1 kbDir 解析 + 启动日志 │ ├── store.ts # ERRORS.md 解析/渲染/追加/归档 + 文件锁 + 原子写 │ ├── signature.ts # §5 规范化 + 指纹 │ ├── match.ts # 精确 / 模糊 / code 兜底 / 误判兜底 │ ├── redact.ts # §4.4 脱敏 │ ├── capture.ts # 4 个钩子的处理体(纯函数,便于测试) │ ├── inject.ts # 通知文本生成 + 各种上限 + 去重 │ ├── resolve-detect.ts # 解决检测 → captureFix 一次性提示 │ ├── tools.ts # err_lookup / err_record / err_list / err_forget / err_stats │ └── state.ts # state.json / .machine.json + 环境指纹 ├── tests/ # signature / match / redact / store / capture / inject / plugin / concurrency ├── errors/ # 运行时知识库(§4.2);随包一起被 git 跟踪 ├── docs/设计说明书.md # 本文件 └── README.md ``` 依赖(peer,按本机实际版本):`@deepseek-ai/cordis ^4.0.2`、`@deepseek-ai/dsh-tools ^0.1.5-rc.2`、`@deepseek-ai/dsh-llm ^0.1.5-rc.2`、`@deepseek-ai/schemastery ^3.18.2`;devDep:`tsdown 0.22.2`、`vitest`、`typescript`、`oxlint`、`prettier`。 类型增强只需 `import type {} from '@deepseek-ai/dsh-agent'` 即可拿到 `agent/*` 事件签名(与本机 `dsh-repeat-tool-reminder` 用法一致)。 --- ## 12. 安装与生效(P6 阶段执行,需你点头) ```powershell cd pnpm install pnpm build dsh plugin --profile web add . # 相对路径基于当前目录锚定;声明了 dsh.bundle.patch 即自动并入 bundles ``` 然后**你**重启 `dsh web`(`dsh.profile.bundles` 变化不在 `patchReload: live` 的监视范围内,必须重启)。我不会替你重启正在跑的服务。 --- ## 13. 失败模式与安全(逐条对应实现约束) | 风险 | 约束 | |---|---| | 监听器抛错 → 回合直接关闭(`dsh-agent-loop` README:125) | 所有监听器包 `try/catch`;失败降级为"本次不记录",并在 `state.json` 累计、节流上报 | | 抢走 `dsh-llm-retry` 的恢复权 | `agent/request-error` 必须 `await next()` 并原样返回 | | 写文件阻塞回合 | 本地写 + 3 次重试 + 500ms 超时;超时则该次跳过记录(不抛错) | | 多进程同写(web + headless 并发) | `.lock` 文件(`wx` 打开,10s 过期可抢占)+ 临时文件 + rename 原子替换 | | 磁盘/权限失败、库目录只读 | 自动回退 `$DSH_HOME\errkb\`;再失败则本次静默跳过 | | 文档被写坏 | 解析严格 + 解析失败**只追加不回写**,原文件另存 `ERRORS.corrupt-.md` 供你救回 | | 密钥/隐私外泄(要上 GitHub) | §4.4 强制脱敏;`share:'public'` 时连 `requestId` 都不保留 | | 条目无限膨胀拖慢上下文 | `maxEntries` 归档 + 每条 400 字符封顶 + 注入去重与次数上限 | --- ## 14. 测试计划与验收 ### 单测(vitest,全绿 + 覆盖率门槛) 1. `signature`:路径/行号/PID/时间戳/UUID 变化 → 指纹不变;不同报错 → 指纹不同;空串/纯 ANSI/超长串不崩。 2. `match`:精确命中、Jaccard 边界(0.71/0.72/0.73)、短消息 code 兜底、`误判:true` 不再注入。 3. `redact`:`sk-`、`Bearer`、`api_key=`、长 hex、邮箱、绝对路径全部被替换;**断言输出中 0 命中**。 4. `store`:写入→解析往返一致;手工改解法后读回改后文本;损坏文件走"另存 + 只追加";归档阈值触发;编号单调递增;并发 50 次记录 → 50 个唯一编号且文档可完整解析。 5. `capture`:4 类载荷 → 正确分类;瞬时限流默认不编号、达阈值升级;配置全关时零写入。 6. `inject`:文本长度上限、每步≤1 条、每回合≤3 条、source 形状 `{kind:'plugin',plugin:'err-kb',form:'notice',summary}` 且 summary ≤120 字符。 7. `plugin`:假 ctx 校验监听器已注册、抛错被吞、`agent/request-error` 返回值与下游完全一致(对象同一性断言)。 ### 手工验收(对你现有环境无破坏) 1. `dsh --profile web --dump-config` 能看到 `err-kb` 条目(证明 bundle 层生效)。 2. 触发一次必然失败的命令(如访问不存在的盘符)→ 出现 `## E-0001`。 3. 换临时目录/行号重跑同类失败 → 仍是 `E-0001`,命中数 +1。 4. 会话里出现 plugin notice 行,模型**不**再重新诊断。 5. `err_stats` 打印解析出的库路径、命中率与估算节省。 --- ## 15. 分阶段实现计划(每阶段可独立验收) | 阶段 | 内容 | 完成标志 | |---|---|---| | **P0(本轮)** | 落盘本说明书,等你补充 §17 | 你说"可以开始" | | P1 | 工程骨架:package.json / tsconfig / tsdown / vitest / cordis.patch.yml / 空 `apply` | `pnpm typecheck` 通过 | | P2 | `paths` + `store` + `signature` + `match` + `redact`(纯本地层,全覆盖单测) | 单测全绿,覆盖率达标 | | P3 | `capture`:接 `agent/error` + `tools/result`(先只记录,不注入) | 手工:失败命令 → 生成 E-0001 | | P4 | `inject`:`tools/post-execute` / `agent/pre-step` / `session-start` / system-prompt 段 + `captureFix` 提示 | 手工:同类失败第二次被自动注入 | | P5 | 5 个工具 + `err_stats` 账本 | 模型可 `err_lookup` / `err_record` | | P6 | 接 `agent/request-error`(LLM 失败分类 + 瞬时噪声规则)+ 安装进 web profile | `--dump-config` 可见;云端与本地报错各记一条 | | P7(可选) | `exportDir` 导出到你的 Obsidian 路径 / `err_export` 单文件 JSON 备份 | 导出文件可读 | --- ## 16. 假设与明确不做 - **假设**:你接受「文档默认会被 git 跟踪」以便跨设备同步(有脱敏兜底);若你要私有化,改一节 `share` 与 `.gitignore` 即可。 - **不做**:不接管 LLM 重试;不做云端/远程共享(只用 git);P0–P6 不含 GUI 面板;不做自动执行修复命令(见 §18 想法 2)。 --- ## 17. 需要你补充/确认的信息(请直接在"你的回答"里写) | # | 问题 | 我的默认提议 | 你的回答 | |---|---|---|---| | 1 | 包名 / 插件 id / 工具前缀 / 编号前缀 | `dsh-errkb` / `err-kb` / `err_` / `E-`(**npm 名是否被占用我未核实**) | | | 2 | 文档字段语言 | 字段名中文(编号/指纹/分类/解法/状态)+ 机器字段英文 key | | | 3 | 是否接受「计数器不进 git、只同步知识」(§4.3) | 接受 | | | 4 | `errors/` 是否直接提交进 GitHub 仓库 | 是(有强制脱敏兜底) | | | 5 | 复用范围 | 所有项目共用一个库,用条目里的「项目」字段区分来源 | | | 6 | `captureExitCodes` 默认开还是关 | 开(命令失败是最常见的复用场景) | | | 7 | P7 要哪些(GUI 面板 / 自动执行修复命令 / Obsidian 导出) | 先只要 Obsidian 导出 | | | 8 | 包根落点 | `` | | --- ## 18. 更多想法(超出你原始描述的部分) 1. **把"回收"做成"预检"(防错 > 记错)**:用 `tools/pre-execute` 加一道 guard,命令指纹命中已知失败条目就直接拦下并附上解法——把一个 3 轮失败循环压成 1 条消息。它与现有的 `dsh-repeat-tool-reminder`(按"参数完全相同的重复调用"提醒)互补:它管"重复",我们管"已知必败"。 2. **`err_fix`:一键复现修复方案**:条目里若记下的是确定性命令(如 `pnpm add -D x`),提供 `err_fix ` 经审批后重跑该命令,真正完成"回收 → 再利用"的闭环。需要接 `dsh-user-approval`,所以默认不开。 3. **把错误库同时变成一个 skill**:用 `dsh-skill` 的文件系统发现能力,让库以 `SKILL.md` 暴露——模型按需"渐进式读取"整篇文档,而不用一次吃满上下文。这是"优先读取问题文档"的另一种形态。 4. **环境指纹 + 解法可信度**:每条记录 os/node/pnpm/shell/provider/模型版本;在其他设备命中时,若环境指纹不同就在通知里加一句「可能不适用」,避免拿 A 机的解法坑 B 机。 5. **压缩(compaction)天然友好**:`dsh-compaction-tool-result-pruner` 会丢旧工具结果,但我们的事实**在磁盘上**、需要时再注入——这是本设计相对"把解法留在对话历史里"的结构性优势,值得在 README 里写清楚。 6. **token 账本可视化**:接 `@deepseek-ai/dsh-token-meter` / `dsh-session-stats`,`err_stats` 输出「注入 0.9k / 机制上省掉的下限 8k」。把"省 token"从口号变成数字,否则你无法判断阈值该调多少。 7. **内置种子条目**:`CONTEXT_OVERFLOW`(压缩 / 提高 contextWindow / 剔除大文件)、`NO_ADAPTER`(provider 名拼错)、Windows 中文路径 EPERM 这类"必踩一次"的坑预置成 `E-0000x`,首次安装就自带价值。 8. **团队共享**:因为文档进 git,把 `errors/` 放进团队仓库就等于一个"踩坑知识库";再加一层 `err_promote`(本地条目提升为公共条目)即可做知识沉淀流程。 9. **Web GUI 面板**(被否掉的选项三的完整版):第三方包可以声明 `dsh.client`(`platform: 'web'`)+ `inject` 客户端模块,做错误库列表/命中曲线/设置页;代价是要重建 Web 产物并刷新页面,建议 P6 跑稳后再评估。 10. **反向利用**:同一个库还能给"新手引导"用——`err_list --status open` 就是一份"这个项目当前还没解决的坑",可以直接当 issue 草稿。 --- ## 19. 审查批注区(留给你随手写) ``` (在这里写你的疑问、反对意见、要加/要砍的功能,我按这里改设计) ```