背景
前端团队派活给 Agent 时,默认「框架只是写法不同」。前 Microsoft Edge 浏览器性能项目经理 Nolan Lawson 做了一个更贴近账单的对照实验,结论不太舒服。
实验对象是 12 个框架:Angular、Astro、Gracile+Lit、Hono+htmx、Next.js、Nuxt、Qwik、React Router、SolidStart、SvelteKit、TanStack Start,以及只用原生 JavaScript/TypeScript 加 Vite 的方案。
量级上最直观的两个数:Hono+htmx 的中位单次成本是 2.67 美元,Angular 是 4.97 美元,前者只到后者的 54%;Token 方面 Angular 达到 976 万,Hono+htmx 是 426 万,相差 2.3 倍。同一个模型、同一个需求、同一套验收标准,只换框架,账单翻倍。细节见 InfoQ 的报道。
方案
先把变量钉死,否则测到的只是 Agent 的随机性。
- 模型与推理:统一 Claude Opus 5.5,High 推理强度,要求 Agent 使用对应框架的最新版本。
- 任务:所有框架完成同一个基于 PokéAPI 的宝可梦图鉴应用,包含列表、属性、技能、游戏版本数据,支持搜索与筛选。单一任务,不掺多项目。
- 隔离与次数:每个框架原则上运行 5 次,每次在独立沙箱内完成。
- 样式:禁用 Tailwind、Bootstrap 及各类组件库,只给统一设计规范保证视觉一致。
几个刻意的取舍,值得单独说明:
不告诉 Agent 会跑 Lighthouse,不指定 SSR 还是 CSR,不要求特定优化手段。 这一条最关键。一旦提示「会被性能审计」,Agent 会额外花 Token 做优化,框架之间的差异就会被优化预算冲淡,测出来的东西也不再是「默认路径」的成本,而是「被提示后的最优路径」。
装上官方 MCP、Skills 或 Claude Code 插件,但不强制调用。 真实团队会装工具,不会拿枪逼着模型用。
不预先搭脚手架。 人手写好工程骨架再让 Agent 填空,测到的是「人+框架」,而不是「框架给模型带来的认知负担」。
失败的样本不重跑。 Angular 有两次产出了拒绝 localhost 以外访问的 SSR 应用,按未完成规范处理,直接记负。代价是性能与代码规模只有 3 次有效测试数据、成本仍用全部 5 次;收益是失败成本留在分母里,没有被洗掉。
实现要点
第一步,一个框架一个隔离目录,一次一个全新容器,避免上一次的文件残留影响判断。
FW="angular astro gracile-lit hono-htmx nextjs nuxt qwik \
react-router solid-start sveltekit tanstack-start vanilla"
MODEL="claude-opus-5.5" # 按当前 CLI 实际可用的模型标识填写
for fw in $FW; do
for i in 1 2 3 4 5; do
mkdir -p "runs/$fw/$i"
docker run --rm \
-v "$PWD/runs/$fw/$i:/work" -w /work \
-e ANTHROPIC_API_KEY node:22 \
claude -p "用 $fw 的最新版本实现这个 PokéAPI 图鉴应用:列表、属性、技能、游戏版本,支持搜索与筛选" \
--model "$MODEL" --output-format json \
> "runs/$fw/$i/result.json"
done
done
第二步,记账。注意四类 Token 必须分开统计,缓存读取的单价远低于新输入,混在一起算会把成本严重低估。
jq -r '[.usage.input_tokens, .usage.output_tokens,
.usage.cache_creation_input_tokens,
.usage.cache_read_input_tokens] | @tsv' \
runs/*/*/result.json \
| awk '{i+=$1; o+=$2; cw+=$3; cr+=$4}
END {print "in="i, "out="o, "cache_write="cw, "cache_read="cr}'
拿到四类计数后再按各家公开单价加权,得到单次成本;同一个框架取 5 次的中位数,均值会被一次异常长的探索拖偏。
第三步,验收。Angular 那两次失败就是靠这一步暴露的:容器内起服务、从另一个容器访问,拿不到 200 就算未完成,不进入性能统计。
docker run -d --name app-$fw -p 3000:3000 "app-$fw:latest"
curl -sf -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/list
效果
| 框架 | 中位单次成本 | 单次 Token 消耗 |
|---|---|---|
| Hono+htmx | 2.67 美元 | 426 万 |
| Astro | 2.76 美元 | — |
| Nuxt | 2.82 美元 | — |
| Angular | 4.97 美元 | 976 万 |
轻量方案(Hono+htmx、Astro、Nuxt)三个都压在 2.7~2.8 美元区间,彼此差距不到 0.2 美元,属于噪声范围。真正拉开档次的是 Angular:成本是最便宜方案的 1.86 倍(54% 口径反过来读),Token 是 2.3 倍。中间还存在一批 Token 明显偏高的框架,但它们与头部方案的差距并不稳定,需要看中位数而非单次。
对选型来说,这个结果的含义很直接:如果团队已经重度使用 Angular,把 Agent 接入现有工程时,Token 预算要按 Hono+htmx 的两倍以上来估。
复盘
教训一:单次跑分不能用。 成本波动很大,某个框架最便宜的一次,可能比另一个框架最贵的一次还低。所以结论只能建立在 5 次的中位数上,任何「我跑了一次发现 X 更快」的说法都不成立。
教训二:训练数据覆盖度会直接变成账单。 SvelteKit 版本较新,Agent 每次测试都要重读迁移指南;Gracile+Lit 更极端,Agent 因为不熟,会去克隆整个仓库,甚至翻 node_modules 里的编译产物。这不是框架本身的成本,是「模型没见过」的成本,而它同样计费。
教训三:别删失败样本。 如果 Angular 那两次跑失败后重跑到成功,成本会被系统性低估——失败的 Agent 往往已经烧掉大量 Token 才卡在最后一步。
还能怎么改:把 Token 预算写进 CI,超过基线就报警,而不是月底看账单;给 Agent 预置目标框架的版本迁移摘要和官方文档片段,抵消新版本带来的额外阅读;再做一组两阶段对照(人工先定骨架,Agent 只做填充),把「框架差异」和「探索成本」拆开。