前言¶
DSH 的 @deepseek-ai/dsh-agent-loop 负责「调模型、跑工具、重复」这条主循环。它的行为是通用的:没有步骤预算概念,模型在长任务里反复重读、重新生成整份文件、用 shell 参数塞大段脚本,循环会一直配合下去。
dsh-agent-loop-experiment 就是针对这一现象的隔离实验。它保持官方循环实现字节级不变,只在公开提示词与 agent 钩子之上添加收敛策略,并作为 bundle 补丁替换官方循环条目。项目由 Jaylor-Wang 维护,MIT 许可,固定支持 DSH 0.2.2-rc.1。
下面介绍它的能力、安装方式、配置与验证流程。
这是什么¶
一层收敛策略实验包,同时是一个可直接替换官方循环的 bundle。
它对应官方循环的四件事:
- 运行时策略(runtime fingerprinting):观察操作指纹,拦截连续无进展的重复调用与过大的 shell 参数。
- 有限恢复(finite recovery):为本轮原生 write 的大段写入失败提供一次短引用恢复,只移除无效的
justification参数。 - 文件改动防护(file mutation guards):识别整文件重写与大文件重写风险,按
observe/enforce分级。 - 软工具要求(soft tool requirements):提醒模型使用预期工具,拦截绕路副作用。
官方循环实现本身不被修改,src/index.js 挂载一个官方循环、在相同作用域挂载策略;替换功能由 dsh.bundle.patch 与 src/replacement.js 提供,替换类继承官方循环。
版本与包结构¶
包名为 dsh-agent-loop-experiment,版本 0.2.2-rc.1,标记 private,license 为 MIT。engines.dsh 精确约束 0.2.2-rc.1,engines.node 要求 >=24.0.0。peerDependencies 里的 @deepseek-ai/dsh-* 系列均对齐 0.2.2-rc.1。
入口通过 exports 暴露四段:
| 入口 | 用途 |
|---|---|
. |
挂载官方循环 + 策略的包入口 |
./baseline |
未修改的官方基线 |
./policy |
只加载收敛策略 |
./replacement |
使用官方平铺配置的单一服务替换入口 |
这是编译产物包,实验层使用原生 ES module,无需编译。README 上标有 63 个测试通过。
收敛策略配置¶
策略配置集中在 src/convergence.js。独立入口默认关闭策略,作为 bundle 安装时由 bundle 配置明确开启。
| 配置 | 默认值 | 含义 |
|---|---|---|
enabled |
false |
显式开启收敛策略 |
softStepBudget |
8 |
完成这么多模型步骤后,在下一次已需要的请求中提醒一次 |
recoverFailedWrites |
true |
大段写入参数错误的短引用恢复,可独立关闭 |
runtimePolicy.mode |
enforce |
off / observe / enforce,enforce 下拦截无进展与大 Shell Payload |
runtimePolicy.noProgressLimit |
2 |
相同命令指纹连续重复的容忍阈值 |
runtimePolicy.recoveryAttemptLimit |
1 |
恢复引用可尝试次数上限 |
runtimePolicy.minLargeWriteBytes |
8192 |
大写入字节阈值 |
runtimePolicy.maxShellCommandBytes |
4096 |
Shell 命令行字节上限(防 spawn ENAMETOOLONG) |
runtimePolicy.maxInlineScriptBytes |
512 |
单次内联脚本字节上限(python -c / node -e) |
fileMutationPolicy.mode |
observe |
off / observe / enforce |
fileMutationPolicy.minLargeWriteBytes |
8192 |
大文件重写阈值(字节) |
fileMutationPolicy.reminderLimit |
1 |
重写风险提示次数上限 |
softToolRequirement.enabled |
true |
软工具要求管理器开关 |
softToolRequirement.reminderLimit |
1 |
每项软要求提醒次数上限 |
softToolRequirement.maxViolations |
2 |
违反后容忍并阻断绕路的最大次数 |
软工具要求需要宿主通过 SoftToolRequirementManager.addRequirement(session, options) 按任务显式激活,要求状态只保留到当前 turn 结束。拒绝结果为带 code、nextAction 与 retryable 的 JSON。当前没有自动强制 tool choice,真实模型误拒绝率也尚未验证,enforce 使用前需完成 A/B。
安装与启用¶
下面是 EXPERIMENT.md 给出的官方插件管理命令。它使用 link: 前缀指向本机已存在的 DSH 安装目录,路径按你自己的机器替换,不要照搬示例里的盘符。
dsh plugin --profile web add "link:E:/Github/dsh-agent-loop"
前置步骤:在本包目录下执行
npm run setup:local
npm test
npm run inspect:profile
setup:local 从本机 DSH 安装复用同版本依赖,缺失的依赖创建本地目录链接,不访问注册表、不写宿主安装。已有依赖版本不符时会拒绝,不会自动覆盖。运行时可以再执行 npm run inspect:profile。它只在内存中合成替换后的配置,不启动服务、不读凭据、不改 profile。检查通过时可看到原循环被禁用、新循环启用、原配置保留、0 警告。
inspect:profile 不通过就不要切换。DSH 的 config 是整体替换,所以覆盖片段必须一并保留 agents 和 maxParallelToolCalls:
- id: dsh-agent-loop-experiment:loop
name: dsh-agent-loop-experiment/replacement
config:
agents: []
maxParallelToolCalls: 10
convergence:
enabled: true
softStepBudget: 8
确认后启动:
dsh web
这是服务端循环替换,不需要重建 Web 前端,也不能只靠刷新页面生效。需要停止 dsh web、安装链接、再重启进程并新建会话。安装后在包目录再跑一次 npm run inspect:profile,确认 bundleAlreadySelected 为 true、activeLoopCount 为 1,再启动。
回退方式:先停止 dsh web,移除手动添加的覆盖片段,然后
dsh plugin --profile web remove dsh-agent-loop-experiment
dsh web
bundle 移除后禁用官方循环的补丁也会消失,原 agent-loop 条目自动恢复。如果 profile 里另外写了 id: agent-loop / disabled: true 的个人覆盖,也要移除或改为 false,避免个人层继续禁用官方循环。
典型用法¶
接入独立宿主(不使用 bundle):
import experiment from 'dsh-agent-loop-experiment';
await ctx.plugin(experiment, {
loop: { maxParallelToolCalls: 10, agents: [] },
convergence: { enabled: true, softStepBudget: 8 }
});
独立宿主须提供官方 agents、sessions、llm、tools、systemPrompt 和 sessionProjections 服务,并配置工具执行器与模型适配器。不要在已有官方循环上再挂载包入口。
已经存在官方循环的独立宿主,可以只加载策略,在 agent setup 的作用域挂载即可只影响该 agent:
import convergence from 'dsh-agent-loop-experiment/policy';
验证线上真实行为之前,先用固定响应跑离线对照:
npm run experiment
它使用固定模型响应脚本,预期两组都是 4 次请求、4 次工具调用,区别是收敛组追加一次提醒。这验证策略接线、正常完成与前缀保留,不能证明真实模型会少走步骤。
真实对照需要复制 experiments/live.example.json 为 experiments/live.local.json,填写自己的模型、协议和 endpoint,两组配置保持一致。凭据只通过 apiKeyEnv 指定的环境变量传入,不写入配置。默认仅验证初始化,不发模型请求:
npm run experiment:live -- --config experiments/live.local.json --variant baseline
npm run experiment:live -- --config experiments/live.local.json --variant convergence
准备好发送模型请求后再加 --run。每条命令启动新的独立进程与内存会话,不改动 GUI,也不恢复当前会话。
策略的实际行为与边界¶
开启后,从第一步开始向提示词加入固定指导,固定指导不随步骤变化。内容主要包括:输出大段内容前先决定落盘路径;新文件用原生 write,计算优先复用已有工具或写入可复用脚本再按路径执行;完整文件和大脚本不放进 node -e、python -c、pwsh -Command 等 shell 参数;独立读取、搜索和检查可在同一响应内批量调用,并保留依赖顺序与独占屏障;局部修改优先复用已有文件内容,只用 edit 和唯一匹配修改,不重新输出整份文件;资源使用后清理、保留顺序。
达到软预算时,每轮至多向历史尾部追加一次提醒,提醒由官方循环提交。预算按本轮已接纳的 step/start 计数,重试不重复计步,不跨整个会话累计。短任务不出现提醒是预期。
这些指导适用于代码修改、数据处理、文档与内容生成等任务,没有针对特定任务名、文件类型、计算轮数或输出大小的启发式拦截。只读问答与审查不会因此获得写入授权。
明确不做的部分:不限制总步骤数、不自动宣布完成、不修改 provider/model/reasoningEffort、不合具有依赖的调用、不改写已有工具结果。自然结束、max-tokens、concludeTurn 与拒绝都保持官方行为。裁剪仍由 dsh-tool-result-trimmer 负责。
大段写入恢复的触发条件很窄:只接受本轮原生根 write、8 KiB 至 1 MiB 的完整内容,且错误精确为 invalid escalation: justification is only valid together with sandbox_permissions;原调用必须没有 sandbox_permissions,只允许 file_path / content / justification 三个参数。插件不解析 shell,也不自动重试。符合条件时追加一次 source.kind 为 loop-write-recovery 的通知,模型可调用 retry_failed_write({"call_id":"原调用 ID"}) 复用原路径与逐字相同的内容。可以在 convergence 下用 recoverFailedWrites: false 关闭这个入口。
如果宿主使用 complete 系统提示,普通提示词 section 会按官方规则被覆盖,收敛指导可能不进入系统提示。运行中开启或关闭会改变当时的提示词,对照实验应从全新会话开始;本实验未承诺热切换时缓存前缀不变。
适用场景与注意¶
适合:已经在 DSH Web profile 或独立宿主上跑长任务、想对照实验收敛策略对提示词与步骤数的实际影响的人;以及想保留官方循环、只叠加一层可选策略的维护者。
注意:
- 插件以当前 DSH 进程的权限运行,可以读写该进程工作目录与凭据环境。安装前先读
src/convergence.js、src/replacement.js、src/failure-recovery.js与cordis.patch.yml,确认补丁范围。许可证是 MIT。 - 安装命令里
link:前缀指向本地目录,私有包可本地安装、不需要发布到 npm。不要在包内另跑npm install或pnpm install,也不要仅为消除 peer 警告补装宿主已有的 Cordis、React。 - 固定支持
0.2.2-rc.1,宿主升级后需重新执行setup:local、相关验证与inspect:profile,版本不符先处理兼容性。当前结果不构成对未来版本的兼容保证。 - 需要确保 profile 里只有一个循环服务启用。同时存在
agent-loop与实验条目会导致冲突。 - 把
enabled改为false可保留替换循环、只关闭收敛策略,适合做同进程对照;更干净的对照是回退到官方循环后再开。
目录页与仓库:
- https://www.skillhub.cn/plugins/Jaylor-Wang/dsh-agent-loop-experiment
- https://github.com/Jaylor-Wang/dsh-agent-loop-experiment