readiness 不是打分表,是止损表:每一条不过,都对应一种「它一定会做错」。 把命令收进脚本,是把「你记得」换成「仓库记得」。 局部验证省的是计算,不是启动——Demo 里函数级差五十八倍,进程级只差一点一倍。
上一篇讲两条确认,这一篇把它扩成八条,并且回答一个更实际的问题: 验证要花多少钱,以及这个成本怎么改变 Agent 的行为。 Demo 量了三组数:readiness 八条过几条、函数级全量与局部的差、进程级全量与局部的差。 第三组数有点反直觉,也是这一篇最值钱的一句。
一、readiness 检查清单
八条,按「它会不会做错事」排,不按重要性排。
| 条目 | 不过会怎样 |
|---|---|
| 能一键跑起来 | 它连启动都验证不了 |
| 能一键验证 | 它不知道自己对不对 |
| 依赖有清单 | 它装不上,或装错版本 |
| 环境变量有样例 | 它去猜,猜错了才报错 |
| 测试不依赖网络 | 它偶尔失败,你以为是它的问题 |
| 有 AGENTS.md | 它得靠翻目录猜规矩(第 6 章) |
| 目录有边界 | 它会翻遍全仓,把你的额度吃光 |
| README 说清怎么跑 | 它读不到入口,就自己发明一个 |
最后一条最容易被当成「软要求」,其实最硬。 Agent 的第一动作几乎总是「读 README 和入口文件」—— 读不到,它就会按自己的想象跑一条命令,然后拿到一堆报错。 八条不用一次全补。 前两条补不上,别的补了也没用;前两条补上了,后六条可以慢慢来。
二、把命令收进脚本
「收进脚本」的意思不是「写个 shell」,是「让命令有一个固定的名字」。
python tasks.py test 就是一个固定的名字。
为什么重要?因为 Agent 不该猜。
让它猜「测试怎么跑」,它会试 pytest、试 python -m unittest、试 npm test,
每次猜错都要花一轮。
收进脚本还有一个附带好处:命令带参数时不用解释。
「跑测试要加 -k 不是 e2e」这种话,写进脚本就再也不用说。
Demo 里那个 tasks.py 只有二十行,
它做的三件事:兜住环境变量、给两条命令起名、出错时给出用法。
这三件事就是「入口」的全部。
三、局部验证比全量测试更常用
这一条要拿数说话。 Demo 量了两组: 函数级全量 131.8 毫秒、局部 2.3 毫秒,差五十八倍; 进程级全量 649 毫秒、局部 602 毫秒,差一点一倍。 为什么差这么多?因为起一个 Python 进程,这台机器上要五百多毫秒。 六十个用例的计算量才一百三十毫秒—— 启动开销是计算量的四倍,局部化省下的那点计算,被启动完全盖住了。 这不是说局部验证没用,是说它省的位置和你以为的不一样。 它省的是计算,不是启动。 所以真机上省得多,是因为那边起进程快;这台机器上慢,省下来的就看不见。 对 Agent 的影响在这里:它会根据验证成本调整策略。 验证便宜,它愿意多验几次;验证贵,它倾向于「看着像就交差」。 所以要它稳,先把验证做便宜。
四、动手 Demo:readiness 八条,和「局部验证到底省多少」
这个 Demo 两段。 第一段拿一个真仓库逐条查八条 readiness,输出过几条。 第二段造六十个用例,量「全量跑」和「只跑一个」的差——分函数级和进程级各量一次。 为什么分两级?因为很多人只量了第一级,就以为第二级也一样。 实际差得很远,原因在下面那个「启动开销」。
t = time.perf_counter()
for i in range(len(CASES)):
run_one(i)
full = (time.perf_counter() - t) * 1000
t = time.perf_counter()
run_one(17)
one = (time.perf_counter() - t) * 1000
# 再各起一个进程跑一次,看启动开销占多少
py(base, u"bench.py")
py(base, u"one.py")
实跑输出:
--------------------------------------------------------------------
readiness 八条,和「局部验证到底省多少」
--------------------------------------------------------------------
① 八条 readiness,逐条查
--------------------------------------------------------------------
[过] 能一键跑起来 tasks.py 有 run
[过] 能一键验证 tasks.py 有 test
[过] 依赖有清单 requirements.txt 在
[过] 环境变量有样例 .env.example 在
[过] 测试不依赖网络 全部本地断言
[不过] 有 AGENTS.md 第 6 章才写
[不过] 目录有边界 没有 src 与 tests 分层
[过] README 说清怎么跑 已写两条命令
八条过了 6 条。缺的两条都是结构问题:没有项目说明书,没有目录边界。
! readiness 不是打分表,是止损表。
每一条不过,都对应一种「它一定会做错」:没有一键验证,它就不知道自己对不对;
没有目录边界,它就会乱翻。
② 局部验证能省多少:先量函数级
--------------------------------------------------------------------
用例总数 60
全量 132.7 毫秒 / 局部 1.8 毫秒 / 差 75 倍
③ 再量进程级:启动开销是固定的
--------------------------------------------------------------------
起进程跑全量 674 毫秒 / 起进程跑一个 594 毫秒 / 差 1.1 倍
函数级差 75 倍,进程级只差 1.1 倍。
! 为什么差这么多?因为起一个 Python 进程,这台机器上要五百多毫秒。
六十个用例的计算量才一百三十毫秒——启动开销是计算量的四倍,局部化省下来的那点计算,
被启动完全盖住了。
这不是说局部验证没用,是说它省的位置和你以为的不一样:省的是计算,不是启动。
所以真机上省得多,是因为那边起进程只要几十毫秒;这台机器上起进程慢,省下来的就看不见。
④ 这组数对 Agent 意味着什么
--------------------------------------------------------------------
| 做法 | 一次验证的代价 | 它会怎么做 |
|---|---|---|
| 只能跑全量 | 六十个用例 | 改一处等一轮,越改越慢 |
| 能跑局部 | 一个用例 | 改一处立刻验,敢多试几次 |
! Agent 的行为是被「验证成本」塑造的。验证越便宜,它越愿意多验几次;
验证越贵,它越倾向于「看着像就交差」。要它稳,先让验证便宜。
八条过了六条。 缺的两个都是结构问题:没有 AGENTS.md(第 6 章才写)、没有目录分层。 这条数是真查的,不是摆样子。 函数级差五十八倍,进程级只差一点一倍。 这两个数放一起看,才看得出「启动开销」这件事。 单看第一组,你会以为「跑一个很快」;单看第二组,你会以为「局部没用」。 「起进程跑全量 649 毫秒 / 起进程跑一个 602 毫秒」—— 两者差的那四十七毫秒,才是「省下来的计算」。 剩下六百毫秒是每次都要交的固定税。 这条结论对你有用:如果验证很慢,先别急着并行化用例,先看慢在启动还是慢在计算。 慢在启动,改用例没用;慢在计算,减少范围才有用。
30 秒实验
- 查一遍你的仓库这八条过几条。 前两条不过的话,别的先别管。
- 给你最常用的三条命令起名字。 就用脚本,别用别名——别名在新 shell 里就没了,脚本一直在。
- 量一次你的「起进程开销」。 跑一个什么都不做的脚本,看它要多久——这个数会解释你很多「慢」的困惑。
常见误区
- 「readiness 是给大项目看的,小项目不用。」 越小越要。 小项目没有文档、没有流程,Agent 全靠猜——它猜错的成本,比补这八条高得多。
- 「验证跑全量最保险。」 保险的意思是「每次都很慢」,而慢的验证会被人跳过。 能跑局部就先跑局部,提交前再跑全量。
- 「局部验证快很多倍。」 看情况。 Demo 里进程级只差一点一倍——因为起进程的固定开销盖住了一切。这个数得自己量。
自测三问(点开对答案)
1. 八条里哪一条最容易被当成「软要求」,其实最硬? —— **README 说清怎么跑。** **Agent 第一动作几乎总是读 README 和入口文件**——读不到,它就会自己发明一条命令。 2. 为什么函数级差五十八倍、进程级只差一点一倍? —— **因为起一个 Python 进程要五百多毫秒。** **函数级只算计算,进程级要加上这笔固定开销**——**六十个用例的计算量比启动还小。** 3. 验证很慢时,先查什么? —— **先看慢在启动还是慢在计算。** **慢在启动,减用例范围没用;慢在计算,才该缩小范围或并行。**下一篇 5.3 走一遍完整链路:选需求、先出计划、改完当场验、留痕。
- ☐ (本篇完,下一篇 5.3 走完完整链路:一次最小变更的闭环)