ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从氛围编码到可控工程:SDD与Harness如何给AI编程套上缰绳

从氛围编码到可控工程:SDD与Harness如何给AI编程套上缰绳 我第一次意识到“氛围编码”这条路线迟早要出事是在一次版本合并现场。同事让AI加一个“简单的导出功能”AI很听话半小时内交出三百行代码。合并进主干时我们才发现它顺手改了订单状态的枚举值、把另一个模块的公共函数复制了一份还绕过了类型检查拼了一段SQL。代码能跑但没人说得清它脑子里那套“需求”是从哪来的。从那天起我开始认真研究SDD规范驱动开发和Harness这类“驾驭工程AI”的工具目标只有一个让AI写代码的产出从“氛围好”变成“可控”。这篇文章写给已经在用AI写代码、但被失控感折磨的人。你会看到SDD怎么把需求语言转成AI能执行的规范Harness怎么在Agent外面套上一根缰绳以及我在Linux和Windows上搭建这套工作流时踩过的五个坑。它不是理论综述是一份能用起来的实操记录。1. 氛围编码的失控时刻它为什么火又为什么让人后怕1.1 氛围编码的兴起从“写代码”到“描述代码”氛围编码vibe coding这个词最早走红描述的就是一种非常放松的写代码方式你不太关心每一行怎么实现只把意图讲给AI听让它生成然后你看一眼结果、点个运行觉得行就提交。它让不熟悉语言的普通人也能“造出”工具也让熟手把大量时间从敲键盘里腾出来专注在更高层的设计上。这个吸引力是真实的我不会贬低它。可问题也藏在这份“放松”里。传统编程里代码是人类思想的执行记录你写出来的每一行背后都有一个明确的“为什么”。而氛围编码把“为什么”埋进了对话上下文模型一旦把需求理解得偏一点上下文里那个错误的“为什么”就会顺着代码长出来。刚开始只是一两处不对等你连续让它改了几天整个代码库会变成一片“氛围”只有氛围没有依据。1.2 失控的具体形态五个高频事故根据我的实践和身边团队的反馈失控的形态其实高度一致可以归纳成下面五类改A坏BAI为了满足你当前的第一条指令偷偷修改了相邻函数的逻辑你review时不会发现因为它的输出看起来“相关”。重复造轮子代码库里明明有现成的公共方法AI不知道重新实现了一份逻辑相近的实现后续维护的人要对付两套“半像不像”的东西。范围蔓延你只让它“加个参数”它顺手做了格式化、重命名、补充了一个异常处理一次提交里混进了七八种意图。测试与实现互相献媚AI写的测试总是恰好覆盖它自己写的实现哪怕实现本身是错的。测试绿了安全感是假的。昨天能跑今天崩上下文窗口有限第二天继续对话时模型记不清昨天的约定常常推翻自己的实现。这五个事故的共同根源是AI在“自由发挥”而它的自由发挥没有一条可被校验的基线。如果有一份规范事先写清了“输入是什么、输出是什么、边界在哪、不许动什么”大多数事故在发生前就会被拦下来。1.3 哪些项目不适合“氛围优先”我现在的判断标准很简单一次性脚本、原型验证、个人玩具项目氛围编码效率极高放开用可维护的产品代码、多人协作的模块、涉及数据一致性和资金逻辑的系统必须以规范驱动为主。很多人说起AI原生软件工程以为就是“堆更多AI”我理解的正相反——是先定规矩再让AI干活。AI越强规矩越要提前定死否则力量越大破坏半径越大。2. SDD规范驱动的运转逻辑把需求语言变成AI可执行的任务2.1 SDD和TDD、提示词工程有什么不同SDD全称是Specification-Driven Development规范驱动开发。它的核心主张是在写代码之前先把“做什么、做到什么程度、边界和禁忌是什么”固化成一份可评审、可版本化的规范然后让实现去贴合规范而不是让模型从对话里自行揣测。经常有人把SDD和TDD测试驱动开发放在一起比较。TDD是用测试来描述行为红绿循环驱动实现SDD在更靠前的位置描述的不仅是行为还包括约束条件和排除项。更好的理解是TDD回答“怎么知道它做对了”SDD回答“到底让它做什么、不做什么”。两者可以叠加使用——先用SDD把任务边界画出来再用TDD把验收条件写下来。提示词工程和SDD的区别更关键。提示词是一次性的今天这个prompt有效明天换个模型版本可能就失效SDD里沉淀出来的规范文件是工程资产有文件名、有版本、有评审记录可以随时回放。你不需要每次跟AI重复解释业务背景它只要读spec文件就够了。2.2 一份合格规范长什么样我在项目里使用的规范结构通常包含以下七块背景与动机为什么要做这个变更解决什么问题。输入定义函数参数、接口字段、外部事件类型、范围、可选性。输出定义返回值、响应结构、副作用明确哪些会产生副作用。边界与异常空值、超时、非法输入怎么处理错误码和日志口径。明确禁止项不许动哪些模块、不许引入哪些依赖、不许绕过哪层校验。验收条件可运行的检查命令、测试用例、性能指标。范围声明这次任务只包含什么不包含什么防止范围蔓延。我拿一个真实例子来说明。下面是一份我写过的批量导出规范的骨架# SPEC-20250601 订单批量导出CSV ## 输入 - 时间范围 start_time / end_time (RFC3339) - 状态过滤 status (可选默认all) - 分页游标 cursor (可选) ## 输出 - text/csv 文件流列顺序order_id,status,amount,created_at - 文件编码 UTF-8 with BOM ## 边界 - 单次导出上限10万行超过返回 400 EXPORT_LIMIT_EXCEEDED - 超时时间60秒 ## 禁止项 - 不得修改现有订单查询逻辑 - 不得新增第三方CSV依赖 ## 验收 - pytest tests/test_order_export.py - 1万行数据导出耗时5秒写完规范后花五分钟自我检查如果把这个文件交给一个完全不了解项目的新人他能照着写出你心里想要的东西吗如果答案模糊AI也会模糊。这个结构其实不复杂但最大的价值在于“禁止项”和“范围声明”。氛围编码翻车绝大多数不是因为AI能力不够而是因为它不知道哪些事不能做。你一旦在规范里明确写了“不得修改order_status枚举定义”“不得新增第三方依赖”它就多了一道硬约束。2.3 规范颗粒度与“谁写规范”的现实问题规范的最大争议是粒度写细了像文档地狱写粗了等于没写。我的经验是分三层处理第一层项目级规则rules。面向所有任务比如代码风格、测试要求、提交信息格式一次配置长期生效。第二层特性级规范spec。一个功能一个文件描述范围、输入输出、禁止项和验收条件。第三层任务级指令嵌入在具体执行时的对话里只写“本次会话的临时约束”。“谁写规范”这个问题也很现实。我见过不少团队直接让AI去写规范我不反对但前提是你得把规范当代码审查AI起草的spec必须经过人来拍板尤其禁止项和边界条件人不说清楚AI猜不准。更有效的分工是工程负责人写骨架AI负责补细节和检查遗漏人最后签收。3. Harness到底是什么它和Agent的边界不在名字而在控制权3.1 用一场驾驶来理解Agent与Harness很多人第一次看到harness这个词会懵因为它在英文里有“马具、挽具”的意思后来引申为“约束和控制工具”。在AI编程的工具语境下Harness不是另一个“会写代码的Agent”而是套在Agent外面的那套控制装置。打个比方Agent是发动机和车轮Harness是方向盘、刹车、仪表盘、行车记录仪。发动机马力再大没有控制装置车只能冲出去装了控制装置你才能决定它什么时候加速、什么时候停下、走的路径是否符合规则。所以社区里流行“无Harness不工程”的说法不是夸张而是因为裸露的Agent默认选择是“无限自由”。热词里经常有人问“Harness和Agent区别”我回答过很多次Agent负责“做”Harness负责“边界”。“做”和“边界”是两个维度。你可以在一个Harness下面接多个Agent也可以是同一个Agent在有无Harness时表现出完全不同的可控性。判断一个工具是不是Harness要看它是否具备规则注入、技能封装、执行审计和回退控制而不是看它叫不叫Agent。3.2 Harness的核心能力拆解在我使用过的这类工具里几个核心能力是共通的规则注入Rules把项目级规范加载进AI的上下文让它每次都看见、也不得不遵循。这对应SDD里的第一层规则。技能管理Skills把高频动作封装成可调用的技能。例如“按规范生成模块”“写单元测试并运行”“做一次不改变行为的重构”。技能是介于提示词和程序之间的东西AI调用它时更像在执行一个明确定义过的流程。插件系统Plugins扩展Harness与外部工具链的连接比如读取Git信息、触发lint、调用静态检查、提交代码。执行审计Trace记录AI每一步做了什么、改过哪些文件、执行过哪些命令。这是“可控”的重要来源——出了问题可以回溯。断点与回退Checkpoint在关键节点暂停等人批准后再继续或者回退到某个之前的稳定状态。它对应SDD里的“范围声明”防止AI一路狂奔。这些能力对应的不是新发明而是把人类软件工程里“评审、权限、审计、批准”这套机制翻译成了机器可执行的控制逻辑。这才是从“氛围编码”走向“AI原生软件工程”的关键一步。3.3 接入不同模型为什么会频繁看到“DeepSeek Harness”“Claude Code Harness”这种说法社区里讨论Harness时经常会看到“DeepSeek Harness”“Claude Code Harness”这样的组合词。理解它很简单Harness负责控制和边界底层模型负责生成能力两者是解耦的。你可以给Harness配置不同的模型端点比如DeepSeek的大模型、Claude系列模型等也可以接本地部署的模型服务。换句话说Harness不去抢模型该干的活它把模型的能力封装进一套可控的流程里。这也解释了为什么很多团队的落地路径是这样的先本地把模型服务跑起来再在Harness里配置好模型端点剩下的就是写规则、写规范、定义技能。模型选型的变化不会推翻整套流程规则和规范仍然沿用这给团队省下了很多重复磨合的成本。4. 从零搭出第一套可控工作流安装、模型配置与插件选型4.1 安装与运行环境准备不同Harness实现的具体安装步骤会有差异但整体思路是相同的我按通用流程来写。以社区里常见的命令行形态为例先保证运行环境有Node.js 18或者Python 3.10看你选的那个Harness基于什么运行时。然后从官方仓库拉取对应版本执行依赖安装。不管是Linux服务器还是Windows桌面环境我建议把Harness的工作目录独立出来不要和项目代码混在一起。我见过有人图省事直接放在项目根目录结果配置文件被AI误当成项目文件读进去白白污染了上下文。独立目录里放Harness自身的配置文件、技能定义和临时缓存项目目录里只放规则、规范和源码。安装完成之后第一件事不是急着跑任务而是初始化一个空项目确认命令行能正常唤起界面、能读到配置文件。如果这一步界面都起不来后面所有插件问题排查难度会翻倍。很多“奇怪问题”其实是版本不匹配造成的先跑通最小闭环再逐步加插件这个顺序不能省。4.2 模型接入公共API与局域网本地模型模型接入的核心是配置“模型提供方”和“端点地址”。如果你使用公共API服务通常只需要填入API Key和模型名称如果你是局域网内使用比如要处理敏感代码或者完全没有外网的环境配置就会多几步在本地或内网搭一个模型服务把base_url指向内网地址保证Harness所在机器能访问到它再填一个模型名称标识。这里有一个容易忽略的细节很多公共API的地址和本地兼容服务使用的路径不一样后者的模型名也可能是自定义的。配置前先看一眼你部署的模型服务对外暴露的名称列表不要照抄别人的配置。我曾经在离线环境里照搬了公共API的模型名结果Harness一直报“模型不存在”查了半天才发现是名字对不上。我之前用过的Harness版本里配置文件常用TOML或JSON格式包含类似这样的关键块以伪配置为例[model] provider local # 可选public / local / custom base_url http://192.168.1.10:8000/v1 model_name deepseek-local api_key no-key-required # 本地服务可能不需要校验如果是在公网服务则把provider改成public填上对应的api_key和model_name。配置文件改完之后建议用一条极小的命令测试连通性比如让AI输出“ok”不要一上来就生成整个模块。这个习惯能帮你把“模型配置问题”和“代码生成问题”隔离开。4.3 插件与技能的初始组合第一次搭建我的建议是只装四类插件代码格式化与lint、单元测试运行、Git操作、规范模板生成。这四类对应日常开发里最频繁、也最容易失控的环节。格式化与lint保证产出符合项目风格单元测试运行提供快速反馈Git操作让提交和撤销受控规范模板生成帮你把SDD落地的成本降下来。技能方面我建议从三个技能起步第一个是“按规范生成模块”让AI读取指定spec文件后生成对应目录和代码骨架第二个是“生成并运行测试”让它为本次改动补测试并实际执行第三个是“自查变更清单”让它列出所有改过的文件、新增的导入、被删除的代码方便人工评审。这三个技能基本覆盖了“从规范到实现再到验证”的主链路。至于那些花哨的自动编码插件等主链路稳定了再慢慢加。工程化的第一原则永远是“先让流程可控再谈效率”。4.4 第一次任务让Harness跑通“规范→代码→检查”配置好之后跑一个最小任务验证整条链路。我通常的做法是在项目的specs目录下写一份很小的规范比如“新增一个工具函数formatOrderId输入字符串输出格式化后的字符串非法输入返回空字符串”然后在Harness里调用“按规范生成模块”技能让它基于这份spec去实现。任务完成后人工检查三步第一生成的代码是否严格对照spec里的输入输出定义第二是否出现了spec没有要求的内容比如额外的依赖、无关的重构第三运行一次lint和测试确认工具链正常接入了。这条链路第一次跑通大概需要半小时到一小时。跑通之后你的工作方式就正式从“对话-生成-提交”切换成了“规范-任务-验证”后面所有复杂功能都复用这个骨架。这一步是整个工程化的分水岭。5. 连踩五个坑之后我把问题排查表留在这了5.1 插件入口激活失败web boot: 1 entry did not activate这类问题我最早遇到时很崩溃Harness本体能正常启动但加载某个插件时报出“web boot: 1 entry did not activate”之类的错误界面某个面板就是不出来。排查下来绝大多数情况是插件版本和Harness主版本不匹配插件的入口声明写法变了宿主找不到对应的激活入口。我的处理步骤是先看插件的manifest文件通常是plugin.json或类似定义确认入口文件名和导出的函数名再对照Harness版本更新日志看激活机制有没有变化最后把插件换成与主版本同一代的稳定版。不要为了用某个新插件去升级主程序除非你确认兼容矩阵否则很容易把好不容易跑通的基座又弄坏。5.2 Skill读取文件被拒setnamedsecurityinfow failed背后的Windows权限逻辑在Windows上使用Harness时我遇到过一个很典型的权限问题技能在执行时读取某个项目文件结果直接报“setnamedsecurityinfow failed”这种Win32安全接口错误。第一反应往往是“是不是杀毒软件拦截了”其实不完全是。这个错误通常意味着进程尝试设置或获取文件的安全属性时没有足够的Windows ACL权限。常见的触发点是项目目录放在系统保护目录下、目录ACL被之前的管理员账户改过、或者Harness进程以普通用户身份运行但目录继承权限被切断。解决路径我按优先级排列把项目目录移到普通用户完全控制的路径下比如用户目录或专用工作目录检查目录的安全属性确认当前用户拥有“修改”和“写入”权限避免用“管理员身份运行”去强行绕过因为这会引入更多权限混乱。这个问题在Linux上往往不明显因为文件权限模型更直白。Windows下踩过一次之后我现在每个新项目初始化时都会先确认目录的ACL再让Harness去读写。5.3 代码回退的粒度陷阱Harness一般会提供某种形式的回退机制但刚开始我天真地以为“回退”等于Git里的reset。实际使用中回退的单位取决于Harness记录变更的粒度。如果你让它一口气完成了七八个文件的改动然后回退到任务开始前它可能把整个任务的中间过程全部撤销而不是只撤销其中某个不太对劲的片段。这个坑的解法不在工具里而在工作方式上把任务拆小一个spec对应一次较小的变更每完成一步就确认一次。每当你想回退时回退的是“一个粒度的决策”而不是“一整段失控过程”。我会在spec的范围声明里明确写“本任务最多涉及N个文件”超过就自动报警人为介入拆分。5.4 局域网模型端点配置失效离线环境里模型端点配置失效是一个好消息和坏消息并存的问题。好消息是证明你的控制层已经接上了坏消息是排查链路比较绕。常见症状是Harness能启动模型请求也发出去了但返回超时或者一直重试。我排查时会按三层走第一层用curl直接打一下base_url的补全路径看模型服务本身是否响应第二层检查Harness所在机器到模型服务器的网络路由有些环境只允许特定端口通信第三层检查模型服务日志看请求是否真的到达了。三层走完基本能定位是网络问题、协议路径问题还是认证配置问题。还有一个小经验很多本地模型服务要求的URL路径带着版本号前缀比如/v1/chat/completions配置时直接写在base_url里或分开配置都行只要最终拼接出来能和curl测通的结果一致。5.5 重装与卸载后的残留问题Harness这类工具卸载起来比想象中麻烦因为除了主程序目录它还会在用户配置目录、缓存目录里写入内容。我一度被人问“为什么卸载重装后还报错”查到最后都是残留的全局配置在起作用新装的Harness实例启动时读到了旧版本留下的配置文件加载了不兼容的插件路径于是一切又回到老问题。所以需要彻底重装时记得清理三个地方主程序安装目录、用户级配置目录、临时缓存目录。清理前先备份那些你自己写的规则和技能文件这些是你的工程资产不要被顺手删掉。装好之后第一次启动不要急着导旧配置先空配置启动确认干净再逐个导入定制内容。6. 回到工程现场SDD文件结构 Harness控制的完整配合6.1 一个可复制的仓库布局到这一步我把整套工作流沉淀成了下面这个仓库布局新项目直接按这个结构搭specs/存放特性级规范一个功能一个文件Markdown格式。rules/存放项目级规则全局长期生效。skills/存放自定义技能定义Harness从这里加载可调用技能。src/业务源码。tests/测试代码。.harness/Harness自身的配置、插件清单、审计轨迹。这个布局的价值在于人和AI看到的是同一个结构。AI读specs下的文件就能知道任务边界读rules就能知道项目纪律读skills就能知道可复用的能力人评审时只需看spec有没有写清、测试有没有跟上、审计轨迹里有没有越界动作。整个项目的“事实”不再散落在聊天记录里而是全部沉淀在文件系统里。6.2 完整实战给订单模块增加批量导出CSV接口我拿一个实际场景演示完整流程。需求是给现有订单模块增加一个批量导出CSV的接口。按照SDD框架我先在specs目录下创建批量导出规范文件内容就按2.2里那个结构写输入参数时间范围、订单状态过滤、分页游标、输出定义CSV文件流、列顺序、UTF-8 with BOM、边界条件最大导出行数、超时时间、并发限制、禁止项不得修改现有查询逻辑、不得引入新的CSV库、复用现有导出工具函数、验收条件一万行订单导出测试耗时小于5秒。规范写完人在Git上单独提交了这个spec文件。这一步很重要规范本身就要进入版本管理后续审查的是“实现是否符合这个版本”。然后在Harness里调用“按规范生成模块”技能让AI读取并实现。实现完成后Harness自动跑一遍单元测试和lint并生成一份变更清单。人工评审阶段我按规范逐条对照变更清单。如果发现AI引入了新依赖或者改了范围声明里禁止动的查询逻辑我可以选择让Harness回退、或者单独下达“不改动查询逻辑只在其上层做过滤”的补丁任务。全部通过后合入主干关闭任务。整个流程里人的角色从“逐行审代码”变成了“审规范和审变更清单”——这才是AI原生软件工程里人的位置。6.3 规范评审与验收清单规范评审不能只看功能是否符合我给自己定了一张固定检查单输入、输出定义是否无歧义空值和异常是否覆盖禁止项是否覆盖本模块“最容易出事的点”范围声明是否能防止一次任务演变成大重构验收条件是否可以在CI里跑起来而不是需要人肉眼判断规范是否独立可读不依赖本次对话上下文。这张检查单让我避免了大多数“AI跑偏”。你不需要每次都逐项写长篇规范但每次都要把禁止项和范围声明这两块写清楚——它们就是缰绳。6.4 一点体会哪些场景请继续“氛围编码”写到最后说点个人感受。我并不认为氛围编码应该被消灭它依然是原型期最高效的工具。但我会在两类场景里主动关掉Harness的严格模式把控制放松一是探索期的技术验证二是写一次性脚本。原因很简单这些场景的产出不需要长期维护失败成本低“氛围”反而是最好的燃料。而只要代码要进主干、要给别人维护、要跟资金和数据沾边我就把SDD和Harness这套组合拉满。氛围编码解决的是“从0到1跑起来”SDD加Harness解决的是“从1到N还不崩”。两者不是对立关系是不同阶段的工具。我现在最顺手的状态就是用氛围编码做原型、生成规范草稿、梳理思路然后切换回规范驱动模式让AI在缰绳之内把可维护的东西真正落地。
返回列表