教你跑通一个全自动 Coding Agent 的 MVP

——大模型规划,小模型干活

我最近想验证一个反直觉的猜想:

在多智能体(agent)编排中,非思考型小模型有时候比思考型大模型更省钱。

为了验证这件事,我搭了一个最小可运行原型(MVP),项目名叫 hero-coding。我用同一个测试框架(harness)跑了三组模型:ChatGPT 5.4、Ling-2.6-flash、Ling-2.5-1T。

跑出来的数据让我推翻了自己一开始的判断——并不是单纯“小模型赢”,而是“用对地方才赢”。

下面我把整个过程拆开讲,尽量让你读完就能明白这个模式怎么跑、怎么配,以及哪些环节最值得投入精力。


一、什么是全自动 coding agent

一句话解释:
你把一个用户需求(user story)丢进 inbox/ 目录,半小时后回来看 git log,就能检查任务是否完成。

项目结构图

整体流程

inbox/us-001.md         ← 用户需求(markdown + frontmatter)
       │
       ▼
   Dispatcher  ──── 监听 inbox/,启动子进程
       │
       ▼
    Worker     ──── pi-coding-agent --mode json
       │           原子化执行,每改一个东西就 git commit
       ▼
    Judge      ──── 读 git log + 全部 diff,给出结构化判定
       │
   ┌───┴───┐
 PASS    FAIL
   │       │
   ▼       ▼
 done/   把失败原因追加到 story,再起一轮 worker

三个核心组件全是 stateless 一次性进程

  • Worker 一次只活一遍,跑完退出;
  • Judge 一次只活一遍,给完结论退出;
  • 所有状态靠 git + 文件系统持久化。

这种设计正好对应“长任务由短任务串成”的工作流形态。
Worker 这一块我直接用了现成的 pi-coding-agent(Mario 在他的 README 里也提到:Pi 故意不做 sub-agent 和 plan mode,留给你自己扩展)。
我做的扩展就是用约 400 行 TypeScript 把它包成一个有 inbox 的自动小助手。

扩展示意图

这个比例说明了一个重点:
搭一个能真正跑起来的 coding agent 工厂,不需要重写一个 coding agent,把现成的 harness 拼好就行。


二、什么是 user story

一个最小可执行的任务单位。我用 markdown + frontmatter 来写:

---
id: us-001
title: Add timezone parameter to formatDate
priority: normal
max_retries: 3
---

## Goal
给 formatDate 加可选 timezone 参数,默认 UTC。

## Acceptance Criteria
- [ ] 函数签名加 timezone?: string
- [ ] 不传时和现在 byte-identical
- [ ] 传 Asia/Tokyo 时按该 tz 格式化
- [ ] 在 tests/utils.test.ts 加 3 个测试
- [ ] npm test 全绿

## Out of Scope
- 不改其他函数
- 不动 locale 设置

写好后丢进 inbox/,Dispatcher 自动接管。

user story 模板

你会发现,Out of Scope 比 Goal 更值钱——非思考小模型容易自由发挥,这一节是给它的“紧箍咒”。输入越清楚,结果越好。


三、跑了什么、用什么跑的

我准备了一个故意带 bug 的 mini TypeScript 项目,包含三个 utility 函数:

  • formatDate(date) —— 不支持 timezone(待加)
  • parseRange(1-5) —— 应该返回 [1,2,3,4,5],但有 off-by-one 返回 [1,2,3,4]
  • formatNumber(-1234) —— 应该返回 -1,234,但代码 bug 返回 --1,234

三个 user story 对应三种典型工作量:

三个任务对比

三组模型对照:

  • ChatGPT 5.4(思考模型)—— 通过本地反代走真实账号
  • Ling-2.6-flash(非思考小模型)—— 蚂蚁百灵 OpenAI 兼容 API
  • Ling-2.5-1T(非思考大模型)—— 同上

Worker 和 Judge 通过 ~/.pi/agent/models.json 配置不同 provider,全部走 OpenAI 兼容协议,改 model 字段就能切换。

模型配置示意图

四、跑出来的数据

我把原始统计整理成表格:

原始数据表

从这些数据可以得出几个明确的结论。

1. 明确 bug 修复:Ling-flash 比 ChatGPT 快 31%

us-002 同一个任务:

  • Ling-flash 用时 90 秒
  • ChatGPT 用时 131 秒

Ling-flash 调了 52 次 tool(比 ChatGPT 的 14 次多得多),但每次极快。这种高频小任务场景就是 Ling-flash 的舒适区:编辑器补全、快速改写、报错修复。

Ling-flash 优势图

2. 加功能任务:Ling-1T 用 ChatGPT 的 11% token、63% 时间

