AI 编程工作流的反模式

前面几篇文章里,我一直在谈同一个问题的不同侧面。

代码、工具与人:AI 时代的程序员 里,我谈的是 AI 并没有让程序员的责任消失;在 GPT 有智能,但不是责任主体 里,我谈的是 GPT 可以参与理解和判断,但不能成为责任主体;在 把 AI 放进工作流 里,我进一步谈到,AI 不是靠一次漂亮提示词用好的,而是要被放进一个有边界、有验证、有沉淀的工作流里;在 TDD 让 AI 可靠,AI 让 TDD 可行 里,我又专门写了测试如何约束 AI 的生成能力。

这篇想反过来看:如果工作流没有设计好,AI 编程会怎样失控?

这里说的“反模式”,不是指 AI 不该参与编程,也不是指某个模型能力不够。相反,很多问题正是因为 AI 太能生成、太会解释、太容易把一个还没被确认的想法变成完整代码。它不是慢吞吞地失败,而是很流畅地失败。

所以本文讨论的不是 prompt 技巧,也不是模型排行榜,而是 AI 进入软件开发流程以后,在任务定义、上下文管理、变更控制、诊断、验证和责任边界上常见的工作流问题。

把局部提速误认为整体提速

AI 确实能降低很多局部任务的成本。查资料、解释代码、生成样板、起草测试、补充边界、整理日志,这些事情都可以变快。问题在于,局部实现变快,不等于整体交付变稳。

软件开发不是只由“写出代码”组成的。一个变更能不能进入系统,还取决于需求是否收敛、上下文是否正确、测试是否覆盖真实风险、review 是否能承受变更规模、发布后是否可回滚。AI 加快的是其中一部分环节。如果其他环节没有同步升级,生成速度反而会把压力传导到 review、测试、CI 和发布流程里。

DORA 关于生成式 AI 的研究里也提示过类似现象:AI adoption 可以和个人生产率、flow、满意度提升相关,但也可能伴随交付吞吐和交付稳定性的下降。[1][2] 这并不说明 AI 对工程有害,而是说明“更快生成”必须被接入更强的验证和交付系统。

第一个反模式就是:只看见 AI 带来的局部提速,没有看见它对整体工作流提出的新要求。

任务没有收敛就进入实现

最常见的反模式,是需求还没收敛,就让 AI 直接实现。

表现通常很熟悉:

  • 只有一句模糊需求,就让 AI “做一下”。
  • 没有定义非目标。
  • 没有说明兼容性、权限、性能、异常路径和边界条件。
  • 没有说清楚哪些行为不能被破坏。
  • 把 AI 给出的方案解释,当成需求澄清结果。

AI 很擅长补全缺失信息。问题是,它补出来的东西不一定是业务真实意图。任务越模糊,AI 越容易用“合理默认值”填空。它会根据常见模式生成一个看起来完整的实现,但这个实现可能从第一步就偏离了真实目标。

这类问题在业务规则里尤其危险。比如一个订单系统里,奖励是在订单创建时确定,还是在支付成功时确定,这不是代码风格问题,而是业务事实。如果这个事实没有先被确认,AI 仍然可以写出字段、状态流转、测试和说明文档。越完整,越容易让人误以为它是对的。

更稳的做法,是先把任务压缩成可讨论、可验证的形式:

  • 目标是什么?
  • 非目标是什么?
  • 输入、输出和状态变化是什么?
  • 哪些旧行为不能变?
  • 哪些异常和边界必须覆盖?
  • 什么证据可以说明完成?

复杂任务可以先让 AI 调查和整理,而不是直接改代码。AI 可以帮我们建立问题地图,但不应该替我们决定问题边界。

上下文管理失控

第二个反模式,是把上下文当成资料仓库。

有些人使用 AI 时,会把大量文件、历史讨论、日志、旧计划、失败尝试一次性塞进上下文里。背后的想法很自然:给得越多,AI 知道得越全。可在真实工作里,上下文不是越多越好。

上下文是一种工作资源。它有容量限制,也有注意力分布问题。即使模型还没有撞上 token 上限,也可能已经开始忽略中间区域的关键约束,或者被无关信息带偏。长任务里常见的漂移、重复、遗忘和自相矛盾,很多都和上下文污染有关。

在 AI 编程里,上下文管理至少要区分阶段:

Research 阶段可以宽一点。这个阶段的目标是找相关代码、相关测试、历史约束和可能风险,所以需要广泛浏览。

Plan 阶段要窄一些。这个阶段的目标是做设计取舍,所以应该聚焦关键模块、关键约束和验证方式。

Implement 阶段应该更窄。真正改代码时,最好只保留当前变更所需的文件、接口、测试和约束。

