1. 从失控到重构:我的AI辅助开发工作流演进实录

作为一名在软件开发一线摸爬滚打了十多年的老兵,我最近一年把大量精力投入到了探索AI辅助编程的实战化上。我的目标很明确:不是做那些炫酷但一次性的演示,而是构建一个能真正用于企业级项目开发、稳定可靠的AI驱动工作流。起初,我和很多人一样,认为“更多的控制”等于“更高的可靠性”。于是,我给我的AI代理系统层层加码:更多的角色、更复杂的审核关卡、更详尽的过程文档。结果呢?系统确实变得更“重”了,但也更慢了,而且出乎意料地更脆弱了。整个流程运行起来像一台过度设计的机器,齿轮咬合处嘎吱作响,最终在自身的重量下濒临崩溃。这篇文章,就是关于那次失败的重构,以及我们如何通过“做减法”而非“做加法”,最终让一个AI辅助的工作流变得足够稳定,值得投入日常使用。

如果你也正在尝试将大型语言模型(LLM)集成到你的开发流程中,希望它不只是生成片段代码,而是能理解需求、进行设计、编写实现并完成测试,那么你很可能正面临或即将面临与我相似的困境。核心矛盾在于:我们试图用非确定性的AI模型去执行一个需要高度确定性的工程流程。增加控制节点本意是减少不确定性,但不当的控制本身又会引入新的复杂性和失败点。接下来,我将完整拆解我的迭代过程,从第一次看似成功的多角色编排,到失控的“控制膨胀”,再到最终通过简化、本地化和确定性检查实现稳定的全过程。这不仅仅是一个技术配置的故事,更是一次关于如何在AI时代重新思考软件开发工程实践的反思。

2. 第一版:多角色编排的曙光与阴影

我的起点是一个基于多智能体(Multi-Agent)协作的构想。传统的单次提示(One-shot Prompting)或简单的聊天交互,对于复杂任务来说,生成结果的质量和一致性就像开盲盒。因此,我设计了一个模拟软件团队的角色体系: 产品负责人(Product Owner) 负责解析原始需求; 团队负责人(Team Lead) 进行任务分解与协调; 架构师(Architect) 制定技术方案与实施计划; 开发工程师(Coder) 负责具体编码; 评审员(Reviewer) 负责质量把关。每个角色都是一个独立的AI代理,拥有特定的系统指令(System Prompt)和上下文记忆。

2.1 初见成效:结构化的价值

这个设计的初衷是好的。在实际运行中,它确实带来了比单一模型“一口闷”更优的结果。代码结构变得更清晰了,因为架构师代理会先输出设计文档;中间产物(如用户故事、API契约、测试计划)开始出现,使得过程变得可追溯;代理之间的“交接”(Handoff)——即一个代理将产出物和上下文传递给下一个——成为了流程的核心,这模仿了真实团队的协作,也带来了一定的过程纪律性。

例如,对于一个“创建GitLab Issues列表API”的需求,流程不再是“直接写代码”,而是变成了:

  1. 产品负责人将模糊需求转化为格式化的用户故事(User Story)。
  2. 团队负责人基于用户故事,拆解出具体的开发任务项。
  3. 架构师针对每个任务项,输出包含技术栈选择、模块划分、接口定义的实施方案。
  4. 开发工程师依据方案进行编码。
  5. 评审员检查代码是否符合方案与项目规范。

这个过程生成了看起来相当不错的代码:结构清晰,甚至有配套的单元测试。从输出物上看,这无疑是一个进步。

2.2 浮现的裂痕:三大核心问题

然而,在几次完整的流程跑通后,深层次的问题开始暴露。表面的成功掩盖了流程内在的脆弱性。

问题一:需求漂移(Requirement Drift) 这是最致命的问题。原始的用户需求,在代理间传递和转译的过程中,像“传话游戏”一样被悄然扭曲或丢失。架构师可能会“脑补”一些他认为合理的约束,开发工程师可能会为了实现方便而简化逻辑。最终生成的代码虽然“干净”,但可能已经偏离了业务初衷。例如,需求明确要求API支持分页和过滤,但最终实现可能只提供了一个返回全部数据的简单端点。AI代理倾向于将模糊地带处理成它们更熟悉或更简单的模式,这种静默的“需求窄化”在复杂流程中会被逐级放大。

