dsh-agent-loop-experiment:给官方 agent loop 加一层收敛策略

前言

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
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