22 分 10 秒 vs 2 小时 44 分:AtomCode 与 opencode 项目开发对比实测,国产agent Atomcode 这么猛吗?

先说结论

2026 年 10 月 17 日傍晚,在同一台机器上、用同一条提示词、同一个终端(PowerShell 7),把两个终端 Coding Agent 放在同一块屏幕里并行跑了一遍:让它们各从零写一个"类网盘"的文件上传/分享/下载服务,并且按提示词要求先规划、后开发、组件化,最后用 UI 自动化测试自己验收。

两个产物都能跑。不是 PPT 工程,不是"思路示意",是真的能上传、生成提取码、在另一个浏览器窗口里下载、并用 SHA-256 校验字节一致的完整闭环。把两边服务同时拉起来,用同一个 Playwright 脚本做了真实用户操作,两边最终下载到的文件都和我喂进去的源文件逐字节一致。

但过程与结果的分野很大:

  • 屏幕口径 :AtomCode 最后一轮显示 22 分 10 秒 ,opencode 显示 2 小时 44 分。
  • 实际口径 :AtomCode 从第一次提交到最终交付是 44 分 08 秒 ,中间被手动取消过一次、因模型渠道 403 中断过一次、换模型后"继续"跑完;opencode 是 单条提示词、零人工干预、2 小时 44 分一口气跑完。
  • 模型口径 :opencode 全程 GLM-5.3-Flash Free;AtomCode 前半程 GLM-5.3-Flash,最后 22 分 10 秒实际是 Qwen3.8-27B 跑完的。这条是从会话日志里挖出来的,界面状态栏一直没更新,后文细说。
  • 交付广度:opencode 交付 54 个应用文件 / 3574 行应用代码 / 5 份文档 / 64 个 E2E 用例;AtomCode 交付 43 个应用文件 / 1698 行 / 1 份 README / 15 个 E2E 用例。
  • 交付深度:opencode 的功能覆盖、测试密度、文档完整度明显更高;但 64 个用例全绿的产品,我打开分享结果页第一眼就看到了"有效期 NaN-NaN-NaN"。AtomCode 的核心链路干净、代码更短,但 README 里"断点续传""大文件不惧内存""下载计数"这几件事,落地程度经不起逐条核对。
  • 一句话:AtomCode 是短跑选手,20 多分钟给你一个能演示的完整切片;opencode 是长跑选手,用 4 倍多的时间换更宽的覆盖面、更完整的测试与文档,也换来了更多肉眼可见的边角缺陷。

这不是一张"谁更强"的排行榜,样本只有一次,变量很多。但这组对照最有价值的地方,恰恰是它把"Agent 写代码"从玄学拉回到可审计的工程数据:会话时长、工具调用、token 流、测试运行次数、最终产物,以及那些测试没覆盖到、但人一眼能看见的缺陷。


一、实验与口径:先把"不公平"讲清楚

1.1 提示词

两个 Agent 收到的首条消息是同一条(我在两边会话日志和窗口截图里做了交叉核对),原文如下:

项目说明:基于服务器实现类似网盘的文件下载和上传功能。

编程要求:不要直接开发,先规划出整个开发任务。组件化开发,模块化开发,不允许出现超级大代码文件(重要)。要具备二次开发维护的条件(重要)。

技术栈:自主规划要使用的技术栈,参考成熟的开源项目。

功能说明:

1、可以分享下载链接,客户输入验证码,下载对应的文件(响应式,多端适配)

2、实现客户端文件上传(链接服务器)。

3、下载上传性能要足够高(这个功能非常重要)

4、UI 界面要简洁大气,参考国外类似网站的设计效果,动画要足够炫酷,颜色要足够的好看,加载要足够的快。

注意:开发完成后,运行 UI 自动化测试,模拟真人用户使用,添加 15 条以上测试数据,真人模拟验证测试每个模块的每个功能(所有功能点,记住,每个模块的每个功能点都要测试),发现 bug 修改 bug,迭代直到所有的功能模块完全可以正常使用。

这是一条"验收条件写得很满"的提示词:先规划、禁超大文件、要测试、要 15 条以上数据、要覆盖每个功能点、要迭代到全绿。它天然把 Agent 往"工程化交付"上逼,也天然制造了后面所有分歧------因为"每个模块每个功能点"到底覆盖到什么程度,两边理解完全不同。

1.2 环境

项 值
机器 Windows 11 Insider Preview 26220(i5-12400F,6C/12T,31.8 GB RAM)
终端 PowerShell 7.6.7
运行时 Node v22.20.0 / npm 10.9.3 / git 2.55.0
浏览器 Playwright Chromium 1248(opencode E2E)、系统 Edge 156(AtomCode E2E)
时间 2026-10-17 16:55--19:39(本地时区),两场会话同屏并行
模型渠道 opencode:glm-5.3-flash-free(opencode provider) AtomCode:AtomGit-glm5.3-flash(CodingPlan),17:16:59 后切到 AtomGit-qwen3.8-27b
工作目录 同一块 F: 盘、两个不同项目目录,路径均含中文

两边是同时跑的。它们共享同一颗 6 核 CPU、同一块盘、同一个网络出口,还各自拉起 Chromium/Edge 做 UI 自动化。所以下面给出的性能数字只适合同机同场景的相对比较,不能当绝对性能榜单。

还有一个容易被忽略的环境因素:工作目录在 exFAT 外接固态 上。exFAT 不支持硬链接和符号链接,npm workspace 那套靠链接组织的安装方式在这里会直接翻车。opencode 的会话里就留下了 npm error errno -4068,它随后把"无 workspace 根目录、各子包独立安装"写进了架构决策记录;AtomCode 也用的是按子包 --prefix 安装,不过它的会话里没有出现同类报错,更像是在规划阶段就绕开了。两个项目的安装脚本因此都不是标准的 npm workspaces 形态。

1.3 计时口径:22m10s 和 2h44m 不是同一把尺子

