SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-25

unlazy 技能与 Depth Tree 深度树方法

了解 unlazy 2.0.0 如何用 Depth Tree、gates 文件和 PLAN.md 合约阻止代理提前报告完成,并查看安装步骤及深度带来的工作成本。

unlazy 技能的作用

unlazy 技能用于阻止一种失败情况:编码代理在工作完成前就报告任务已完成。它的核心是 Depth Tree,这是一种将任务拆分为多个层级的方法,并且只有最底层才算实际工作。版本 2 将强制检查从说明文字移到文件中,因此代理必须根据一组可运行的命令证明任务已完成,而不能只自行声称已完成。

该项目由 Leonxlnx 在 github.com/Leonxlnx/unlazy 发布,采用 MIT 许可证。本指南基于 2026-08-10 发布的 2.0.0 版本编写。该领域的技能变化很快,因此在将其中内容复制到持续运行的环境前,请先阅读仓库中的 CHANGELOG。

这里的技能指通常意义上的技能:一个 SKILL.md 文件,任务符合其描述时,harness 会将其加载到模型上下文中。如果你不熟悉这种机制,请先阅读什么是代理技能,以及 harness 如何加载代理技能。unlazy 由普通 Markdown 和少量 Node 脚本组成,因此不需要服务器,也不需要自己的 API key。

为什么智能体会在完成 80% 后停止,并漏掉您的第 3 条指令

这种行为有明显的模式。您要求完成 4 件事。回复涵盖了第 1 件、第 2 件和第 4 件。结尾摘要却将 4 件事都列为已完成。整个过程没有抛出错误,因此也没有触发任何告警;您直到一周后才发现遗漏。

unlazy README 将这一问题与关于模型惰性的已发表研究联系起来,并引用了“响应过早截断,以及对多部分请求的部分遵循”(引用文献为 arXiv 2512.20662)。同一份 README 也明确说明了设计前提:文字无法约束文字。要求智能体更加努力,只是向生成缺失内容的同一上下文中再添加更多文字。

因此,版本 2 将状态保存在文件中。GATES.md 中的复选框不受模型自行决定。复选框下方要么有证据行,要么没有;脚本无需询问智能体,就能告诉您是哪种情况。

深度树:逐层展开

深度树是一种分解方法,并规定了工作允许发生的位置。方法参考中这样描述:

在自然连接处分解;如果自然连接适合二分,就进行二分;深度为 N 层。
只有叶节点可以执行实际工作;其上每一层都负责分解和整合。

叶节点不只是一个项目符号。参考中规定了最低规模:

叶节点是一个实际工作单元。需要投入至少十分钟的专注工作,产出一个完整且连贯的交付物,并对应一个 gates 文件。

这个最低规模可以防止深层树退化为无效忙碌。如果叶节点写成“重命名变量”,就不符合要求,说明上一级分解多做了一层。

随后,每个叶节点都要经过四轮处理:完整实现,不保留占位内容;以领域专家的方式重新阅读;查找缺陷;如果润色成本很低,再进行润色。这也是叶节点必须达到最低规模的原因。对一个两分钟的修改执行四轮处理,只是在走形式。

调用 skill 时,可以指定深度:

/unlazy tree 5 refactor the payment module

也可以使用自然语言,因为 skill 的描述会根据意图匹配,而不是只匹配 slash 命令:

tree 3 build the landing page and do not stop until every gate is checked

参考资料给出了深度范围。tree 2 或 3 适用于功能、缺陷排查或文档,通常由一个人通过一次会话完成,并分为 2 到 4 个叶节点。tree 4 或 5 适用于子系统、重构或正式评审,通常包含 8 到 16 个叶节点;这已经“超出单个上下文能够良好容纳的范围”。tree 6 或 7 适用于整个项目,在编排模式下运行,并将叶节点映射到互不重叠的工作单元。

如果不指定深度,skill 会被要求“选择能够匹配任务自然组成部分的最小 N”,并明确要求默认不要再深入一层。深度应当描述工作本身;盲目增加数字不会提升质量。

gates 文件的格式

开始任何工作前,代理都会将验收标准写入 gates 文件。每个 gate 都是一个复选框,下面附有一条命令。

# Gates: pricing section

- [ ] G1: three tiers render with real copy
  CHECK: node check.js pricing --tiers
  EXPECT: 3/3 tiers ok
  EVIDENCE: pending

- [ ] G2: annual toggle changes both price and label
  CHECK: node check.js pricing --toggle
  EXPECT: toggle ok
  EVIDENCE: pending