如果把这三个阶段混在同一个不断膨胀的对话里,AI 很容易进入一种“看起来还在工作,但已经不再稳定利用关键信息”的状态。

所以稳定规则不应该长期留在聊天记录里。构建命令、测试命令、命名约定、禁止事项、目录边界、review 习惯,这些应该进入 AGENTS.md、项目文档、测试、lint、CI 或脚本。OpenAI 的 Codex 实践也强调把项目上下文和重复指导沉淀到仓库里,而不是每次都靠临时 prompt 重新说明。[3]

好的上下文管理,不是把所有东西都给 AI,而是让 AI 在正确阶段看到正确材料。

变更范围自然膨胀

第三个反模式,是让变更范围自然膨胀。

AI 很容易给出完整方案。它会顺手整理命名,顺手改接口,顺手抽象一层,顺手重构附近代码。很多时候,这些改动单独看并不糟糕,甚至局部上更漂亮。但工程变更不只看局部漂亮,还要看任务边界、review 成本、回归风险和长期一致性。

典型表现包括:

  • 修一个 bug,顺手重构多个模块。
  • 为了适配新实现,改变既有接口。
  • 把功能修改、重构、测试迁移、文档调整混在一个 diff 里。
  • 生成大批量代码,但没有明确分层。
  • 代码看起来更现代,但破坏了项目既有风格。

这类问题的根源,是 AI 的生成倾向和工程变更的审查需求并不天然一致。AI 倾向于补齐、扩展、圆满化;工程上却经常需要局部、保守、可回滚。

所以在 AI 编程里,review 不能只问“这段代码写得好不好”,还要先问“这些代码是否应该出现在本次变更里”。

更稳的做法是:

  • 明确允许修改哪些文件。
  • 明确哪些模块只读。
  • 把重构和功能修改拆开。
  • 优先小批次提交。
  • 大改动先 review plan,再进入实现。

AI 生成速度越快,越需要人为压住变更批次。

诊断过程被跳过

第四个反模式,是 debug 时直接让 AI 猜根因。

这件事很诱人。把错误日志、代码片段丢给 AI,它很快就能给出一个解释和修复方案。很多时候这个方案还挺像回事。但诊断的核心不是“给出一个合理叙述”,而是“用证据收敛假设”。

没有复现,就不知道问题是否真的存在于当前观察里。没有假设列表,就不知道自己排除了什么。没有能区分假设的证据,就不知道改动是不是只碰巧修到了附近。

AI 在这里的风险,是它会很快生成一个故事:因为 A,所以 B,应该改 C。这个故事可能正确,也可能只是流畅。OpenAI 关于 hallucination 的讨论提醒过我们,模型的流畅输出和事实正确性不是同一件事。[4]

更稳的诊断流程应该是:

  1. 先复现问题。
  2. 明确当前已知事实。
  3. 列出可能假设。
  4. 找能区分假设的证据。
  5. 根据证据更新判断。
  6. 最后再修改代码。
  7. 修复后留下回归测试或最小验证命令。

AI 可以参与这个过程。它可以帮我们读日志、找相关代码、提出假设、设计插桩点、解释测试失败。但它不应该跳过证据,直接进入修复。

debug 中最坏的情况,不是 AI 没有给出答案,而是它给了一个足够像答案的答案,让人停止收集证据。

同一个 Agent 自写自验

第五个反模式,是让同一个 AI 自己写实现、自己写测试、自己修改测试、自己宣布通过。

这看起来像闭环,实际上可能是闭门造车。

如果测试来自同一个模型对需求的即兴理解,它测到的可能不是外部规格,而是模型自己的解释。更糟的是,实现失败以后,AI 可能会为了绿灯去修改断言、放宽条件、删除不方便的用例,或者把当前错误行为写进测试里。

这就是我在 TDD 让 AI 可靠,AI 让 TDD 可行 里反复强调的点:AI-TDD 的关键不是“让 AI 先写测试”,而是先有外部行为规格,再让测试约束实现。

测试在 AI 编程里有双重身份。对 CI 来说,它是验证;对 AI 来说,它也是上下文。好的测试可以把需求稳定地暴露给模型;坏的测试则会同时污染上下文和反馈回路。

更稳的分工应该是:

  • 人定义行为规格和验收边界。
  • AI 起草测试和实现。
  • 自动化工具给出反馈。
  • 人或独立 review 审查测试是否表达了正确规格。

新测试最好先失败,而且失败原因应该是目标行为缺失,而不是测试本身写错、环境缺失或依赖问题。通过以后,也要防止 AI 为了绿灯修改测试语义。

实现写错了,可以重写。测试写错了,会把错误规格固化下来。

完成声明没有验证证据

第六个反模式,是把 AI 的完成声明当成工程事实。