opencode 的 2h44m :会话日志 time_created 到 time_updated 是 16:55:06 → 19:39:25,共 164 分 19 秒。这是一条提示词进去、中途没有任何人工输入、由 Agent 自己规划---编码---测试---修 bug---收尾的完整时长。

AtomCode 的 22m10s:这是它最终成功那一轮的时长(17:17:07 → 17:39:17,1,330,433 ms)。但这场构建总共分了三段:

  1. 17:00:47--17:01:11(23.3 秒):给出开发规划,然后停住等用户确认;
  2. 17:01:47--17:16:31(14 分 44 秒) :用户输入"执行"后开始搭建,54 轮、100 次工具调用,其中有一次 300 秒的 Playwright 浏览器下载超时,最后撞上模型渠道报错:CodingPlan 未领取或已失效(HTTP 403);
  3. 17:17:07--17:39:17(22 分 10 秒):用户输入"继续";此前 17:16:59,会话模型已从 GLM-5.3-Flash 切到 Qwen3.8-27B,之后跑完,65 轮、74 次工具调用。

更早还有一段:16:55:09 第一次提交同一提示词,跑到 16:58:43 被手动取消(已产出规划并开始写代码,3 分 34 秒、10 轮、17 次工具调用)。所以从"第一次把题丢进去"到"拿到可运行的最终交付",实际是 44 分 08 秒。

把口径摆在一起:

口径 opencode AtomCode 比值
界面/最后一轮耗时 2h44m19s 22m10s 7.4×
构建会话墙钟(本会话首条提示词到交付) 2h44m19s 38m30s 4.3×
从第一次提交算起(含被取消的预研) 2h44m19s 44m08s 3.7×

4.3× 是最接近"同一条起跑线"的对比数字;22m10s 是 AtomCode 的"最后一轮成绩单",把它当全程耗时对它不公平,但 7.4× 也确实不够严谨。本文后面会同时保留这三个口径。

1.4 证据链

  1. 两个 Agent 的会话日志 :opencode 的 opencode.db(session_v2 / session_message 表,330 条 assistant 消息、425 次工具调用都有时间戳与输入输出);AtomCode 的 .events / .index / .todos 文件(近千条事件记录、120 轮请求、174 次工具调用、11 次权限询问、4 次重复调用纠偏注入)。
  2. 两个项目的源码与测试:逐文件阅读、统计行数、检查依赖与实现,不只看 README。
  3. 真实运行截图:把两份产物分别启动,用 Playwright 以真人操作路径上传/分享/验证/下载,两边各截取一组界面截图(其中 9 张直接放进本文,另有对比合成图),并对下载文件做 SHA-256 比对;两边下载内容均与源文件一致。
  4. 独立复跑测试:opencode 的 64 个 E2E 用例复跑 64/64 通过(2.4 分钟),AtomCode 的 15 个 E2E 用例复跑 15/15 通过(24.3 秒);两份冒烟测试分别 14 条、24 条断言全通过。
  5. 自己的针对性测试:64 MB 文件吞吐、Range/416/304、票据一次性、限流触发、非法 Range、下载计数、纯 JS SHA-256 实现的正确性等。

二、两个产物:都跑起来了,而且不是花架子

2.1 首页与视觉

opencode 给项目起名 SkyLink 云上快传 ,AtomCode 起名 DropVault。两边都是深色底 + 渐变光效 + 玻璃拟态,风格取向接近,但细节密度不同。

图 1:同一需求、同一视口下的两个首页。SkyLink 有全局统计条、步骤向导、特性区;DropVault 更聚焦,首屏只有上传区和最近分享列表。

SkyLink 的首屏信息层级更完整:品牌、标语、全局统计(生效分享/流转字节/下载次数)、上传向导、三张特性卡。DropVault 走"一眼看到上传框"的极简路线,首屏就是中心化上传区 + 最近分享列表。

移动端:

图 2:两边移动端都没有横向溢出。SkyLink 的导航压缩成两个胶囊按钮,DropVault 保持单列布局。

2.2 完整链路走一遍

两个项目都按真实用户路径走完了"上传 → 分享 → 提取码 → 下载 → 校验"全链路,下面是实际截到的界面与 API 行为。

**SkyLink(opencode)**的上传配置、上传中、分享结果:

图 3:SkyLink 上传前可以自定义提取码,选有效期(1/7/30 天)和下载次数(不限/1/5/10)。

图 4:上传中状态有实时速率、预计剩余时间、暂停/放弃。注意这个上传卡片外层缺少容器样式(后文会讲),这是本次实测发现的一个真实前端缺陷。

图 5:分享结果卡包含链接和提取码(二维码在下方未截入)。有效期显示"NaN-NaN-NaN"不是截图事故,是真实 bug------64 个 E2E 用例没拦住它。

验证提取码后的下载页:

图 6:验证通过后显示"立即下载",并展示文件大小、剩余次数、到期时间。

**DropVault(AtomCode)**同样走通了:

图 7:DropVault 的分享结果卡,链接 + 6 位提取码 + 最近分享列表。

图 8:提取页只提示"输入提取码",不展示文件名------这是比 SkyLink 更保守的设计取舍。

图 9:验证成功,浏览器开始下载。

两边下载文件的 SHA-256 都与源文件完全一致(我在 download 事件落盘后直接比对了哈希),这说明核心传输链路两边都是真的通,不是"看起来能跑"。

真实 API 交互记录(节选)

界面之外,两套 HTTP 接口拉出来单测了一遍。以下是真实抓取(非示意):

复制代码
# SkyLink:init → 分块直传 → complete → verify → download
POST /api/upload/init            -> 201 {sessionId, chunkSize:4194304, chunkCount:16}
PUT  /api/upload/<sid>/c/0       -> 200 {index:0, received:4194304, receivedCount:1}
POST /api/upload/<sid>/complete  -> 201 {share:{shareId, fileName, fileSize, sha256, code}}
POST /api/shares/<id>/verify     -> 200 {token, expiresIn:1800}
GET  /api/download/<id>?token=.. Range: bytes=-100
                                 -> 206 Content-Range: bytes 924-1023/1024(100 字节)
