unlazy:给 AI Agent 的完成度加一道可执行闸门
Anti-laziness skill for AI agents. Core: the Depth Tree method, which splits a task N layers deep and gives every leaf the full time budget of the whole task, so effort multiplies with depth. Grounded in 2025-2026 research on model laziness, underthinking and premature completion.
秒懂
- 它是什么?
- unlazy 把「任务做完没有」从一句自我声明变成一份可运行的验收账本。它用 GATES.md 记录检查命令与期望输出,由 gate-check.mjs 逐条执行并绑定证据。适合把 Agent 接入真实仓库、又不想只看它说「已完成」的团队。
- 适合谁用?
- 适合已经在 Claude Code 或 Codex CLI 里跑多步任务、并且愿意为每个任务写一份 GATES.md 的工程师。不适合只想加一句提示词就完事的人,也不适合把闸门当成沙箱的人:文档明确写着 approval is consent, not a sandbox,审批记录不哈希被调用的脚本和依赖。
- 能商用吗?
- 可以。MIT 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
- 还在维护吗?
- 在维护。仓库最近一次提交在 13 天前。
- 用什么语言写的?
- 主要是 JavaScript(依据 GitHub 的语言统计)。
以上回答依据项目的 GitHub 数据(最近同步于 2026年9月15日)和我们的分析,不构成法律意见。
开源项目深度解析
它要解决的不是「写不出代码」,而是「提前收工」
Agent 的失败模式很少是语法错误。更常见的是它读了两三个文件就宣布重构完成,跑了一条测试就说整条迁移路径已验证,或者把「应该没问题」写进总结。unlazy 针对的正是这一类:README 把自己的定位写成 completion discipline for substantial AI-agent work, backed by runnable gates,即把「完成」这件事从叙述改成可执行的判定。
它面向的读者很具体:已经在用 Claude Code、Codex CLI 这类支持 skill 的 Agent,并且任务规模到了「需要一份清单才能记住自己承诺过什么」的程度。仓库主题里同时列了 ai-agents、claude-code、prompt-engineering 和 productivity,说明作者并不把它当框架,而当一个可安装的技能包。
安装走 skills CLI:npx skills add Leonxlnx/unlazy,加 -g 是用户级安装,加 --all 覆盖所有检测到的 Agent。手工路径是 ~/.claude/skills/unlazy 或 ~/.codex/skills/unlazy,把仓库克隆进去即可。调用方式是 /unlazy(支持斜杠技能的地方)、$unlazy(Codex),或者由技能描述触发的自然语言。
值得注意的是仓库的版本状态写得相当克制:源码指向 2.1.0,但 README 明说 It is not identified here as a tagged GitHub release,需要不可变安装就钉住确切 commit。没有 release 记录,也就没有版本回退的锚点。
Depth Tree:把时间预算按层复制,而不是按层切分
README 的描述里,Depth Tree 是核心方法:把任务拆成 N 层,每个叶子节点拿到的是整个任务的完整时间预算,于是投入随深度相乘。触发方式是 /unlazy tree 5 refactor the payment module and verify every migration path 这样一行,其中 5 是层数。
这个设计与常见的任务分解不同。通常拆解意味着把预算分摊下去,子任务各自分到一小块;Depth Tree 反过来,让每个叶子都按「这就是全部工作」的强度去做。代价是显而易见的:层数和叶子数一起增长,总消耗不是线性而是乘法关系。README 没有给出任何关于层数上限、成本估算或实测数据的说明,所以 tree 5 和 tree 8 之间差多少,只能靠使用者自己在具体任务上试。
仓库把方法论的依据指向 2025-2026 年关于模型懒惰、思考不足和提前完成的研究,但提供的材料里没有列出具体论文或实验。这部分属于「作者声称有依据」,读者可以接受它作为设计动机,但不该把它当作已验证的结论。
闸门契约:一条 gate 通过需要同时满足两个条件
GATES.md 的格式是 Markdown 清单加固定字段。README 给的例子是这样:
- [ ] G1: pricing fixtures render the expected tiers,下面跟 CHECK: node scripts/verify-pricing.mjs,EXPECT: pricing verification passed,EVIDENCE: pending。第二条 gate 多了一个 CWD: packages/checkout,表示在子包目录下执行。
判定规则很硬:可运行的 gate 只有在进程退出码为 0,并且 EXPECT: 匹配合并输出时才算通过。捕获的 stdout/stderr 载荷、以及 EXPECT 用来比对的规范化 UTF-8 合并字符串,都必须落在 1 MiB 限制内;超限不会被截断成成功。
自动证据以一段带版本号的完整 SHA-256 摘要开头,摘要覆盖解析后的 CHECK、EXPECT 和原始 CWD 定义,随后是退出码与成功输出指纹,最后才是被截断的环境细节。这带来一个明确后果:已勾选的可运行 gate,如果证据缺失、是普通散文、是旧格式、格式错误,或者与定义不匹配,一律判为 stale 且未满足。老的人工 gate 配人工证据仍然兼容。
作者自己划了这条设计的边界:This unkeyed binding detects structural drift, not ledger tampering。能改账本的人也能伪造看起来规范的证据。这句话应该被认真对待,它决定了这个工具在什么场景下不该被信任。
审批、shell 与 PATH:三个容易踩空的地方
第一次运行 node <path-to-skill>/scripts/gate-check.mjs GATES.md 时,如果 oracle 没有精确的审批记录,检查器会打印解析出的命令、期望、工作目录、shell 和 PATH,但不执行。README 特别提醒不要把普通模式当永久 dry run:一旦那个确切的 oracle 被批准,普通模式就会真的执行它。想只看不跑,用 --status,这是唯一始终不执行的模式。
批准并运行是 --approve,重新跑全部可运行 gate(包括已标记完成的)是 --reverify。CHECK: 行本身就是 shell 代码,所以批准前必须逐条读完命令和它调用的脚本,这是文档反复强调的前提。
shell 的解析顺序是 --shell、UNLAZY_SHELL、Node 平台默认(Unix 是 /bin/sh,Windows 是 process.env.ComSpec)。这里有个具体陷阱:从 Git Bash 启动的检查器能看到 Unix 风格工具,从 PowerShell 启动的同一个检查器看不到。--shell 只换解释器,不会给你装上 grep、tail、tr。所以可移植的写法是调用仓库自带的 Node 脚本,而不是拼一串外部命令。父级复验也必须用同一个声明的 shell 和工具链,shell 或 PATH 不一致属于待解决的验证失败,不是成功证据。
安全边界方面,审批记录默认放在 ~/.unlazy/approved,可以用 UNLAZY_APPROVAL_DIR 换目录,但规范路径必须留在被检查仓库之外。符号链接存储、被替换或非私有的记录一律 fail closed。每条记录绑定绝对账本路径与 gate、确切的 CHECK 与 EXPECT、解析后的 CWD 与 shell、超时、输出与正则限制、平台以及完整继承的 PATH。改任何一个绑定输入都要重新审批。
它证明不了什么:审批是同意,不是沙箱
README 里最该被引用的一句是 Approval is consent, not a sandbox。审批不哈希被调用的脚本、fixture、依赖或其他传递输入。--status 和 Stop 会校验记录的定义绑定,但不会去检查那些产物。依赖变了要重新检查并跑 --reverify。SECURITY.md 里给了有界摘要模式,用于需要自定义依赖身份的场景。
另一个更根本的限制写在契约章节:检查器只能证明你声明的那个命令 oracle。它无法推断一个英文标题和一段任意 shell 代码是同一件事。所以 gate 写得好不好,直接决定这套机制有没有意义。README 列出的几条标准值得抄下来:读被结果命名的那个产物或服务;在所有断言通过后打印只表示成功的标记;对「应当不存在」的检查配一个已知的正例作为对照;对给出的数字做测量而不是照抄进 EXPECT;对重要的人工结论,用与风险相称的证据来复核。
还有一条是机械层面的:仓库提供 scripts/gate-lint.mjs 作为建议性的、不执行的账本检查,用来抓机械上薄弱的模式,加 --strict 时警告会变成失败。解析器本身会拒绝零 gate 的账本、重复 id、不完整的可运行 gate、无效期望,以及缺少原因或 gate id 未知的放弃记录。合法的放弃不是成功,而是终态交接:检查器以 HANDOFF REQUIRED 退出,退出码 1,Stop 允许退出并报告符合条件的 id。
与纯提示词约束的差别在哪里
常见的替代做法是把「不要偷懒、不要提前结束、必须验证」写进系统提示词或 CLAUDE.md。这类约束的优点是零安装、零维护;缺点是无法区分「Agent 说它跑了测试」和「测试真的跑了并且输出了预期字符串」。unlazy 的差别不在措辞,而在把判定交给一个独立的 Node 进程:退出码、输出匹配、证据摘要三者缺一不可,而且证据形状有版本化的规范。
代价也跟着来。你需要在任务开始前写出一份 GATES.md,为每条 gate 想出可执行命令和一个只表示成功的标记字符串,还要处理审批、shell 和 PATH。对于「改个错别字」这类任务,这套流程的成本远高于收益,README 也把适用面限定在 substantial work 上。
另一个现实差别是审批的粒度:记录绑定到绝对路径、确切命令、期望、CWD、shell、超时、平台和完整 PATH。换一台机器、换一个 shell、改一次超时,都要重新审批。这在多人协作或 CI 里意味着额外的摩擦,而 README 没有描述任何跨机器共享审批记录的机制。
维护成本与许可
核心是 SKILL.md,检查器和可选 hook 要求 Node 16 或更新,并且不使用任何第三方运行时依赖。这一点对长期维护是有利的:没有依赖树要跟,没有供应链升级要盯。
许可为 MIT。仓库没有 release 记录,README 明确说当前源码指向 2.1.0 但没有对应的 tagged release,未发布的变更集要看 CHANGELOG.md。这意味着「升级」在这套东西里不是一个能自动化的动作:没有版本号可比较,只能比对 commit。要复现某次安装,就钉住确切 commit。
由于审批记录与绝对路径、确切命令、shell 和平台绑定,升级技能本身、改动被调用的脚本、或者换 shell 之后,既有审批都会失效并需要重新批准。这不是缺陷,是设计选择,但它确实让「升级」变成一件需要重新走一遍审批流程的事。
谁该装,谁该绕开
该装的人:已经在用 Claude Code 或 Codex CLI 跑多步工程任务,并且反复被「Agent 说完成但实际没完成」消耗过时间。他们愿意在任务开始前写一份 GATES.md,愿意逐条读 CHECK 行,也接受审批记录绑死在当前机器和 shell 上。
该绕开的人:任务以探索、问答、写文档为主,没有可执行的成功判据;或者把审批机制误当成安全隔离的人。文档已经把话说清楚,审批是同意不是沙箱,不哈希传递依赖,能改账本的人也能伪造证据。
上手前先验证三件事。第一,node --version 不低于 16,然后跑 node <path-to-skill>/scripts/gate-check.mjs --status GATES.md,确认账本能被解析而不是被拒。第二,在真正要用的那个 shell 里确认每条 CHECK 行调用的程序存在,别从 Git Bash 验证完就拿到 PowerShell 里跑。第三,如果任务里有人工判定的部分,先想清楚用什么证据来复核,因为这部分检查器帮不上忙。做完这三步,再决定要不要把 /unlazy tree 5 写进日常流程。
编辑结论
适合已经在 Claude Code 或 Codex CLI 里跑多步任务、并且愿意为每个任务写一份 GATES.md 的工程师。不适合只想加一句提示词就完事的人,也不适合把闸门当成沙箱的人:文档明确写着 approval is consent, not a sandbox,审批记录不哈希被调用的脚本和依赖。上手前先确认三件事:Node 版本不低于 16;用 --status 跑一遍 GATES.md 确认解析通过;在目标 shell 里确认 CHECK 行调用的工具真实存在。最后记住版本状态:源码指向 2.1.0,但仓库没有对应 tag,需要可复现安装就钉住具体 commit。
社区笔记