CHECK 是命令。EXPECT 是判定通过的输出。EVIDENCEpending 开始,必须替换为该命令实际打印的内容。该 skill 提供 scripts/gate-check.mjs,用于扫描这些文件并报告哪些 gate 仍处于打开状态。这样无需阅读运行记录,也能审计一次运行。

这套理念可以概括为一句话,SKILL.md 也将其写成一句话:

报告是一组由台账支持的声明,而不是完成工作的主观感觉。

随后遵循的规则是“台账完整前不得报告”。同时,报告规则要求最终摘要中的每个数字都必须在报告时重新测量,或标记为未经验证。代理不能因为感觉工作已完成就关闭 gate;关闭 gate 意味着粘贴与 EXPECT 匹配或不匹配的输出。

编写陌生人也能运行的 gates。“看起来没问题”不是检查。test -s dist/index.html && echo ok 打印 ok 才是检查,因为文件为空或不存在时,该检查会明确失败。

并行工作开始前的 PLAN.md 契约

当树状任务足够宽、叶节点在不同上下文中运行时,这些叶节点不再共享相同的假设。方法参考要求在分发任务前先定义契约:

先定义契约,再分发任务。接口、数据所有权、命名规则和错误约定,必须在任何叶节点启动前写入 PLAN.md。

原因很明确。分别要求两个子代理“添加错误处理”时,它们会设计出两种不同的错误结构,而且两个叶节点都能通过各自的检查,因为它们在本地都是正确的。只有在二者交汇时,问题才会出现。分支检查就是为此设置的:分支检查应验证“子节点已合并、接口匹配、端到端行为正常,且没有同级节点回归”。

在编排模式下,驱动程序会将 PLAN.md 中的契约部分交给每个子代理,而不是整个文件,也不会提供驱动程序自身的历史记录;同时还会原样提供该叶节点的 gates 文件。子代理返回后,驱动程序会自行重新运行检查。如果子代理“在没有证据的情况下自行勾选完成”,驱动程序会指出具体未满足的检查项,并将其退回。

编排存在最低工作量。实际工作时间大约少于半小时,就应保持单人处理。原因是每个子代理都必须从头重新理解任务,而这部分准备成本高于额外注意力带来的收益。

如何安装 unlazy skill?

推荐使用 skills CLI:

npx skills add Leonxlnx/unlazy

手动安装时,将其克隆到 agent 的 skills 目录:

git clone https://github.com/Leonxlnx/unlazy ~/.claude/skills/unlazy
git clone https://github.com/Leonxlnx/unlazy ~/.codex/skills/unlazy

然后确认文件已写入该目录:

ls ~/.claude/skills/unlazy/SKILL.md

回显该路径表示文件已在磁盘上。No such file or directory 表示克隆到了其他位置,通常是因为 skills 目录不存在于您假定的名称下,而 git 创建了一个新目录。也不要只相信 agent 的判断。README 自带的安装提示最后也有相同警告:“除非您已实际确认文件在磁盘上,否则不要告诉我它已安装。”

如果运行环境没有 skill loader,请将 SKILL.md 的内容粘贴到系统提示词或规则文件中。这是文档规定的备用方法,因此该方法可用于 Claude Code、Codex、Cursor 以及其他能够读取普通 Markdown 指令文件的工具。如果您想了解 SKILL.md 如何才能可靠加载,编写您自己的 agent skill介绍了决定它是否会触发的 frontmatter 和描述匹配规则。

Stop hook 能在 Claude Code 之外工作吗?

不能。这里需要明确区分。上文内容全部属于指令,而指令可能被忽略。唯一提供结构化强制检查的是 Stop hook,而 Stop hook 是 Claude Code 的功能。

node <path-to-skill>/scripts/install-hooks.mjs            # this project only (settings.local.json)
node <path-to-skill>/scripts/install-hooks.mjs --global   # every project
node <path-to-skill>/scripts/install-hooks.mjs --uninstall

Stop hook 会在代理尝试结束本轮时运行。此 hook 会扫描 gates 文件;只要仍有 gate 未满足,就会阻止本轮结束。因此,本轮不能在存在未检查复选框时结束。它只读取文件,不调用模型,所以 README 才会说明它不消耗 token。有关这一机制以及可附加 hook 的其他事件,请参阅 Claude Code hook 如何在一轮执行期间触发