GET  /api/download/<id>?token=.. Range: bytes=99999-100000
                                 -> 416 Content-Range: bytes */1024

# DropVault:session → chunk → finish → share → verify(ticket) → download
POST /api/upload/sessions        -> 201 {sessionId, chunkSize:8388608, totalChunks:8}
PUT  /api/upload/sessions/<sid>/chunks/0 -> 201 {received:[...], totalChunks:8}
POST /api/upload/sessions/<sid>/finish   -> 200 {fileId, sha256}
POST /api/shares                 -> 201 {shareId, token, extractCode:"8F9DGV", ...}
POST /api/share/<token>/verify   -> 200 {ticket, expiresAt}
GET  /api/download/<token>?ticket=.. Range: bytes=-100
                                 -> 206 Content-Range: bytes 2302056-2302155/2302156(100 字节)
GET  /api/download/<token>?ticket=..(复用票据)-> 401

这两段不是"设计稿",是运行中的服务逐条发出、原样记录的状态码与字段。

2.3 技术栈与架构

维度 SkyLink(opencode) DropVault(AtomCode)
后端 Node 22 + Express 4 + better-sqlite3(WAL) Node 22 + Fastify 5 + better-sqlite3(WAL)+ bcryptjs
前端 Vue 3 <script setup> + Vite + Vue Router,无 UI 框架 Vue 3 + Vite + Vue Router + Pinia(注册了,但没有 store)
上传 4 MB 分块 × 3 并发,raw body 按 offset 流式定点写盘 8 MB 分块 × 3 并发,分块 Buffer 落盘后合并
下载 createReadStream(start,end) + Range + ETag/304 + 流式 createReadStream + Range 206,流式
安全 提取码 SHA-256 存储 + 时序安全比较;分享+IP 限速 15 分钟 8 次;一次性下载 Token 提取码 bcrypt(10) 存储;IP 限速 20 次/分钟;一次性下载票据(5 分钟)
存储抽象 services 内直接操作本地文件系统,路径较集中;README 给出换 S3 的改法 StorageProvider 接口 + LocalStorageProvider,抽象更"教科书"
应用代码 54 文件 / 3,574 行 / 单文件最大 241 行 43 文件 / 1,698 行 / 单文件最大 91 行
前端产物 静态产物 gzip 合计 79.9 KB 静态产物 gzip 合计 52.1 KB

两边都符合提示词里"模块化、拒绝超大文件"的硬约束:opencode 最大源文件 241 行,AtomCode 最大业务源文件 91 行,都没有出现"一个文件塞一个应用"的情况。区别在抽象方式:AtomCode 的分层更像样板间------routes(薄)→ services → dao/storage,接口都摆得很正;opencode 的分层没那么整齐,但每个模块职责边界同样清楚,而且在 README、ARCHITECTURE、API 里把"怎么改、改哪里"写明白了。

2.4 文档

这是两边差距最直观的地方之一:

  • opencode 交付了 README.md、docs/PLAN.md(190 行规划/WBS/测试矩阵/验收标准)、docs/ARCHITECTURE.md(数据流图、关键决策记录)、docs/API.md(接口与错误码)、docs/TEST-REPORT.md(64 用例结果 + 7 个缺陷的根因与修复)。合计 5 份、428 行、约 25 KB。
  • AtomCode 交付了 1 份 README.md(97 行、4.8 KB),包含功能、架构、API 表、环境变量、性能实测与二开方向。信息密度不低,但只有这一份。

对"二次开发可维护"这个要求,架构文档不是装饰品。opencode 的 ARCHITECTURE 里甚至记录了"为什么放弃 npm workspace""为什么 E2E 重置放在服务端启动时"这类决策,这类信息对后来者比代码注释值钱。


三、质量对账:测试全绿 ≠ 产品可用

3.1 测试规模

指标 SkyLink(opencode) DropVault(AtomCode)
E2E 用例 64(9 个 spec,桌面 34 + 移动 30) 15(4 个 spec)
E2E 断言调用(静态计数) 113 处 expect 37 处 expect
E2E 复跑结果 64/64 通过,2.4 分钟 15/15 通过,24.3 秒
测试造数方式 15 条全部走真实 UI 路径上传(含中文名、1 KB--2 MB、不同有效期/次数/提取码),并断言服务端 SHA-256 与源文件一致 16 条通过 API 直接造(1 KB--41 MB,含 8 MB 整块边界),UI 上传用例用 32--100 KB 小文件
冒烟测试 14 条断言(本次复跑全过;文档写的是 13,脚本实际 14 条) 24 条断言(本次复跑全过)
性能断言 有:首屏 ❤️.5s、6 MB UI 上传 <10s、下载 ≥1.5 MB/s、3 路并发 <5s 无
测试配置 固定 Chromium,自动拉起独立服务端(4173),数据目录隔离、启动重置 依赖系统 Edge(channel: 'msedge'),需手工先起 3000 端口服务

先给 AtomCode 说句公道话:15 个用例不是凑数。它覆盖了点击/拖拽上传、错误提取码抖动、粘贴提取码、下载内容 SHA-256 一致、Range 206 拼接、票据一次性、404、桌面/平板/手机三档响应式。24 条冒烟断言也覆盖了乱序分块、断点查询、sha256 校验、小写提取码归一化、403/404/401、票据一次性、Range 内容一致性、下载计数。对 22 分钟量级的产物,这是相当扎实的自测。

但"测试全绿"和"产品可用"之间隔着一条河,河两边各躺着一批证据。

3.2 opencode 的三个"测试看不见"缺陷

打开 SkyLink 页面做真实操作时,第一眼就撞上了三个测试覆盖不到的缺陷。

缺陷一:分享结果页的有效期显示为 NaN。

