4.5 KiB
dsh-timeout-policy
English | 中文
工具调用超时强制执行器:一个 tools/execute 环绕分发监听器。它会在 exec.signal 上启动单次调用的协作式截止时间;适用条件是工具声明了 timeoutMs,且声明位于其 ToolDefinition 上。截止时间先到时,它返回结构化 TOOL_TIMEOUT 结果。预算从工具自身的声明中读取(ToolDefinition.timeoutMs,由拥有该工具的插件设置),因此此插件是 零配置 的。它是 tools/execute 包装层的参考实现,也是面向模型工具调用预算的强制执行归属地(超时库 Agent Note 所预见的中间件)。
插件(命名空间:timeout-policy)
它是函数/命名空间插件(name/inject/apply),而非服务。它不注册工具,也不接受配置;它消费 ctx.tools 的 tools/execute waterfall(由 dsh-tools 注册表始终提供),并读取每个已分发工具声明的 timeoutMs;该声明来自注册表(ctx.tools.get(exec.name))。
- id: timeout-policy
name: '@deepseek-ai/dsh-timeout-policy'
每工具预算由工具插件声明(例如 dsh-tool-web 的 fetchTimeoutMs/searchTimeoutMs 配置,会附加为 ToolDefinition.timeoutMs);此插件只负责强制执行,因此不可能拼错工具名。
行为
对 声明了 timeoutMs 的工具,监听器会:
- 从注册表中的工具自身声明(
ctx.tools.get(exec.name)?.timeoutMs)读取预算,并启动deadline(exec.signal, timeoutMs, 'TOOL_TIMEOUT'):一个将调用方中止与此插件计时器融合的信号(@deepseek-ai/dsh-timeout)。 - 将该派生信号替换到
exec上用于下游分发,然后恢复调用方自身的信号(cordisnext()忽略传入的参数,因此包装层会原地修改共享exec;恢复可使tools/post-execute看到调用方的信号)。 - 分发后,如果
timeoutOf(d.signal, 'TOOL_TIMEOUT')匹配,即此插件自身的计时器触发,则将结果替换为结构化TOOL_TIMEOUT工具结果:{ isError: true, error: { message, info: { name: 'ToolTimeoutError', code: 'TOOL_TIMEOUT' } }, content: 'Error: tool call timed out after <ms>ms' }。
未声明预算的工具 会原样委托(不启动截止时间)。
基础 next() 是 tools/execute 在注册表中带规范化的分发 thunk,因此当超时信号到达抛出自身上游中止错误的提供方时,分发会先将其转换为普通错误结果,再由此包装层替换为 TOOL_TIMEOUT。这一顺序就是替换依据信号(timeoutOf)而非已分发结果形状的原因。
协作式,而非硬终止
派生信号只会 通知;终止仍属于工具以及它将 exec.signal 转发给的能力(dsh-timeout 库不拥有 kill)。因此,声明 timeoutMs 意味着「与 exec.signal 协作」:忽略该信号的工具不会在超时时停止。只有转发信号的工具才应声明该字段;已交付的 web_fetch/web_search(通过 ctx.web 转发给提供方)是参考实现。TOOL_TIMEOUT 无需会话事件以满足可重建性:它是最终面向模型的 tool/result,已由循环记录。
与其他 tools/execute 包装层组合
多个 tools/execute 监听器按 cordis 注册顺序组合。与未来的重试/沙箱/指标包装层一起使用时,注册顺序决定语义:「超时覆盖整个重试操作」(超时注册在外层),或「超时覆盖每次尝试」(超时注册在内层)。
模型体验
条件工具结果
模型所见内容
此插件不添加提示词或 schema。如果已声明的截止时间先到,它会将提供方结果替换为 Error: tool call timed out after <ms>ms 与结构化 TOOL_TIMEOUT;否则原结果保持不变。
Token 影响
未超时调用为零 token。超时会添加一条短小且保留的错误结果,并可防止体积更大的延迟提供方结果进入上下文。
KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
已知限制与延后工作
- 协作式,绝不是硬终止:截止时间只通过
exec.signal通知;忽略该信号的工具不会在超时时停止(参见「协作式,而非硬终止」一节)。 - 没有统一预算:只有声明
timeoutMs并将其放在ToolDefinition上的工具才会获得截止时间;未声明工具没有注册表级默认值(已交付的bash/read/write/edit有意不声明)。