编码代理低估了自有代码的成本
编码代理让写代码变得很便宜,但也能让你在任务完成后留下远超所需的代码量去维护。
我在用 Claude 构建 Land of Assets(一个企业级 3D 内容管理平台)时,曾多次把自定义实现替换成成熟的库:
- 数据获取:原本满屏的加载标志、副作用和清理守卫,变成了几个 TanStack Query 调用。
- 3D 预览:一个经过好几轮反馈才搭出来、满是 bug 的自定义查看器,被
<model-viewer>替换。 - 表单:数百行的字段校验和状态管理,让位给了 TanStack Form。
每一次,都是我主动提出要用库。
这些是我项目里的观察,不是对照实验。但它们指向一个值得量化的问题:代理写出来的代码,在接下来一百个任务里,改动起来要花多大代价?
自定义代码要付四遍成本
引入依赖会带来明显的维护义务,自定义代码同样会,只不过它是一次几行、几行地累积,每一行在当下看起来都合情合理。
-
写它。生成的代码越多,输出的 token 和评审就越多。把手写的 fetch 状态换成几个库调用,能立刻省下不少工作量。
-
测它。用库你照样要写集成测试。但如果你自己写获取层,就得对重试、取消、竞态和缓存失效负责。测试范围于是从产品本身扩大到了这层管道。
-
重新理解它。熟悉的 API 能压缩含义。知道
useQuery的代理,往往看调用处就能上手,再翻翻文档就够了。而自定义的useFetchWithRetry,得从实现和测试里反向理解。当新的代理接手,或者某次改动推翻了最初的假设,这份理解成本就会重新冒出来。上下文缓存能压低 token 账单,但省不掉“读懂代码”这件事。 -
扩展它的设计。成熟的库可能凝结了多年对边界情况和 API 设计的决策。而一个初版实现,通常只反映了一个页面的需求。第二个页面需要不一样的东西,于是补丁开始出现。下一个功能又继承了这些补丁。
廉价生成降低了第一项成本。其余三项成本会在应用的整个生命周期里不断累积。使用按量计费的 agent 时,部分维护工作会直接出现在账单上。使用包月套餐时,它仍然会消耗时间和上下文。

添加依赖看起来有风险;添加代码则不然
我不知道是什么导致了 Claude 在我的项目中的选择。我的工作假设是,有几种激励因素让自定义代码看起来比实际更安全。
“遵循现有模式”可能会倾向于扩展现有的手写代码。“避免范围蔓延”可能会让依赖项看起来像需要批准的架构决策,而另外几百行代码则感觉像是在完成请求。
包推荐也有一个真实的失败模式:模型会发明看似合理的名称。研究人员已经记录了包幻觉及其供应链影响。对未经验证的包保持谨慎是合理的。但这并不能解决一个既定实现是否比自定义实现更可取的问题。
评估设置也很重要。SWE-bench 在真实仓库问题上衡量 agent。解决一个问题是有用的能力证据,但它并不能告诉我们,这种设计是让接下来的五十个问题更容易还是更难。
自定义实现可以通过今天的测试,同时为明天制造维护负担。一个在今天测试结束时就停止的评估永远看不到那项成本。
“使用这些库”只有在你了解它们时才有帮助
我可以把首选库放在 CLAUDE.md 或 AGENTS.md 中。这在我已经知道答案时会有所帮助。
使用 agent 的一个原因是,它可能知道我从未遇到过但有用的工具。如果我必须说出每个库的名字,那我就是在提供需求的同时也在提供架构判断。
经验不足的开发者可能根本意识不到,自己的 AI 编程助手正在重新造一个表单库。就算给它三个不熟悉的包供选择,也未必帮得上忙。真正有用的做法是:主动调研、给出站得住脚的推荐,并简要说明取舍。
成熟的开源库还能提供一套人与 AI 助手都能理解的共同语言。贡献者看到某个查询或表单字段时,不用先重建一套私有框架就能认出来。允许列表(allowlist)也许能减少安装时的摩擦,但它没法让 AI 助手第一时间想到该用哪个库。
衡量接下来的一百次改动
我希望有人能做一个基准测试,围绕「反复修改一个应用的总成本」来设计,并让 AI 助手能自主做出依赖选择。就叫它 LongHaul 吧。
已有研究覆盖了这个问题的一部分:
-
SlopCodeBench:要求 AI 助手在之前的方案上继续扩展,并跟踪代码的冗长程度和结构性退化。
-
SWE-EVO:采用真实项目的发布说明驱动演化任务。
-
SWE-Chain:让每次版本级升级都继承 AI 助手之前的代码库。
-
MaintainCode:考察需求变化下的可维护性。
LongHaul 则要把「全生命周期成本」放在评分卡的核心位置。每个应用大约会收到一百个连续的改动请求:新功能、修改假设、删除功能,以及迫使重构的变更。
分为两条赛道:从零开始构建一个应用,或接管一个已有、且设计决策有些别扭的应用。接管赛道更接近我做过的大量软件工作。
连续所有权:每个任务都从 AI 助手上一次的输出开始。保存检查点用于检查和复现,但不要重置回干净的参考实现——那样会抹掉我们想衡量的后果。
同等访问权:每次运行都提供相同的包仓库快照和文档访问权限。包的选择应当可复现,下载失败也不该决定最终结果。
评分卡会跟踪:
| 衡量维度 | 具体内容 |
|---|---|
| 正确性 | 每个阶段的验收测试、回归测试,以及任务序列的完成情况。 |
| 总成本 | 输入和输出 token、工具调用成本、耗时,以及人工介入。 |
| 自有代码量 | 智能体需要维护的应用代码和测试代码,包括复制或引入的第三方实现。 |
| 设计指标 | 重复代码、复杂度热点,以及依赖健康状况。 |
记录用量和花费时都要留痕,这样定价变化不会影响比较结果。任何效率主张都必须以正确性为前提:一个做到一半就放弃的智能体,不该因为用的 token 少就算赢。
代码指标只是辅助证据。短代码可能难以阅读,低圈复杂度也可能用几十个小函数掩盖糟糕的设计。真正决定性的衡量标准是:随着变更不断累积,应用是否还能以合理成本持续满足需求。
LongHaul 不会因为智能体装了 TanStack Query 就给它加分,而是给它机会,让它在接下来一百个任务里从这个决定中获益。如果自定义实现更便宜、更清晰,并且同样可靠,那它就应该赢。
觉得有用?
分享给同样需要的人。