页面显示"有效期至 NaN-NaN-NaN NaN:NaN(剩余 NaN 天)"(图 5)。根因在服务端:share.service.createFromUpload() 里构造了一个只含 {id,name,size,mime,sha256,path} 的 row 字面量,然后直接 publicOf(row) 返回;而 publicOf 读取的是 row.expires_at、row.created_at、row.downloads 等字段------这些字段要到 INSERT 之后由数据库行才有。接口返回的 share 对象因此缺少 expiresAt、createdAt 等字段,前端 fmtDate(undefined) 就渲染成了 NaN。我用一条 1 KB 文件复现了同样的响应:

json 复制代码
{"ok":true,"share":{"shareId":"5up2djj852","fileName":"probe.bin","fileSize":1024,
 "mime":"application/octet-stream","sha256":"6663...","downloadsLeft":null,
 "codeLength":6,"code":"953482"}}

share 里确实没有 expiresAt。E2E 用例只断言了链接格式和提取码非空(toMatch),没有断言结果卡上的日期文本,于是绿灯通过。

缺陷二:复制按钮是空的。

"我的分享"列表里链接/提取码复制按钮渲染成了两个空白方块(图 10)。原因是 UiCopy.vue 模板里用的是具名插槽 <slot name="text">,而 ShareResult.vue 和 MySharesPage.vue 都把文案写成了默认插槽内容。按钮可点、复制功能正常、测试也通过------因为测试用 data-testid 定位按钮并断言剪贴板,从不看按钮里有没有字。

图 10:SkyLink「我的分享」,三条记录、统计、撤销都在;注意每行右侧两个无文字的空按钮。

缺陷三:上传卡片没有卡片外壳。

UploadCard.vue 根节点写的是 class="up-card card anim-fade-up",但 .card 这个类在全局样式里根本不存在(.card-glass 只定义在 DownloadPage 的 scoped 样式里)。结果是"上传中"状态直接裸在页面背景上,与旁边有卡片的选项面板、分享结果卡视觉不一致。测试不做视觉回归,自然发现不了。

三个缺陷都不影响主链路,也都好修:一个字段映射、一个插槽写法、一个样式类。但它们说明了一件更重要的事:Agent 的测试写得越"顺着代码",越容易在"代码自己没意识到"的地方留盲区。 E2E 断言了行为,却没断言"人看到的文本长什么样"。

另外还有一个接口契约的小毛病:POST /api/upload/:sid/complete 不传 expiresDays/maxDownloads 会返回 400,而不是走默认值(前端总是传参,所以用户无感)。属于"API 鲁棒性"级别的瑕疵。

3.3 AtomCode 的六个"声称 vs 落地"问题

AtomCode 的页面级视觉缺陷反而比 opencode 少,核心链路也干净。但把代码和 README 放在一起读,会发现几处"说的比做的大":

问题一:断点续传名不副实。

README 明确写"支持提取码、断点续传、并行分块上传"。但 useUploader.js 每次上传都 createSession() 新建会话,拿到的 received 永远是空数组;随后虽然会 getSession() 查询一次已收分块,但查的是刚建的新会话。前端没有保存过 sessionId 到 localStorage,也没有暂停/恢复入口。服务端确实提供了"查询会话已收分块"的接口,冒烟测试也验证了这个接口能返回 2/3 块------但完整产品里没有任何路径能走到"续传"这一步。这是典型的"组件能力有、产品路径无"。

问题二:上传链路不是真流式。

Fastify 的 content-type parser 用 { parseAs: 'buffer' } 接收分块,每块 8 MB,3 路并发就是 24 MB 的 Buffer;"合并"时 mergeChunks() 用 readFileSync 逐块读进内存再 writeSync。下载侧确实是 createReadStream 流式,但上传侧"大文件不惧内存"这句话只在 8 MB 分块粒度上成立。opencode 那边是 raw body 直接 req.pipe(createWriteStream(..., {start: idx*chunkSize})),磁盘即进度,没有整块驻留。

问题三:过期文件不清理。

服务端没有任何定时清理逻辑(服务端代码里 grep 不到 setInterval、unlink、过期删除),分享过期/下载超限只在读取时判断。也就是说,分享过期后数据库行还在、data/files/ 下的实体文件也还在,磁盘只会越来越大。opencode 有一个每 5 分钟跑一次的 cleaner,扫过期分享、过期 Token、孤儿会话。

问题四:下载计数语义不清。

download.service.resolve() 在任何下载请求开始时先 shareDao.addDownload(),然后才做 Range 解析。实测:一个限制次数的分享,依次发起"后缀 Range 下载""非法 Range 请求""完整下载"三次请求后,下载次数从 1 变成了 4------其中一次是必然返回 400 的非法 Range。也就是说,用 Range 断点续传会按次消耗下载额度,甚至失败请求也会扣次数。opencode 的处理是"带 Range 的请求不计数",这个语义更适合"限制下载次数"的产品目标。有意思的是,AtomCode 的冒烟测试里恰好把这个行为固化了(完整下载 + Range 下载后断言计数为 2)------测试与实现自洽,但"Range 该不该计数"这个产品语义问题,测试本身不会替你想。

问题五:非法 Range 返回 400,不是 416。

bytes=999999999-1000000000 这种越界 Range,AtomCode 返回 400 BAD_REQUEST;opencode 返回 416 且带 Content-Range: bytes */size。两者都能工作,但后者才是 HTTP 语义下的标准行为,也影响浏览器/下载器的续传判断。

问题六:限流是"全 IP 连坐"。

限流器按 IP 计数 20 次/分钟,不区分分享。实测连续 19 次错误提取码后,第 20 次开始 429;此时换一个完全不同的分享、输入正确提取码,依然 429,且响应没有 Retry-After。防爆破目的达到了,但粒度过粗:一个人试错会把自己所有分享的验证入口堵住。opencode 是"分享 + IP"维度、15 分钟 8 次失败,且 429 会带剩余秒数。

