前言¶
在 DeepSeek Harness(DSH)里跑编码 Agent,文件编辑往往是 token 消耗和出错的重灾区。常见的 str_replace 或按行号定位,要求模型在输出里逐字复述要被替换的旧代码——这部分是输出 token,计费通常是输入的约 5–6 倍。上方多插入一行,下方行号整体偏移,改错行的风险随之上升,且工具侧往往缺少「这段范围是否就是模型刚才看到的内容」的校验。
下面介绍 dsh-better-edit(维护者 Rianico)。它为 DSH 提供基于内容哈希(hashline)的 read、edit、undo_last_edit 工具:每行分配一个 3 字符哈希作为地址,编辑时只传起止哈希和替换文本,不回显旧内容;范围在写入前对照模型已读状态校验,过期或未见行直接拒绝并回传新锚点。项目采用 MIT 许可证,npm 当前版本为 0.4.0,GitHub 约 15 stars。
这是什么¶
dsh-better-edit 是 DSH 的客户端插件,分类为「客户端」。它通过 cordis.patch.yml 挂载到 DSH 运行时,在 agent/session-start 时将 hashline 版 read/edit 注册到 agent 作用域,从而替换 preset 内置的同名工具,并保留内置 write。
与按行号或全文搜索替换不同,hashline 把每一行当作内容寻址:哈希由行内容推导,上方编辑不会使下方未改动行的锚点失效。插件在 SkillHub 社区目录(skillhub.cn/plugins/Rianico/dsh-better-edit)可检索;该目录为独立社区站点,与 DeepSeek / 幻方无官方从属关系。
核心功能¶
三个工具¶
| 工具 | 作用 |
|---|---|
read |
以 HASH│内容 返回文件,支持 offset(1 起始)、limit 分页 |
edit |
按 remove_from / remove_to 哈希范围替换;同文件最多 32 条编辑原子批量 |
undo_last_edit |
撤销指定路径的上一次 hashline 编辑,重启后仍可用 |
read 展示过的行、diff 回显行、拒绝并回传(reject-and-serve)的行,都计入「已提供」状态。edit 写入前逐行校验:从未展示的行触发 [E_RANGE_UNSERVED],磁盘内容与锚点不一致触发 [E_RANGE_STALE],批次中任一项失败则整批不写([E_BATCH_ABORT])。
相对 str_replace 的差异¶
README 中的对比要点如下,均来自项目文档与可复现基准,而非第三方案例:
- 编辑调用不回显被替换文本,只传两个 3 字符哈希加新内容
- 锚点为内容地址,连续编辑时未改动行哈希保持有效,diff 输出带新锚点,通常不必每次改完都
read - 范围与模型所见逐行核对;错锚点、过期内容在写入前硬拒绝,并回传带新哈希的当前行供重试
- 对 ASCII 空白变化有一定容忍(如
prettier、black介入后锚点仍可解析);字符串字面量内的空白不在此列
项目在固定 103 行语料、12 次编辑的 token 基准中报告:相对 str_replace,hashline edit 输出 token 约省 31%(多行范围 29–47%);同一外部漂移重构任务中,工具调用约 3 次 vs 6 次(对比 OMP 封装,方法说明)。确定性正确性电池为 23/23。本地可运行 npm run benchmark 复现计数部分。
存储与撤销¶
哈希快照、已提供状态、撤销历史默认存放在 central 模式:
$DSH_HOME/plugins/dsh-better-edit/runtime/<name>-<hash8>/hash-store.sqlite
DB 为可丢弃缓存,删除后下次 read 会按文件内容重建哈希。撤销默认 TTL 7 天(undo_ttl_s: 604800,-1 为永久)。
安装与启用¶
环境要求:Node ^22.19.0 || >=24.0.0(DSH 要求,存储依赖 node:sqlite);已有一个 dsh profile。
任选一种安装方式:
npx @deepseek-ai/dsh plugin --profile web add github:Rianico/dsh-better-edit # 从 GitHub
npx @deepseek-ai/dsh plugin --profile web add dsh-better-edit # 从 npm
npx @deepseek-ai/dsh plugin --profile web add /path/to/dsh-better-edit # 从本地源码
无需额外配置。该 profile 的下一次会话即加载 hashline 工具。验证插件层是否生效:
dsh --profile <name> --dump-config # 输出中应出现 "# == dsh-better-edit" 层
可选配置写在 $DSH_HOME/plugins/dsh-better-edit/config.yaml,例如存储位置、撤销 TTL、central 清理策略。环境变量 DSH_BETTER_EDIT_STORE_DIR、DSH_BETTER_EDIT_AUTO_GITIGNORE 可覆盖 yaml。首次启动时若文件不存在,插件会生成带注释的默认配置,不覆盖已有文件。
典型用法¶
读取:每行带哈希前缀¶
read 返回的每一行,行首哈希即该行地址:
ve7│function hello() {
szJ│ console.log("world");
kQm│}
编辑:按哈希范围定位¶
下面示例把 szJ 这一行替换为新内容:
{
"path": "src/main.ts",
"edits": [["szJ", "szJ", " console.log('hi');"]]
}
工具返回 diff,并附带新锚点,便于链式编辑:
- szJ │ console.log("world");
+ a3m │ console.log('hi');
kQm │ }
同一文件可一次提交多条编辑,全部成功才写入:
{
"path": "src/main.ts",
"edits": [
["a1b", "a1b", "new line 1\n"],
["c3d", "c3d", "new line 2"]
]
}
撤销¶
对刚改过的文件调用 undo_last_edit,传入 { "path": "src/main.ts" }。仅当磁盘内容与上次编辑后快照一致时生效;若中间被外部修改,返回 [E_UNDO_STALE]。
适用场景与注意¶
适合:
- 长会话中的结构性改动,需要连续多次编辑且不能落错行
- 希望减少编辑相关输出 token、降低
str_replace式复述成本 - 文件可能在编辑间隙被格式化工具或外部进程改动,需要过期检测而非静默覆盖
不太适合:
- 单行微调(token 节省接近持平,README 有说明)
- 新建文件(继续用内置
write;插件会在write后自动read以建立锚点)
安装前请注意:
- 插件以当前 dsh 进程权限读写工作区与
$DSH_HOME下的存储,安装前应阅读 源码 与 MIT 许可证 - DSH 仍处于开发者预览阶段,插件 README 注明当前针对特定 dsh 版本接线,升级 DSH 后宜重新核对兼容性
- 基准数字衡量的是请求负载 token,未完全建模转录失败重试等真实会话成本;正确性优势需结合具体工作流评估
链接¶
- 社区目录:skillhub.cn/plugins/Rianico/dsh-better-edit
- 源码与文档:github.com/Rianico/dsh-better-edit
- 中文 README:README.zh.md
若你已在 DSH 里反复遇到行号漂移或 str_replace 复述出错,可以先按上文命令装入 profile,用 read → edit → 看 diff 新锚点走一遍最小闭环,再决定是否用于日常 Agent 会话。