第 13 讲:Guard 循环卫生与超时控制

DeepSeek Harness 源码解析 · 第 13 讲

DeepSeek Harness 源码解析系列 第 13 讲:Guard 循环卫生与超时控制 基于 DeepSeek Harness 源码 · 2026-08-28 一、Guard 模块的定位 在 DeepSeek Harness 的插件架构中, packages/guard/ 目录下存放了两类"循环卫生"插件:它们不直接提供工具能力,而是 监控和约束 Agent 循环的行为 ,防止模型陷入无意义的重复调用或无限阻塞。这两个插件分别是: Guard 模块组成 🔹 repeat-tool-reminder — 重复工具调用检测器,当模型连续用相同参数调用同一工具时发出提醒 🔹 timeout-policy — 工具调用超时强制器,为每个工具声明的超时时间兜底 🔹 dsh-timeout (util) — 底层超时工具库,提供 deadline 融合、idle watchdog 等原语 二、重复工具调用检测器 (repeat-tool-reminder) LLM 在 Agent 循环中一个常见问题是 死循环调用 :模型反复用相同的参数调用同一个工具(比如反复读取同一个文件、反复执行同一个命令),却从不分析返回结果。repeat-tool-reminder 插件就是为了解决这个问题而设计的。 2.1 插件配置与校验 插件的配置通过 schemastery 校验,支持阈值、包含/排除模式、参数预览长度等选项: 📄 packages/guard/repeat-tool-reminder/src/index.ts(第 28-50 行) export interface Config { /** 触发提醒的连续重复次数(默认 [3, 5, 8]) */ thresholds?: number[] /** 要追踪的工具名模式;为空表示追踪所有工具 */ include?: string[] /** 对链透明的工具名模式(既不计数也不重置) */ exclude?: string[] /** * 详细提醒中引用的参数最大字符数(默认 500)。 * 大负载(如 write 的 body、长命令)如果不限制会无限制进入 * 下一次请求——正好在循环场景中;这个上限限制提醒的体积, * 但不限制检测(链的 key 始终比较完整的规范字符串)。 */ argumentsPreviewChars?: number } export const Config: z<Config> = z.object({ thresholds: z.array(z.number()).default([3, 5, 8]), include: z.array(z.string()).default([]), exclude: z.array(z.string()).default([]), argumentsPreviewChars: z.number().default(500), }) 默认阈值 [3, 5, 8] 意味着:连续重复 3 次时发出第一次提醒,5 次时第二次,8 次时第三次。阈值在加载时被严格校验——不能为空、不能小于 2、不能有重复值、必须是整数。这种"fail-loud"策略确保配置错误在启动时就被发现,而不是在运行时静默降级。 2.2 参数规范化:深排序去重 判断两次调用是否"相同"的关键在于参数的规范化。两个 JSON 对象可能属性顺序不同但语义相同,插件通过深度排序来解决这个问题: 📄 packages/guard/repeat-tool-reminder/src/index.ts(第 89-105 行) /** 深度 key 排序,使得属性顺序不同但语义相同的两个对象规范化为相同字符串 */ function sortJsonValue(value: unknown): unknown { if (Array.isArray(value)) return value.map(sortJsonValue) if (value !== null && typeof value === 'object') { const record = value as Record<string, unknown> const sorted: Record<strin…

阅读全文:DeepSeek Harness 源码解析 第 13 讲