此外还有两条"计划与交付漂移":AtomCode 的规划文本里出现过 TypeScript、Naive UI、tus 风格续传(第一版规划还提到 Vitest 单测),最终交付是纯 JS、无任何 UI 框架、无单测、续传仅服务端半成品;web/package.json 里装了 Pinia,main.js 也 app.use(createPinia()),但全项目没有一个 defineStore------依赖重量换了,状态管理没用上。这些不算错,但说明它对自己规划的执行度没有闭环检查。

3.4 一个正面样本:AtomCode 的纯 JS SHA-256 是对的

公平起见,也要记一笔亮点。AtomCode 在 Web Worker 里放了一份零依赖的纯 JS SHA-256 实现(tinyHash.js,76 行),用来在浏览器端对大文件做流式摘要。我把它单独拿出来,对 0 B、1 B、3 B、55/56/63/64/65 B、127/128/129 B、1 KB、4 KB、1 MB+17 B 共 14 组输入,按不规则 chunk 切分喂给它,与 Node crypto 的结果逐一比对:14/14 全部一致。一个 22 分钟产出的项目里能出现这种手写密码学实现且边界正确,说明模型在"短任务+明确目标"下的代码质量并不低。


四、性能:API 级 64 MB 对拉

把两边服务同时启动,用同一个 Node 脚本跑 64 MB 文件上传(各自原生分块大小、3 路并发)与完整下载,各跑两轮,取两轮区间。两边下载内容都用 SHA-256 校验一致。

指标 SkyLink(opencode) DropVault(AtomCode)
分块 4 MB × 16 块 8 MB × 8 块
上传总耗时(init→finish) 238--249 ms 268--294 ms
上传吞吐(API 级) 257--269 MB/s 218--239 MB/s
服务端完成阶段(校验/合并) 77.5 ms(SHA-256) 108--120 ms(合并 + SHA-256)
下载耗时 132--172 ms 186--243 ms
下载吞吐(API 级) 372--483 MB/s 263--344 MB/s
完整性 SHA-256 一致 SHA-256 一致
  1. 两者都快。64 MB 文件在 loopback 上 0.2--0.3 秒完成上传、0.1--0.2 秒完成下载,瓶颈基本在磁盘与 Node 单线程哈希上,不在协议。opencode 的"定点写盘、无合并步骤"在完成阶段体现为更短的服务端耗时(77 ms vs 114 ms 左右),符合设计预期。
  2. 这两组数字和项目 README 里的宣传数字不可直接比。AtomCode README 写"上传 36 MB/s、下载 337 MB/s",那是走浏览器 UI 的端到端数字,上传侧还包含 Web Worker 里纯 JS SHA-256 的时间;opencode 的 PLAN 写"下载 ≥100 MB/s、上传 ≥60 MB/s",那是设计目标,其 90-performance 用例的实际断言是"6 MB UI 上传 <10s、下载 ≥1.5 MB/s"(本次复跑中,对应上传用例整体耗时 1.3 秒)。
  3. 两场会话并行跑测试、跑浏览器,对性能数字有干扰:同一台机器上两个 SQLite、两套 Node 服务、两个浏览器内核抢 CPU,绝对值偏保守。这组数据只回答"同机同场景下谁更利索",不代表生产吞吐。

五、过程:Agent 的工时花在哪了

5.1 会话数据对照

指标 opencode AtomCode
最终交付会话墙钟 164m19s 38m30s(构建会话)
界面显示的"最后一轮" 2h44m 22m10s
用户输入条数 1(首条提示词) 3(提示词、"执行"、"继续");另有 1 次手动取消、1 次模型切换(17:16:59)
助手轮次 330 120(1 + 54 + 65),另有预研 10 轮
工具调用 425 次(shell 170 / write 97 / edit 102 / read 46 / grep 9 / glob 1) 174 次(write_file 58 / edit_file 17 / bash 35 / bash_start 11 / bash_poll 30 / bash_kill 3 / todowrite 16 / search_replace 3 / list_directory 1)
工具错误 8 次(状态 error) 1 次(npx playwright install chromium 撞 300 秒工具上限)
上下文事件 1 次自动压缩(18:51:17,耗时约 61 秒) 无压缩;换模型后上下文窗口从 512K 降到 256K
Provider 错误 0 1 次(HTTP 403,CodingPlan 失效)
被动纠偏 无 4 次"同一工具调用重复"注入警告;11 次权限询问全部被 auto 模式放行
新增输入 token 588,721 约 376,469(未缓存部分;累计 prompt 7.8M,命中缓存 7.43M)
输出 token 129,074(其中推理 58,625) 约 49,966(三轮合计)
缓存读取 token 30,632,082 7,428,416

opencode 的新增输入 token 是 AtomCode 的 1.6 倍,缓存读取量是 4.1 倍,输出是 2.6 倍。这符合直觉------它跑了 330 轮、阅读了 46 次文件、21 次全量/子集 E2E、写了 97 次文件、改了 102 次文件,每一轮都要重新喂一遍上下文。Agent 的"深度"是要用 token 换的。

5.2 时间线

横轴是墙钟时间,纵轴是累计工具调用次数。橙线在 17:39 前陡峭爬升后停住;蓝线用 2 小时 44 分缓慢铺满 425 次调用。注意 17:16 附近橙色线的"断点"------那是 GLM 渠道 403 造成的。

  • opencode:16:55 开工,第一条回复就用了 4 分 20 秒、输出 12,313 token 的规划;17:00--17:35 高强度写代码;17:40 后进入"写测试---跑测试---修 bug---重跑"的循环;18:51 撞上上下文窗口,自动压缩一次;19:30 前后收尾文档与构建;19:39 结束。全程零人工输入。
  • AtomCode:16:55 第一次提交,16:58 被取消;17:00 重新提交,23 秒给出规划;17:01 用户"执行",随后 14 分 44 秒里 54 轮、100 次工具调用,其中 5 分钟卡在 Playwright 浏览器下载超时上,最后被 403 打断;17:16:59 会话切到 Qwen3.8-27B,17:17 用户"继续",22 分 10 秒、65 轮、74 次工具调用收尾。

