Agent coding 的核心不是 prompt,而是反馈回路

5 小时前
/ , , , ,
1
摘要
从一次紧急需求和家庭中断开始,记录一个 senior engineer 如何从手工 coding 转向 agent coding:先把任务交给 agent 做,再用验收、工具、记忆和调试流程把它改成一个能持续工作的系统。

Agent coding 的核心不是 prompt,而是反馈回路

cover

cover

有一次我接到一个很急的需求。

那天我本来已经准备加班了。结果一边工作,一边娃也有事。我没法一直坐在电脑前面盯着代码写。

我当时也没有什么完整方法。就是把我能想到的背景、需求、限制、代码位置,全都丢给 AI。然后我去带娃。

上下文交接:人离开键盘,系统继续保留任务现场

上下文交接:人离开键盘,系统继续保留任务现场

回来以后,我的第一反应是:这玩意儿居然能跑。

第二反应是:写得真糙,很多细节都没处理。

第三反应比较复杂。剩下的工作其实不多,但这些工作恰好是最重要的。很多细节、边界、异常状态,平时我自己写的时候也不一定一开始就想全,通常是做到后面才补。它这次反而提前想了一些。

这件事以后,我对 AI 写代码的看法变了。

不是因为它能写代码。能写代码这件事已经不新鲜了。真正让我在意的是:它把第一版做出来以后,我的工作变了。我不再从空文件开始写,而是先看一个能跑但不够好的东西,然后判断它哪里不对。

这比从零写代码更接近我现在的工作状态。

Prompt 不是最重要的

很多人聊 AI 编程,第一反应是 prompt。

怎么写 prompt?要不要先让它出方案?要不要让它列 task?要不要一步一步确认?

这些当然有用。但我现在不太把它们放在第一位。

大部分任务,我会直接让 agent 先做。如果它做得不对,我再补充要求,或者给它一个更明确的方案,让它重做。

原因很简单:很多时候,提前把所有事情说清楚,比让它先做一版成本还高

这也是我现在对 agent coding 的一个判断:在 AI 时代,很多任务的对齐成本会比纠错成本高。以前我们习惯先把需求、方案、边界都对齐好,是因为人写代码的返工很贵。但现在 agent 先做一版的成本很低,看到实物以后再判断哪里不对,反而更省力。

假设我有 10 个问题。如果我每个问题都先写完整方案,时间会花很多。但如果我直接让 agent 做,它很快会给我 10 个结果。可能其中 6 个能用,3 个要改,1 个完全不行。那我只需要把精力花在后面 4 个上。

这个体感差很多。

以前手工写代码,任务没想清楚就开始写,返工成本很高。现在很多执行成本被 AI 吃掉了。真正贵的是三件事:

  1. 我有没有看出哪里不对。
  2. 我能不能判断这个问题值不值得改。
  3. 我能不能把问题讲清楚,让它下一轮改对。

所以我现在更关心反馈回路,而不是 prompt。

prompt 只是输入。它不能保证事情真的做对。

真正有用的是后面这一串:任务丢出去,agent 做一版,我验收,发现问题,让它改,必要时补工具、补记忆、补测试。下一次再遇到类似问题,它应该更容易做对。这才是重点。

我一定会自己验收

我不会因为 AI 说完成了,就相信它完成了。

尤其是 UI 和真实行为,必须自己跑一遍。

UI 我会看 pixel diff。这个东西很直接,哪里变了,一眼就能看到。很多时候你光读代码看不出来 UI 被它搞坏了,但截图一对比就很明显。

常规行为我反而不太担心。比如输入框最大长度、错误 Toast、异常处理、复用项目已有的交互方式,这些它通常能做好。项目里有现成例子,它会照着学。我说太多反而浪费上下文。

真正要看的,是运行起来以后是不是对的。我一般会看这几类东西:

  1. 请求数量对不对,参数有没有多发、漏发。
  2. 过滤有没有真的生效,不只是 UI 看起来变了。
  3. 异常状态有没有处理,失败以后用户还能不能继续走。
  4. PC 切到 mobile 以后,布局和交互有没有崩。
  5. 页面上那些边界路径,是不是真的能跑通。

这些东西只看代码不够。

从逐行实现,到验收、纠错和系统设计

从逐行实现,到验收、纠错和系统设计

以前我写代码时,会一边写一边处理这些问题:这里要防御,那里要抽象,这个状态不能这么放。

现在很多时候是 agent 先写,我再验。我的能力变成了另外一种形式存在。以前这些判断发生在写代码过程中,现在更多发生在验收过程中。执行可以交给 AI,验收不能交,因为验收的本质是责任分配和信任问题,目前 AI 还不能承担。

一个它做坏的例子

我遇到过一个对话流的问题。

系统里有多个对话。原来的设计是,每个对话可以单独 pending,互不影响。同时还有一个全局 pending,用来表示整体状态。

也就是说,这里其实有两层 pending。

我让 agent 修一个中间状态。它修完以后,用了一个单例 pending。

结果就变成:第一个对话 pending 的时候,第二个对话也不能继续聊了。

原来应该是:

conversation A -> pending A
conversation B -> pending B
conversation C -> pending C
global -> overall pending

它改成了:

conversation A/B/C -> one pending

这个问题不是语法错误,也不是少写了一个判断。它把状态维度改错了。

而且我不是读代码发现的。我是在测试别的功能时发现:一个对话 pending,会影响另一个对话。

然后我让它 debug。过程大概是这样:

  1. 它一开始说,代码就是这么设计的。
  2. 我继续问,为什么要这么设计。
  3. 我让它从 git log 看看,这个设计是什么时候变成这样的。
  4. 它解释完以后,我才意识到问题在哪里:它只看到 pending 这个状态,没有理解这里需要两层 pending。

这类问题很常见。AI 修局部问题时,可能会改坏系统原来的语义。尤其是状态、并发、作用域、所有权这些地方,它很容易把复杂结构压成一个简单结构。

但我不会因为这个就不让它做。

这种错可以接受。关键是我能发现它,让它解释,再把正确模型补进去。下次再遇到类似情况,这个信号应该被记住。

记忆不要都塞进 agent.md

我写了一个记忆系统,用来记录这些判断。

不是把所有东西都永久塞进上下文里。它会蒸馏,也会遗忘。如果一条经验长期没被用到,那就忘掉。忘了说明它没那么重要。

很多人会把这些内容放进 claude.mdagent.md。比如:不要把 pending 写成单例;这个项目里 pending 有两层;某个组件不能这么改。

我现在不太喜欢这样做。原因不是 agent.md 没用,而是它和 memory 的职责不一样。

agent.md 适合放少量稳定规则,比如项目命令、编码规范、绝对不要做的事情。它不适合越写越长。

原因也很简单:它是常驻上下文。每次都加载。内容越多,干扰越多。而且项目一直在变,早期写下来的设计不一定还对。

我更倾向于把经验放进 memory。memory 是按场景触发的。只有遇到相似问题,它才应该出来。

Memory 与 agent.md 的分工:常驻规则要少,经验缓存要条件触发

Memory 与 agent.md 的分工:常驻规则要少,经验缓存要条件触发

代码本身应该是最高优先级。项目现在是什么样,应该先看代码,而不是先相信一份很久以前写的说明。

所以我的原则很简单:

  1. agent.md 要少。 只放稳定规则和项目约束。
  2. memory 可以多,但要能忘。 它应该按场景触发,而不是每次都出现。
  3. 代码永远优先。 当前代码比旧文档更可信。

这件事对我很重要。因为 AI 编程最麻烦的不是它没上下文,而是它带着一堆过期上下文还以为自己懂了。

不要一直停在结对编程

AI pair programming 当然有用,但如果一直停在那里,人还是很累。你说一句,它写一段;它错了,你改一段;你再解释,它再写。这样看起来用了 AI,但人的注意力一直被绑在每一步上。

我现在更想要的是让 agent 自己先跑几轮:先做,自己检查,自己修。 多轮之后,我再看剩下的问题。

大概流程是这样:

agent 执行
agent 自检
agent 修正
agent 再执行
human 验收
human 提供判断
memory 记录判断
下一轮继续

测试它自己会跑,但我不会要求它每次都跑,太慢,只有有价值的地方才补测试。UI 我会先看 pixel diff,再自己点页面,因为 UI 很容易被它改坏。Network 我会让 Chrome 持久化到本地,因为请求数量、请求参数、过滤条件,这些都是真实行为的一部分。多轮 review 我也会用,但我不希望它每一步都问我。

我希望它有一条固定路径:该查就查,该跑就跑,该看日志就看日志。它自己判断哪些值得做,不要每次都等我确认。这里的变化是,我不再当低级调度器。我只定边界,最后验收。

以后我会先改流程,而不是先改代码

如果我要带一个 AI-native 的工程团队,我大概会要求几件事:

  1. 任务先给 agent 做,不要一上来就手写。 不是说手写代码不好,而是你一手写,就绕过了这套流程。团队需要训练的是“怎么让 agent 做对”,不是继续训练每个人当手工高手。
  2. 验收必须自己做。 AI 写完不代表完成,自己点一遍,自己看请求,自己看异常,自己看移动端。
  3. 遇到问题时,不要急着自己修。 先想:为什么它没自己发现?为什么测试没覆盖?为什么工具没给它足够的信息?为什么 memory 没触发?为什么代码结构让它理解错?

很多时候我不应该直接修 bug,而应该修那个让 AI 能修 bug 的流程。这句话听起来有点绕,但意思很朴素:如果我每次都亲手补洞,那下次还是我补;如果我把工具、验证、记忆、代码结构改好,后面同类问题就可以少找我。

我早期还是会大量介入代码,重构项目,让它更容易被 AI 理解。等这个模子形成以后,我改得最多的就不是业务代码了,而是工具能力和验收机制。对我来说,收益最大的两个东西是 pixel diff本地记忆系统。pixel diff 让验收变简单,本地记忆系统让我不用反复解释同一类问题。这两个东西比多写几个 prompt 模板有用。

我相信 AI 的解释,但是不相信 AI 的结论

当 agent 修 bug 失败时,我不会马上告诉它答案。我会先让它 systematic debug:先复现,再找原因,再解释代码路径。它要说清楚现象是什么,原因是什么,为什么这个修法能解决,以及怎么验证。我相信解释,不相信结论。 如果它解释清楚,我就继续让它做;如果它开始前后矛盾,我会新开一个对话,让它先总结事实。

这不是重来,只是清理上下文。一个对话太长以后,经常会混进很多错误假设。继续在里面追问,只是在坏上下文上继续打补丁。该丢就丢,该总结就总结。

这也是我为什么说,agent coding 的核心不是 prompt,而是反馈回路。Prompt 追求一开始说清楚,但真实开发里,一开始很难说清楚,很多判断要等东西跑起来以后才会出现。所以我现在的做法是:先让它做一版,尽快得到一个可以验收的结果,然后我用工具、运行结果和自己的判断去纠正它,重要的纠正再进入 memory 和流程。

我不是不写代码了,只是我越来越少把第一反应放在“这段代码我来写”上。我更关心的是,下一次类似问题,AI 能不能自己发现,自己解释,自己修好。这就是我现在理解的 agent coding。

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...