问题二:软弱的评审关卡 最初的评审完全依赖于评审员代理。但问题在于,这个评审员同样是另一个LLM。它可能被一段看起来合理、语法正确但逻辑完全错误的代码所“说服”,给出虚假的通过信号。这种“自信的幻觉”是LLM的固有缺陷。当评审员说“代码看起来不错”时,你无法确定它是真的进行了逻辑推演,还是仅仅在生成一段符合人类评审口吻的文本。这种软验证无法为交付质量提供任何实质性保障。

问题三:过程黑盒与版本混乱 随着我不断调整各个代理的指令、尝试不同的模型(如GPT-4, Claude等)、修改交接格式,我很快发现自己也陷入了混乱。哪个配置组合真正提升了稳定性?哪个改动导致了后续环节的崩溃?由于缺乏详尽的、结构化的运行日志,每次迭代都像是在迷雾中摸索。成功或失败无法归因,优化变成了玄学。

实操心得 :在AI工作流开发的早期,就必须建立强大的可观测性(Observability)体系。记录每一次API调用的输入输出、每个代理的决策上下文、每个环节的耗时。这比优化某个提示词更重要,因为它是你进行科学迭代的基础。

3. 错误的强化:当“更多控制”导致系统过载

面对第一版的问题,我的直觉反应是典型的工程师思维:加强控制。我认为问题在于约束不够强、检查不够多、流程不够“重”。于是,我启动了第二版的重构,目标不是改进代码生成本身,而是围绕它构建一个更坚固的“管控外壳”。

3.1 新增的控制机制

  1. 需求锁(Requirement Lock) :为了解决需求漂移,我在团队负责人环节增加了一个强制步骤。它必须从原始需求中提取并固化一个“需求锁”文档,明确列出:

    • 不可变更的输入输出契约(API接口、数据类型)。
    • 必须实现的默认行为和校验规则。
    • 明确排除在外的功能(非目标)。
    • 遗留的、不允许代理自行假设解决的开放问题。 这个文档将作为“宪法”传递给后续所有代理,任何偏离都需要显式的理由和记录。
  2. 强化评审与质量门禁

    • 我将评审员角色独立出来,并赋予其更严格的职责:不仅要评审最终代码,还要在架构师产出方案后,先行评审实施方案本身是否合理。
    • 引入了 SonarQube 作为强制质量门禁。代码必须在通过Sonar的静态代码分析(包括代码异味、漏洞、测试覆盖率等)后,才能进入下一阶段。
    • 增加了 冒烟测试(Smoke Test) 作为发布前最后一道关卡,确保应用能启动,核心流程能跑通。
  3. 增强的过程透明化 :我构建了完整的运行日志系统,记录每一次迭代的配置变更、每个代理的输入输出快照、每个检查点的通过状态。试图让整个过程变得可追溯、可复盘。

3.2 系统的崩溃:重量压垮了稳定性

从纸面设计看,第二版无比严谨。实际运行一次“创建GitLab Issues列表API”的任务,结果也确实如此:代码更规范,测试覆盖更好,Sonar报告全绿,应用成功运行。单看输出物,这似乎是次胜利。

但过程的代价是灾难性的:

  • 耗时 :整个流程跑了 超过两个小时
  • 循环 :团队负责人和开发工程师之间出现了 四次 纠错循环。
  • 崩溃 :最终的评审产出物在传递过程中 损坏 ,导致流程无法正常结束。
  • 摩擦 :新增的Sonar检查极其耗时且报告噪音很大(对生成代码的编码风格过于苛刻),反而拖慢了核心反馈循环。

最根本的问题浮出水面: 上下文腐化(Context Rot) 。随着流程变复杂,代理间需要传递和读取的中间产物(用户故事、需求锁、架构方案、评审意见、Sonar报告摘要等)越来越多。所有这些文本都被拼接到后续代理的上下文窗口(Context Window)中。当流程进行到开发工程师的第二次调用时,上下文已经变得无比臃肿且充满冗余信息。LLM在处理超长上下文时,会出现“中间丢失”或理解能力下降的现象,这就是“腐化”。它导致代理开始忽略关键指令,做出匪夷所思的决策。