opencode 的"工具调用曲线"在中后段明显变缓,这不代表它闲着------那段时间单轮耗时在涨(读测试输出、分析失败原因、改多处代码),单位时间的工具调用数反而下降。AtomCode 的曲线则几乎是一条直线:每一步都很短、很快、很确定,因为它做的事情面更窄、验证更浅。

5.3 两场会话的原始现场

这组对比最有意思的地方在于,它们真的是同屏并行跑出来的。下面几张是在运行过程中留下的屏幕截图:

16:56,AtomCode 已经产出规划并停在"确认后开始开发"的提问上;同一块屏幕的右侧,opencode 的会话标题已经生成,正在思考。

17:01,opencode 先做环境体检(Node 22.20 / npm 10.9 / Git 2.55 / npmmirror / TCP 443 连通性),然后才建目录写规划。左窗 AtomCode 已经把规划写到"技术栈选型"和"模块化架构"。

17:05,AtomCode 在写分享/下载模块;左下角的待办清单显示 8 个阶段。

17:31,AtomCode 在修 E2E 里的路径问题,opencode 在跑全套 E2E;右侧能看到 opencode 内部"修测试脚本 bug 后重跑全量 E2E"的记录。

19:39 之后,AtomCode 的最终状态"Nailed it · 17:39 · 22m10s · 65 轮 · 70 工具 · 289.04K tokens",以及右侧 opencode 的 2h 44m。注意 AtomCode 状态栏仍显示 glm5.3-flash------而 17:16:59 之后它实际用的是 Qwen3.8-27B。

5.4 为什么快?拆开看

AtomCode 的"快"来自四个叠加因素:

  1. 做的少。1698 行应用代码 vs 3574 行;没有自定义提取码/有效期/下载次数选项、没有管理台、没有撤销、没有统计、没有二维码、没有 ETag/304、没有清理任务;E2E 15 个 vs 64 个。
  2. 探索少 。opencode 有 46 次 read、9 次 grep、1 次 glob,它在动手前大量阅读自己刚写的东西、翻测试输出;AtomCode 没有独立的 read/grep 工具,回读主要靠 bash 里的 cat/ls/sed 翻测试产物,绝对次数明显更少(十余次 vs 55 次专用读工具调用),更像"想清楚直接写,写完拿测试输出对一眼"。
  3. 验证浅。AtomCode 的 E2E 把造数放在 API 层(16 个文件),UI 用例只喂 32--100 KB 小文件;opencode 的 15 条种子数据全部走真实 UI,且每条都回查服务端 SHA-256。验证越浅,迭代越快,漏掉的也越多。
  4. 模型换了。最后一轮 22m10s 是 Qwen3.8-27B 跑出来的,与前面 GLM-5.3-Flash 的表现不能混为一谈。从 54 轮到 65 轮的上下文轨迹看,换模型后工具调用节奏明显更"碎",但终究把活干完了。

顺带说一个正面细节:AtomCode 在 E2E 阶段发现 Chromium 下载不通(两次 npx playwright install chromium,其中一次撞了 300 秒上限),于是去查本机 Edge 路径,最后把 Playwright 配置改成 channel: 'msedge' 用系统 Edge 跑通了全部用例。这个"绕路"很聪明,也解释了它的 E2E 只用了 24 秒------但代价是可复现性绑在了"目标机器装了 Edge"上,换台 Linux CI 机器就得改配置。

5.5 一个容易被忽略的可观测性问题

AtomCode 换模型时,注入提示写得很清楚:

The model for this conversation was switched from glm5.3-flash ... to qwen3.8-27b ...

但 TUI 底部的状态栏在 17:31、甚至 20:00 的截图里都还显示 glm5.3-flash。对使用者来说,这是一个危险信号:看着"GLM 跑了 22 分钟交付"的界面,实际是另一个模型在跑。Agent 产品的可观测性不能只做"进度条",模型、渠道、配额、重试、降级都应该实时可见且可追溯。opencode 这边全程模型稳定,反而不涉及模型切换的问题;但它的自动压缩、后台任务这些事件同样更适合事后翻日志,而不是盯着屏幕实时观察。长任务的可观测性,两边都还有空间。


六、把"快"和"好"放回同一个坐标系

写到这里,结论已经不太像"谁赢了",更像两组权衡:

维度 胜者 说明
速度(同口径) AtomCode 38m30s vs 2h44m,约 4.3 倍
功能覆盖 opencode 自定义提取码、有效期/次数、管理台/撤销、统计、二维码、ETag/304、416、清理任务
测试深度 opencode 64 用例 vs 15;UI 真造数 vs API 造数;有性能断言
代码整洁度/体量 AtomCode 1698 行 vs 3574 行,抽象更"教科书",单文件更小
文档完整度 opencode 5 份 25 KB vs 1 份 4.8 KB
前端细节质量 AtomCode(相对) opencode 有 NaN 日期、空按钮、缺样式三个可见缺陷;AtomCode 页面本身更干净
后端语义严谨度 opencode Range 不计次、416、限流粒度、清理任务都更细
可审计性 opencode 文档、测试报告、决策记录齐全;AtomCode 的声称与实现落差更大

一个反直觉但重要的观察:opencode 的缺陷集中在"前端边角",AtomCode 的缺陷集中在"声称与语义"。前者是"做得多、露怯多",后者是"做得少、说得满"。对使用者来说,这两类风险的性质完全不同:前端 NaN 你打开页面就能发现,后端"限流把正确码也锁死""Range 请求偷下载次数""过期文件永不清理"则要出了事才会发现。

如果只让我写一句选型建议:

  • 做演示、做原型、做内部工具、需要 20 分钟看到东西:AtomCode 这类短跑选手更合适。它的核心链路干净、代码短、心智负担低,改起来快。
  • 做要交给别人维护的小产品、要求可审计的交付:opencode 这类长跑选手更合适。它贵在"过程留痕"(规划、架构、API、测试报告)和"覆盖广",但你必须自己补最后一公里的 UI 审查。
  • 两者都不适合直接上生产。两个项目都没有账号体系/管理鉴权(AtomCode 的分享列表接口对任何调用者返回全量记录),都是单机 SQLite + 本地盘,都没有部署与运维方案。这不是 Agent 的问题,是"从 demo 到产品"本来就还有一大段工程。

七、这件事对当下和未来的意义

7.1 生成代码不再是瓶颈,验证才是

这两场会话里,模型"写代码"都只占一小部分时间。opencode 的 425 次工具调用里,真正写文件 199 次,剩下的 226 次是跑命令、读文件、看输出、改测试;它跑了 21 次 Playwright,其中 14 次以非零退出结束,最后才收敛到 64/64。AtomCode 的 174 次工具调用里,写文件 75 次,其余是 bash、轮询、待办更新。

Agent 的产出质量,越来越取决于验证回路的质量:测试跑不跑得起来、失败信息能不能读懂、能不能定位到根因、修完敢不敢回归。opencode 的流程是"测试驱动长跑",AtomCode 的流程是"短跑 + 断点续跑",差异不在模型多聪明,而在"验证"这件事被摆在了流程的什么位置。

7.2 Harness 是新时期的 IDE

同一个模型,在两个不同的 Agent 壳子里,表现可以差出数倍。这次对照里能看到很多"壳"的细节直接决定成败:

  • 工具超时策略 :AtomCode 的 bash 工具 300 秒硬超时,让 npx playwright install chromium 白等了 5 分钟;opencode 用后台 job + 轮询的方式绕过了这个问题。
  • 上下文管理:opencode 在 18:51 自动压缩了一次上下文,代价是 61 秒 + 一段摘要;AtomCode 靠换模型把窗口从 512K 降到 256K,没有压缩但也失去了前半程的模型连续性。
  • 权限模型:AtomCode 的 auto 模式把 11 次权限询问全部自动放行(包括"写敏感目标""在工作区外写文件"),这提升了效率,也意味着沙箱边界形同虚设;opencode 的运行环境默认放行,没有出现权限中断。
  • 可观测性:模型切换、限流重试、后台任务、压缩事件,这些对使用者来说都该是一等公民。AtomCode 的"状态栏显示旧模型"就是一个反面案例。

未来 12--18 个月,Agent 的竞争力大概率会从"模型榜单位置"转向"harness 工程能力":环境快照与回滚、容器化一次性环境、浏览器自动化与截图回归、测试合成、失败归因、成本核算、权限与沙箱、多 Agent 编排。这些能力现在还是各家产品的差异化卖点,之后会变成及格线。

7.3 评测范式也要换

用 SWE-bench 这类"修 bug/补函数"的基准衡量 Agent,已经越来越不够用了。这次实测里,两个产物的"测试全绿"程度都很高,但用户能感知的质量差异、以及两个产品各自的真实缺陷,几乎都不在"测试通过率"里。更接近真实交付的指标应该包括:

  • Time-to-green:从提示词到"用户可完整走通主链路"的时间;
  • Cost-to-green:期间消耗的 token/费用/机器时长;
  • Defect escape rate:交付后被人眼/人工测试发现的缺陷占已实现功能的比例;
  • Claim accuracy:README/文档/总结里声称的能力,有多少经得起逐条核对;
  • 维护性代理指标:文档完整性、单文件复杂度、测试可读性、依赖合理性;
  • 可复现性:别人拿到产物能不能按文档跑起来(这次两个项目都能,算加分项)。

如果给这次两边各打一个"工程化交付分"(满分 10,纯个人主观):opencode 7.5,AtomCode 6.9。opencode 赢在广度、测试、文档和留痕,扣分在三个可见前端缺陷与个别接口契约问题;AtomCode 赢在短小、干净、核心链路可靠,扣分在"声称大于实现"的几处、验证深度不足、以及可观测性缺口。两个分数都不高,因为"人工智能写的项目"离"生产可用"确实还有距离------这恰恰是接下来一两年工程侧要补的课。

7.4 对人的影响

短期看,最直接的变化是"从 0 到 1 的原型成本"被压到了分钟级。一个熟悉业务的后端,原来花一天搭的"上传 + 分享 + 提取码 + 下载"骨架,现在可以 20--40 分钟拿到第一版,2--3 小时拿到带测试和文档的版本。这会先冲击外包原型、内部工具、活动页后端的报价体系,也会让"写代码"在初级岗位中的权重继续下降。

中期看,工程师的价值会进一步向"定义问题、设计约束、做验收"迁移。这次提示词里"每个模块的每个功能点都要测试"这句话,两个 Agent 都"听懂了",但一个用 64 个用例 + UI 真造数去落实,另一个用 15 个用例 + API 造数去交差。Agent 不会替你决定"什么叫测到位",这个判断仍然是人给的。 未来团队里最稀缺的,可能不是会写代码的人,而是能把验收标准写成"可执行、可审计、可回归"的人。

长期看,如果 Agent 能稳定产出"带测试、带文档、可复现"的完整切片,软件交付的组织形态会变:一个人 + 一组 Agent 可能就是一个"全栈小队",代码评审的重点从"语法和风格"转向"边界条件、失败路径、安全与可观测性",CI 里会多出"Agent 证据包"(测试录像、截图、会话日志、成本账单)这一环。这一天不会突然到来,但这次 4 小时的对照,已经能看出它的雏形。

