第 8 讲:Agent Native 开发:状态机、自动化门控与工程反馈环境建设
导读
在前序课程中,我们探讨了如何用需求分析、架构设计与版本控制来弥合人类意图(Intent)、规格说明(Spec)与代码实现(Implementation)之间的缝隙。然而,纸面上的分析终究要迈出构建可运行原型的第一步。当 AI Agent 从辅助问答工具演变为能够直接读写文件、执行命令的自主开发者时,软件工程的核心命题发生了深刻转移:问题从“如何用提示词让模型写出一段代码”,扩展为“如何围绕一个能力强大却不可信、缺乏工程品味的开发者,设计一套可持续推进、自动验证与自我修正的运行与反馈配套设施(Harness)”。
本讲从“Let it fail”的原型哲学出发,将工作区建模为状态、将 Agent 的每轮执行建模为状态迁移,系统阐述如何通过受预算与验收约束的 Agent Loop 推进长程任务,并从“开发者不可信”的第一性原理重新推导 CI/CD 门控、异常发布 Spec 以及工业界的前沿反馈实践,帮助工程师在遏制 AI Slop 泛滥的同时,看清局部技术债清理与深层架构重构的边界。
机制总览
实现 Agent 可以反复修改代码,但必须由独立的规则控制是否合并与部署。失败反馈用于修复,预算耗尽时停止自动重试。
读图:机制与适用边界
实现进入 Lint 与测试,再经过合并门禁;主干更新后重新检查并构建,部署绑定该次提交的产物。失败返回修复分支,超出预算则停止并交接。
实现者不能自行降低验收标准。部署还应拒绝过时产物覆盖新版本。
第一章 从意图到可运行的原型:用“Let it fail”暴露隐含假设
在软件工程中,人类的主观想法如何转化为可靠运行的计算机系统?前序课程探讨了意图、规格说明与代码实现之间的两道核心缝隙,并介绍了如何用版本控制管理状态变化、用需求与架构设计管控系统复杂性。然而,纸面上的规格说明无论多么严密,都无法在静态推演中穷尽真实世界的运行条件与边界冲突。为什么在理解了这些理论与缝隙之后,软件工程必须尽快迈出构建可运行原型的第一步?本章将阐明原型验证的核心机制,并引出以智能体(Agent,能够自主感知环境、调用工具并执行动作的AI系统)为开发主体的工程新范式。
意图、规格与实现的缝隙回顾
在进入智能体原生(Agent Native,从项目创立之初就将AI智能体作为核心开发者并为其定制工作环境)开发之前,我们需要先审视传统软件工程一直在解决什么根本问题。当一个想法诞生时,为什么不能直接变成最终产品?软件从概念走向落地的链条中存在两道结构性断层,前序课程为此准备了相应的版本管理工具与架构设计原则。
前半程课程建立了两大支柱。
- 第一部分是软件工程与项目管理:核心任务是弥合意图(Intent,人类脑海中期望解决问题的初衷)、规格说明(Spec,对系统应当具备何种行为与约束的明确定义)与实现(Implementation,具体编写出的可执行代码)之间的缝隙(Gap);项目管理则依赖 Git 与 Jujutsu(支持灵活管理工作区状态与变更的现代分布式版本控制系统)等工具来保存、比较和协调代码变化。
- 第二部分是需求和架构:软件系统面临不可避免的本质复杂性(Essential Complexity,由业务问题本身的逻辑难度决定的复杂性)以及需要应对并消减的偶然复杂性(Accidental Complexity,由开发工具、表示形式或特定实现方式额外引入的复杂性)。
课堂提示:从意图到规格说明是一对多的映射,从规格说明到具体实现同样是一对多的映射。在一个庞大的设计空间(Design Space,所有可能的设计与实现方案构成的集合)中,缺乏经验的开发者往往只采样到一个点就一路走到黑。我们在需求阶段做访谈与核对单、在架构阶段用统一建模语言(UML)建模或讨论是否采用事件溯源(Event Sourcing,通过记录所有状态变更事件而非仅保存当前状态来持久化数据的方法),本质上都是在约束搜索空间,以便尽早与协作者达成共识。然而,仅仅停留在理念探讨和纸面建模上,永远无法真正闭合这些缝隙。
构建可运行原型:用失败检验隐含假设
既然需求分析与架构设计如此重要,我们是否应该在动工前把规格说明推敲到完美无缺?答案是否定的。纸面逻辑的自洽并不等于真实系统的可用。“Let it fail(让它暴露失败)”在软件工程中有着具体的检验指向:工程先驱正是通过原型的运行失败,来反推底层设计前提与基础数据的偏差。
无论前期讨论多充分,在某个时刻我们都必须开始行动:构建一个符合当前规格说明的可运行原型,允许它运行并暴露问题(Let it fail),从而观察我们究竟造出了什么。原型的核心工程价值在于检验预期:代码符合规格说明,并不保证规格说明中潜藏的假设成立;原型能将隐含假设转化为可观察的具体结果。美国国家航空航天局(NASA)记载的航空史案例给出了直观印证:1901 年,莱特兄弟测试滑翔机时发现实测升力仅达到理论预测值的 1/3;这次失败促使他们随后自建风洞,重新测量翼型的升力与阻力数据,从而修正了原先设计所依据的底层前提数据。
讲义提醒:允许原型暴露错误绝不是鼓励敷衍交差。例如一个在纸面上逻辑完全自洽的选课系统,一旦放进真实环境,遇到两个并发请求同时争抢最后一个课程名额,就会暴露出静态规格说明未曾覆盖的竞争缺陷。先把想法变成可观察的运行实例,团队才能判断下一步究竟该修改代码实现、修正规格说明,还是推翻原有架构。随着AI大幅降低了编码门槛,开发者随时可以将初步想法转化为原型版本;在课程实验“Deeper Research”中,老师甚至已将个人播客生成、晨间论文推送等日常定时任务(Cron Jobs)迁移给个人智能体。当越来越多实现工作交给AI,核心问题便从“如何让模型写出一段代码”转向“如何组织一个能持续开发、验证与修正的软件生产过程”。
讲义提醒:“Let it fail”的三层检验机制
构建原型并允许其失败,除了验证代码语法与基本逻辑,还同时检验软件工程的三道防线:第一,检查具体实现是否真正满足了既定的规格说明;第二,检查规格说明是否准确表达了用户的真实业务需求;第三,即使实现与规格严丝合缝,还要检验需求背后关于用户行为模式、并发规模和底层运行环境的隐含假设在现实世界中是否依然成立。
课堂提示:借助AI降低原型试错门槛
在传统开发中,构建原型需要耗费大量人力编写基础代码;而在AI时代,大模型赋予了开发者随时随地启动原型的能力。哪怕当前只有一个初步的意图或粗略的规格说明,也可以立即让智能体生成一个可运行的版本,通过构造典型用例(Use Case)直接观察系统的实际表现,甚至在多个终端窗口并行探索不同的子系统方案,从而更快地发现模块间未曾预料的交互问题。
核心易错点:切勿曲解原型验证与史实边界
切不可将“Let it fail”曲解为不需要需求分析和规格说明的盲目乱写;恰恰相反,原型必须基于当前规格说明构建,其失败才有明确的对照基准来反推假设偏差。同样,在理解1901年莱特兄弟滑翔机案例时,应严格把握其“实测升力仅为预测值1/3,进而通过自建风洞重新测量翼型升阻力数据以修正设计依据”的工程检验逻辑,不可脱离史实过度演绎无关的工程细节。
术语消化:第一章核心软件工程术语与机制对照表
| 术语 | 解决的问题 | 机制与课程关系 |
|---|---|---|
| 意图、规格与实现(Intent, Spec, Impl) | 人类模糊想法无法直接变为可靠可执行软件的断层问题 | 通过需求分析将意图转化为规格说明,再通过编码将规格转化为实现,构成软件工程致力于弥合的两道核心缝隙 |
| 本质复杂性与偶然复杂性(Essential & Accidental Complexity) | 区分系统中哪些复杂度不可消除、哪些可以靠工程手段削减 | 本质复杂性源于业务问题本身的固有难度,需靠架构设计合理安置;偶然复杂性源于工具或表示方式,需通过工程演进不断消减 |
| 版本控制系统(Git / Jujutsu) | 多人或多智能体协作时工作区状态保存、比较与演进的混乱 | 将每次开发变更记录为可追溯、可分叉与可合并的历史状态,为后续将开发过程建模为状态机提供底层支撑 |
| 可运行原型与“Let it fail” | 静态规格说明无法暴露并发冲突、环境限制等隐含假设的缺陷 | 尽早构建符合当前规格的可运行系统,通过实际运行中的失败反推实现偏差、规格遗漏或底层前提错误 |
本章小结
本章回顾了软件工程从意图、规格说明到代码实现的核心链条,区分了本质复杂性与偶然复杂性。
在此基础上,本章阐明了构建可运行原型的必要性:符合规格说明并不保证其背后的隐含假设成立,通过“Let it fail”让原型在实际运行中暴露并发或底层数据偏差,才能看清修改方向。AI大幅降低了构建原型的门槛,也将课程引向如何为智能体构建可持续开发、验证与修正的工程环境。
第二章 Agent Loop 与状态机视角:在变化的工作区中推进长程任务
第一章明确了用可运行原型暴露隐含假设的必要性,但当原型构建与演进交由 AI Agent 承担时,新的工程挑战随之浮现:单次对话中的模型能力有限,容易在超长上下文中发生注意力漂移,甚至缺乏决定何处应当抽象、何处应当保持简单的工程品味。本章将探讨如何通过循环机制(Agent Loop)与工作区状态机模型,将跨越多次会话的长程任务(Long-horizon Task)拆解为受控的状态迁移,并结合设计空间搜索与客观评估,让算力真正转化为可靠的工程进展。
从命令行完备性到最小 Bash Agent 的工程边界
大语言模型从最初的聊天问答演进为能够读写文件、调用命令的自主智能体(Agent),引发了一个关于自主性极限的思考:如果只给模型提供一个命令行入口,它究竟能走多远?这里需要区分命令行工具链在理论上的表达完备性,与当前模型在真实软件工程中面临的能力边界。
从 ChatGPT 到 AutoGPT、Devin 与 Claude Code,AI 系统的行动自主性不断增强。讲义提出了一个推演自主性极限的思想实验:假设模型足够聪明,并拥有充足的工具、资源与行动权限,那么仅凭一个 Bash 工具入口,它理论上就能像早期 UNIX 大师在无图形界面的 Shell 中利用命令行工具链完成排版与编程一样,去组织完成造芯片或证明黎曼猜想等极端复杂的任务。
然而,这只是对工具完备性的夸张表达,绝不意味着今天的模型已经具备这些能力。
现实的瓶颈在于,当前模型远未达到万能,尤其难以仅凭一句提示词就学会人类的工程品味——即判断什么值得抽象、哪里应该保持简单、什么改动正在破坏可维护性。
为了看清理论自主性与真实工程之间的差距,课堂拆解了一个仅 150 行 Python 代码的最小 Bash Agent 原型:外层是一个 while 循环,读取终端输入并调用 api.deepseek.com 的对话接口;若返回消息包含工具调用(Tool Call),则解析出 command 参数交由本地 run_bash 函数执行,并将执行输出封装为角色(role)为 tool、携带对应调用 ID 的消息回传给模型。这 150 行代码跑通了 ReAct(推理与行动)协议的核心,但也立刻暴露出玩具原型与工程产品的鸿沟:原型中硬编码了命令执行的超时时间(Timeout)。老师以亲身经历举例:在境内教育网与境外高带宽网络下执行 git clone,下载速度存在巨大差异;缺乏环境感知的硬编码超时会直接误杀正常进行的长耗时任务。此外,课堂提示在终端调试此类拥有 Shell 权限的 Agent 时,必须严密保护本地配置文件中的 API Key,避免离开座位未锁屏时被直接 cat 读取泄露。既然单次执行与简单循环如此脆弱,我们便需要更结构化的外部控制框架(Harness)。
Ralph Wiggum Loop:笨拙但执着的外部循环
既然单次模型回答无法一次性解决复杂工程问题,且模型在单次长会话中容易偏离方向,最直接的工程直觉便是引入重试与迭代。Ralph Wiggum Loop 正是将这种直觉极简化的代表:它放弃复杂的单次长程内部记忆,转而采用朴素的外部循环。
Geoffrey Huntley 提出的 Ralph Loop 借用了动画《辛普森一家》中角色 Ralph Wiggum 的名字。Huntley 用这一角色来形容这种工作方式的核心特质:看起来有点笨拙、有点可爱,但从不放弃。在面对复杂的软件开发任务时,与其寄希望于模型在一次极其漫长的推理中完美规划所有细节,不如接受模型单步能力的局限,通过外部脚本驱动它一轮又一轮地反复面对同一个问题。这种不依赖精巧内部状态、只靠持续外部触发推进任务的朴素形式,构成了现代 Coding Agent 外部控制循环的雏形。
为什么“有点笨但从不放弃”的策略在工程上反而有效?这与当前大语言模型的训练机制和上下文特性密切相关。在强化学习训练下,模型往往背负着尽快完成任务以获取奖励的压力,这使它在面对开放式设计空间(Design Space)时,极易像缺乏经验的博士生一样陷入深度优先搜索(DFS):抓住第一个看似合理的想法就一路写到底。更糟糕的是,当已有代码库中存在质量低劣的冗余代码(AI Slop)时,模型出于遵循指令和迎合用户的倾向,往往不会像严厉的导师那样推翻重写,而是试图把烂代码强行“扶起来”,在泥潭中越陷越深。因此,打断模型的单次长驱直入,强制其在外部循环中分步推进,成为了约束模型行为的关键手段。
同一个提示词与不断变化的世界
打断单次长会话后,如果每一轮都向模型发送完全相同的提示词,系统为何不会陷入原地踏步,反而能逐步推进项目?答案在于“提示词不变”而“外部工作区在变”,这一机制也减少了跨轮长上下文累积带来的性能退化。
Ralph Loop 的核心原理由一句总结概括:“The prompt stays the same, but the world changes(提示词保持不变,但世界在变化)。”其骨架代码仅有三行 Shell 脚本:while true; do cat PROMPT.md | claude -p; done。在这套机制中,每一轮执行都是一次全新的对话会话;模型读取固定的 PROMPT.md,检查当前工作区中的成果,选择下一项具体工作进行修改并验证,随后当前会话直接结束。跨轮进展保存在工作区文件(如已实现的代码、测试报告、失败原因记录)中,而非不断累积的聊天历史里。但这套循环有严格的工程前提:while true 本身没有任何魔法,它既不会自动识别任务“已经完成”或“无法改进”,也不保证下一轮一定比上一轮更好。
讲义与课堂特别强调,单靠一段不断增长的聊天记录无法维持长程任务。当单次会话的上下文逼近上限时,现有的 Coding Agent(如 Codex 等)会触发上下文压缩(Compact);而在压缩发生后,模型往往会经历明显的注意力涣散与“降智”,丢失关键约束并频繁犯错。通过每轮重启会话并将关键状态落盘,Ralph Loop 显著减少了跨轮长上下文累积的依赖;不过单轮 claude -p 若执行时间过长仍可能触发会话内压缩,且落盘交接若遗漏关键证据也会丢失信息,因此仍需控制单步任务粒度与交接质量。在课堂演示中,老师用约 150 行的 Agent 配合 5 行循环脚本,在 Prompt 中要求模型:“每轮先检查当前状态,只推进一个明确步骤;未完成时写文件说明下一步;确认全部完成后输出 RALPH_DONE。”但在严肃工程中,仅靠模型自报 RALPH_DONE 仍不够安全,必须额外定义明确的验收条件、Token 或时间预算以及强制停止条件;当连续多轮在同一错误上原地打转时,更需要人工或高层机制介入,判断是否是任务表述、工具链或底层架构出了问题。
一切皆状态机:工作区状态与受控迁移
为什么每轮重启会话的 Agent 能够接续上一轮的工作?要形式化地理解并设计这种跨越多次会话的开发过程,我们需要引入计算机科学中最基础的抽象模型。将整个软件开发生命周期重构为状态机后,需要厘清“状态”所包含的完整工程要素,以及版本控制如何支持状态分叉与并行探索。
理解 Agent Native 开发的最佳视角是“Everything is a state machine(一切皆状态机)”。在这个模型中,工作区(Workspace)就是状态(State),而 Agent 的单轮执行就是状态迁移(Transition)。工作区状态不仅包含当前的源代码,还包含需求与设计文档、当前部分实现、测试用例、程序运行结果、错误日志以及待办事项(TODOs)。Agent 读入当前工作区状态,推进一小步并验证,将其转化为下一份状态:例如从“功能未实现”迁移到“已有实现但测试失败”,再迁移到“定位原因并修复通过”。这也使跨越许多步骤与会话的长程任务(Long-horizon task)成为可能,其前提是新一轮 Agent 仅凭工作区留下的材料就能恢复完整判断。此外,借助 Git 或 Jujutsu 等版本控制工具,状态可以分叉(Fork),从而支持多个 Agent 在不同工作区并行推进。
将工作区视为状态,改变了工程文档的角色:文档承担的是跨会话的协作状态,而非工作量展示。讲义对比了两种交接方式:写下“选课功能已完成”是毫无价值的含糊交接;而记录“普通选课已通过,两个请求竞争最后一个名额的测试失败,失败记录位于某份日志”,则提供了确凿的证据与下一轮继续工作的起点。在非确定性状态机视角下,同一个状态还可以引出多条迁移边,构成一棵设计空间搜索树(对应实验二 Deeper Research 的“算力换智力”):我们可以结合宽度优先搜索(BFS)与深度优先搜索(DFS),不仅让同一个模型单线推进,还可以引入异构模型(如让 GPT 与 Claude 互相挑刺评审),或定期启动专门的重构 Agent 清理劣化代码。在物理执行上,开发者可以利用 git worktree 配合终端分屏(如老师在 tmux 中开启四分屏同时监工四个 Agent)实现多分支并行探索。但讲义明确提醒,版本控制允许状态分叉,并不自动保证并行任务彼此独立;两个各自正确的改动合并时仍可能冲突或破坏系统,因此任务划分、依赖协调与集成检查不可或缺。此外,投入更多算力与循环并不必然带来更好的结果,在课堂生成个人 HTML 名片的背靠背对比中,单次直接生成(One-shot)的版本虽然拥有华丽的玻璃动效与终端界面,却写错了邮箱和院系标签;而经过 Loop 查证的版本虽然外观朴素,却准确核对了计算机学院副教授、邮箱与 GitHub 链接。这也说明必须通过固定输入与评价标准的基准测试,才能客观检验状态迁移是否真正带来了正向收益。
有效状态交接与长程任务恢复
在长程任务(Long-horizon Task)中,每次会话都会重置上下文,工作区文件是连接前后两轮 Agent 的唯一桥梁。有效的工作区状态必须让接手者无需阅读上一轮聊天记录就能恢复工程判断:明确当前目标是什么、已经完成了哪些改动、哪些结论有测试或日志证据支撑、哪些具体边界情况尚未通过(并附带日志路径与复现方式)。冗长却缺乏证据的总结只会污染后续轮次的上下文。
结合宽度与深度的“算力换智力”搜索
在实验二(Deeper Research)中,解决问题被建模为在求解状态(Solve State)上的树搜索。单靠强化学习训练的模型容易沿单一思路深度优先(DFS)一路走到黑;通过注入高层引导提示词(如“找出证明中最可疑的一步”“寻找相反结论的资料”)或引入异构模型(如 GPT 与 Claude)交叉评审,可以在浅层设计空间实现宽度优先(BFS)采样,在算力预算内保留最优候选解。
警惕无限循环与无效的算力堆砌
切忌将 while true 视为自动解决一切问题的魔法。第一,缺乏预算限制、停止条件与客观验收标准的循环,只会在错误方向上无限燃烧 Token;第二,复杂的循环与提示词框架未必优于单次直接输出,且旧提示词在模型版本升级后可能产生负作用。工程上必须固定输入资料与基础模型,对直出、低预算与高预算方案进行盲评或背靠背消融测试。
术语消化:Agent Loop 与状态机开发模型核心术语
| 术语 | 解决的问题 | 机制与课程关系 |
|---|---|---|
| Ralph Wiggum Loop | 单次会话无法完成复杂任务,以及超长上下文压缩(Compact)引发的模型注意力涣散与降智。 | 通过外部循环每轮重启全新会话并传入相同提示词,依靠工作区文件传递跨轮次进展,减少跨轮聊天历史累积。 |
| 工作区状态机(Workspace as State Machine) | 跨步骤、跨会话协作时的工程上下文丢失与状态漂移。 | 将代码、文档、测试、日志与待办事项统一建模为工作区状态(State),将 Agent 的每轮小步修改与验证建模为状态迁移(Transition)。 |
| 长程任务(Long-horizon Task) | 任务复杂度远超单次模型调用的规划能力与上下文窗口容量。 | 通过将大任务拆解为一系列可独立验证的状态迁移步骤,并把中间结论与失败证据持久化到工作区,实现跨会话接力。 |
| 算力换智力(树搜索与异构评审) | 模型在开放设计空间中容易陷入单一路径(DFS),缺乏全局方向直觉。 | 将候选解与反馈视为求解状态,结合 BFS 探索多条提示词分支或利用异构模型互相挑刺,在预算内筛选更优解。 |
# 1. 最小 Bash Agent 核心循环(课堂约 150 行原型之协议骨架)
while True:
user_input = read_from_terminal()
messages.append({"role": "user", "content": user_input})
while True:
reply = call_chat_completions_api(messages, tools=[bash_tool_schema])
messages.append(reply)
if not reply.get("tool_calls"):
print(reply["content"])
break
for tool_call in reply["tool_calls"]:
if tool_call["function"]["name"] == "bash":
cmd = parse_json(tool_call["function"]["arguments"])["command"]
# 注意:原型中硬编码 timeout 易因网络/环境差异误杀正常任务
output = run_bash(cmd, timeout=30)
messages.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"content": output
})
# 2. 带停止条件与预算控制的受控 Ralph Loop 伪代码
# 原始骨架:while true; do cat PROMPT.md | claude -p; done
for step in range(MAX_STEPS):
output = run_agent_new_session(prompt_file="PROMPT.md", workspace=".")
if "RALPH_DONE" in output and verify_acceptance_criteria(workspace="."):
break
上半部分展示了课堂演示的最小 ReAct Agent 协议:通过嵌套 while 循环解析模型的 bash 工具调用,执行本地命令并将标准输出与错误封装为 tool 角色消息回传。下半部分展示了如何将原始的无条件 while true Ralph Loop 改造为具备最大步数预算(MAX_STEPS)与客观验收检查(verify_acceptance_criteria)的受控状态迁移循环。
本章小结
本章从命令行工具的理论完备性出发,结合 150 行最小 Bash Agent 的超时局限,阐明了为何需要外部控制循环。通过剖析 Ralph Wiggum Loop“提示词不变而世界变化”的机制,我们将软件开发抽象为以工作区为状态、以 Agent 为迁移的状态机,并融入设计空间树搜索与盲评对比。
但既然每次开发都是状态迁移,且 Agent 自身并不可信,我们如何保证进入主干的每一个状态都是“好”的?下一章将由此重新发现并构建面向 Agent 的 CI/CD 门控体系。
第三章 每次状态迁移都检查:从“开发者不可信”重新发现 CI/CD
上一章我们将软件开发建模为状态机:工作区是状态,Agent 的每次执行都是一次状态迁移。然而,无论是人类开发者还是 AI Agent,在推进状态迁移时都天然伴随着过度自信、投机心理或上下文漂移。如果任由未经检验的迁移直接合入主干,错误就会迅速累积并污染后续所有开发循环。既然不能依赖开发者的自觉来维持状态质量,我们该如何把“好状态”的标准转化为每次迁移时自动执行、且具备强制约束力的工程门控?本章将从这一第一性原理出发,带你重新发现并构建 CI/CD 流水线。
开发者不可信:每次迁移后的强制验证
在 Ralph Loop 中,提示词明确要求每轮只推进一个明确步骤,随后必须验证并评估结果。为什么不能让开发者连续改完所有代码再一次性检查?当工作区作为系统状态不断演进时,人类开发者的投机心理与模型的上下文漂移,都容易在单次状态迁移中引入破坏性变更。
状态机开发模式下的核心防线是:每次状态迁移(Transition)都必须伴随检查。这一要求源于“开发者是不可信的”这一基本假设。无论是人类还是 Agent,在修改工作区(Workspace = State)时,都容易产生“自以为只改一点点不会有事”的盲目自信。既然我们希望进入主干的每一个状态都是“好”的,就必须在每次迁移发生时执行自动化检查,而不能仅靠开发者的自我约束。
只有把抽象的“好”转化为可机械执行的条件——代码能够构建、既有功能未被破坏、符合项目的各项约定——并在检查通过后才允许放行甚至直接发布,状态机的演进才能保持收敛与可靠。
课堂上回顾了计算机系统基础课程的两条公理:“机器永远是对的,未测试的代码永远是错的”。人类开发者在做算法题时常有投机心理(Fast Path 博弈),觉得样例过了提交又不花钱,结果往往出错;在工程中,“只改了一行文档”“只把循环变量从 1 改成 0”或实习生未经充分测试推上来的 PR,往往就是生产环境宕机的开场白。Agent 同样容易在长上下文中发生思路漂移。因此,不信任任何开发者的口头保证,强制在每次状态迁移后停下来做检查,正是软件工程走向自动化的起点。
从集成地狱到小步快跑:CI 与两种 CD 的演进
当我们规定“每次状态迁移后都自动运行构建与测试,通过后自动推进发布流程”时,我们实际上已经在概念上重新发明了现代软件工程的核心基础设施——CI/CD。梳理这一实践从 1990 年代至今的演进脉络,需要明确区分持续集成(CI)以及两个容易混淆的 CD 各自划定的自动化边界。
CI/CD 的演进围绕着如何缩小集成粒度与延伸自动化边界展开。在 1990 年代,Kent Beck 与极限编程(XP)推动频繁集成,核心逻辑是单次改动越小,出错后需要排查定位的范围就越窄,从而摆脱多人长期各自开发最后合并时爆发冲突的“集成地狱”。2000 年 Martin Fowler 与 Matthew Foemmel 系统总结了持续集成(CI),2001 年 Thoughtworks 开源了 CruiseControl 以支持自动构建持续运行;到 2010 年,Jez Humble 与 David Farley 在《Continuous Delivery》中将自动化验证延伸至发布流水线。由此形成了三个边界分明的核心术语:持续集成(Continuous Integration)指频繁向共同主干集成并自动构建验证;持续交付(Continuous Delivery)要求软件始终处于可发布状态,但何时真正上线可由人工决定;持续部署(Continuous Deployment)则将通过检查的变更全自动发布给最终用户。
在大模型出现之前,搭建这套流水线往往需要深入理解容器环境与企业级构建系统,门槛较高;但其背后的工程直觉始终如一:用高频、小步、可重复的自动化检查替代低频、大批量的人工集成。明确区分持续交付与持续部署尤为关键:许多严谨的业务系统要求主干随时具备上线条件(持续交付),但最终推向用户的开关仍掌握在发布负责人手中;而在完全信任自动化门控的场景下,才进一步跨越到打上标签或合入主干即自动上线的持续部署。
事件驱动的 Hook 与具备约束力的远程门控
理解了 CI/CD 的概念后,接下来的工程问题是:这些自动检查究竟挂载在开发流程的什么位置?如果开发者在本地配置了检查脚本,是否就意味着主干状态得到了保护?从事件驱动(Hook-Based)的视角看,自动化检查需要绑定到明确的生命周期节点上,且本地检查与远程门控在强制约束力上存在本质差异。
CI/CD 的运行机制是事件驱动的钩子(Hook-Based):当特定生命周期事件发生时,系统自动执行绑定好的动作。前序课堂尝试让 Agent 在输出结束时“播放声音”提醒人,说明不能指望模型自己“记得”在合适时机做某事,而必须把动作挂接到明确的生命周期事件节点上。在版本控制流程中,commit、push、发起 pull request 等都是标准事件节点;例如配置 GitHub Actions 在 PR 新增提交时自动运行静态检查(Lint,即不运行程序只做静态分析就能发现格式与规范问题的代码检查)和测试。同时,检查的位置决定了它的约束力:本地 pre-commit 钩子虽能提供快速反馈,但可被 --no-verify 参数轻易跳过;只有将远程检查设为必过条件(Required Checks)并配合分支保护(Branch Protection),才能让检查失败真正阻止代码合并。
讲义提醒我们,“有检查”和“检查有约束力”完全是两回事。如果测试失败后代码依然能照常合入主干,那么 CI 就只是在旁边亮起了一盏无人理睬的红灯。在具体实践中,我们可以利用本地 pre-commit 快速拦截缩进、空格等基础 Lint 问题;而在远端,无论是利用高校 GitLab 提供的免费 Docker 容器,还是利用 GitHub Actions 每月约 2000 分钟的免费额度,都必须在仓库设置中开启分支保护,禁止直接向主干推送并强制要求状态检查全绿。甚至还可以基于这套事件机制,在云端 Action 中拉起 Claude Code 构建自动修复的 Ralph Loop。
软件工程第一性原理:开发者是不可信的
无论是人类工程师还是 AI Agent,在修改代码时都存在天然的脆弱性。人类出于节省时间的投机心理(Fast Path 博弈),常在修改一行代码或调整循环边界后跳过完整测试;Agent 则会因长上下文注意力涣散或指令漂移而破坏既有约束。因此,围绕 Agent 构建的运行与反馈配套设施(Harness)不能依赖开发者的自觉或口头承诺,必须假设每一次状态迁移都可能引入错误,将独立、自动化的机械检查作为状态准入的必要门槛,并配合对需求与架构变更的独立审查。
课堂提示:从“播放声音”理解事件钩子(Hook)
在课堂演示中,老师曾希望 Agent 在完成每轮输出、等待人类输入时自动播放提示音。如果只在提示词里嘱咐 Agent“结束后记得发声”,模型很容易遗忘或执行失败;只有将“播放声音”写成挂接在 Agent 会话结束事件上的系统级 Hook,提醒才变得完全可靠。CI/CD 的机制与此相同:将构建、Lint 与测试直接绑定到 Git 的 commit、push 和 pull request 事件上,而不是在提示词或口头交代中嘱咐实习生与 Agent“提交前记得跑测试”。
易错点辨析:混淆两种 CD 与无约束力的“假门控”
学习本章时需警惕两个常见误区:第一,切勿将持续交付(Continuous Delivery)与持续部署(Continuous Deployment)混为一谈,前者保证主干产物随时可发布但保留人工上线决策权,后者则消除了人工干预,检查通过即自动上线;第二,切勿以为写好 .github/workflows 脚本就完成了门控建设,若未在远端配置分支保护(Branch Protection)与必过检查(Required Checks),任何人都可无视红灯强行合并或用 --no-verify 绕过本地检查。
术语消化:CI/CD 核心术语与门控机制对照表
| 术语 | 解决的问题 | 机制与课程关系 |
|---|---|---|
| 持续集成 (Continuous Integration, CI) | 多人或多 Agent 长期独立开发导致合并时爆发大规模冲突(集成地狱)及错误难以定位。 | 频繁将小规模状态迁移集成到共同主干,每次集成自动触发构建与测试,缩小故障排查范围。 |
| 持续交付 (Continuous Delivery) | 构建与测试虽自动化,但软件打包、环境验证等交付环节仍存在手工壁垒,无法随时响应发布需求。 | 将自动化流水线延伸至交付环节,确保主干软件始终处于可发布状态,何时真正上线由人工决定。 |
| 持续部署 (Continuous Deployment) | 人工审批与手动发布在大规模高频迭代下成为效率瓶颈。 | 将通过全部自动化检查的变更直接、自动地发布给最终用户,实现从状态迁移到上线的全闭环。 |
| 静态检查 (Lint) | 代码格式混乱、缩进不规范及明显的可疑写法污染代码库,降低人类与模型的阅读维护效率。 | 无需运行完整业务程序即可通过静态分析拦截基础规范问题,充当软件工程最基础一级的守门员。 |
| 分支保护与必过检查 (Branch Protection & Required Checks) | 本地 pre-commit 钩子可被 --no-verify 绕过,或远端 CI 亮红灯后开发者依然强行合并代码。 |
在远端仓库限制直接推送权限,强制要求指定的远程状态检查全部通过后才允许合入主干,赋予检查真正的阻止约束力。 |
本章小结
本章从“开发者不可信”这一第一性原理出发,阐述了为何必须在每次工作区状态迁移后执行强制检查。
我们梳理了从持续集成(CI)到持续交付、持续部署(两种 CD)的演进边界,揭示了基于生命周期事件(Hook)触发检查的底层机制,并说明了只有将远程检查配合必过条件与分支保护,自动化门控才具备阻止错误合入的工程约束力。
第四章 把合并与发布写成 Spec:门控约束力与异常业务规则
上一章确立了把代码库视为状态机、在状态迁移上挂载自动化钩子的基本原理。然而,配置好一条能运行的流水线,并不等于建立起了可靠的工程护栏。在大模型出现之前,复杂的 YAML 语法和平台专用表达式曾将许多初学者挡在门外;如今 Agent 可以轻松代写这些配置文件,但真正决定系统生死的是隐藏在配置背后的异常业务规则。当大模型包揽了语法细节后,人类工程师在设计 CI/CD 规格说明(Spec)时,究竟需要厘清哪些并发冲突、失败处置与权限边界?本章将剖析如何把合并与发布流程本身编写为严密的工程 Spec,并通过负向验收确认门禁真正具备阻止错误变更的约束力。
简单机制背后的琐碎异常规则
在理解了“事件触发动作”的 Hook 机制后,许多开发者误以为 CI/CD 只是一个把本地测试命令搬到云端容器的简单脚本。事实上,正常路径的自动化极易实现,真正的工程复杂度全部集中在多任务并发、不稳定测试、紧急故障响应等异常路径上。当多个合并请求或发布任务在时间上发生交叉时,单纯依赖单分支检查就会失效,工程上必须正面处理四类高频异常场景。
CI/CD 的触发机制本身很简单,但围绕它的异常业务规则却十分琐碎。在 Pre-LLM 时代,手写 .github/workflows 下的配置文件及 ${{ github... }} 等领域特定表达式非常劝退。今天虽然可以把配置生成交给 Agent,但人类必须把异常规则定义为清晰的 Spec,具体包含四类核心问题:
- 第一,组合冲突:两个合并请求(PR)在各自独立分支上测试全绿,但合入主干后却因逻辑互斥破坏了构建。因此必须在合并前验证待合入改动与最新主干的组合;面对高并发提交的忙碌主干,还需引入合并队列(Merge Queue),按序验证当前改动与主干及队列前序改动的组合状态。
- 第二,偶发失败测试(Flaky Tests):当测试因时序或环境抖动偶尔失败时,规格必须明确允许自动重跑的最大次数、何时将该测试隔离(Quarantine),以及由谁负责修复,防止团队养成无视红灯的习惯。
- 第三,紧急例外放行(Override):生产环境突发故障急需热修复上线时,必须明确谁拥有绕过门禁的权限,且放行后系统如何强制留痕并在事后补齐检查。
- 第四,部署并发控制(Deployment Concurrency Control):当两次发布任务在时间上重叠运行,若新版本的构建部署先完成,而旧版本的流水线随后才跑完,必须依靠并发控制机制阻止后结束的旧任务用过时产物覆盖已上线的新版本。
这四类场景说明,没有异常路径定义的流水线在真实协作中极易崩溃。如果不在 Spec 中规定组合验证与并发互斥,即使每个开发 Agent 都在自己的分支上看到了绿色对勾,主干依然会陷入构建损坏或版本回退的混乱。明确了这些边界问题后,下一节我们将以一个具体的课程网站项目为例,看如何把合并与发布的完整要求写成一份可供 Agent 执行的正式 Spec。
课程网站 CI/CD 的完整 Spec 与负向验收
既然配置语法可以交给 AI,人类工程师的核心职责就转化为编写无歧义的流程 Spec,并验证生成出的流水线是否真的有效。如果只要求“测试通过后发布网站”,Agent 写出的工作流很可能遗漏状态重算或产物绑定。一份合格的 CI/CD Spec 需要从“合并条件”“发布条件”与“验收手段”三个维度封堵流程漏洞。
以将课程网站发布到 github.io 为例,一份结构化的 CI/CD 规格说明包含三个部分:
首先是合并的条件。CI 门控必须同时要求静态检查(Lint)与动态测试(通过实际执行程序检验具体业务行为)全部通过;二者职责互补,不可相互替代。每当 PR 有新提交推送时,必须重新触发全套检查,并在合并前验证该分支与最新主干的组合状态。一旦检查失败即禁止合并,且这一要求不能停留在口头,必须落实为代码托管平台的分支保护规则(Branch Protection Rules),在权限层面限制直接向主干 push 以及违规绕过检查的行为。
其次是发布的条件。当 main 主干更新后,流水线必须针对这次特定提交重新运行 Lint、测试和页面构建;仅当全部步骤成功时,才将本次构建生成的精确产物部署到 github.io;若中途任一步骤失败,线上环境必须保留上一版正常页面。
最后是对门控本身的负向验收。流程配置完成后,工程师应当故意在代码中制造一次 Lint 错误和一次测试失败,亲眼确认合并请求确实被系统拦截、错误页面确实无法发布,修复后再确认正常更新,以此检验“门是否真的关得住”。
通过把合并、发布与负向验收明确写入 Spec,我们就在不可信的开发者(无论是人类实习生还是 AI Agent)与生产主干之间筑起了一道有物理约束力的防火墙。当这套门控真正运转起来后,Agent 在高速编码中暴露出的各种非人类偏执与代码劣化倾向,就能被自动化检查及时拦截并转化为自我修正的信号,这正是下一章即将探讨的“CI 阻止,Agent 修正”闭环。
产物绑定与并发防覆盖机制
在持续部署流程中,“主干变绿”绝不等于可以随意抓取工作区文件上线。严格的发布 Spec 要求实行产物绑定:部署阶段上线的文件,必须是针对该次特定主干提交重新构建并通过全部检查后打包的精确产物(Artifact)。同时,必须配置部署并发控制以防止旧任务覆盖新版本:在工作流层设置 concurrency group 串行化或取消排队中的旧构建以减少竞争;由于平台并发组不保证严格的 FIFO 执行顺序,且取消运行中的任务无法撤回已发往外部部署服务的请求,最终发布入口(或部署环境锁)还必须校验待发布产物对应的提交仍是当前允许上线的最新主干代次,使代次校验与发布切换具备原子性或平台等价保证。
课堂提示:向门禁注入故障的负向验收
讲义特别提醒:验收 CI/CD 流程时,只看正常提交能否顺利上线是远远不够的,因为一份漏配了分支保护或错误忽略了返回码的脚本同样能让正常代码通过。正确的工程直觉是进行“负向验收”——故意提交一段违反格式规范的代码或一个必定失败的断言,验证远程平台是否真正锁死了合并按钮、阻断了页面部署。只有亲眼验证过失败路径能够拦住错误,这套门禁才算交付完成。
易错点:单分支测试全绿不等于主干安全
初学者常犯的错误是认为“只要 PR 自己的 CI 显示绿色就可以安全合并”。假设主干某函数签名为 f(a),PR1 将其改为 f(a, b) 并更新了已有调用,PR2 新增了一处对 f(a) 的调用;两个 PR 在各自分支上基于旧主干测试时全都通过,但先后合入主干后系统立即崩溃。因此,门禁必须强制验证待合并分支与最新主干(或合并队列中前序变更)的组合提交;并且不能因 Lint 通过就省略测试,或因测试通过就跳过 Lint。
术语消化:CI/CD 规格说明中的关键控制机制消化
| 术语 | 解决的问题 | 机制与课程关系 |
|---|---|---|
| 分支保护规则(Branch Protection Rules) | 开发者直接向主干推送代码,或在 CI 失败时强行点击合并 | 在服务端强制要求指定状态检查(Required Checks)通过后才解锁合并,并限制直接 push 与绕过权限,赋予检查真正的约束力 |
| 合并队列(Merge Queue) | 多个各自测试通过的 PR 先后合入忙碌主干后,因逻辑冲突导致主干构建损坏 | 将待合并 PR 排入队列,自动构造包含最新主干与队列前序 PR 的临时组合提交并运行 CI,全部通过才正式合入 |
| 静态检查(Lint) | 代码格式混乱、存在可疑写法或违反项目结构约定,增加后续维护负担 | 通过静态分析快速拦截格式与规范偏离,与验证运行时行为的动态测试互补,任一通过都不能省去另一项 |
| 部署并发控制(Deployment Concurrency) | 多次发布任务在时间上重叠,后跑完的旧版本任务覆盖掉已上线的新版本 | 为部署环境设置并发组与时序锁,拒绝或取消过时的流水线部署请求,保证线上产物与最新通过验证的主干状态严格一致 |
本章小结
本章阐明了在 AI 能够代写流水线配置的时代,工程师的核心工作是将合并与发布的业务规则编写为严密的 Spec。
我们剖析了组合提交冲突、偶发失败测试、紧急例外放行与部署并发覆盖四大棘手场景,并以课程网站为例定义了包含 Lint、动态测试、最新主干组合验证、精确产物绑定及分支保护的完整条件,最后强调通过故意制造错误的负向验收来确保门控真正关得住。
第五章 CI 阻止与 Agent 修正:遏制 AI Slop 的自动化防线
前文将 CI/CD 的合并条件与发布流程写成了可执行的规格说明。进入 Agentic AI 时代,这套自动化防线的含金量急剧上升。大模型在软件开发中展现出极高的产出速度,但也伴随着特有的非人类偏执,极易以光速积累维护负担,使项目滑向 AI Slop(低质、冗余且难以维护的生成内容)深渊。本章将探讨:当软性的提示词劝告因上下文膨胀而失效时,如何借助丰田生产方式中的自働化思想,将工程规范转化为 CI 中的硬性机械拦截与可操作报错,构建“CI 阻止—Agent 修正—再验证”的质量闭环。
开发者不可信与自働化(Jidoka)防线
在将持续集成引入 Agent 循环时,首先需要明确我们在防范什么风险。人类开发者常因盲目自信而省略测试,而 AI Agent 则因与人类认知天然不对齐,会产生人类开发者罕见的极端行为。从“开发者是不可信的”这一前提出发,可以看到软性约定为何难以在长上下文中持续约束模型,以及如何把工业制造中的停线检查机制迁移到软件工程。
CI 在 Agentic AI 时代的价值体现在两个维度。
- 首先是拦截 Agent 在实际协作中具有代表性的偏执表现:一句话能交代清楚的事项写成 20 KB 文档;事无巨细地重复交代细节,制造严重冗余与技术债;在不重要的细枝末节上过度反应、疯狂出力;以及写出勉强能运行但人类完全无法阅读的代码。
- 其次是确立应对原则:只要软件项目还需要人类共同维护,就必须强行阻止上述倾向。具体机制是“CI 阻止,Agent 修正”,对应丰田生产方式中的自働化(Jidoka,发现异常立即停线,把质量检查内建到生产过程中)。
同时必须划定职责红线:负责修改代码的 Agent 不能单方面降低过关标准。
课堂上进一步剖析了文档冗余为何会导致项目崩溃:当代码明明已经自解释时,Agent 仍喜欢把思考过程和实现细节抄进 doc.md;在当前会话中它知道 doc.md 与 implementation.py 相互关联,但一旦跨会话或压缩上下文,后续 Agent 修改了 Python 代码却遗忘更新文档,两处事实来源便发生脱节。此外,Agent 在验证网页时常把浏览器截图直接塞入主上下文,导致上下文迅速膨胀、注意力滑坡(更好的做法是交由子 Agent 处理并只向主上下文返回简短摘要)。课堂还借“狗无法理解人类世界却能快乐生活”的讨论指出:除非把 AI 当作完全接管一切的超级智能而人类彻底放弃干预,否则只要项目仍需人机共同维护,就绝不能容忍人类无法定位和修改的 AI Slop。
把偏执转化为可判定的约束与反馈闭环
认识到 Agent 的偏执倾向后,下一步是工程落地:如果只在 AGENTS.md 规范文档里写一句“请保持简洁”,当项目规模达到十万行或上下文接近百万 Token 时,模型的注意力依然会涣散并忽略劝告。将工程规范落地,需要把常见问题拆解为“机械自动检查”与“人工判断职责”两部分,并设计能够直接引导下一轮状态迁移的报错信息。
针对 Agent 的典型偏执,工程上围绕常见问题、自动检查与人工判断建立映射,核心转化规则包括四类:第一,针对文档无休止膨胀,为特定入口文件设置合理的大小上限(如课堂举例限制某文档不超过 1000 字,或 DeepSeek Harness 中检查根目录文档词数预算),强制将详细材料分流到有组织的位置;第二,针对模块边界被绕过,通过静态分析检查被禁止的依赖方向;第三,针对代码难以阅读,使用格式化工具、静态检查(Lint)和复杂度规则拦截明显问题;第四,针对同一规则或代码到处复制,自动检查重复内容,并交由共同维护者人工判断应当保留哪一处作为唯一事实来源。
要让这些检查真正驱动状态机演进,自动检查不仅要能拦截,还必须提供面向 Agent 的可操作反馈。如果 CI 只返回一个冰冷的红叉,下一轮 Agent 只能盲目猜测规则;报错信息必须明确指出违规依赖的文件位置以及架构允许的访问方式,从而构成“CI 阻止 \(\rightarrow\) Agent 读取报错修正 \(\rightarrow\) 同一套标准再验证”的闭环。同时必须守住权限边界:测试确实可能过时,架构规则也可能需要演进,但修改规则必须回到人类对需求与设计理由的审查。删掉失败断言得到的绿色,与修好程序得到的绿色,颜色相同但工程含义完全不同。
自働化(Jidoka)与面向 Agent 的可操作报错
自働化(Jidoka)源于丰田生产方式,核心是发现异常立即停止生产线,将质量内建于过程中。迁移到 Agent 开发中,意味着不能依赖模型自觉记住规范,而要在 pre-commit 与远程 CI 中设置可判定的硬性门控。更重要的是,门控失败时的诊断输出(通常写往标准错误 stderr,或按工具协议输出结构化报告)必须包含违规位置与修复指引,使报错信息本身成为下一轮 Agent 状态迁移的明确依据。
课堂提示:跨会话的文档与代码脱节机制
为什么事无巨细的文档是高危技术债?课堂指出,文件系统本身不维护 Markdown 文档与源码之间的语义绑定。Agent 在同一次会话中同时生成代码与长篇细节文档时,关联存在于当前上下文中;一旦进入下一轮 Ralph Loop 或上下文被压缩,新会话中的 Agent 修改了代码实现,却不知道文档中某段话复述了旧实现,最终导致文档与代码自相矛盾。
启发式防线的边界与“改卷子”风险
第一,课堂列举的 20 KB 文档、过度防御等现象是实际协作中的典型观察,不应夸大为所有模型在一切场景下的绝对定论;第二,文档字数上限、文件长度限制等规则是针对具体项目设计的启发式防线,绝不能机械地将“文件短”简单等同于“软件设计好”;第三,必须通过权限隔离或文件校验,严防实现功能的 Agent 为通过检查而单方面删改测试断言或放宽 Lint 规则。
术语消化:遏制 AI Slop 的质量控制机制对照
| 术语 | 解决的问题 | 机制与课程关系 |
|---|---|---|
| AI Slop | 模型高速生成冗余、重复、难以阅读且与代码脱节的内容,导致人类失去维护能力 | 通过硬性门控拦截低质状态迁移,防止坏模式在工作区状态中扩散并污染后续上下文 |
| 自働化(Jidoka) | 写在提示词或规范文档中的软性要求随上下文膨胀被模型遗忘 | 将丰田停线机制引入 CI/CD,每次状态迁移遇到违规立即阻断合入,强制进入修复循环 |
| 可判定约束(Decidable Constraints) | “保持代码简洁”“架构清晰”等主观工程品味无法被脚本直接执行 | 转化为入口文档字数上限、禁止的模块依赖方向、圈复杂度与重复度检查等机械规则 |
| 可操作报错反馈(Actionable Feedback) | CI 仅返回失败状态码,导致 Agent 在下一轮循环中盲目猜测修复方向 | 在检查脚本报错中直接输出违规位置与允许的修改方式,驱动“阻止—修正—再验证”闭环 |
#!/usr/bin/env bash
set -euo pipefail
# 该门控脚本自身与受保护路径列表应由受保护的主干基线提供
BASE_REF="${BASE_REF:-origin/main}"
HEAD_REF="${HEAD_REF:-HEAD}"
# 1. 检查必需入口文档及其字数预算(可判定约束)
MAX_CHARS=1000
DOC_FILE="AGENTS.md"
if [ ! -f "$DOC_FILE" ]; then
echo "[CI BLOCKED] 缺少必需的入口文档 $DOC_FILE。" >&2
echo "[修复指引] 请恢复 $DOC_FILE,保持精炼目录结构并指向 docs/ 下的详细文档。" >&2
exit 1
fi
CHAR_COUNT=$(wc -m < "$DOC_FILE")
if [ "$CHAR_COUNT" -gt "$MAX_CHARS" ]; then
echo "[CI BLOCKED] $DOC_FILE 当前为 $CHAR_COUNT 字符,超出上限 ($MAX_CHARS)。" >&2
echo "[修复指引] 请保持入口文档精炼,将详细设计拆分至 docs/ 目录下并在此处仅保留索引链接。" >&2
exit 1
fi
# 2. 显式校验 Git 基线存在,防止浅克隆或引用缺失时 diff 失败被 if 吞掉
if ! git rev-parse --verify --quiet "$BASE_REF" >/dev/null || \
! git rev-parse --verify --quiet "$HEAD_REF" >/dev/null; then
echo "[CI BLOCKED] 无法解析比较基线 ($BASE_REF 或 $HEAD_REF)。" >&2
echo "[修复指引] 请在 CI 检出步骤配置 fetch-depth: 0 并拉取完整 $BASE_REF 分支后重试。" >&2
exit 1
fi
MERGE_BASE=$(git merge-base "$BASE_REF" "$HEAD_REF")
CHANGED_FILES=$(git diff --name-only "$MERGE_BASE" "$HEAD_REF")
# 3. 拦截对独立验收测试与门禁配置的修改(允许在 tests/unit/ 新增普通回归测试)
PROTECTED_PATTERN='^(tests/acceptance/|\.github/workflows/|scripts/ci-gate\.sh$)'
if grep -Eq "$PROTECTED_PATTERN" <<< "$CHANGED_FILES"; then
echo "[CI BLOCKED] 检测到当前分支修改了受保护的独立验收测试或门禁配置:" >&2
grep -E "$PROTECTED_PATTERN" <<< "$CHANGED_FILES" >&2
echo "[修复指引] 实现功能的 Agent 不能单方面修改验收标准或门禁脚本。请还原上述受保护路径并修复业务实现;若可在 tests/unit/ 下补充单元回归测试。若需求变更确需调整验收标准,须由维护者通过受保护流程独立合入。" >&2
exit 1
fi
echo "[CI PASSED] 门控检查通过。"
该脚本展示了如何将软性规范转化为可靠的 CI 门控。第一部分要求入口文档 AGENTS.md 必须存在且字符数不超过预算,超限时向 stderr 输出违规位置与拆分指引并返回非零退出码。第二部分先显式验证 BASE_REF 与 HEAD_REF 引用及 merge-base 是否存在,并在匹配受保护路径时使用 Bash here-string(<<<)替代管道,避免在浅克隆缺失基线或 set -o pipefail 下 grep -q 提前退出触发 SIGPIPE 时,被 if 条件误判为“无匹配放行”。第三部分区分普通回归测试(如 tests/unit/,允许随实现补充)与受保护的独立验收目录及门禁配置(tests/acceptance/、.github/workflows/、门禁脚本自身),拦截实现分支单方面调低验收标准或篡改工作流的行为;如需变更验收标准,必须由人类维护者通过受保护的独立流程审查合入,而不能依赖被测分支可自行设置的本地环境变量。
本章小结
本章剖析了 CI 在 Agentic AI 时代的核心价值。面对 Agent 制造 20 KB 冗长文档、复述易变细节、过度防御及塞满主上下文等非人类偏执,只要项目仍需人类共同维护,就必须引入丰田自働化(Jidoka)思想建立强行拦截防线。
通过将软性规范转化为文档大小预算、依赖方向与重复度等可判定检查,并在报错中提供明确的修复指引,同时严禁实现端 Agent 单方面调低检查标准,工程团队得以构建起“CI 阻止—Agent 修正—再验证”的可靠质量闭环。
第六章 Harness Engineering 与工业界把反馈做实的三种实践
前几章確立了“工作区是状态、Agent 是状态迁移”的抽象模型,并通过具有强制约束力的 CI/CD 流水线构建了“CI 阻止、Agent 修正”的反馈闭环。然而,在实际的大型工程中,仅仅依靠基础的编译构建和单元测试,依然不足以约束不可信的 Agent 持续产出可维护的代码。除了基础检查之外,领先的工业界工程团队如何通过组织仓库知识、编排确定性工作流、改造验证粒度以及构建外部环境替身,来打造一套完整的 Agent Harness?本章将深入剖析 OpenAI、Stripe、Anthropic 与 StrongDM 的四项工程实践,揭示如何把工程经验与架构品味转化为可执行的环境机制。
Agent Native 开发:把工程知识放进环境
当人类开发者不再亲手编写每一行业务代码,软件工程的重心便转移到环境建设上。如果每次启动新会话的 Agent 都像一个毫无背景的陌生人,且只能依赖人类口头转述程序报错,开发循环就会极其低效。为了让 Agent 具备自主推进任务的能力,我们需要重构代码仓库中的文档组织方式,并赋予 Agent 直接启动和观察程序运行状态的工具。
围绕 Agent 建立的运行环境、工具、约束与反馈机制,在工程上被统称为 Harness(即让模型能力能够稳定用于具体工作的整套配套设施)。因此,Agent Native 开发(从立项之初就将 Agent 视为核心开发者并为其量身设计工作环境的开发范式)不仅意味着代码由 AI 编写,更要求将工程知识转化为 Agent 找得到、看得见、能够验证的环境状态。OpenAI 在从空仓库起步的 Harness Engineering 实验中提出了两项基础建设:
- 第一,将人类脑中的工程知识外部化并组织为可发现的版本化状态,以简短的 AGENTS.md 作为入口目录,将设计背景、计划与决策存入仓库供 Agent 按需查阅;
- 第二,让每份工作区都能独立启动应用实例,使 Agent 能够自主操作页面、查阅日志并复现 Bug。
讲义提醒我们,口头约定“我们一直这样做”对刚开始一轮工作的 Agent 毫无意义,但也不能把全部规范一次性塞爆上下文,而应采用渐进式导航(Progressive Disclosure)。同样,在开发课堂选课页面时,如果每次都等人类转述“选课按钮好像没反应”,反馈既慢又容易丢失关键信息。只有让 Agent 自己拉起独立服务实例、模拟点击按钮并检查请求是否发出、服务端如何处理、界面最终显示什么,才能形成高带宽的自我修正闭环,这也自然引出了下文如何将架构品味进一步转化为自动化检查机制。
把品味做成机制与后台持续清理
即便 Agent 能够查阅设计文档并直接观察程序运行,随着迭代轮数增加,模型依然会因为注意力涣散而违背架构约定,或者照猫画虎地复制仓库里已有的不良代码模式。OpenAI 实验的后两个核心支柱,正是将抽象的“架构品味”转化为每次提交都能触发的机械检查,并通过后台常驻机制防止局部技术债在仓库中扩散。
本页展示了将工程品味机制化的两条路径。首先,必须将重要的架构品味转化为可检查的规则,而非仅仅停留在规范文档的文字要求中。例如,页面组件是否绕过业务层直接访问数据、某类日志是否缺少必要字段、模块依赖方向是否合规,都应通过 Linter 与结构测试进行拦截,且报错信息必须直接包含明确的修复指导,指明违规位置与允许的访问方式。其次,技术债的清理必须持续发生:团队配置后台 Agent 定期扫描偏离规则的代码,持续提交小规模重构 PR,抑制不良模式扩散。同时,该文并未公开其内部项目可直接复用的完整门控配置清单。
课件中“ClosedAI:什么都没有……”的课堂调侃,指的是该文章没有附上内部项目可直接复用的完整 CI 门控配置文件,而非该项目没有 CI——原文明确阐述了 Lint、结构测试与后台定期清理机制,课堂上老师也推测 Codex 内部必然运行着更庞大的门控体系来防止项目劣化。对比开源社区公开完整检查脚本的实践可以看到:读懂别人的高层经验总结,并不等于自己手里已经拥有了一套现成可用的工程系统。
Stripe Minions:混合编排与受限重试
在构建了自己的 Harness 之后,一个直接的工程问题随之而来:是不是工作流里的每一个步骤都应该交给大模型去自主决定?如果某次代码修改触发了 CI 失败,是不是应该让 Ralph Loop 无休止地重试下去?Stripe Minions 的工程实践区分了工作流中“需要理解与抉择的步骤”与“确定性的固定工序”,并对自动重试次数设定了硬性上限。
Stripe 在其 Minions 项目的 Blueprint 工作流中,采用了一种确定性代码与 Agent 混合编排的架构。工作流明确混合了两类节点:第一类是需要理解上下文并作出抉择的任务,例如根据需求实现功能、分析并修复无法自动处理的 CI 失败,这类节点交由 Agent 决定怎么做;第二类是每次都必须执行且执行方式完全明确的固定工序,例如运行 Linter、自动格式化、Git 提交与推送分支,这类节点直接执行确定性的普通程序代码,完全不调用大模型。此外,该流程规定完整 CI 最多只跑两轮:本地修完 Lint 并推送跑完第一轮 CI 后,若失败则交给 Agent 再修一次;第二次推送并跑完 CI 后,无论结果如何,分支都会移交人类工程师审查。
Stripe 的实践为 Harness 设计提供了极为清晰的工程直觉:不要让大模型去思考“现在该不该跑一次 Linter”,把确定性的固定工序直接固化到宿主程序代码中,既节省 Token 又消除了模型遗忘步骤的风险。同时,连续重试是有边际成本的。当 Agent 经过两轮完整 CI 修复仍然失败时,往往意味着反馈没有带来新信息,或者任务本身的规格说明与架构边界存在盲区;此时强行截断循环并交回人类审查,才能避免在收益递减时无限空转。
Anthropic:验证方式决定任务的可分性
当单 Agent 工作流稳定后,工程团队自然会尝试启动多个 Agent 并行加速开发。然而,有了 Git 分叉和多个并行进程,任务就能自动实现并行加速吗?在 Anthropic 使用 16 个 Agent 协作编写 C 编译器的案例中,早期 16 个 Agent 在编译 Linux 内核时撞上了同一个错误;直到引入 GCC 作为可信参照并缩小失败文件子集后,任务的并行可分性才发生改变。
Anthropic 团队在让 16 个 Agent 协作开发 C 编译器时,初期直接以“编译通过 Linux 内核”作为验证目标,结果 16 个并行 Agent 全部卡在了同一个入口 Bug 上:大家看到的都是“整个内核编译失败”,于是纷纷涌向同一个错误位置,重复分析、重复修复并互相覆盖彼此的改动。为了打破僵局,团队改造了验证机制:引入可信的 GCC 编译内核中的绝大多数文件,仅将不同的文件子集交给新编写的编译器处理;一旦发生失败,就通过将部分文件切换回 GCC 编译(类似二分排查)来逐步缩小导致失败的文件集合。由此,不同 Agent 终于能够各自认领并修复不同文件子集暴露出的编译问题。同时,团队仍需进一步检查文件组合后的情况,因为有些文件单独通过新编译器测试,合在一起链接运行时却会失败。
这个案例深刻揭示了“验证方式决定了任务的可分性”。在没有缩小故障范围的手段之前,反馈粒度是粗放的“整体编译失败”,16 个并发进程在状态空间中只能坍缩到同一个故障点上,根本无法形成有效分工。只有借助可信参照物(GCC)构造出能够独立验证、独立失败的细粒度子任务,并行 Agent 才能真正转化为并行生产力。此外,单文件编译通过不等于组合正确,集成验证依然不可或缺;这也说明在盲目增加 Agent 数量之前,先改善任务表示与验证反馈往往更加有效。
StrongDM:独立场景验收与数字替身宇宙
即便任务能够拆分、CI 能够自动运行,当负责写代码的 Agent 能够直接读取甚至修改仓库里的测试脚本时,又会出现什么新的工程隐患?此外,如果系统严重依赖第三方外部服务或极端并发时序,如何在不干扰真实生产环境的前提下高频验证?StrongDM 通过“代码库外独立场景验收”与“数字替身宇宙(DTU)”来应对这两类挑战。
StrongDM 在工程自述中指出,面对狭窄的单元测试,Agent 可能会直接写出一行 return true 来糊弄过关,甚至通过修改测试断言来迁就错误的实现代码(即“改卷子”)。为此,他们将端到端用户故事制作成 Scenarios(验收场景),并将其存放在被测代码库之外,作为对实现 Agent 隐藏或只读的独立考试题;对于包含 Agent 的软件产品,还会统计多条执行轨迹中满足用户意图的比例,而非仅看单次测试是否全绿。同时,他们构建了 Digital Twin Universe(数字替身宇宙,简称 DTU):为 Slack、Okta 等外部服务编写行为替身,模拟其 API 行为与边界异常。在先与真实服务对照并修正差异的前提下,团队得以高频重放测试场景并注入极端故障。
课堂上老师将这套 DTU 思想迁移到了选课系统的开发实验中:让外部环境替身专门高频模拟“最后一个选课名额刚好被别人抢走”“选课请求已在服务端提交成功,但网络响应在返回途中丢失”等极端并发与网络故障。这样一来,开发 Agent 不必等到真实学生在选课高峰期踩坑,就能在本地练习场里观察到失败状态并修正代码。但讲义特别强调了一条硬性前提:行为替身并不自动证明真实外部服务的行为,DTU 必须先与真实系统对齐校准、验证差异,否则我们只是造出了一个特别容易过关的假考场。
Harness 的四个工程支点
构建 Agent Native 的运行与反馈配套设施(Harness),需要将人类经验落实为四项具体机制:第一,知识外部化,用入口目录引导 Agent 按需查阅版本化的设计决策与背景;第二,行为可观测,赋予每个工作区独立启动实例、操作界面与查日志的能力;第三,品味规则化,用带修复指导的 Linter 和结构测试拦截架构违规;第四,清理常态化,由后台 Agent 持续提交小规模重构 PR 遏制坏模式扩散。
验证粒度决定并发效率
开启多个终端窗口或多个 Git Worktree 只是提供了进程级的并发能力,并不自动带来工程进度的并行加速。如 Anthropic 编译器实验所示,当验证反馈只有粗粒度的“整体成功或整体失败”时,所有并行 Agent 都会涌向同一个入口故障并互相覆盖。只有利用可信基线(如 GCC)将大系统拆解为可独立验证、可二分缩小范围的文件子集,才能让多 Agent 真正分工协作。
警惕“改卷子”与失真的数字替身
在自动化验收中必须防范两类虚假的“测试全绿”:一是实现 Agent 为了通过门控,直接修改同仓库下的测试脚本或用硬编码返回值应付狭窄用例,因此关键端到端验收场景(Scenarios)应独立于被测代码库之外并禁止实现者篡改;二是使用外部服务替身(DTU)模拟并发与网络故障时,若未事先与真实服务对照校准行为差异,替身测试通过并不能保证系统在真实生产环境中可靠运行。
术语消化:Agent Harness 工业界核心实践机制对照表
| 术语 | 解决的问题 | 机制与课程关系 |
|---|---|---|
| Harness(运行配套框架) | 裸模型缺乏项目背景、无法感知运行状态且易违背工程规范 | 整合版本化知识库、独立运行实例、带修复指导的 Linter 与后台清理 Agent,构成受控的状态迁移环境 |
| Minions Blueprint(混合编排) | 纯靠 Agent 决策导致固定工序遗漏,以及无限重试造成算力空转 | 将需要理解抉择的编码修复交给 Agent,将 Lint 和推送固化为普通程序代码,并限制完整 CI 最多重试两轮 |
| 参照基线子集验证(GCC 基线) | 多 Agent 面对粗粒度整体失败时卡在同一入口 Bug、互相覆盖改动 | 用可信 GCC 编译大部分文件,仅将不同子集交给新编译器并二分缩小失败范围,改变任务可分性并配合组合检查 |
| Scenarios 与 DTU(独立场景与数字替身) | Agent 篡改同仓库测试脚本(改卷子),且外部依赖与极端并发故障难以高频复现 | 将端到端用户故事置于代码库外做多轨迹评估;经与真实服务对照校准后,用行为替身高频模拟极端边界故障 |
本章小结
本章围绕 Harness Engineering 展开,总结了工业界将工程反馈做实的系统方法。
- 通过 OpenAI 的四项支柱,我们看到如何将知识外部化、赋予 Agent 直接观测能力、把架构品味转化为带修复指导的检查并持续清理技术债;
- 通过 Stripe、Anthropic 与 StrongDM 的实践,我们进一步掌握了“固定代码加 Agent 决策”的有限重试编排、利用可信基线改变任务可分性,以及通过库外独立场景与校准后的数字替身(DTU)构建高保真验收环境。
第七章 开源项目的 Harness 剖析与超越局部清理的架构重构
在前文中,我们看到工业界如何通过固定工序、拆分验证子集与构建外部行为替身来约束 Agent 的状态迁移。然而,很多企业级方案并未公开其内部可直接复用的完整门控配置。一套成熟的开源 Agent Native 门控体系在工程细节上究竟长什么样?当团队需要在同一仓库中维护多语言文档、严格控制上下文膨胀并防止代码与文档漂移时,应当如何设计机械检查?更进一步,如果我们将这类高频的日常技术债清理做到极致,它能否解决软件演进中的所有问题?为什么局部清理依然无法替代深层的架构重构?本章将剖析开源标杆项目的 Harness 细节,并结合真实历史复盘探讨架构演进的边界。
开源标杆项目的 Harness 门控细节
阅读企业级工程博客能帮助我们理解 Agent Native 的理念,但要真正搭建起防止项目滑向 AI Slop 的防线,工程师需要看到可落地的配置与脚本细节。当一个开源项目既要面向全球开发者维护中英双语文档,又要防止 Agent 在多轮迭代中制造海量冗余、破坏接口一致性时,应当如何组织工作区与 CI 门禁?本节结合课件与课堂剖析,拆解开源项目 DeepSeek Harness 与教师团队自研项目中的具体配套机制。
DeepSeek Harness 的配图(身着蓝白女仆装、配备现代武装的鲸鱼少女)直观对应了本讲的核心概念:模型本身如同核心角色,而围绕模型建立的运行环境、工具、约束与验证机制则是那一整套现代武装(Harness)。
在课堂剖析中,教师团队首先展示了自研项目的 Agent Dashboard 机制:项目维护一份 architecture.md 与记录任务状态的 logbook(日志簿),并借助 GitLab CI 将正在执行的任务、模块位置与完成百分比实时渲染在可视化架构看板上,使架构师无需逐行盯看终端即可掌握全局并行进度。
而在开源标杆项目 DeepSeek Harness 中,这套“武装”体现为极其严密的文档与代码门控。为了解决中英双语文档随手修改后极易失步的难题,该项目设计了“三件套加一道门禁”的配对契约:同一目录下并列存放英文 foo.md、中文 foo.zh.md 以及记录一致性状态的 foo.i18n.yaml。
在三件套机制中,中英文文档具有同等权威,先行修改的一侧即为当次更新的源。foo.i18n.yaml 记录中英两侧文件已登记版本的 Git blob hash。当 pnpm run verify-translation-pairing 检测到某一侧文件的内容 hash 与记录不符时,即可机械判定存在未同步的内容变更,进而交由工作流中的 Agent 采用最小补丁(Patch-based)方式对另一侧做增量翻译并更新记录。需要注意,blob hash 配对只能检测未登记的文件改动与配对失步,无法单凭哈希值证明中英文本语义等价,译文准确性仍需依靠术语表、文风规范与语义审阅来保障。开发者还可通过本地 IDE 配置隐藏非母语文件以保持目录清爽。此外,项目采用渐进式规范披露(Progressive Disclosure):分目录放置 AGENTS.md,仅在翻译或写作时按需加载 docs/i18n/style-samples.md(文风样例)与 terminology.md(统一术语表)。配合限制根目录文档不超过 1960 词、architecture.md 不超过 2400 词的 verify-doc-budgets,以及强制要求文档代码块中的接口定义与源码逐字一致的 verify-type-equivalence,真正将工程品味转化为了机械门禁。
日常垃圾回收与底层架构重构的边界
前面展示的字数预算、译文配对与类型等价检查,构成了精密的自动化防线。这些机制显然不是坐在教室里凭空想象出来的,而是真实的踩坑产物。但这引出了一个更深层的工程问题:如果我们配置了完善的后台 Agent 和 CI 门禁,每天自动消除重复代码、同步文档、统一接口,项目就能永远健康地演进下去吗?这里需要区分“日常技术债清理”与“底层设计假设失效”两个不同层面的工程问题。
前面展示的各项门控与规范都来自真实的踩坑经验,很难在缺乏实践时凭空想出;因为只有真实的负载、依赖和异常,才会暴露出原型里隐藏的设计假设。与此同时,及时重构依赖一个关键判断:Garbage collection(垃圾回收)无法解决所有问题。讲义明确提醒,这里所说的垃圾回收是针对工程维护的类比,指消除重复、修正文档、统一接口、防止坏模式扩散等日常技术债清理工作,并非编程语言运行时的内存自动回收机制。即使把这些日常清理工作全部做好,团队也可能只是在持续维护一个已经失效的设计假设。真正有价值的失败,能够迫使开发者看清系统瓶颈所在,从而理解架构的修正方向。
为什么日常清理替代不了架构重构?因为局部修补只能在既定的系统结构内优化代码形态。课堂上老师提到,当发现学生的项目架构已经腐化时,靠局部修修补补往往无济于事,更有效的做法是新建一个空白项目,在良好架构下将旧功能逐一迁移进去。讲义也用数据库架构给出了直观说明:假设系统最初建立在“单节点集中写入、多个只读节点保存完整副本以分担读请求”的设计假设上。在用户量较少时,这个假设直观且有效;但当写入量和数据规模持续暴增后,每个只读副本依然必须重放全部写入更新。此时无论把代码格式整理得多漂亮、文档同步得多及时,都不会自动改变系统的负载结构。
Friendster 案例:技术认知与组织重构能力
既然失效的设计假设必须通过架构重构来修正,那么在真实的软件工程实践中,是不是只要工程师找出了架构瓶颈、提出了正确的技术演进方案,系统就能顺利完成蜕变?本节通过社交网络先驱 Friendster 的数据库架构复盘案例,探讨当系统扩容撞上架构边界时,技术方案的提出与组织层面的落地之间存在着怎样的鸿沟。
社交网络先驱 Friendster 提供了“重构没赶上用户离开”的典型案例。作为早期社交网络先驱,Friendster 迅速吸引了海量用户,但随后频繁遭遇高延迟与宕机,最终在竞争中掉队。根据亲历工程师 Dathan Pattishall 在 2006 年撰写的复盘文章,当时系统的核心瓶颈在于数据库采用了“单写入主库带多个完整只读副本”的架构:每一个完整副本都必须重放主库的全部写入操作,沉重的磁盘 I/O 压力导致了严重的复制延迟。面对这一架构边界,工程师提出了按用户分片(Sharding)的改进方案,即按用户拆分数据,使不同服务器只承担部分数据与相应的读写负载。然而,当时的内部环境使该方案未能顺利推进,容量问题的根因没有得到解决;而同一套按用户分片的设计,后来在图片分享平台 Flickr 得到了成功落地。
讲义对这一案例给出了严谨的边界说明:单凭这份工程师的技术复盘,不能单独解释一家公司的全部兴衰,但它极其清晰地暴露出了一个核心工程问题——“技术上看出架构应该怎样改”和“组织上真的能够把它改掉”是两件同样关键的事。在真实生产环境中推进一次底层架构重构,不仅要求工程师具备准确识别瓶颈的技术判断力,还要求团队与组织有能力协调部门分工、安全迁移存量数据、周密安排上线窗口,并共同承担架构变化过程中的业务风险与成本。如果组织缺乏推动结构性改变的能力,团队就只能停留在局部症状的持续修补上,在更认真地维护一个已经不合时宜的架构前提。这一认识自然将我们的视野从代码内部推向了更广阔的产品落地与社会实践层面。
双语文档的配对契约与最小补丁同步
在 DeepSeek Harness 中,多语言文档的一致性不靠人工记忆,而是靠同目录下的 foo.md、foo.zh.md 与 foo.i18n.yaml 三件套协同维护。yaml 文件记录中英两侧的 Git blob hash,两侧具备同等权威。当 verify-translation-pairing 检测到某侧产生未登记的新 hash 时,即捕获配对失步,要求对另一侧执行基于差异的最小补丁(Patch-based)增量更新并刷新 hash 记录;至于双语语义是否准确对应,则进一步结合术语表检查与语义评审来把关。
课堂提示:渐进式规范披露(Progressive Disclosure)
许多初学者习惯把所有写作要求、架构规范和代码约定一股脑塞进根目录的 AGENTS.md。课堂提醒,当项目规模扩大后,巨型常驻提示词不仅消耗大量 Token,还会导致模型注意力涣散。成熟的做法是分层放置指令,并将 style-samples.md(文风样例)等细则设为按需加载:只有当 Agent 执行文档撰写或翻译子任务时,才路由加载对应规范。
辨析工程“垃圾回收”与历史案例的归因边界
本章有两处重要概念边界需特别注意:第一,讲义中的“垃圾回收(Garbage Collection)”是针对消除重复代码、同步文档、统一接口等日常技术债清理的工程维护类比,切勿与编程语言运行时的内存管理混淆;第二,Friendster 的复盘用于说明主从完整副本的写入扩展瓶颈及组织推进重构的难度,绝不能将一家公司的全部兴衰简单归因于单一数据库技术方案。
术语消化:开源 Harness 门控机制与架构演进概念对照表
| 术语或机制 | 解决的具体问题 | 运行机制与课程关系 |
|---|---|---|
| 翻译配对契约 (foo.i18n.yaml) | 中英双语文档随手修改后极易失步、互相矛盾 | 记录中英文档已同步版本的 Git blob hash,由 verify-translation-pairing 检测未登记改动并触发最小补丁增量同步 |
| 渐进式规范披露 (Progressive Disclosure) | 常驻提示词无限膨胀导致上下文溢出与模型注意力下降 | 分目录配置 AGENTS.md,将文风样例与术语表等规范拆出,仅在执行特定子任务时按需加载 |
| 文档预算与类型等价检查 (verify-doc-budgets / verify-type-equivalence) | Agent 生成冗长文档 Slop,以及文档中的接口定义与源码实现脱节 | 在 CI 中硬性限制根目录与架构文档词数,并强制校验文档代码块中的类型定义与源文件逐字一致 |
| 工程垃圾回收 (Engineering Garbage Collection) | 代码坏味道、重复逻辑与局部文档腐化的日常扩散 | 类比日常技术债清理;虽能维持局部整洁,但无法修复底层设计假设失效,不能替代架构重构 |
| 按用户数据分片 (User-based Sharding) | 单主库加完整只读副本架构下,每个副本仍需重放全部写入导致的 I/O 与复制延迟瓶颈 | 将不同用户的数据拆分到不同服务器以分散读写负载;体现了识别架构方向与组织落地重构的双重条件 |
本章小结
本章深入剖析了开源标杆项目 DeepSeek Harness 与教师团队的工程实践:通过 Git blob hash 配对契约、渐进式规范披露、文档词数预算及类型逐字等价检查,将防 Slop 要求落实为严密的机械门禁。
同时本章阐明,日常技术债清理(工程垃圾回收)无法拯救失效的底层设计假设;如 Friendster 案例所示,成功的架构演进既赖于从失败中看清重构方向的技术判断,也取决于组织推动变革的落地能力。
第八章 落地验证、试错成本与工程判断力的培养
上一章我们通过 Friendster 的数据库重构困境看到,即便持续清理局部技术债,也无法挽救已经失效的底层架构假设。当视角从代码维护扩展到整个产品的生命周期时,一个更尖锐的问题浮现出来:在 AI Agent 极大降低代码生成成本的今天,为什么一个听起来极具吸引力的好想法,依然会在落地过程中走向失败?同时,当工程品味与架构判断力必须依赖大量真实系统的试错与对比才能形成时,学习者又该如何跨越实践资源的门槛,避免在教育与产业的过渡期失去竞争力?
好想法在落地中的失败与产品级 Slop 化
当团队借助 Agent 获得极高的开发速度时,很容易产生一种错觉:只要把功能迅速堆砌出来,好想法就能自然转化为成功的产品。然而,软件工程的历史反复证明,实现代码只是验证假设的起点,架构规模、运营成本与真实用户需求之间的失配,随时会让项目折戟。Webvan 与 Quibi 两个历史案例展示了未经验证的业务与规模假设如何在落地阶段放大试错成本。
如果不经历多次构建与广泛观察,好想法很容易在执行中逐步退化为平庸冗余的产物(即产品级 Slop 化),最终导致失败。互联网时代有两个典型案例:
- 其一是做网上买菜业务的 Webvan:其 2000 年年报披露,配送中心的实际业务负载不足原设计容量的 40%,且当时尚无任何一处设施实现现金流盈利,公司最终于 2001 年 7 月申请破产。课堂由此提炼出的工程推论是:当订单密度假设尚未得到验证时,重资产的仓配扩张会让每一轮试错变得极其昂贵。
- 其二是主打移动端精品短视频的 Quibi:该项目在 2020 年宣布关停,管理层在官方告别信中回顾并解释了两种可能原因——产品构想本身不足以支撑独立的流媒体服务,或者推出时机不恰当。
这两个案例是多因素共同作用的结果,讲义提醒我们不能将 Webvan 的破产简单归咎于某一种技术架构,也需明确 Quibi 的两点原因是管理层在告别信中的事后回顾而非严格的实验鉴定。但它们共同指向了一个核心教训:拥有技术、内容和完整的产品形态,并不等于真实用户需要这样一个独立服务。在 AI 时代,Agent 让生成页面、文档和功能变得异常廉价;如果团队不去做真实的需求与架构验证,就会沿着错误的方向高速生成大量代码。局部测试可能全部呈绿色通过状态,但核心假设的缺陷却被深埋在日渐膨胀的冗余实现之下。这也印证了前文的核心观点:快速构建原型与及时推倒重构并不矛盾,原型的真正价值在于尽早触碰现实并暴露错误,从而指导下一轮架构修正。
Token 试错门槛与工程判断力的来源
既然避免产品级 Slop 化和架构腐烂依赖于人类工程师对全局方向的把控,那么这种高维度的工程判断力究竟从何而来?它能否仅靠课堂听讲或背诵规范文档获得?在 Agent Native 开发模式下,工程能力的培养同时取决于试错的物质资源与学习者的主观条件,实践机会的差距会逐步积累为工程判断力的差距。
围绕“尝试的机会”与“学习好的设计”,可以推演出两条现实约束。首先,在 Agent 开发中,Token(模型处理和生成文本的基本计量单位)直接对应着开展自主开发尝试的算力与资金预算。“没有 token,就没有做很多次尝试的机会”,由此带来的课堂推论是:实践机会的差距,会逐步积累成工程判断力的差距。其次,要想真正学会好的设计,仅有资源还不够,学习者必须同时具备很强的学习动机与学习能力。在此基础上,课堂提出了关于“斩杀线”的风险警示:在适应新生产方式的现代新大学形态成熟之前,缺乏高质量实践闭环的一代学习者面临着掉队的风险。
讲义对这一页的社会性风险判断做出了严密的界定:强调“没有 Token 就没有尝试机会”,指出的是开展大规模自主开发试错存在客观的物质资源门槛,但这绝不意味着花钱消耗的 Token 越多就一定学得越好。工程判断力来自在真实系统中的反复比较:为什么相似的需求有人做得简洁,有人却越做越乱?为什么同一条门控规则在这个项目里能拦住缺陷,在另一个项目里却只制造报警噪声?什么时候应该继续局部修补,什么时候必须像老师指导学生重构烂架构那样开一个空白仓库重新迁移?要回答这些问题,学习者必须将 Token 资源与自身的主观能动性结合起来——既要有追问失败根因的强烈动机,也要具备阅读优秀开源项目(如课上剖析的 DeepSeek Harness,对比最小原型与成熟系统之间的增量演进)的学习能力,把每一次观察转化为下一轮可验证的改进。
跨越教育过渡期的实践断层
当企业的用人标准因为 Agent 能力的跃升而急剧拉高,传统的教育培养周期正面临前所未有的冲击。面对个人成长与教育转型期的挑战,软件工程学习者需要思考如何跨过资源与环境的断层,主动搭建自己的实践反馈闭环。
当 Token 耗尽、学习者被断裂的实践阶梯挡在未来大学门前时,教育过渡期的结构性矛盾便凸显出来:一方面,顶尖模型配合精良的 Harness 已经能够承担许多原本交由初级开发者或研究生的具体实现工作,企业更倾向于雇佣具备全局视野(Big Picture)的高级工程师并为其配备充足的算力预算;另一方面,传统高校的培养体系具有较大的组织惯性,未必能及时为所有学生提供充足的 Agent 实践资源与真实的工程反馈环境。
需要明确的是,讲义提出“被斩杀的一代”是针对教育过渡期的风险警示与对学习者的鞭策,而非已经注定无法改变的宿命。面对这一断层,等待外部环境自然成熟是危险的,学习者必须主动出击。正如课堂与实验二所倡导的那样:不要只做停留在纸面上的“语言律师”,而要亲手去写一个哪怕只有 150 行代码的最小 Agent 原型,去观察真实的执行轨迹(Trajectory);去克隆并剖析开源社区里真正优秀的 Agent Native 标杆项目,用元层级的认知去拆解别人是如何设计文档配对契约、如何配置 CI 门禁的。软件工程课程的最终落脚点,正是推动大家在一次次“运行系统、观察失败、理解好设计、完成有反馈的修改”的闭环中,把自己培养成知道怎样让下一次尝试比上一次更有价值的人。
产品级 Slop 化的工程机理
在 AI 辅助开发中,Slop 化不仅发生在代码与文档层面,还会蔓延至整个产品形态。当团队缺乏对核心业务假设(如 Webvan 的订单密度、Quibi 的独立服务需求)的验证时,Agent 极高的生成速度反而会成为掩盖问题的帮凶。无数看似完整的前端页面、冗长的规范文档和全绿的局部单元测试被迅速堆砌出来,使项目在表面上越来越像成品,实则把真正需要检验的架构与需求假设深埋在维护泥潭之下。
课堂提示:从最小核心对比复杂开源系统
课堂提示我们,在算力与 Token 有限的情况下,最高效的学习路径之一是“先自己搓一个最小核心,再与成熟的开源系统做对比”。例如先用 150 行代码实现一个带 Bash 工具的极简循环,亲身体会超时控制、上下文漂移等真实痛点;随后再去研读 DeepSeek Harness 等开源项目,观察成熟工程如何通过分层指令、配对契约与自动化门禁逐一补齐这些差距。这种对比能极大加速工程品味的形成。
易错辨析:案例归因与资源决定论的误区
分析工程与商业案例时必须严守证据边界:Webvan 与 Quibi 的失败均是多因素共同作用的结果,年报中的容量利用率数据或管理层告别信中的反思,只能作为剖析试错成本与需求假设的切入点,不可简化为单一的技术决定论。同理,指出 Token 预算构成实践门槛,是为了揭示试错机会的不均等风险,绝不等于“花钱买 Token 越多就自动学得越好”;脱离了追问根因的动机与总结反馈的能力,单纯让模型无限输出只会制造更多噪声。
术语消化:落地验证与工程判断力培养的核心概念消化
| 术语或案例 | 解决或揭示的问题 | 核心机制与课程关系 |
|---|---|---|
| Webvan 仓配失配案例 | 揭示核心假设未验证时重资产扩张的试错代价 | 2000年报显示配送中心负载低于设计容量40%且无设施盈利,说明未验证需求密度前的庞大系统会让每次判断错误极其昂贵。 |
| Quibi 告别信反思 | 警示技术与内容齐备不等于真实用户需求成立 | 管理层回顾认为产品构想不足以支撑独立服务或时机不当,提醒开发者切勿用高速生成的功能掩盖未经验证的产品假设。 |
| 产品级 Slop 化 | 防止用廉价生成的页面与局部测试掩盖方向错误 | Agent 快速堆砌冗余功能与文档,虽局部检查全绿却偏离真实需求;需靠早期原型暴露问题并配合果断重构来遏制。 |
| Token 试错门槛 | 衡量自主开发时代获取大规模实践机会的物质成本 | Token 是模型处理与生成的基本计量单位;充足预算提供多次试错的门票,但必须结合学习动机与能力才能转化为工程判断力。 |
本章小结
本章从代码维护走向产品落地与个人成长的全局视角。Webvan 与 Quibi 的历史教训表明,Agent 虽降低了实现成本,却无法自动验证需求密度与产品假设,盲目堆砌功能只会招致产品级 Slop 化。
工程判断力必须在反复构建真实系统、经历失败与重构的对比中习得。
面对 Token 试错门槛与教育过渡期的挑战,学习者需要将算力资源与强烈的学习动机、开源研读能力相结合,在完整反馈闭环中锤炼品味,成为驱动下一次尝试更有价值的工程师。
总结与延伸
- 本讲围绕 Agent Native 开发范式,构建了一套从最小原型循环到工业级反馈环境的方法论体系。在底层机制上,我们确立了“工作区即状态(Workspace = State)、Agent 即状态迁移(Transition)”的状态机视角。无论是课堂设想的“给足够聪明的模型一个 Bash 工具就能造芯片、证明黎曼猜想”的思想实验,还是朴素的 Ralph Loop,都揭示了单次模型调用的局限:循环本身并不天然保证进展,实际工程必须引入明确的验收条件、Token 与时间预算以及停止规则,并结合宽度优先与深度优先的搜索策略(如异构模型交叉评审与背靠背盲测)来实现有效的“算力换智力”。
- 在质量保障层,本讲从“开发者不可信”的公理出发,重新发明并强化了 CI/CD 的工程含金量。面对 Agent 生成 20 KB 冗余文档、代码与文档脱节、偷改测试迁就实现等非人类偏执,软性规范劝告极易在长上下文中失效,必须借鉴丰田自働化(Jidoka)理念,将工程品味转化为挂接在生命周期事件上的硬性门控。同时,我们严格区分了持续集成(CI)、人工决定上线时机的持续交付(Continuous Delivery)与全自动上线的持续部署(Continuous Deployment),只有配合分支保护、必过检查(Required Checks)、与最新主干的组合提交验证、精确构建产物绑定以及防旧产物覆盖机制,CI 绿灯才具备真正的准入约束力。
- 放眼工业界与开源社区实践,OpenAI 的知识外部化与带修复指导的 Linter(尽管未公开内部完整门控配置,但明确包含 Lint、结构测试与 CI)、Stripe 结合固定程序工序并限制最多两轮重试的 Minions Blueprint、Anthropic 借助可信 GCC 作参照并缩小编译文件子集以改变任务可分性的编译器实验、StrongDM 置于仓库外的独立 Scenarios 与需经真实服务校准的数字替身(DTU),乃至开源项目 DeepSeek Harness 的双语配对契约与字数预算门禁,都在致力于把反馈做实。本讲承前启后:它不仅将前半程的需求、架构与版本管理落地为自动化生产流水线,更为后续课程深入系统测试、模糊测试与复杂验证奠定了环境基础。
- 但正如 Friendster 数据库架构瓶颈、Webvan 仓配容量失配与 Quibi 独立服务假设受挫等多因素交织的历史案例所示,日常技术债清理(工程维护意义上的“垃圾回收”)无法替代对失效底层假设的架构重构;将试错资源与强烈的学习动机、工程剖析能力结合,才能在真实系统的反馈闭环中建立起可靠的工程判断力。
学习练习
用下面的延伸练习检查自己能否应用本讲方法。
练习 1:设计带预算、停止条件与背靠背评估的受控 Agent Loop(呼应 Lab 2 算力换智力)
选择一项需要跨多步查证与修改的任务(如生成一份带真实引用的技术调研报告,或实现并优化一个含并发边界的模块),编写一个管理工作区状态的循环驱动脚本。禁止使用无限空转的裸 while true,必须在状态目录中持久化任务目标、当前成果、验证日志与待办事项,并支持配置不同 effort 档位(控制最大轮数或 Token 预算上限)以及引入另一模型或独立脚本进行挑刺评审。
验收标准:
- 工作区状态文件中包含明确的结构化交接信息(已验证通过项、失败原因及日志路径、下一步具体动作),新一轮无上下文 Agent 仅凭工作区文件即可恢复工作;
- 脚本具备硬性停止机制:达到预算上限、满足独立验收条件或连续两轮遇到相同错误无新进展时自动终止并保留历史较优版本;
- 提交一份固定输入下的背靠背盲评记录,遮蔽 effort 标签对比 One-shot 直出与 Loop 迭代版本在事实准确性、需求完成度与 Token 消耗上的真实差异。
练习 2:为项目编写完整的合并与发布 Spec,并实施 CI/CD 门控的“负向验收”
针对一个静态网站或 Web 服务仓库,先用自然语言编写涵盖正常路径与异常业务规则的 CI/CD 规格说明(涵盖合并组合检查、偶发失败处理策略、紧急 Override 留痕机制及并发部署防覆盖规则),再交由 Agent 生成对应的 GitHub Actions(或 GitLab CI)工作流及仓库分支保护配置。
验收标准:
- 仓库配置了明确的分支保护与 Required Checks,本地
--no-verify无法绕过远程准入拦截; - 合并门控包含静态 Lint 与动态测试,且验证待合并分支与最新
main主干的组合状态; - 发布流水线对更新后的
main重新构建,仅部署本次通过检查的精确产物;配置concurrency串行化或取消排队中的旧任务,并在最终发布入口校验待上线提交仍是允许发布的最新主干代次; - 附上“负向验收”证据(PR 与 Actions 截图或日志):故意制造一次 Lint 违规、一次测试失败,以及一次旧构建晚于新版本完成的并发时序,证明错误合并被拦截、旧产物被拒绝覆盖,修复后恢复正常更新。
练习 3:构建遏制 AI Slop 的可操作机械门禁与校准替身验证环境
针对 Agent 协作中常见的文档膨胀、文档与源码接口脱节或应付狭窄测试的倾向,在一个示例模块(如课堂提及的选课系统抢名额场景)中实现两项工程约束:一是编写自定义静态检查脚本拦截特定 Slop 模式并输出修复指导;二是将端到端验收场景独立于实现目录之外,并构建一个模拟极端边界(如并发争抢最后名额、请求成功但响应丢失)的轻量级服务替身。
验收标准:
- 静态检查脚本至少包含文档字数预算检查(如限制核心架构文档词数上限)或文档代码块与源码接口定义的逐字等价检查,且报错输出必须指明违规位置与具体的修复方式;
- 将独立验收场景与门禁脚本置于受保护路径或独立仓库(允许实现分支新增普通单元回归测试),通过受保护 CI 检查拦截实现 Agent 针对独立验收标准或门禁阈值的未授权修改;
- 外部服务替身附带与真实接口协议(字段格式、状态码、超时行为)的对照校准说明,并能稳定重放“最后名额竞争”与“响应丢失”两类故障场景以检验系统的幂等性与并发安全性。
拓展阅读
下面列出本讲的原始讲义、课件和相关项目,便于回看案例、查阅代码并继续实践。
- 官方讲义:https://jyywiki.cn/GSE/2026/lect8.md
- 官方课件:https://jyywiki.cn/GSE/2026/slides8.html
- 社区字幕校订稿(固定版本):https://github.com/NinZeige/NJU-2026-GSE-Captions/tree/0456f3a46fd5fa2f2a86123e02b7ed48696adacb
- 原课程许可 CC BY-NC 4.0:https://creativecommons.org/licenses/by-nc/4.0/
- NASA - 1901 Wright Glider Tests:https://www.grc.nasa.gov/WWW/k-12/airplane/wrights/test1901.html
- Geoffrey Huntley - Ralph Wiggum as a "software engineer":https://ghuntley.com/ralph/
- LinearB Podcast - Inventing the Ralph Wiggum Loop:https://linearb.io/dev-interrupted/podcast/inventing-the-ralph-wiggum-loop
- Martin Fowler - Continuous Integration:https://martinfowler.com/articles/continuousIntegration.html
- Martin Fowler & Matthew Foemmel - Continuous Integration (Original 2000 Article):https://martinfowler.com/articles/originalContinuousIntegration.html
- Thoughtworks History (CruiseControl):https://www.thoughtworks.com/about-us/history
- Jez Humble - Continuous Delivery:https://continuousdelivery.com/2010/02/continuous-delivery/
- Jez Humble - Continuous Delivery vs Continuous Deployment:https://continuousdelivery.com/2010/08/continuous-delivery-vs-continuous-deployment/
- GitHub Docs - Events that trigger workflows (pull_request):https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request
- GitHub Marketplace - Claude Code GitHub Integration:https://github.com/marketplace/actions/claude-code-github-integration
- Git Documentation - Git Hooks (pre-commit):https://git-scm.com/docs/githooks#_pre_commit
- GitHub Docs - About protected branches:https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches
- GitHub Docs - Require merge queue:https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-merge-queue
- GitHub Docs - Control deployments (Concurrency):https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/control-deployments
- GitHub Docs - Using custom workflows with GitHub Pages:https://docs.github.com/en/pages/getting-started-with-github-pages/using-custom-workflows-with-github-pages
- Toyota Global - Toyota Production System (Jidoka):https://global.toyota/en/company/vision-and-philosophy/production-system/
- OpenAI - Harness engineering: leveraging Codex in an agent-first world:https://openai.com/index/harness-engineering/
- Stripe Blog - Minions: Stripe's one-shot, end-to-end coding agents (Part 2):https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents-part-2
- Anthropic Engineering - Building a C compiler with a team of parallel Claudes:https://www.anthropic.com/engineering/building-c-compiler
- StrongDM Software Factory:https://factory.strongdm.ai/
- StrongDM - Digital Twin Universe (DTU):https://factory.strongdm.ai/techniques/dtu
- Wired - What I Learned From Friendster: Jonathan Abrams' New Startup:https://www.wired.com/2007/04/what-i-learned-from-friendster-jonathan-abrams-new-startup/
- Dathan Pattishall - An Unorthodox Approach to Database Design (Friendster Retrospective):https://mysqldba.blogspot.com/2006/11/unorthodox-approach-to-database-design.html
- SEC EDGAR - Webvan Group, Inc. 2000 Annual Report (Form 10-K):https://www.sec.gov/Archives/edgar/data/1092657/000101287001001485/0001012870-01-001485.txt
- SEC Administrative Proceeding - Webvan Group Bankruptcy Record:https://www.sec.gov/litigation/admin/34-50771-o-2.htm
- Quibi - An Open Letter from Quibi:https://quibi-hq.medium.com/an-open-letter-from-quibi-8af6b415377f
- David Silver et al. - Mastering Chess and Shogi by Self-Play with a General Reinforcement Learning Algorithm (AlphaZero):https://arxiv.org/abs/1712.01815
- Humanize: Judgement Engineering for Agentic Coding:https://arxiv.org/abs/2610.08900