我原本希望通过增加“控制点”来提升稳定性,但实际上,我增加了“复杂度”。系统变得笨重、缓慢,且因为上下文过载而更加脆弱。一个简单的功能需要两小时和多次循环,这本身就宣告了该工作流不具备实用性。我意识到,能产出合格代码,绝不意味着编排系统本身是可靠的。

避坑指南 :警惕“控制幻觉”。在AI工作流中,每一个新增的检查点、每一个额外的交接文档,都会增加系统的复杂度和认知负荷。在添加之前,必须问:这个控制措施是降低了不确定性,还是仅仅增加了运行成本?它带来的收益是否远超其引入的延迟和故障风险?

4. 重构之路:做减法,回归工程本质

第二版的失败是一个转折点。我放弃了“更多控制”的思路,转而追求一个 更轻、更快、更确定 的系统。第三版的设计哲学是:最大化核心路径的效率,最小化非核心的流程开销,用低成本、高确定性的本地工具替代重量级的外部服务。

4.1 核心简化策略一:轻量化交接

上下文腐化的根源是过重的中间产物。我的优化策略是:

  • JSON化交接 :将代理间传递的、主要供机器读取的文档(如任务状态、文件变更列表、检查结果)从Markdown格式改为结构化的JSON。JSON更紧凑,解析更确定,能极大减少无意义的文本膨胀。
  • 保留必要的Markdown :仅对那些需要人工阅读或AI进行复杂推理的文档(如用户故事、架构设计思路)保留Markdown格式。
  • 精简代理指令 :重新审视每个代理的系统提示,删除冗余的、鼓励“废话文学”的指令,聚焦于清晰、可执行的动作。例如,将“请你作为一个资深的架构师,仔细思考并输出一个全面、稳健、可扩展的方案…”这类模糊指令,改为“基于需求锁,输出一个包含以下必填章节的实施计划:1. 模块划分;2. 接口定义(使用OpenAPI格式);3. 核心类结构;4. 测试策略。”

4.2 核心简化策略二:引入“断路器”与“红牌”逻辑

为了避免无限循环的修复黑洞,我引入了软件工程中常见的**断路器(Circuit Breaker)**模式。

  • 机制 :当开发工程师代理连续多次(例如3次)提交的代码都无法通过基础编译或单元测试时,流程不会在原地死循环。相反,系统会触发“红牌”,将任务 升级(Escalate) 回给架构师代理,要求它重新评估并修订实施方案。
  • 价值 :这模仿了真实的工程管理。当执行层反复失败时,问题可能出在方案层。这个简单的机制将无脑重试变成了有意义的流程反馈,防止了资源在错误路径上的空转。

4.3 核心简化策略三:拥抱轻量级本地工具链

我彻底放弃了SonarQube这种重型、外部、耗时的质量门禁。取而代之的是一套在本地瞬间运行的静态分析工具组合:

  • Checkstyle :检查代码风格(缩进、命名、导入等)。
  • PMD/CPD :检查潜在代码问题(如空循环、未使用变量)和代码重复。
  • SpotBugs :基于字节码分析,查找潜在的Bug模式。
  • JaCoCo :检查单元测试覆盖率。

更重要的是,我将这些工具的调用封装进了简单的本地Shell脚本,如 ./scripts/quality-check.sh 。这个脚本按顺序、安静地运行所有检查,并汇总结果。相比于调用外部Sonar服务、等待扫描、解析HTML报告,本地工具链的反馈是 即时 确定 的。它没有网络延迟,没有服务依赖,报告格式稳定,完美契合自动化流程对“确定性”和“速度”的要求。

同时,我明确了测试金字塔,为代理提供了清晰的测试指令模板:

  • 单元测试(Unit) :使用JUnit/Mockito,针对单个类或方法。
  • 组件测试(Component) :使用Spring Boot Test,测试单个Spring Bean或一组协作的Bean。
  • 集成测试(Integration) :使用Testcontainers,测试与真实数据库、外部API的集成。

4.4 核心简化策略四:产出真正有用的“施工图”

我强化了对架构师代理的要求。它产出的“实施方案”不能再是泛泛而谈的设计原则,而必须是一份对开发工程师直接可用的“施工图”,强制包含:

  • 任务切片 :将功能拆解成可独立实现和验证的小代码块。
  • 负载示例 :明确的API请求/响应示例(JSON格式)。
  • 类结构图 :核心类的名称、职责和关系。
  • 日志规范 :在哪些关键点需要打印何种级别的日志。
  • 测试覆盖要求 :明确哪些类/方法必须被单元测试覆盖,以及集成测试的边界。

