DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

herenfor /

herenfor/dsh-runtime-tuning

Verified

DSH 运行时优化实验:从真实会话数据里找 agent 的时间浪费,再用插件修掉

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@3684bfdd

DSH Runtime Tuning

MIT DSH 测试

一个关于 agent 运行时的长期实验:从 DeepSeek Harness(DSH)的真实会话记录里找出时间花在了哪里、哪些是白白浪费的,再尽量用插件把它修掉,并且每一项都用数据验证。

这里既是一组可以安装的 DSH 插件,也是这个过程的记录。随着分析推进,插件会不断增加。

起点:一份 9.7 小时的会话

我把自己日常使用 DSH 的一份会话导出来,按事件逐条统计。这份会话有 119 轮对话、2347 次工具调用,跨度 9.7 小时,内容主要是一个项目的反复发行(测试、编译、归档、上传)。

时间大致花在这些地方:

去处 时间 备注
模型生成 约 4.3 小时 每步中位数约 6 秒
阻塞式等待后台任务(job_output wait: true) 约 100 分钟 152 次,最长一次 240 秒
等人审批 约 58 分钟 24 次,最长一次 44.7 分钟
提问后等人回答 约 79 分钟 这段时间模型什么也做不了
前台 sleep 约 20 分钟 33 次,比如 sleep 90; echo waited

几个值得注意的现象:

  • 有工具调用的步骤里,87% 只调用了一个工具。每多一步,就多一次重读上下文和生成。
  • 同一个后台任务被反复查询,最多一个查了 13 次。
  • 模型会从文件里 grep 出 GitHub 令牌,再用 curl 调 API,令牌因此出现在命令行和日志里。

发现一:机制都在,只是没人强制

读了 DSH 的源码之后发现,DSH 其实已经做得不错:

  • 后台任务结束时会通知模型:模型忙时插进下一步,空闲时直接开启新的一轮;
  • 前台命令超时后不会被杀掉,而是转到后台;
  • 系统提示词里明确写了"不要轮询、不要 sleep"。

问题在于模型并不总是照做:它还是会 wait: true 加上 120 到 600 秒的超时,或者直接前台 sleep 90。等待期间整轮对话被占住,用户插话只能排队。

所以第一个插件 wait-guard 做的事很简单:在工具执行前强制执行这条规则,拦截前台长 sleep 和顶层 agent 的长时间阻塞等待,并告诉模型应该怎么做。

装上第一版之后,同一个发行流程的对比:

改动前 A 改动前 B 装插件后
对话被占住 22.1 分 19.6 分 4.6 分
阻塞等待 13.0 分 15.2 分 0.2 分
自动唤醒 0 次 0 次 3 次,都在任务结束的同一秒

要说清楚一点:收益是"不占住对话",不是"更快"。这次总耗时也变短了,但主要是因为那次编译本身快。以前的阻塞等待在任务结束时也会立刻返回,并没有"白等"。真正的变化是编译期间用户随时可以插话,而不是对着一个卡住的对话。

发现二:通知没来怎么办

让模型结束一轮去等通知,就要回答一个问题:如果任务卡死,通知永远不来呢?

wait-guard 为此加了一个 task_status 工具。模型在一轮的最后声明自己是 done、waiting(在等后台任务,附上预计时间)还是 asking(在等用户回答)。声明 waiting 的一轮正常结束后,插件开始计时;超过两倍预计时间还没有新一轮,就主动唤醒模型去检查。

几个边界是刻意设计的:

  • 最多连续唤醒 2 次,用户发消息后重新计数,避免无人值守时无限循环;
  • 用户按停止结束的一轮不计时:用户叫停的事,插件不应该再拉起来;
  • 实测:一个 sleep 300 的任务,模型预计 1 分钟,插件在第 2 分钟准时唤醒。模型看了一眼输出,重新声明预计 3 分钟;任务结束后模型被正常唤醒并声明完成,兜底计时随之取消。

发现三:提问会把模型卡住

ask_user_question 默认是阻塞的:问出去之后,在用户回答之前,模型什么也做不了,哪怕手上还有不依赖这个回答的事。

DSH 其实已经实现了非阻塞的 timed 模式(超时返回 pending,用户的回答之后作为插话送进来),但默认关闭,而且这个工具嵌在 agent preset 内部,用户的配置补丁改不到它。具体的复现过程我发在了 DSH 讨论 #8547。

插件 ask-nonblocking 的绕法是:在每个顶层 agent 创建时,用宿主里同一份官方模块在 agent 自己的作用域里注册一个 timed 版本,遮蔽掉 preset 里的阻塞版。实测提问约 1 秒后放行,72 秒后的回答作为新的一轮送了进来。

这个绕法依赖 DSH 的内部接口,所以失败时只写日志、退回阻塞版,不影响任务。

发现四:令牌是模型自己要来的

同一份会话里,同一个 GitHub 令牌出现在 73 条命令(共 83 处)和 74 条模型回复里。往回找它第一次出现的地方,是模型用 ask_user_question 让用户把令牌贴进来,还承诺"会存到权限 0600 的本地文件里"。可令牌一旦作为回答进入对话,就已经在上下文里了:之后每次请求都会把它发给模型服务商,它也会永久留在会话记录里,模型再把它直接写进 curl -H "Authorization: …"。

插件 secret-guard 分三道防线:

  • 拒绝向用户索要令牌、密码、API key 的提问,引导改用已经登录好的 gh,或者请用户自己运行 gh auth login。这一道最关键:在非阻塞提问下,用户迟到的回答是以插话送进来的,后面的打码管不到;
  • 拒绝参数里带完整令牌的工具调用,以及 gh auth token;
  • 工具结果交给模型之前,把其中格式明确的凭据替换成 [REDACTED:…]。

管不到的是用户直接在聊天框里粘贴的令牌:那不是工具结果。

还在看的问题

问题 数据 状态
等人审批时没有提醒,人不在就一直停着 58 分钟,最长一次 44.7 分钟 已在 dsh-task-notify 里加上"需要审批 / 需要你回答"的提醒
令牌进入上下文 1 个令牌,73 条命令、74 条回复 secret-guard
前台命令设了过长的超时 139 条前台命令超时设到 5 分钟以上,但真正跑超过 1 分钟的只有 2 条 已评估,不做:插件只能拒绝、不能改参数,拒绝 139 次多花的时间比省下的还多
一步只调一个工具 87% 的步骤 需要先做 A/B 对比
反复 tail 日志、重读同一文件 同一文档同轮最多重读 7 次 计划中
审批会卡住整个 step — DSH 核心行为,插件做不到(#8916)
插话打断不了正在运行的工具 插话排队最长 128 秒 DSH 核心行为,插件做不到(#11005)

目前包含的插件

插件 作用
wait-guard 拦截前台长 sleep 和长时间阻塞等待;task_status 声明和兜底唤醒
ask-nonblocking 让 ask_user_question 问完不阻塞
secret-guard 不让令牌进入模型的上下文

安装、配置项、日志和卸载见 docs/configuration.md。

开发

npm test

测试只用 Node.js 自带的 node:test,不需要安装依赖。

许可证

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 3684bfddcad1

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout