经过大半个月高强度的 Vibe Coding,我的直观感受是:很多过去觉得门槛很高、实现成本很重的事情,现在都变得容易了许多。做项目时甚至会产生一种“随心所欲”的感觉——想到一个功能,就能把它做出来;曾经那道看似难以跨越的技术鸿沟,正在被快速填平。
比如下面这两个小项目:我发现 CC Switch 这个软件很好用,就想在它中间加一个 upstream proxy,把代理插在 Agent 和 LLM endpoint 之间,用来分析它们的原始数据交互。放在过去,这件事往往意味着你需要长期的技术积累:会 Rust、会 TypeScript、熟悉网络协议与工程化细节……但现在,即使这些都不完全具备,也依然可以把想法落地,做出一个能用的工具。
顺便安利一下:我最初做这个工具的目标,就是为了抓取并分析 Agent 与 LLM API 之间最原始的交互数据。如果你也有类似需求,希望它能对你有帮助。我粗略评估了一下:过去想达到“能对这类项目做贡献”的程度,可能至少要为某门语言投入两三个月;而现在,这不再是硬性前置条件了。
- CC Switch-ProxyVersion:https://github.com/leemojiang/cc-switch/releases
- LLM Agent Debugger:https://github.com/leemojiang/LLM-Agent-Debugger
接下来这篇文章,我主要想总结一下这段时间对 Vibe Coding(尤其是与 Agent 协作)的一些感受与想法,并尝试把我最近的实践方法论做一个阶段性整理。
一些感受
这段时间我几乎把市面上能接触到的 Coding Agent 都试了一遍:从一些工具链型产品到专门面向代码的 Agent(例如 Antigravity、Codex, Claude Code),也尝试了不同的模型,比如Google 的 Gemini,以及 Claude Code 接入 DeepSeek,GPT,还有一些国产工具(如 Trae、CodeBuddy 等)。
我得到的第一个结论是:Coding Agent 能不能“跑起来”,模型能力往往是最关键的变量。 目前 Coding Agent 的效果之所以看起来“突然变好”,很大程度上还是来自模型能力的增强。比如 Claude Code 如果接入 DeepSeek API,整体效果就会明显弱一些。
我今天也刚好用自己的工具分析了一下 Claude Code 的对话数据:你会发现现在的大模型输出早就不只是“自然语言文本”,而是带有更强结构化倾向的“Agent 输出”。如果模型没有做过相应适配,它在工具调用、状态复述、行动规划等环节的表现往往会差很多。
第二个结论是:这件事也不完全由模型决定。Agent 的“交互层/产品层”同样重要。 我更愿意把它看成一个人机交互问题:例如 Gemini 的模型能力很强,但我在 Antigravity 里体验到的交互与编辑闭环,整体就不如 Claude Code 顺手,最终落在“编辑效果”上也会打折。
所以,如果从“方法论”的角度切入,我觉得人机交互是一个很好的抓手:不应把所有问题都归因于“模型还不够强”,或者寄希望于“等模型更强这些问题就都消失”。真正关键的是:如何让用户更好地与模型协作,如何在工程上“harness the model”。换句话说:我如何借助软件工程与项目管理的方法,更系统地与大模型协作(人机协作)。
Agent 以及 VibeCoding的一些基本原则
这一部分我想先总结一下我的一些看法:在当前阶段,无论是 LLM 还是 Coding Agent,它们的一些核心原则与重要概念是什么。
Agent设计相关原则:
1) LLM 无状态:一切能力来自 Context
LLM 本质上是概率语言模型,它没有“长期状态”。它此刻的表现与输出,几乎完全由**当前输入的上下文(context)**决定。因此,如何为模型提供必要信息——也就是所谓的 context engineering——非常重要。
在实践中我觉得可以把 context 粗略分成两类:
- 信息/数据类:背景知识、需求说明、接口协议、目录结构、代码约定等。
- 操作/执行类:当前改动目标、已执行的命令与结果、失败原因与下一步行动等。
如果借用“内存模型”的类比:它们有点像 data block 与 code block——一个存放事实与约束,一个存放动作与流程。
另外有两个细节在近期变得尤其重要:
- 渐进式加载(progressive disclosure):context 太长会带来注意力稀释。与其一次性把所有文件与信息塞给模型,不如先给概要(index/摘要/约定),再让模型按需展开读取。这也是很多工具(包括 Claude Code 的部分 skill/工具设计)在用的模式。
- 按“栈(stack)”思路组织上下文:把更通用、更稳定的信息尽量放在前面;把短期、易变的“当前任务状态”放在后面,让它更容易被替换/弹出,从而更好地利用 KV cache。
2) 步骤应尽量原子化:单线程、短链路、可反馈
我倾向于把 Agent 的最佳运行方式理解为:
输入 → 一步行动/一次工具调用 → 明确反馈 → 再输入 → 再行动
也就是说,每一步应该尽量原子化,并且能拿到清晰的结果(可观察的输出)。反过来,应该尽量避免“多线程并发 + 多层嵌套调用 + 长依赖链”的复杂结构。
原因很直白:每一步行动都可以理解为一个条件概率 p(action | context);如果你设计一长串互相依赖的调用链(例如 A 调 B、B 调 C、C 调 E……),本质上是把多个不确定性相乘,最终会得到一个更不稳定的整体。
这也是为什么我觉得 Claude Code 采用“单线程 + 每次用户输入都重新 planning”的模式,在工程上很关键:它减少了隐式状态,也减少了依赖链的长度。
3) 记忆仍是短板:尽量把过程落到可序列化的产物上
目前 LLM 在“长期记忆”和“长期流程一致性”上依然不够可靠。比较务实的做法是:尽量让 LLM 把关键过程与结果,落实到可序列化、可追溯的产物上,例如:
- 计划与约定写进文件
- 变更说明写进文件
- 测试结果写进日志/文件
- 最终用 Git 历史做审计与回滚
把这些东西做成“项目的外部记忆”,比寄希望于模型“自己记住并始终一致”更稳。
4) 边界与可验证性:给 Agent 一个可控的运行环境
当你开始认真做工程化,就会发现另一个关键问题是:如何限制模型的边界?
我觉得边界至少包括两层:
- 结果可验证的边界:产出是否能用测试/检查验证?是否能复现?
- 运行环境可控的边界:能不能给 Agent 一个既满足它需要、又足够安全可控的运行环境(例如容器、沙箱、虚拟机),让它大胆尝试,失败就回滚重来。
如果未来 token 更便宜、模型更强,那么很多“限制 Agent 的复杂流程设计”可能都不是最优解。更理想的形态也许是:给 Agent 一套可审计的基础工具与可回滚的环境,让它在其中自由试错;结果可验证就合格,不可验证就重跑。
我发现 CodeX 对“沙箱/虚拟机/可审计环境”的强调与引入,其实非常有价值。Claude Code 目前在编程体验上很强,我也很喜欢他使用的简单CLI的交互原则,但在“给 Agent 一个完全可隔离、可重置的环境”这件事上,还没有完全做到产品化闭环。
人机交互相关原则:
总体上,我越来越觉得:和 AI 协作要更像“管理员工”,带一个项目组或者与人协作,而不是“调用一个确定性的工具”。AI 很强、很快,但它依然有不确定性:你不能假设它每次都按你预期执行,也不能假设它永远不会偏题。
在这种协作模式下,我认为有几个关键点:
- 抓住“设计”和“验证”两端:目标、边界、验收标准由人把控;中间实现细节尽量交给 AI。
- 接受 Spec-driven 的反人类性:人类很难一次性把所有需求写得完美无误,所以更现实的方式是短周期迭代。
- 把任务描述得更具体:任务必须“可定义、可验证、可测试”。越抽象,越容易跑偏。
- 用清晰架构做分解:承认 AI 目前仍不擅长直接处理大型复杂项目,把复杂系统拆成边界清晰的小项目/小模块,能显著提高成功率。
- 让产出可追溯:设计要落在纸面(文件)上,既是给 AI 读,也是给人读;并且这些文档最好支持渐进式加载。
如果用一句话概括:设计 → 实施 → 验证 → 循环。每一个循环最好都是完整的、可运行的,能交付一个明确的功能节点。
软件工程的尝试
基于上面这些原则,我尝试从软件工程的角度,构建一套“如何与编程 Agent 协作”的工作流。
很巧的是,我们大领导上周五也做了一次演讲,内容意外地非常深入:他也强调要基于软件工程的方法,建立人与 AI 交互的项目管理方法论。我基本把他那套思路搬了过来,并结合自己的使用习惯做了调整,形成了一份更偏落地执行的细则。
这套流程使用了之前几个核心原则:
- 流式加载:让 AI 在需要时读到必要信息,而不是一开始就塞满上下文。(Claude.md分层描述内容,不同类型的文档比如conventions放在不同位置,给AI提供目录而不是放在一起)
- 任务具体:尽量把任务描述成“执行某个命令/修改某个文件/完成某个可验证结果”,而不是“把 Git 提交一下”这种抽象指令。(类似Skill,流程文档中描述具体的操作,甚至给出代码)
- 持久化记录:把关键计划、约定、变更、测试结果都落到文件中,做到可追溯、可审计。(拆分出很多文档,每个文档承载不同类型的内容,并且要求AI在修改代码时也要修改相应的文档,把修改内容记录下来,尤其是实现前线Plan供人确认,实现后的Review和Modify文档)
- 人在循环/人为验证 : 持久化的另一点也是让人进入AI循环,对AI的计划和产出进行监督与审查,相当于每一步的结果是文档,然后由人来检查,而不是完全放手让AI自己跑。(具体来说拆了四步:设计阶段的Design,计划阶段的Plan,实现阶段的Modify,以及最后的Review.每个阶段都要求人确认,尤其是计划阶段,先使用Planning Mode和AI交流,然后让AI把自己的计划写成Plan文档供人审查;代码修改完检查Modify和Review文档.)
下面是一套更“工程化”的协作流程描述(可按项目规模裁剪):
约定:整体项目设计与协作规范
- 把项目规范写在
Claude.md(尽量保持简短、稳定)。 - 约定开发模式、变更模式、文档放在哪里、变更后需要做什么检查。
- 例如我会用几个目录承载不同类型的“外部记忆”:
ai/conventions/:全局约定与流程(flow),偏“规范/制度”。ai/steps/(或ai/.plan/):阶段性设计与计划文件,偏“怎么做”。ai/rfc/(或ai/.modify/):变更记录与实现说明,偏“改了什么”。ai/design/:更完整的项目设计(从概要到模块设计)。
- 把项目规范写在
澄清:让 AI 重写计划(先聊天,后落盘)
- 先通过对话澄清目标、约束、验收标准。
- 然后让 AI 把自己的计划重写成文档(而不是只停留在对话里)。
- Claude 的 Plan mode 很适合用在这里,而且这一步可以在多个阶段反复出现。
设计:把关键设计写进可追溯的
.plan文件- 要求可审计、可追溯。
- 分层设计:整体设计可以跟更擅长发散的模型聊;具体实现细节再跟更擅长落地的工具/模型聊。
任务执行:实现必须对应计划,并产出变更说明
- 要求 AI 在
ai/.modify(或你的变更目录)写清楚修改内容。 - 让 AI 复述一遍自己做了什么,并把关键命令与结果记录下来。
- 最终由人 Review(或交给另一个 AI Review)。
- 测试也要跑,并把测试结果更新进记录。
- 要求 AI 在
变更驱动:快速原型与里程碑
- 每一个里程碑都应当是“可运行”的。
- 任何修改都必须通过清晰的变更记录进入系统,这样才可回滚、可追踪、可审计。
我把这套执行方式做成了一个 Claude Code 项目模板,目前在我自己正在写的一个项目里使用,效果还不错。
一些值得强调的细节
我觉得这套工作流的价值在于:它基本覆盖了前面提到的关键原则,同时能把 AI 的不确定性“工程化地收敛”到可控范围里。
第一点:澄清与落盘。
具体来说,就是要让 AI 把它的计划与要做的事情落实到文件里。最大的目的不是为了“让 AI 记住”,而是为了:
- 稳定上下文(避免每次都翻对话)
- 提升可审查性(把关键事实写成可读产物)
- 提升可追踪性(变更可回溯)
第二点:让 AI 重写你的计划。
这是一个挺有意思的思维转变:你完全可以让 AI 来重写你的计划、工作流、甚至工程规范。
AI 非常擅长结构化输出,往往比人手写更快;而且它输出的格式与组织方式,很多时候也更“适合 AI 自己阅读与执行”。某种意义上,你在做的是“人在回路的监督”,让 AI 不断更新自己的计划与产物,而人负责把关。我觉得这也是一个很重要的点:不要害怕让 AI 来“重写”你的计划,因为它往往能做得更好.(人在回路的AI自举,AI的能力经常能让我感到惊讶.)
第三点:让 AI 输出“变更说明”能反过来约束幻觉。
我发现一个很实用的技巧是:代码写完后让 AI 写一遍“我到底改了什么”。当它需要回到具体事实层面复述时,幻觉会被明显限制。
因此,不仅要让 AI 写 plan,也要让 AI 写 modify(变更)。在写变更时,它也相当于在核对自己是否按 plan 执行,从而稳定整体效果。
第四点:把步骤缩短,把结果做实。
这套模板本质上是在构建“人在回路的监督系统”。它要求 AI 每做一步都落到可验证、可检查的结果上:一个文件、一段变更、一条测试输出。
反过来,给 AI 一个很抽象的目标,让它在没有边界的情况下“自己跑、自己重构、自己规划”,有时确实能靠“大力出奇迹”跑出来,但对于大项目来说,这并不是可持续的工程化方法。
最后补一句:在这个模板里,我现在用的 skill 也非常少,最常用的就是一个简短的 commit skill。我的理解是:skill 更像是“打包 prompt”来完成某个具体动作,而不是把一个又长又复杂的工作流硬塞进 skill 里。skill 做得太长,调试成本会急剧上升,反而得不偿失。如果对于只用一两次的事情,甚至完全不需要skill,聊天的prompt就足够了,反馈也更及时.
一些问题 以及目前的一些总结
这套 workflow 在大型项目或工业实践里其实是比较成熟的,但放到个人开发场景,我确实也遇到了一些现实问题。
其中最大的问题是:当你把“人”放进 AI 的迭代循环里,人反而可能变成最薄弱的一环。
AI 输出代码已经不再是瓶颈;真正要命的是:我读不过来。 不仅代码读不过来,AI 产出的 plan、文档、变更记录也容易变成“草草扫一眼就略过”的材料。一旦后续架构与实现发生大变化,前面的文档可能迅速失效。
这也带来一个悖论:
- 你想让工程可控、规范化,就不得不增加人的参与与审计。
- 但人的参与越多,AI 带来的效率提升就越容易被抵消,因为人拖慢了迭代闭环。
抽象一点说:“有多少人工,就有多少智能。” 这可能是严肃工程化在当下阶段无法回避的矛盾:到底要工程化到什么程度?这是一个需要长期权衡的点。
在后续实践里,我觉得有几个更值得重点平衡的方向:
AI 的输出越来越偏“给人看”
- 随着模型越来越强,很多规范化记录对 AI 的价值可能反而下降:它直接看代码就能继续推进。
- 但这些记录对人很关键:它让开发者知道 AI 在干什么,并对项目保持掌控感。
完整迭代越来越重要:每次改动都应当可运行、可回归
- 因为 AI 实现速度太快,每一次实现完成,本质上都是对系统的一次变更。
- 所以需要强调:变更前后系统都能稳定运行,并通过既有测试。
- 这对代码设计能达到“模块边界清晰”的要求更高:最好让 AI 每次只改一个模块/一个功能点,而不是全局性重构。
步骤单一化 + 结果可验证:把 AI 的操作变成“函数式”的输入输出
- 我希望 AI 的每次操作更像一个函数:输入明确、输出明确、影响范围明确(就像函数里变量的作用域,它只会影响当前函数,不会离开自己的作用域)。
- 另一个关键是可验证性:如果单元测试与集成测试足够充分,并且你对测试有信心,那么就可以让 AI 自己跑测试、根据失败结果自我修复。
- 当验证自动化程度足够高时,你甚至可以把“构建某个功能”交给 AI 作为一个相对完整的 task,从而显著提高开发速度。
到底要“放手”到什么程度:用边界与验证替代过程控制
- 我越来越倾向于:中间规划尽量放手,让 AI 自由发挥;你负责提供框架与环境,并在结果上做验证与约束。
- 很多时候,当你真的放手让 AI 去跑,它能做到的事情会超过预期。包括工作流、规范、文档这些“看起来需要人写”的东西,AI 往往能写得更清晰。
- 关键在于:你是否有足够的工程化基础(模块拆分、测试、版本管理)来兜底,让结果不符合预期时能快速回退,而不会把系统搞乱到无法收拾。
总结笔记版(太长不看版):
软件工程化的 Vibe Coding 是一个Tradeoff的悖论
- 需要可控 → 增强人的监督
- 增强人的监督 → 抵消 AI 的效率
- 有多少人工,就有多少智能
让 AI 写/让 AI 输出的目的
- 让 AI 在“复述/总结”时减少幻觉
- 把记忆持久化,让项目可追溯、可审计
- 但这些产出越来越主要是给人看的(审计与追溯,Git 往往做得更好)
- 重点是:让 AI 输出能帮助人理解(例如结构图/UML 有时比长文档更有效)
边界与可测试:Let it go 到什么程度?
- 如果模型更强、token 更便宜,与其纠结 AI “怎么做”,不如让它在边界内自由试错。
- 边界可以拆成三类:
- 逻辑边界:一次只做一件可验证、影响有限的事(重写一个模块通常比重构整个项目更靠谱)。
- 环境边界:给 AI 一个沙箱,它可以自由操作,不行就重置再跑一遍。
- 影响边界:通过良好模块拆分,让 AI 的改动局限在特定模块中。
- 可测试是另一种“控制 AI”的方式:只要测试通过,就算 OK;最好能自动化,从而降低人工参与度。
一些具体的操作建议
- 重视软件工程与编码规范(命名、目录结构、Commit 规范、文档规范等)
- 把这些当成“让人更好理解 AI 输出”的基础设施,而不是形式主义