这份文档的目标是,在写第一行代码之前,就最大限度地消除开发工程师的猜测空间,将非确定性的设计决策提前并固化。

5. 稳定性的诞生:简化带来的收益

经过上述重构,第三版工作流虽然没有变得完美,但发生了一个根本性变化: 它不再因为自身的复杂性而在运行中途崩溃了

  • 上下文使用显著改善 :轻量化的JSON交接和精简的提示词,使得整个流程的上下文长度减少了约40%。代理不再因为信息过载而“失忆”或“胡言乱语”。
  • 交接稳定性提升 :结构化的JSON交接确保了状态传递的准确性,再也没有出现评审产物损坏的情况。
  • 虚假通过率下降 :开发工程师代理提交明显错误代码(如无法编译)并声称通过检查的情况基本消失。本地工具链提供的即时、准确的反馈,让代理的“自我评估”有了可靠的依据。
  • 运行时间减半 :整个流程的耗时从超过两小时稳定控制在一小时左右。效率的提升主要来自于移除了重型外部检查、减少了无效循环以及更高效的信息传递。

这个对比清晰地表明,问题不全在于模型本身的能力,而在于工作流设计所产生的 操作噪音(Operational Noise) 。当交接变得轻便,检查变得快速、本地且确定后,编排系统自身的稳定性得到了质的提升。

6. 未竟之战:稳定之上的成熟度挑战

流程稳定了,但距离产出“可维护的工业级代码”还有差距。第三版暴露了新的问题,这些是下一阶段优化的焦点:

1. 计划文档的“肥胖症” 即使是一份好的实施方案,如果它超过200行,本身就会成为下游代理的负担。我们必须对架构师产出的计划长度做出硬性限制,强制其精炼、聚焦。好的设计文档应该是地图,而不是地貌百科全书。

2. “测试通过”不等于“功能可用” 开发工程师代理倾向于在单元测试通过后就标记任务完成。但绿色测试只是必要条件,而非充分条件。必须在评审之前加入强制的、自动化的API冒烟测试,覆盖 正常路径(Happy Path) 和关键的 异常路径(Error Path) ,验证接口的真实可用性。

3. 交接纪律的最后一公里 虽然交接格式规范了,但代理有时仍会在计划外创建临时文件或进行未记录的修改。需要更严格的“沙箱”或“变更集”管理,确保每个代理的产出都严格对应其输入和计划,做到完全可追溯。

4. “红牌”机制的自动化 目前红牌逻辑还需要人工判断或简单的规则触发。下一步需要将其深度集成到流程状态机中,实现全自动的失败检测与升级策略。

5. 代码“形貌”质量的缺失 本地工具链解决了基础的代码风格和Bug问题,但对一些更高级的“代码味道”缺乏约束。例如:

  • 重复的字面量字符串应提取为常量。
  • 构造函数风格应保持一致(使用Builder模式或全参构造)。
  • Mockito桩应使用更规范的匹配器(如 eq() any() )。 这部分可能需要引入定制化的代码模板或更高级的静态分析规则。

7. 回归与启示:为什么我决定保留这个“不完美”的版本

第三版代码并不完美,但它是我第一个决定保留下来,作为未来工作流 基础版本(Seed Version) 的迭代。它不是一个终点,而是一个真正可用的起点。

这次重构给我的最大启示,或许与技术无关,而与工程实践的本质有关。在云原生、一站式平台大行其道的今天,我们几乎忘记了那些简单、本地、可理解、低成本运行的 本地工具 的价值。Checkstyle、PMD、一个精心编写的Shell脚本——它们没有炫酷的仪表盘,但提供了即时、确定、无依赖的反馈。正是这种确定性,给了AI工作流最急需的稳定性。

当流程本身不再成为最大的风险来源时,我们的关注点才能真正回归到本质问题上:AI协作产出的代码,其可读性、可维护性、架构合理性究竟如何?它是否像一位优秀工程师的作品?这才是AI辅助开发能否进入生产级的最终考验。我的旅程还在继续,但至少,我现在拥有了一条不会自己塌方的跑道。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