它还提供了释放机制,这一点比听起来更重要。如果代理连续六次被阻止结束,但 gates 没有取得任何进展,hook 会发出警告并允许代理结束,而不是一直阻塞。ABANDON: <gate> <reason> 行始终会被视为有效的主动退出信号。如果没有这两种退出方式,在你的环境中无法满足的 gate 可能会持续消耗 token,直到你手动终止会话。

在 Codex、Cursor 或其他 harness 中,install-hooks.mjs 没有可安装的目标。你仍然可以使用 gates 文件和可运行的检查,但没有结构化机制阻止模型提前结束本轮。在这些环境中,gates 文件只是需要由你读取的文档。

unlazy 还是 ponytail:您需要哪一个?

这两项技能在同一时期流行,且在工作量上方向相反,因此很容易混淆。

ponytail 让代理表现得像一名资深开发者,首先会判断这段代码是否根本不需要存在。它会缩小范围、优先使用标准库,并减少差异内容。unlazy 则假设范围已经确定,并持续投入精力,直到范围内的每一部分都完成且经过验证。

因此,应根据您实际遇到的故障选择。如果代理把一个小功能扩展成框架,您需要 ponytail 技能及其“懒惰资深开发者”角色设定。如果代理遗漏四项请求中的第三项,却报告成功,您需要 unlazy。

两者可以同时运行,但顺序很重要。先用 ponytail 的问题确定范围,再将已确定的范围交给 unlazy 的各项检查。如果顺序相反,您会为 ponytail 原本会删除的工作构建一棵叶节点树,然后为所有这些工作承担深度倍增成本。这个顺序是我的建议,并不是两个项目之间有文档说明的集成方式。

VPS 托管的 agent 会产生多少深度成本?

深度是工作量倍增器,而工作量以 tokens 计。在 VPS 上使用您自己的 API key 运行 agent 时,这个倍增器就是成本。

ChartEffort and cost multipliers reported by the unlazy authors, August 2026
The data behind this chart
[
  {
    "label": "Skill run vs no skill, output tokens",
    "low_multiplier": 1.6,
    "high_multiplier": 3.9
  },
  {
    "label": "tree 6 vs tree 3, total cost",
    "low_multiplier": 1.0,
    "high_multiplier": 1.5
  }
]

这些数据来自作者自己的测试,日期为 2026-08-10。我们尚未复现这些结果,因此请将其视为趋势,而不是预测。单独执行时,输出量约为基线的 1.63.9 倍;而在同一上下文中将树深度从 3 增加到 6,仅增加了 1.01.5 倍,远低于再进行 3 次二叉拆分所预期的 8 倍。

深度增加不会带来成比例的成本,因为深度是在重新分配工作量,而不是增加上下文数量。token 成本参考明确指出,真正的倍增因素在哪里:“会倍增成本的是编排,而且成本本来就应该倍增,因为每个叶节点都会创建一个全新的上下文。”单独模式会在同一上下文中增加输出 tokens。编排模式会增加上下文数量,而每个新上下文在执行任何有效工作前,都要重新读取契约及其 gates 文件。

还有一种容易忽略的成本,该参考资料也提到了。在他们的测试中,一次单体深度运行“消耗了约 58 million 个缓存输入 tokens”,因为单个不断增长的上下文承载了全部内容。缓存输入按每个 token 计费更低,但达到这个规模后,仍会出现在账单中。

因此可以采用以下 4 项设置:

  • 选择叶节点能够代表实际工作单元的最小深度,然后停止。不需要的深度,就是不需要的支出。
  • 如果工作时间少于 half an hour,请保持单独模式,因为在这种情况下,subagent 的设置成本高于新上下文带来的收益。
  • 如果您使用 Claude Code,请安装 Stop hook。这是其中唯一免费运行的部分。
  • 在开始任何长时间运行的任务前,先在账户级别设置硬性支出上限。

最后一点最重要。一个设计为拒绝提前停止的 skill,按设计就会持续运行。预算和告警与提示词属于不同的工作,控制 VPS 上 agent 的成本介绍了应首先设置的限制。

作者测量了什么,以及这证明了什么

该仓库公开了自己的测试结果,这种做法比应有的更少见。README 中引用了他们的测试设置:“两个从零构建的任务(一个营销网站和一个 three.js 太阳系),每个任务包含三种条件(无 skill、tree 3、tree 6);每次运行都使用一个全新的文件夹和全新的会话;使用相同的模型和相同的提示正文。每个输出都由独立 agent 进行代码审查,再经过对抗式重新验证,并在浏览器中进行实际测试。”

