1. 副驾和全职司机,差的不只是“自动补全”
先说个结论:把写代码“全外包”给AI,不等于你往对话框里丢一句需求然后等结果。真正靠谱的做法,是让一个终端型编码Agent负责动手,再让一个编排层负责盯着它反复返工。我最近一直在用的这套组合,就是很多人讨论的 OpenCode + Sisyphus。OpenCode 是那个“能碰你仓库”的Agent终端,Sisyphus 是那个“不把石头推到山顶不罢休”的编排循环器。配合起来,我才第一次觉得:写代码这件事,是真的可以外包出去了。
很多朋友是从 Cursor 这类AI编辑器入门的,我自己也重度用过。副驾型工具的优势非常明显:即时、低风险、侵入感小。你选中一段代码,它给你补全、改bug、解释报错,你看着 diff 决定留还是丢。这种模式在“你已经知道要改什么”的时候效率极高,但它永远缺一个东西:闭环。它不会主动去跑测试,不会自己把失败结果读回来分析,更不会在你走开之后继续把任务往前推。说白了,它帮你处理的是“我怎么改这一段”,而不是“这个任务怎么从0到1完成”。
全职司机就不一样。它需要同时具备两样东西:一是能在仓库里自由读文件、改文件、跑命令的执行能力;二是一个能把任务拆解、验收、返工、再验收的流程大脑。OpenCode 可以作为前者的载体,而 Sisyphus 这类编排层负责后者。你不需要盯着每一步,只需要在启动前把“目的地”和“验收标准”写清楚。这个变化看上去不大,但实际用下来,人的角色直接从“写代码的人”变成了“定规则和验收的人”。
这篇文章我就围绕这套组合,讲讲我为什么觉得它才是全职司机、我实际是怎么接入的、用了哪些配置和参数,以及踩过哪些坑。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 这套组合的架构:Agent动手,编排层动脑
2.1 终端Agent不是聊天机器人,是“能碰到仓库的手”
OpenCode 这类终端Agent,和IDE内嵌的聊天机器人有本质区别。它启动之后不是悬浮在你的编辑器侧边栏,而是直接站在你的项目目录里。它能列出文件、读取指定文件、全局搜索关键词、修改代码,甚至执行 shell 命令。这意味着它不只是“给你一段建议代码”,而是真的会把改动写到磁盘上。
我习惯把它理解成一个行动力很强、但对大局没有概念的实习生。你告诉它“把订单接口改成支持分页”,它确实会去改,但它可能只改了路由,忘了测试,或者顺手把某个无关文件格式化了。它不是故意乱来,而是模型本身的特性:它会专注于当前步骤,不太能一直记着整个任务的目标和约束。所以这时候就需要第二个角色兜底。
工具能力这块,我一般会关注几个核心动作:文件读取、文件编辑、目录搜索、文本搜索、命令执行。有的终端Agent还支持调用 LSP、git 操作、代码搜索索引,能力更强。但工具不是越多越好,权限越大,风险也越大。我建议默认只开最必要的那几个,后面我会详细说。
2.2 编排层的“推石头上山”闭环
Sisyphus 这个名字很有意思。它来自那个反复推石头上山、石头滚下来再推的希腊神话人物,用来做编码任务编排特别贴切。它的核心工作不是自己写代码,而是盯着 Agent 干活,像极了项目中那个“不合格就不让合入”的测试同学。
一个完整的闭环是这样的:
- 读取任务契约,拆成一个可执行的任务清单。
- 启动 Agent,让它按清单动手。
- 运行验收命令,比如 pytest。
- 如果失败,把失败输出和上下文一起回灌给 Agent,请它自己判断是哪里出了问题。
- Agent 修复后再跑验收,循环直到通过,或者达到最大轮数。
这个过程中最关键的一步,是把失败信息“回灌”给 Agent,而不是简单地让它重试。很多人的 AI 编码体验差,就是因为失败之后只会换个说法再来一次。Sisyphus 这类编排层会明确告诉 Agent:这是测试输出、这是错误堆栈、这是和上次运行之间的 diff,请基于这些信息判断问题在哪。这种带上下文的循环,才是 Agent 能稳步进步的原因。
2.3 为什么一定要两段式,而不是一个Agent干到底
有人会问,为什么要拆成 OpenCode 加 Sisyphus 两层?一个全能Agent直接干到底不行吗?
我在实际测试中发现,把“执行”和“编排”混在一个角色里,任务稍微复杂就会出现两个问题。第一,上下文会被过程信息挤爆。Agent 既要记着任务目标,又要记着当前改了哪些文件、测试输出是什么,几轮下来很容易忘掉最开始的需求。第二,返工决策不客观。让一个Agent既当运动员又当裁判,它很容易在测试失败时给自己找借口,或者“修好”一个表面问题,却忽略了更深层的破坏。
两段式最大的好处,是让编排层保持冷静。编排层不写代码,不做主观判断,它只做三件事:对照验收条件、收集失败信息、决定是否继续返工。这样 Agent 的每一次尝试都会留下日志,你也能清楚看到它到底在哪里绕圈子。对人来说,这也意味着你可以随时介入,而不是被一个黑盒牵着走。
3. 实操:把一个小需求“外包”给AI司机
3.1 第一次接入:先把环境收拾干净
我开始尝试这套组合时,犯过一个很蠢的错误:直接在主力分支上让 Agent 改代码,结果它改了不该改的文件,差点把没提交的本地改动也搞乱。所以第一步我必须建议你,先给 Agent 划一个干净的“工位”。
实操上我会这么做:
bash复制git checkout -b feat/ai-orders-pagination
git status # 确认工作区是干净的
pip install -e . # 或者 npm install,确保依赖齐全
pytest tests/ -q # 先跑一遍人工基线,确认当前是绿的
这个“先跑一遍人工基线”特别重要。如果项目在你交给 AI 之前测试就是红的,那后续所有失败都说不清是 Agent 的锅还是项目本身就有问题。Agent 会很无辜地把本来失败的测试“修好”,但你完全不知道它动了什么。
另外,不建议直接在容器外裸跑高风险命令。项目如果没有隔离环境,先把本地环境跑通。我习惯用一个独立的 feature 分支,这样就算 Agent 把仓库折腾乱了,也能随时 git checkout -- . 撤回。
3.2 写任务契约,比写需求文档更重要
很多人以为给 AI 下任务就是把需求说清楚,其实不是。AI 真正需要看的是一份“任务契约”:目标、验收条件、约束、禁止触碰的路径。这套东西写清楚了,比模型选多强都重要。
我常用的任务契约大致长这样:
markdown复制## 目标
给 GET /orders 接口增加分页参数 page 和 page_size,
返回结构里包含 items、total、page、page_size。
## 验收条件
- page_size 默认 20,范围 1-100。
- page 小于 1 时返回 422。
- 原有接口路径和返回字段结构不能变。
- 必须新增或修改分页相关单元测试,pytest 全绿。
- git diff --check 不报空白错误。
## 约束
- 只允许修改:
- app/routes/orders.py
- app/models/order.py
- tests/test_orders.py
- 不允许触碰数据库迁移文件。
- 不允许引入新的第三方依赖。
- 不要顺手格式化无关文件。
这里面最容易被忽略的是“验收条件”。如果你只说“性能好一点”“代码优雅一点”,AI 是没办法围绕这种模糊目标工作的。验收条件必须能被命令或 diff 检查。比如“分页在 1000 条数据下响应小于 200ms”就比“性能更好”好得多。
还有一点,约束要用白名单而不是黑名单。告诉它“只能改这几个文件”比“别乱改”有效太多。因为模型对“别乱改”的理解很有限,但对“只允许修改这三个文件”的遵守程度高很多。
3.3 编排配置里,我最看重的4个参数
Sisyphus 这类编排层真正需要你关心的参数并不多,我用表格整理一下:
| 参数 | 作用 | 我的默认值 | 备注 |
|---|---|---|---|
| max_rounds | 最大返工轮数 | 8 | 小需求 5-6 轮够,重构可以给 10 轮 |
| test_command | 验收命令 | pytest tests/ -x |
越具体越好 |
| allowed_tools | Agent 允许使用的工具 | read、edit、grep、run | 默认不开放任意安装依赖 |
| block_paths | 禁止触碰的路径 | .env、dist、node_modules、迁移文件 |
和任务契约里双写 |
配置样例我用 YAML 写出来:
yaml复制mission: "给订单接口加分页,并补齐单元测试"
repo: "/workspace/sim-project"
max_rounds: 8
test_command:
- "pytest tests/test_orders.py -x"
- "git diff --check"
allowed_tools:
- read
- edit
- grep
- run
block_paths:
- ".env"
- "dist"
- "node_modules"
- "migrations"
这里我特别说明一下 max_rounds。它不是越大越好,反而是越小越安全。给 3 轮意味着 Agent 必须在有限次数内找到方向,如果它一直绕圈,你会早点看到。给 20 轮的话,它可能把同一个错误用不同说法解释来解释去,最后 token 费用爆炸,问题还没解决。我一般先给 8 轮,如果任务明显复杂再加。
allowed_tools 也是一个容易被忽视的点。终端 Agent 如果能执行 pip install,它很可能为了解决一个小问题引入一个不必要的新依赖,或者在报错时选择“升级大版本”这种暴力方案。我默认只给它读文件、改文件、搜索和执行测试命令这四样,限制能大幅减少意外。
3.4 跑一单“外卖”:给订单接口加分页
理论讲了半天,实际跑一次才有感觉。我用一个模拟的小项目来演示,项目里有一个订单接口,目前一次返回全部订单。我按上面的任务契约交给组合去处理,完整过程大概是这样:
text复制[sched] 读取任务契约,拆成 4 个步骤
[oagent] 打开 app/routes/orders.py,确认当前返回结构
[oagent] 在路由参数里增加 page、page_size,加入 total 计算
[oagent] 新增 tests/test_orders.py::test_pagination
[sched] 运行 pytest,1 failed,2 passed
[sched] 把失败测试的输出回灌给 oagent
[oagent] 分析后发现旧测试没有传 page_size,导致断言拿到空列表
[oagent] 修改旧测试,重新运行
[sched] pytest 全绿,git diff --check 通过
这个轨迹看起来顺利,但你仔细看会发现一个危险信号:Agent 在测试失败后,选择修改了旧测试。这可能是对的,因为旧测试本身没有适配分页参数;但也可能是在“迎合实现”,把本该暴露的 bug 当成测试过期。所以我在跑完之后一定会做一次人工 diff review,重点看它改了哪些测试、为什么改。
我建议你无论过程多顺利,都不要跳过这一步:git diff。你可以不看模型是怎么想的,但必须看它最终改了哪些文件、每处改动是否合理。
3.5 中途人工介入的三个信号
依赖这套组合不是彻底放手,尤其是刚开始用的时候。我总结了三个信号,只要出现其中一个,我基本会中断任务、手动介入。
第一个信号,是 Agent 修改了任务契约之外的路径。比如我明确说了只允许改三个文件,但它把配置文件也动了。这种情况没有讨论余地,直接回滚,然后在约束里补充一句“你已经违反了路径约束,请只修改白名单内的文件”。
第二个信号,是同一个错误连续出现三到四轮。这说明 Agent 已经陷入局部最优解,它可能一直在同一个错误堆栈里绕。这时候继续加轮数没有意义,正确做法是中断,把失败信息里最关键的线索提炼成一句提示,塞进任务上下文里,再重新跑。
第三个信号,是测试越改越“温柔”。比如原本会失败的关键边界测试,被 Agent 改成一个只检查快乐路径的测试。这种情况经常发生在 model 认为“测试太严格”的时候。你要么在任务契约里写明“不允许删减断言”,要么在 review 时直接还原它的测试改动。
4. 常见问题与排查技巧实录
4.1 Agent 乱改无关文件的三种表现
我踩得最多的一类坑,是 Agent 改完目标文件后,顺手把整个文件格式化了一遍,或者把 import 语句重新排列,甚至把其他模块的注释删掉。它不一定是有意的,但结果就是你的 diff 里混进了一堆和需求无关的噪音。
排查时先看这个命令:
bash复制git diff --stat
如果发现改动文件数量超出预期,就用 git diff 查看具体内容。对于那种“只是格式变化”的代码块,我会直接 git checkout -- <file> 还原,再重新生成任务。如果你不想每次都手动处理,可以在任务契约里加上一条“不要格式化无关代码,不要重排 import,不要删除注释”,实测会减少很多噪音。
另外,把 format 工具从 allowed_tools 里去掉也是一个有效手段。没有工具,它就不能主动跑格式化命令,只能在修改代码时顺带调整格式,影响范围会小很多。
4.2 测试挂了我到底要不要管
这个问题几乎每次跑任务都会遇到。我的处理原则很简单:先看测试的改动,再看实现。
如果测试改动是合理的,比如接口新增了分页参数,旧测试没传这个参数,那我会接受测试修改。如果测试被删了关键断言,或者把“预期失败”改成了“预期成功”,这种情况我基本会还原测试,然后要求 Agent 重新解释实现为什么能通过。
我也遇到过一种 flaky 测试,就是同一个任务跑两次,一次绿一次红。这种问题不要怪 Agent,应该先把测试本身的稳定性修好。你可以在测试里避免依赖真实时间、随机数、外部服务。否则 Agent 会陷入“明明没改什么,但测试突然红了”的困惑,返工轮数也会白白增加。
4.3 上下文太长被截断怎么办
终端 Agent 最怕的是让它读整个仓库。小项目还好,稍微大一点的项目,模型上下文很容易被文件内容占满,导致后面的任务目标反而被遗忘。我见过 Agent 在前几轮还挺正常,后面突然开始改无关文件,就是因为最初的约束被挤出了上下文窗口。
解决办法不是换更大的模型,而是拆任务。我会在 Sisyphus 的编排逻辑里强制做一件事:每完成一轮小结,只把“当前进度摘要”传给下一轮,而不是把整个历史都保留。同时,在任务契约里指定 Agent 先读哪些入口文件,避免它自己从根目录开始扫描。
我常用的方式是:
bash复制grep -n "GET /orders" app/routes/orders.py
用搜索定位,而不是用 cat 去读整个项目。搜索能快速找到关键代码,上下文占用也小得多。
4.4 成本控制:别让返工变成烧钱机器
AI 编码不是免费的,尤其是带自动返工的编排工具,token 消耗会比普通对话快很多。使用这一套组合之后,我每个月账单明显涨了,但和人写代码的时间成本比起来,还是划算的。问题只在于,你要对“失控”设置预算。
我一般会在配置里加一个 token 预算,比如单次任务最多 50 万 token。超过之后编排层不再继续返工,而是把当前状态整理成报告交给我。实际跑下来,我建议先用便宜且速度快的模型跑第一轮,让 Agent 把整体框架和测试写出来,再用更强的模型做最后一轮修正和 review。这样既省钱,又能保证最终质量。
还有一个小技巧:不要同时并行跑太多任务。我之前试过一次性丢五个小需求进去,结果几个 Agent 同时改同一个文件,直接产生冲突。更稳妥的做法是一次跑一个,或者至少按模块隔离,确保任务之间没有文件交集。
5. 我现在是怎么安排“人机分工”的
5.1 哪些任务适合全外包
用了一段时间之后,我总结出适合外包的任务画像:边界清楚、有可运行验收命令、改动路径可控。典型例子是给接口加参数、写单元测试、做局部重构、迁移配置文件、修复已知报错。
这些任务普遍有一个特点:结果好不好,可以用命令判断。测试过了、diff 符合预期,基本就能合入。即使有问题,也容易回滚。所以我把这类任务交给组合去反复循环,自己在旁边做 review 就好。
我现在的一个习惯是,把“能不能定义验收命令”作为是否外包的第一判断。如果我不能在任务契约里写出一条明确的通过标准,那我基本不会让它跑。因为 AI 擅长的是在明确标准下迭代,而不是替我做产品决策。
5.2 哪些任务必须自己写
反过来,有些任务我绝对不外包。第一是系统级架构设计,比如服务拆分、数据库选型、消息队列方案。这类决策影响面太大,AI 可以用作讨论对手,但不能直接落地。第二是权限模型、支付、合规相关代码,这些领域一旦出错,代价远高于省下的时间。第三是还没有任何测试覆盖的老模块,我的人工策略是先补基线测试,再考虑用 Agent 做优化。
还有一个必须人写的场景:当你自己都不知道想要什么的时候。如果你只能说出“我觉得这里不对劲”,但说不清具体哪里不对劲,那 AI 给出的方案再漂亮,你也没有能力验收。这个问题不是工具能解决的。
5.3 一些真实体会:人从写代码变成验代码
这套组合对我最大的影响,不是代码产出变快了,而是我的日常动作变了。以前我花大量时间在“怎么写”上,现在大部分时间花在“怎么写清楚规则”和“怎么检查结果”上。看起来我只是换了个流程,但工作性质完全不同。
最后分享一个小习惯。我每次跑完一单,会把这次任务契约、Agent 犯过的典型错误、最终 diff 里的关键改动,整理成几条简短记录,追加到项目根目录的 ai_lessons.md 里。下次再跑类似任务,我就在任务契约里加一句“参考 ai_lessons.md 第X条”。这个文件积累越多,Agent 绕圈子的次数就越少,返工轮数也会肉眼可见地往下降。这个方法不复杂,但对“把写代码外包给 AI”这件事的长期体验影响非常大。
