
我最近一直在琢磨一个实验名字叫 Ducklab。项目标题只有一句话但信息量很足一个 dev harness开发工具集通过 416 次运行花了 176 美元用本地模型把自己构建了出来。第一次看到这组数字我没有觉得“AI 真便宜”反而在想另一件事真正难的不是让模型生成代码而是让“生成、测试、修复、再生成”这种循环收敛下来并且跑完之后你还能知道每一步花了多少钱。Ducklab 真正有意思的地方就在这里。这个项目给我的第一个触动不是它有多自动化而是它把“用 AI 构建自己的开发工具”这件事做成了一个可记录、可观测、有成本上限的迭代流程。416 次运行听起来很多但如果折算下来每次不到半美元这种试错密度决定了你可以怎么设计流程。这篇文章想聊的就是这个循环本身它为什么能收敛、成本为什么能压下来、本地模型在这里到底承担了什么角色以及如果你想在自己的项目里复刻这套思路真正要抓的是哪些细节。1. 先搞清楚这个工具真正解决的是哪类重复劳动1.1 从一次手动构建的困境说起先回到一个很普通的开发场景。你想要一个脚手架工具用来生成测试夹具、初始化目录结构、批量修改配置或者验证某个接口返回是否符合预期。这类工具的共同特点是输入输出相对明确逻辑不算复杂但代码量很琐碎。过去一个人的做法通常是打开编辑器新建一个 Python 脚本写函数、写 if 分支然后手动跑两遍看到输出符合预期就收工。问题在于这类任务对开发者的消耗并不是“写那几百行代码”而是整个过程中的上下文切换你要记得输入格式、编码、路径、异常分支还要写一堆临时断言来确认结果。更麻烦的是这类工具通常一次性使用三个月后再回来改你已经忘掉了当初的约定。dev harness 想解决的就是这类重复劳动。它把验证过程、测试环境、输入样例、断言逻辑都沉淀成一堆可重复运行的脚本和配置。Ducklab 的特殊之处在于这个 harness 不是人工一次性写出来的而是它自己在构建过程中承担了校验者角色每次运行之后工具会根据运行日志判断当前实现是否正确再把错误信息反馈给模型让模型修改自身代码。听起来像是一个“程序在写自己”的故事但落到工程层面它其实是把整个开发过程做成了一条流水线描述需求、生成实现、自动验证、反馈修复。流水线的每一条规则都可以被记录、被重复这就是它和普通 AI 辅助开发最大的区别。1.2 “自己构建自己”和普通 AI 生成有什么不同普通的 AI 辅助编程人给出指令模型生成代码然后人把代码贴进项目里自己去跑测试。这个流程里模型的输出是一次性的人承担了所有验证和调试工作。人和 AI 之间的关系更像是“打字员”和“校对员”的关系。Ducklab 这个模式不太一样。它会先定义一个验证脚本这个脚本描述“一个正确的实现应该满足什么条件”。模型生成的代码必须通过这个验证脚本才能被保留下来如果不通过错误日志会带着差异信息重新回到模型手里经过下一轮修正再跑再看日志直到收敛。差异看起来很小但影响很大。普通 AI 辅助开发只解决了“从需求到初稿”这一段。Ducklab 模式解决的问题是把“初稿到可运行”这一段也自动化了。它把人类的判断前置到了定义验证条件这一步后端的生成和执行循环交给机器。这也是为什么它值得被关注不是因为模型写出了多复杂的代码而是因为流程里多了一个“验证者”整个系统从“生成一次看运气”变成了“迭代多次直到符合标准”。416 次运行本质上就是这条迭代链路的长度。2. 用四步闭环把构建流程变成可迭代系统2.1 第一步先写验证脚本而不是先写功能如果你也想做一个能自动迭代的 dev harness第一件要做的事可能和直觉相反不是先让模型写功能代码而是先写一个验证脚本。这个验证脚本决定了“什么样的实现算对”。这个脚本可以很简单。比如你的工具需要把一个 JSON 输入转换成另一个 JSON 输出那验证脚本就负责调用当前实现传入预置输入拿到实际输出和期望输出做比较输出 PASS 或 FAIL并在 FAIL 时打印尽可能具体的“差异描述”。这个步骤之所以重要是因为它把“模糊的目标”变成“可执行的断言”。没有验证脚本模型生成的代码只能靠人肉读代码来验收那你仍然在原地方排队。在常见实践里验证脚本本身应该遵循一个原则足够稳定且足够严格。过于宽松会导致模型生成一个“刚好骗过测试”的实现过于严格则会导致大量无效失败浪费时间。建议先跑一两个已知正确的样例确认验证脚本本身不会误报再进入生成循环。2.2 第二步让模型生成最小实现而不是完整系统模型既然要自己改代码就不能一开始就要求它生成一个包含几十个函数的大模块。更好的做法是拆任务一次只让模型生成一个小功能甚至一个小函数然后立刻验证。拆分到多小才算合理我的判断标准是让一次验证返回的错误日志足够聚焦。如果你把整个工具的所有逻辑都放在一次生成里失败的时候日志会指向十个不同的地方模型根本不知道该改哪里。反过来如果你只让它实现“读取 JSON 文件并去除空值字段”这一个行为那么一旦失败错误日志会直接指向输入输出格式不匹配修复路径清晰得多。最小实现跑通之后再逐步叠加行为。比如 Ducklab 这类工具可以先实现“读取配置”再实现“生成测试夹具”最后实现“输出统计报告”。每一步都依赖上一轮的验证脚本作为回归保障确保新增功能不破坏旧功能。这一步极其关键因为它直接决定了后续迭代是否可控。单次生成跑通记录再单次生成跑通记录。416 次运行听起来多但如果你把大型任务拆碎每次运行都只验证一个很小的行为这个数字就不吓人了。2.3 第三步把失败日志变成下一轮的上下文模型生成的代码第一次运行失败太正常了。真正拉开差距的是失败之后怎么反馈。很多人的第一反应是把报错信息直接丢给模型让它重新生成。如果问题简单这样可能有用但如果问题出在输入输出边界或者模型对需求理解有偏差光靠报错信息往往不够。更好的做法是构造一个“反馈模板”至少包含四类内容目标描述这个行为要完成什么功能输入是什么输出应该是什么当前代码上一轮生成的完整实现实际结果验证脚本运行后得到的真实输出差异分析期望输出和实际输出之间的具体差别最好带上样例。在每次迭代时只把最近一轮的失败日志和目标描述放进上下文而不是把所有历史记录都堆进去。这有实际原因模型上下文窗口有限塞入过多历史会让注意力被稀释。通过的行为应该固化成回归测试失败了再去补充反馈但不需要每轮都提醒模型“你上次改对了”。这个反馈循环是整个系统能否收敛的核心。如果错误日志本身不够精确模型就会进入盲猜阶段每轮都在改但每轮都改错。所以我的建议是花时间把验证脚本的输出写详细让你拿到日志的那一刻不用看模型代码就已经大概知道问题出在哪一层。2.4 第四步用“可收敛”作为停止条件一个自动迭代系统最怕什么不是失败而是无限循环。模型反复修改代码但始终有一两个测试过不去白白消耗时间和电量。所以在设计这个系统时至少要设置三个停止条件成功条件所有验证脚本全部通过且输出在预期范围内轮次上限比如 15 轮超过就给人在回调接口里接管成本上限比如每轮运行有最大 token 数或最大时间超过就中断。定期记账也很重要。每一轮运行都记录模型调用次数、token 消耗、运行耗时、测试结果。这不仅是成本管理更是行为纪律如果一个功能迭代了 30 轮还没过记录数据会告诉你问题大概率不在模型能力而在需求描述或验证脚本本身。Ducklab 那 416 次运行我相信不是靠模型一口气跑到终点而是里面有大量这种小步迭代和成本记录。真正让一个 self-building harness 能持续工作的不是魔法是它知道什么时候该停下来。3. 本地模型和 176 美元预算意味着什么3.1 为什么值得优先尝试本地模型Ducklab 项目的一个明确特征是使用本地模型。这不是一句简单的“省钱”背后有几层很实际的考量。第一隐私和隔离。构建内部工具的过程中代码之间可能互相引用有时还会涉及内部 token、数据库地址、业务字段。把这些信息发给远程 API虽然未必违规但很多人心理上过不去合规上也可能有顾虑。本地模型可以避免这批数据离开机器。第二访问频率。在迭代循环里模型会被反复调用而且调用次数和失败轮次绑定。如果一次循环要跑 30 次模型请求每次都要走公网 API时延和网络抖动都会成为不可忽略的变量。本地模型把这些请求压在本地吞吐更可控。第三成本结构更适合高频小步验证。176 美元的总预算放在远程 API 上可能很快就烧完了放在本地模型上更多是硬件折旧和电费单次运行成本可以压到极低。这也是为什么这个项目能跑 416 次实验而不担心成本爆炸。当然本地模型不等于没有成本。硬件、显存、电力这些都要算进去。而且它更依赖机器配置如果你手头只有一台没有独显的笔记本本地模型的推理速度会很难受。如果你想复刻这个流程建议先确认自己的机器能跑得动一个小规模模型否则还是要走 API。3.2 成本不只是钱更是纪律176 美元这个数字最值得注意的其实不是“少”而是“有上限”。当我看到一个项目强调运行次数和总成本时我会本能地想这个项目一定发生过很多次失败但失败没有失控。416 次运行里如果一次成功都没有那这钱就打水漂了如果前 50 次就成功那后面 300 多次说明它在持续加功能。所以这个数字背后藏着两种成本纪律。第一种是任务级成本单个功能最多迭代多少轮超过就要让模型从别的方向重试。第二种是整体预算整个项目最多花多少钱这个预算决定了任务的优先级——小活先做风险大的活留到后面。这种“以成本倒推工作方式”的做法在个人项目里非常有参考价值。它逼着你把需求拆细、把验证写准而不是让模型在一个错误方向上越跑越远。从这个角度说176 美元不是省钱的结果而是约束的产物。3.3 量化与日志让每次运行都可追踪要让一次迭代流程真正可维护日志系统必须和代码生成系统同步建设。每轮运行至少需要记录时间戳和运行编号模型名称和参数配置输入格式和输入摘要验证脚本的输出结果调用 token 数和预计成本本轮保存的代码版本。这些数据有几个用途。当你发现某一类 bug 反复出现时日志能帮你定位到是需求描述问题还是测试用例问题。当你发现成本增长速度异常时日志能帮你找到是哪一轮行为失控。当你想复现某个结果时日志能让你回退到当时的输入和模型版本。我自己的经验是哪怕只是一个私人小项目运行日志也比代码注释更有用因为它记录的是系统“实际发生了什么”而不是你“以为发生了什么”。4. 实际落地时最容易踩的坑4.1 坑一验证脚本写得太宽模型“骗过”测试这是自动化迭代里最隐蔽的问题。如果验证脚本只检查输出 JSON 里有没有某个字段而不检查字段值是否符合格式模型完全可能生成一个“看起来对但实际没用”的实现。它通过测试了但业务逻辑是错的。要避开这个坑验证脚本应该覆盖三类断言返回值结构、关键字段取值、边界输入行为。边界输入尤其容易忽略比如空字符串、空数组、None 值、超长字符串。模型生成的初始代码往往只适配了“正常输入”根本没有处理边界。所以我的建议是至少准备三个输入样例。一个标准输入让它跑通过一个边界输入验证容错一个异常输入验证报错方式。每加入一个新功能样例库就要跟着扩展。4.2 坑二上下文越积越长模型改坏旧功能迭代系统最常见的问题是用“最近 20 轮的所有日志”作为下一轮输入。模型开始还清楚自己在做什么后来它被大量历史文本淹没经常为了修新 bug 把旧行为改坏。更合理的做法是维护一个“行为基线”。已经通过的验证脚本不允许被后来的改动破坏。每次生成完新实现后先跑全量回归测试一旦有回归失败就把旧实现和失败日志再次给模型明确告诉它新增功能可以失败但旧行为不能被破坏。上下文策略也值得固化反馈中只保留最近一轮的实现、最近一次失败日志和目标描述。更早的历史应该以回归测试的形式存在而不是以文本形式堆进上下文。4.3 坑三把单次跑通误当成稳定可用在自动化生成的流程里模型很容易输出“只有一个输入下恰好正确”的代码。单次跑通只能说明流程没有断不能说明工具在长期使用中可靠。怎么判断一个实现是否真正达标可以从三个角度验证样例多样性多个输入都能产生正确结果重复稳定性同一输入连续跑三次结果一致错误处理非法输入不会导致整个进程崩溃而是返回明确错误信息。在你没有达到这三条之前不要急着把它当成完成品。宁可多迭代几轮也不要留下一个不可解释的随机行为。4.4 一个通用排查顺序如果自动迭代的过程中出现了反复无法收敛的问题我建议按下面的顺序排查而不是直接改提示词先看验证脚本本身。是不是断言写错、依赖缺失、环境差异导致误报再看输入样例。是不是某个样例的格式和预期不一致或者样例本身有歧义再看失败日志。日志是否足够具体有没有把关键差异点暴露出来再看上下文。反馈中是否包含了无关历史导致模型判断被带偏最后再看模型本身。是不是模型能力不足以处理当前任务或者本地模型参数配置过低这个顺序的核心理念是先消灭系统自身的干扰项再判断模型能力。因为系统层面的问题一旦存在模型能力再强也解决不了。5. 这类方法适合谁不适合谁5.1 值得尝试的三种场景第一类是脚手架和工具类项目。比如生成测试夹具、批量转换文件、初始化项目结构、生成配置文件。这类任务输入输出清晰验证成本低非常适合自动化迭代。第二类是内部自动化脚本。比如 CI 里的辅助程序、日志分析工具、数据清洗工具。它们不需要复杂架构重点是功能正确而且可以反复用测试来约束行为。第三类是原型验证。你想快速试一个想法但不想手写完整实现可以先让模型生成一个最小版本再靠验证脚本驱动它逐步补全。这种场景下迭代流程能让你避免“写着写着就失控”。5.2 不适合的几种情况如果场景是实时用户系统、涉及资金操作、安全边界敏感的业务或者需要严格架构评审的大型项目我不建议用这种自动迭代方式直接生成主要代码。它更适合作为辅助工具帮人写测试、写文档、写脚手架而不是完全替代人的设计和判断。另外如果任务本身无法被自动验证也很难用这个流程。比如你想让模型生成一套 API 设计规范判断标准是主观的那验证脚本根本写不出来迭代也就无从谈起。这类任务还是需要人来评判。还有一个前提条件你的机器要能跑得动本地模型或者你有预算支持高频 API 调用。否则就算流程再合理执行成本也会击穿预算。5.3 如果要做更大规模需要补什么从单次实验到稳定框架中间还差几块拼图。首先是版本管理每一步生成都要有 commit失败提交和成功提交都能回滚其次是模块化把“需求描述、验证脚本、反馈模板”拆成独立配置文件方便复用最后是可视化面板能直观看到每一轮运行的成本、耗时和测试结果让你在迭代时第一时间发现异常。这些能力的价值不在于“看着高级”而在于让整个构建过程可观察、可控制、可复现。Ducklab 用 416 次运行、176 美元的实验证明了这种思路在个人项目规模上是可行的。你可以理解成一次低成本验证也可以看作是新一代开发工作流的小样本预演。对我来说这个实验真正值得学习的地方不是省了多少钱也不是 AI 多聪明而是“把验证前置、把循环记录、把成本约束”这三个习惯能让一个原本充满不确定性的过程变得可控。先跑通一次最小闭环再考虑扩展。哪怕你的工具最终只运行 10 次、花 20 美元只要流程是收敛的你就已经拿到了这套方法里最有价值的东西。