deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
English | 中文
给 DSH 的每个 step 加上工具调用配额:同一个 step 内超过配额的调用被直接拒绝。
DSH 的 Agent 在一个 step 里可能并行发出多个工具调用,也可能在同一个工具上反复重试。本插件在工具真正执行之前按注册名检查配额:还有名额就放行,超了就拒绝——只限流,不改工具本身的行为。装好即用,无需改动 DSH 源码。
web_search: 1 之后,一个 step 内第二次调用会被拒绝,而不是让 Agent 继续消耗0,该 step 内所有调用都被拒绝web_search 只有一个能通过dsh plugin --profile web add "github:zhourenke/dsh-tool-call-limit"
安装后必须重启 DSH——bundle 集合是进程启动时的快照,重启前新插件不会被加载,刷新页面无效。
卸载:
dsh plugin --profile web remove @zhourenke/dsh-tool-call-limit
本插件默认不限制任何工具(limits: {})。要启用限制,在 profile 补丁里覆盖插件那一行:
# ~/.dsh/profiles/web/cordis.patch.yml
- id: tool-call-limit
name: '@zhourenke/dsh-tool-call-limit'
config:
limits:
web_search: 1
上面的配置表示:同一个 Agent 在同一个 step 里最多调用一次 web_search;下一个 step 重新获得配额,其他 Agent 有各自的配额。
没有写进 limits 的工具完全不受限——web_fetch 之所以不限,只是因为它没被列出来;需要时单独配它即可。
改配置保存即生效,不需要重启 DSH。 profile 补丁是热重载的;只有安装或卸载插件才需要重启。这两件事经常被混为一谈,结论正好相反。
确认配置已被加载:
dsh --profile web --dump-config
在输出里能看到 tool-call-limit 与预期的 limits 即已生效。
⚠️ 配置必须用
- id:覆盖的写法,不要写成- insert:。 插件自带的 bundle patch 已经用insert把这一行插进去了;在 profile 里再insert一次不报错,而是多出一个同 id 的实例——插件跑两遍、配额算两遍。两者的区别只在动作:- id:按 id 查表覆盖已有条目,- insert:无条件追加。
name可以省略(覆盖只看id);但一旦写了就必须与上面完全一致,写错一个字母会静默不生效,只在日志里留一行 warning。config:是整体替换,不是逐字段合并。 本插件的config只有limits与onExceeded两个字段,写全即可。
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
limits |
object | {} |
工具注册名 → 该工具每个 step 允许的最大调用次数。未列出的工具不受限。 |
onExceeded |
"deny" | "ask" |
"deny" |
配额用尽时怎么办:deny 直接拒绝;ask 每一次都交给你确认。 |
取值规则:
0 表示在该 step 内拒绝该工具的所有调用;NaN、Infinity、超出 JavaScript 安全整数范围的数字、数组都会被拒绝;limits: null、省略 limits、省略整个 config 都按 {} 处理(即不限);onExceeded 只接受 "deny" 与 "ask" 两个取值,省略或写 null 都按 "deny" 处理。例如:
limits:
web_search: 1
grep: 8
write: 0
配额按四个维度分别计算,任一维度不同就是一份独立的配额:
| 维度 | 说明 |
|---|---|
| Agent | 父 Agent 与 subagent 创建的子 Agent 是不同的 live Agent 对象,各自独立计数,不会合并成一个总预算 |
| turn | 一个 turn 内的多个 step 各自计数 |
| step | 配额的重置单位——进入新 step 时计数清零,重新获得全部名额 |
| 工具名 | 每个工具名单独计数,web_search 的调用不占用 web_fetch 或 grep 的配额 |
next() 之前就扣掉,所以同一个 step 里的并行调用不会同时看到同一个剩余名额。web_search: 1 时,两个并行的 web_search 只有一个能继续进入后续管线。默认(onExceeded: "deny")下,拒绝使用三条稳定的英文原因文本:
tool <name> exceeded its per-step limit of <n>
per-step tool limit requires an agent context
per-step tool limit has no active agent step
第一条是配额用尽;后两条是缺少 Agent 上下文或该 Agent 没有有效的 step,此时采取 fail-closed(拒绝而非放行)。读到第一条时不要重试同一个工具——配额要到下一个 step 才会恢复。
onExceeded: "ask":超额的那一次交给你确认配成 ask 后,配额用尽不再直接拒绝,而是申请一次授权:批准则这一次调用照常执行,拒绝则按上面第一条处理。它走的是 DSH 0.2.0 的审批通道,因此有四件事需要知道:
ask 只是把「要不要破例」交给你,不是把上限调大。ask,否则不会弹窗。 策略只有 ask 与 never 两个值,由 permission preset 决定:read-only / workspace-write 是 ask,danger-full-access 是 never。策略为 never 时宿主根本不会把请求交给界面,在审批服务内部就返回了拒绝——哪怕审批界面就在那里。改 preset 定义不会回溯到已有会话(策略是记在会话日志里的持久事实),要执行一次 /permission <preset> 重新应用,重启也没用。the user rejected tool "<name>",而并没有人拒绝过。 宿主复用了「用户拒绝」那条分支,"没人问过" 与 "人说了不" 在这条路径上分不出来。看到这句话先去查会话的审批策略,而不是去找那个"拒绝的人"。onExceeded 影响:缺少 Agent 上下文或没有有效 step 时始终 fail-closed。web_search、web_fetch、grep、write、bash、run_code本插件限制的是进入 DSH ToolRuntime 的调用次数,不是工具实现内部发生的操作次数。它不会限制:
web_search 调用内部发出的多个 query;bash 调用内部执行的多条 shell 命令;要限制单次 web_search 的 query 数量,需要另外配置 Web 工具的 searchMaxQueries;provider 提供 maxUses 时也要单独配置。它们与本插件的 ToolRuntime 调用配额属于不同层级。
maxParallelToolCalls 管的是并发数量,不是每 step 的总调用次数——本插件负责后者,两者互补。
在 DSH v0.2.0-rc.2(2026-09)下测试通过。
插件列表里的名称、说明与图标来自包内的 locale/{en,zh}.json 与 icon.svg(DSH 0.1.7 起的显示元数据),因此不用激活插件就能看出它是做什么的。
MIT
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。