“已完成”“已修复”“测试通过”“应该可以了”,这些都是文本。工程上的完成必须绑定证据。

证据可以是:

  • 实际运行过的测试命令。
  • lint、typecheck、build 的输出。
  • 失败用例从红到绿的记录。
  • diff review 的结果。
  • UI 截图或手工验证步骤。
  • CI 状态。
  • 明确说明未验证部分和剩余风险。

如果没有这些证据,AI 的完成声明只是它对自己输出的总结。这个总结可能有帮助,但不能替代验证。

这里最容易出现的问题,是 AI 用语气弥补证据。它会写得很确定,甚至列出“已完成事项”。但如果没有实际命令和结果,这些文字并不能说明系统状态已经改变。

所以我现在更倾向于把完成判断写成固定要求:完成之前必须说明运行了什么检查,结果如何,哪些检查没有运行,为什么没有运行,剩余风险是什么。Codex 的最佳实践也把测试、检查、行为确认和 review 放在可靠性收口里。[5]

AI 可以宣布候选结论,但工程完成需要证据。

人工介入退化成橡皮图章

第七个反模式,是所谓人在环只剩下“点继续”。

很多 AI 工具会在关键时刻请求用户确认。这个机制本身是必要的,但如果只给一个确认按钮,而不给足够上下文,人工介入就会退化成橡皮图章。

有效的人工介入,至少要让人看到:

  • 当前目标是什么。
  • AI 准备做什么。
  • 影响范围是什么。
  • 风险在哪里。
  • 是否可回滚。
  • 有哪些替代选项。
  • 当前证据支持到什么程度。

人在环不是让人频繁打断 AI,也不是让人最后兜底。它应该出现在高杠杆位置:计划确认、风险动作、测试规格、发布决策、证据不足时的暂停。

Microsoft 的 Human-AI Interaction Guidelines 强调 AI 系统应该让用户能够理解、纠正和恢复;NIST 的 AI Risk Management Framework 也把风险放进治理、度量和管理结构里看。[6][7] 这对应到日常 AI 编程,就是不要把责任模糊地推给“用户已确认”。确认必须基于上下文,反馈也要能沉淀。

如果用户纠正了 AI 的行为,这个纠正不应该只停留在一次对话里。它应该尽量进入规则、测试、文档、模板或脚本。否则同样的问题下次还会发生。

一个更稳的工作流

把这些反模式放在一起看,它们其实都指向同一个问题:AI 编程不能只围绕“生成”设计,而要围绕“可验证交付”设计。

一个更稳的流程可以很简单:

  1. Research:先理解现有代码、测试、约束和风险。
  2. Plan:定义改动范围、非目标、验证方式和人工介入点。
  3. Implement:小步修改,避免混入无关重构。
  4. Verify:运行测试、lint、build,必要时做人工检查。
  5. Document:把稳定结论沉淀到项目规则、文档或知识库。

这不是为了把流程变重。简单任务当然不需要完整仪式。改一个拼写、补一个小配置、调整一个局部样式,没有必要强行走完整流程。

关键在于,任务一旦涉及模糊需求、存量系统、跨模块变更、生产风险、测试缺口或长期维护,就不能只依赖 AI 的生成能力。越是复杂任务,越需要阶段化、上下文压缩、小批次变更和验证证据。

AI 编程成熟度的标志,不是让模型一次生成更多代码,而是让生成结果更容易被约束、审查、验证和回滚。

真正需要升级的,不只是 prompt,而是工作流。

参考与延伸阅读


  1. DORA, Accelerate State of DevOps Report 2024。这里引用它来支撑“AI 对个人体验和团队交付系统可能产生不同方向影响”的边界判断。 ↩︎

  2. DORA, Impact of Generative AI in Software Development。这里引用它来支撑“生成速度可能通过更大 batch size 压力传导到 review、测试和交付系统”的解释。 ↩︎

  3. OpenAI Developers, Codex best practices。这里引用它来支撑“项目上下文、重复指导和验证命令应该沉淀到仓库和工作流中”的实践判断。 ↩︎

  4. OpenAI, Why language models hallucinate。这里引用它来提醒:模型输出的流畅性不能直接等同于事实可靠性。 ↩︎

  5. OpenAI Developers, Codex best practices。这里引用它来支撑“完成断言需要测试、检查、行为确认和 review 收口”。 ↩︎

  6. Microsoft Research, Guidelines for Human-AI Interaction。这里引用它来支撑“人需要能够理解、纠正和恢复 AI 输出”的交互原则。 ↩︎

  7. NIST, AI Risk Management Framework。这里引用它来支撑“AI 风险需要进入治理、度量和管理结构,而不是由模型或单次确认承担”的边界。 ↩︎