ChartPer-run counts claimed in the unlazy README, August 2026
The data behind this chart
[
  {
    "label": "Self-found defects fixed, skill runs",
    "low_count": 4,
    "high_count": 10
  },
  {
    "label": "Wrong numbers in report, skill runs",
    "low_count": 1,
    "high_count": 3
  },
  {
    "label": "Wrong numbers in report, baseline runs",
    "low_count": 0,
    "high_count": 0
  }
]

请把中间一行读两遍。在作者自己的测试中,使用 skill 时,agent 在交付前自行发现了 410 个缺陷;随后每次 skill 运行交付的最终报告都包含 13 个错误数字,而基线运行中为 0 个。更多工作带来了更好的构建结果,却带来了更差的摘要。这就是账本规则背后的发现,也是要求在报告时重新测量每个数字,或将其标记为未经验证的原因。

他们的另一个结果也值得保留:“唯一一次实际运行失败的是基线构建,但其报告声称该案例已处理。”在损坏的构建之上给出自信的摘要,正是这些门控机制要防止的情况。

接下来是限制条件。测试只有 6 次运行、2 个构建任务和 1 个模型,而且由 skill 的作者自行运行并报告。这里没有任何独立复现;截至 2026-08-10,我们引用的只是作者的声明。请改为在自己的工作中进行测试:将同一任务运行 2 次,一次不使用额外配置,一次使用 gates 文件;然后统计那些已关闭且有证据可供你自行重新运行的门控项。这个数量是这一领域中唯一真正属于你的数字。

FAQ

unlazy 技能中的深度树是什么?

它是一种分解方法。任务会在 N 层中沿自然边界拆分,只有最底层的叶节点才算作工作。该技能将叶节点定义为:需要专注投入十分钟或更长时间、具有一个连贯交付物和一个 gates 文件的工作单元。因此,如果一个叶节点两分钟就能完成,说明拆分多深入了一层。叶节点之上的每一层都属于分解和集成,每个分支都有自己的 gates,用于证明子任务已合并且接口匹配。调用该技能时可以选择深度,例如 tree 5;文档规定的默认值是:选择能产生真实工作单元叶节点的最小深度。

unlazy 技能能在 Claude Code 之外使用吗?

部分可以。该技能使用普通 Markdown 编写,因此 Codex、Cursor,以及任何能够读取 SKILL.md 或系统提示的工具,都可以使用深度树、gates 文件、可运行检查和每个叶节点的四轮处理。强制执行方式有所不同。阻止在 gates 未满足时结束回合的 Stop hook 是 Claude Code 的功能,需要使用 node <path-to-skill>/scripts/install-hooks.mjs 安装。在其他环境中,没有任何结构性机制阻止模型提前结束回合。因此,需要由您读取 gates 文件并将其发送回去。

unlazy 会增加多少 token 费用?

作者报告称,在单人模式下,与不使用技能的运行相比,输出 token 数量约为 1.63.9 倍,此外 gates 文件本身还会产生几百个 token 的开销。在同一上下文中继续加深的成本相对很低:从树 3 加深到树 6 时,约为 1.01.5 倍。编排模式的成本较高,因为每个叶节点都会创建一个新上下文,并在开始工作前重新读取契约及其 gates。Stop hook 不会增加成本,因为它只扫描文件。这些是作者截至 2026-08-10 的数据,不是我们重复测量的结果。

我应该使用 unlazy 还是 ponytail?

根据当前问题选择技能。ponytail 适用于写得过多的代理,因为它会扮演高级开发人员,先判断代码是否确实需要存在,再优先考虑标准库。unlazy 适用于未完成您要求的工作量过少的代理,因为它会强制进行分解,并且没有证据就不会关闭 gate。如果两者都需要使用,应先用 ponytail 确定范围,再将该范围交给 unlazy。这样可以避免为本应删除的工作支付额外的工作量倍数。

安装 unlazy 后,为什么我的代理仍然提前停止?

请检查四项内容。第一,使用 ls ~/.claude/skills/unlazy/SKILL.md 确认技能已写入磁盘,因为将仓库克隆到不存在的目录是最常见的遗漏。第二,确认工作开始前已写入 gates 文件,因为 hook 会扫描 gates 文件,而缺少 GATES.md 时没有可阻止的对象。第三,确认 hook 的安装位置:普通的 install-hooks.mjs 运行只会将其写入此项目的 settings.local.json,因此其他项目需要使用 --global。第四,请注意释放机制按设计工作:如果连续六次停止操作被阻止且 gate 没有进展,代理会收到警告后继续执行;而 ABANDON: <gate> <reason> 行会按预期结束本次尝试。