7.5 别忽略的风险

  • "全绿"幻觉:64 个用例能全绿,但一个 NaN 就能让用户第一眼失去信任。测试是必要的,不是充分的。
  • 限流与安全的反噬:AtomCode 的全 IP 连坐限流、开放分享列表、无鉴权的管理接口,都是演示阶段无所谓、生产阶段要命的点。
  • 配额与渠道风险:一次 CodingPlan 403 就中断了 14 分 44 秒的工作并迫使换模型。企业用 Agent,必须把"模型渠道可用性/配额/降级策略"当基础设施来设计。
  • 过度信任总结:Agent 的最终总结往往写得很漂亮,但"声称"和"实现"之间需要人逐条核对。这次如果不是我把两个服务真的跑起来,opencode 的 NaN 和 AtomCode 的续传问题都不会暴露。
  • 样本局限:一次任务、一个模型、一台机器、一个中文提示词,两场并行会话还会互相抢资源。这不是榜单,只是一份可复现的田野记录。

八、复现与数据来源

项目位置

  • opencode 产物:opencode项目/skylink/(客户端 client/、服务端 server/、E2E e2e/、文档 docs/)
  • AtomCode 产物:atomcode项目/(server/、web/、e2e/、README.md)

启动方式(两边都支持生产模式单进程托管前端)

powershell 复制代码
# SkyLink(默认 8787,前端已构建在 client/dist)
cd opencode项目\skylink\server
$env:DATA_DIR="$env:TEMP\blog-run\skylink-data"; $env:PORT="8787"; node src/index.js

# DropVault(默认 3000;注意 DB_FILE 与 DATA_DIR 需同时指定,否则数据库仍会落到项目 data/ 目录)
cd atomcode项目\server
$env:DATA_DIR="$env:TEMP\blog-run\dropvault-data"
$env:DB_FILE="$env:TEMP\blog-run\dropvault-data\dropvault.db"
node src/server.js

测试复跑

powershell 复制代码
# SkyLink:E2E(会自起 4173 端口服务、隔离数据目录、启动重置数据库)64/64
cd opencode项目\skylink\e2e; npx playwright test

# SkyLink:冒烟(需先起服务;默认 SMOKE_BASE=http://127.0.0.1:8899)
$env:SMOKE_BASE="http://127.0.0.1:8787"; node ..\server\test\smoke.test.js

# DropVault:需先手工起 3000 端口服务,E2E 使用系统 Edge 内核,15/15
cd atomcode项目\e2e; npx playwright test
# DropVault:冒烟 24 条断言
node ..\server\test\smoke.mjs

数据出处(供核对)

  • opencode 会话:%USERPROFILE%\.local\share\opencode\opencode.db(session ses_eb6ed9478ffeoIZqEM8fQegYlP,330 条 assistant 消息、425 次工具调用、1 次 compaction)
  • AtomCode 会话:%USERPROFILE%\.atomcode\sessions\b4735426efe64935\afef8432-*.events|index|todos(3 个 turn、120 轮、174 次工具调用;另有 90514147-*.events 记录第一次被取消的尝试)
  • 界面截图:本文 assets/ 目录下的运行截图与对照图(2026-10-17 20:26--20:36 复跑生成),以及 5 张过程会话截图(来自当天 16:56--20:00 的原始屏幕截图)。两份 E2E 报告的截图如下:

附:左边是 SkyLink 的 64/64,右边是 DropVault 的 15/15,均来自 2026-10-17 20:33 的独立复跑,不是 Agent 自己贴出来的截图。

  • 性能与行为测试:64 MB 文件 API 级吞吐(各 2 轮)、Range/416/304、限流触发、下载计数、纯 JS SHA-256 对拍(14 组输入)

附:给准备用 Agent 交付项目的团队的一份验收清单

基于这次踩到的坑,把"接收 Agent 产物"时要检查的东西整理成了一张表,按重要性排序:

  1. 自己跑一遍主链路,不要只看测试报告。用真实文件、真实浏览器、真实提取码,完整走"上传→分享→换浏览器→提取码→下载→哈希校验"。
  2. 核对"声称":README 里每条特性,去代码里找实现路径。重点查"断点续传、限流、计数、清理、鉴权"这类容易只做半截的。
  3. 看测试怎么造数:UI 造数 > API 造数 > 手写 fixture。造数路径越接近真实用户,测试越可信。
  4. 检查断言质量:有没有断言"用户看到的文本/状态",还是只断言了 HTTP 200 和 DOM 存在。
  5. 过一遍失败路径:错误提取码、过期链接、撤销、超次数、非法 Range、超大文件、并发下载。
  6. 看单文件规模与依赖:单文件是否失控、依赖是否真的在用(Pinia 装了但没用就是信号)。
  7. 确认可复现:按 README 从零装依赖、构建、启动,能不能跑起来;数据目录有没有污染项目目录。
  8. 留证据:截图、测试录像、会话日志、成本清单。Agent 交付的"过程证据"和"最终代码"同样重要。

做完这八条,你大概就能判断手里的东西是"能演示"还是"能交付"。这次两个 Agent 的产物,前者都合格,后者都还差一截------而差的这一截,恰好是未来一两年 Agent 工程化最值得投入的地方。

相关推荐
Eloudy1 小时前
ADR-042: Gas Town 与 Beads 分析 —— 对 Claude Flow V3 的启示
agent
墨心@2 小时前
AgentSessionRuntime 分析
agent·pi·coding agent·harness
墨心@2 小时前
Pi Services 层
agent·pi·coding agent·harness
hpoenixf13 小时前
从 Trace 到可信观测:我们的 Agent 可观测体系怎样一步步建立起来
人工智能·agent
hpoenixf13 小时前
从能读取到能研究:我们如何把 agent的 PDF tool升级为可追溯的多模态能力
agent
goehou13 小时前
Agent 接飞书实战:回调验签、事件去重与异步回复(附踩坑)
实战·飞书·agent·mcp·im集成
Rosanci15 小时前
多个 AI 会话同时改一个项目,怎样避免任务互相踩?
人工智能·agent·ai 编程
七夜zippoe17 小时前
Agent 接入企业微信/钉钉:渠道对接实
ai·钉钉·企业微信·agent·渠道对接
aichitang202417 小时前
QCU 2.0.0:补齐 Windows、Linux 与 GNOME Wayland
linux·运维·windows·ai·agent·ai编程