us-001 同一个任务:

  • Ling-1T 1 轮 130 秒,13K token 就过了
  • ChatGPT 用了 2 轮 205 秒,120K token

差距接近 10 倍 token 量级。
原因在于 ChatGPT 5.4 思考更多,每次响应都隐式 reasoning,prompt 也被反复带入 context。Ling-1T 的思考强度更克制,token 主要花在理解和输出上。

3. 加输入校验:Ling-1T 快 33%,token 少 40%

us-003 同一个任务(给 parseRange 加边界校验 + 友好错误信息 + 测试):

  • Ling-1T 1 轮 58 秒,7 tools,5K token
  • ChatGPT 1 轮 86 秒,8 tools,13K token

两个都通过,但 Ling-1T 用时和 token 都更少。三组对照里 Ling 全部胜出,胜幅 30–36%。这是同一个 harness、同一个 user story、同一个 Judge 跑出来的数据,仓库里的 runs/*.json 全部公开。


五、总结

我一开始全部用 Ling-2.6-flash 当 worker,结果 us-001 死循环了:

worker → bash: echo All criteria met.
worker → bash: echo All criteria met.
worker → bash: echo All criteria met.
...(直到 80 tool 上限触发)
worker 日志记录

后来我调整策略:

  • Ling-2.6-1T 负责理解、规划、拆解
  • Flash 负责快速执行、快速补全和快速修补

非思考小模型一点规划能力也没有,只能是 Ling-2.6-1T 来做设计任务。把 us-001 的 worker 换成 Ling-1T 之后,1 轮就过了。

实测结论:

  1. Ling-2.6-flash 在高频小任务上确实更快(us-002 比 ChatGPT 快 31%)
  2. Ling-2.5-1T 做规划和理解类任务能省到夸张的 token(us-001 比 ChatGPT 省 89%)
  3. 小模型跑 agent 必须配 harness,否则容易死循环或漏 commit
  4. Ling 1T 作为大脑 + flash 作为手的分工是有效的

如果你已经有真实 harness,换成 Ling-2.6 系列并按上述分工使用,token 会下降一个数量级,速度反而提升。

仓库在评论区,自取。跑起来你会看到 Worker 实时滚动每个工具调用——这一段我特别推荐你亲自跑一次,比读这篇文章更有价值。


六、harness 工程的简单且有效的实践

这次开发 Coding Agent 一共翻车五次,全是非思考小模型的典型坑。虽然看似简单的一个小小编程任务,但只有真跑过才知道需要怎么样的 harness 去做兜底,才能让小模型顺利完成任务。

harness 实践图 1
harness 实践图 2

Claude 写的护栏代码加起来不到 50 行。

循环检测(worker.ts,约 10 行)

const recent: string[] = [];
if (sig) {
  recent.push(sig);
  if (recent.length > 6) recent.shift();
  if (recent.filter(s => s === sig).length >= 4) {
    child.kill(SIGKILL);  // 同一 sig 在 6 次窗口内出现 4 次 = 循环
  }
}

Auto-rescue commit(dispatcher.ts,约 10 行)

async function autoRescueCommit(repo: string, round: number) {
  const status = await git([status, --porcelain], repo);
  if (!status.trim()) return false;
  await git([add, -A], repo);
  await git([commit, -m, `chore(rescue): ${round}`], repo);
  console.log(   ↳ auto-rescue: committed pending changes left by worker);
  return true;
}

特别值得说的是:Ling-1T 在 us-003 上把代码全改对了,但忘了 commit。如果没有 dispatcher 这一层兜底,Judge 看 git log 会说没 commit 直接 FAIL,整个 round 白跑。dispatcher 自动 commit 之后 Judge 立刻 PASS。


七、写在最后

这篇文章一直躺在想法库里,趁 Ling 模型发布,就顺便验证了自己的猜想。

  1. Ling-2.6-flash 在它擅长的高频小任务上确实更快(us-002 比 ChatGPT 快 31%)。如果你在搭 IM 助手、编辑器补全、批量改写、code review 标注这类高频低延迟工作流,flash 是好选择。
  2. Ling-2.5-1T 做规划和理解类任务能省到夸张的 token(us-001 比 ChatGPT 省 89%)。1M 上下文的优势在 multi-agent 场景里特别突出——可以把整个仓库 + 所有 git history 一次喂进去,而且很便宜。
  3. 小模型跑 agent 必须配 harness。这不是 Ling 的问题,是非思考模型本身的特性。我用约 400 行就把 harness 写出来了,放进去跑就很安逸。
  4. Ling 1T 作为大脑 + flash 作为手的分工是有效的。

如果你让我给一个最简单的建议:
在你已经有真实 harness 的场景下,换成 Ling-2.6 系列,按上面说的分工使用——你的 token 会下降一个数量级,速度反而上去。