ARTICLE DETAIL

资讯详情

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

从Vibe Coding到工程化:用SDD和Harness构建可控的AI编码流程

从Vibe Coding到工程化:用SDD和Harness构建可控的AI编码流程 这段时间身边几乎所有人都在聊 vibe coding——氛围编码。聊的是用 AI 生成一堆代码像搭积木一样把功能拼起来demo 做得飞快。我前阵子也沉浸在这个阶段里一个上午能出好几个原型界面、接口、数据流全是 AI 生成的那个爽感是真的。但爽完之后就到了没人想面对的部分产品说要加一个新字段我打开项目看了一眼十几个文件都是 AI 写的彼此之间的依赖关系我只能靠猜测试跑一遍5 个用例有 3 个红的红的点跟我要改的地方毫无关系我想把某个模块抽出来复用发现 AI 在三个文件里写了三份差不多的逻辑只是变量名不一样。这个时候我才反应过来——氛围编码的问题不是生成的代码能不能跑而是跑起来之后有没有人能接管。这篇文章想聊的就是这个问题的解法用 SDD规范驱动开发把 AI 编码从氛围拉回工程再用 Harness 这类工程 AI 封装层把规范、上下文、工具调用和过程回放串成一条可控的流水线。适合谁看自己已经在用 AI 写代码、但觉得项目越写越乱的人以及在团队里被推着用 AI 提效、又怕代码库失控的技术负责人。核心思路只有一个别让 AI 自由发挥而是让它在一个明确、可验证的契约框架里干活。1. 氛围编码的失控时刻demo 很爽落地很痛理念上我不反对 vibe coding——它把从 0 到 1 做 prototype的成本打到了几乎为零这是巨大的进步。问题出在1 到 10这个阶段。举一个我实际遇到的情况我让 AI 生成一个带后台管理的 Todo 应用它第一次生成了 6 个文件数据存在一个简单的 JSON 文件里第二次我让它加标签功能它新增了 3 个文件顺便把 JSON 存储换成了 SQLite第三次我让它加用户权限它又生成了 4 个文件并且把 SQLite 的访问全部改成了 ORM。单看每一次生成AI 做得都没错。但站在全局看为什么换存储为什么引入 ORM哪些文件是核心的哪些是 AI 顺手创建的没有任何一份文档能回答。如果你自己从头一步步写这些问题在每一行代码里都有答案因为每一步都是你做出的决策但 AI 帮你写的时候决策过程没有经过你直接落成了代码。代码越多你自己缺失的决策越多项目就越像一个黑盒。这不是 AI 变笨了而是人的认知载荷并没有因为 AI 提速而降低——只是换了个方向堆积。我在自己的项目里总结过一个现象叫AI 代码熵增每生成一轮代码项目里会多出一些 AI 自以为有用但你根本不需要的抽象同时也会删掉一些你觉得以后肯定用得到的东西。它不是在维护你的代码它是在生成代码。两者性质完全不同。所以问题的本质不是AI 写得质量差而是AI 的决策没有接受你的约束。氛围编码最大的代价就是让一个高产出、低感知的参与者混进了你的工程流程。1.1 为什么 demo 爆发式增长复杂度也会爆发式增长很多人以为 vibe coding 只是个人快速做原型的手段但我在小团队协作里也见过同样的失控轨迹。团队负责人觉得 AI 提效很猛把一批需求丢给 AI 生成一开始每个人每天能交付原来两倍的功能量。结果两周之后集成测试开始频繁炸A 同事让 AI 生成的模块和 B 同事让 AI 生成的模块用了不同版本的内部工具函数接口格式也各写各的联调变成了互相猜。氛围编码在单机状态下是爽一旦进入多人协作、多模块集成它的失控成本会指数级上升。这个失控的根源其实是上下文不共享。你跟 AI 的每一次对话在它看来都是一段新的关系它没有项目级记忆也没有团队级记忆。它只会基于你当前贴进去的内容生成代码。两个人分别让 AI 干活AI 给两份代码这两份代码之间唯一的公约数就是各自对话里那点零碎上下文。比如你们可能口头约定过所有接口返回格式统一为 { code, message, data }但如果这个约定没有落到 AI 能读到的规范文件里那它就是个不存在的约定。所以我的第一个结论是想让 AI 参与真正的工程第一步不是买更贵的模型而是把共享上下文这件事补齐。一个模型再强如果它每次生成都只看到当前这一小块信息那它的输出在项目全局看来就是随机的。这就像让两个没见过面的厨师分别做一道菜还要求他们最后装出来是同一套餐——你需要的不是更好的厨师而是一份所有人都必须遵守的菜单。1.2 SDD 在这里不是老掉牙的文档哲学而是给 AI 装上的第一道闸门Spec-Driven DevelopmentSDD规范驱动开发其实是老概念传统的做法是先把需求、接口、行为定义写成一份 spec再让开发去实现。以前很多人觉得这是形式主义因为最后 spec 都会过期代码才是真相。但在 AI 编码时代它的地位完全变了spec 不再只是给人看的文档而是给 AI 看的契约。原因也很直白模型没有长期的工程记忆。你跟 AI 说过这个项目我们用 SQLite 不用 ORM它生成完这段代码后下一次对话它可能就忘了。但如果这条约束写进了项目级的规范文件Harness 每次启动都会把它注入到上下文里AI 每次生成时都会看到。规范和上下文才是让 AI 的行为保持一致性的关键。所以 SDD 在 AI 原生软件工程里的新玩法可以简单用一条链路来概括用一句话理解这条链路人类写 spec意图 → Harness 将 spec 项目规范 历史上下文一起交给模型 → 模型在约束内生成实现 → 测试和静态检查验证实现是否符合 spec → 不符合就回去迭代 → 记录整个过程可回放。这个链路的目标不是消灭 AI 的自由发挥而是把自由发挥的范围收进一个围栏。围栏内 AI 随便跑围栏外禁止。至于怎么搭围栏、怎么设置约束的粒度、怎么让围栏真正可执行而不是一句废话这正是 SDD Harness 这套组合要解决的事。2. Harness 驾驭的不是模型是模型的驾驶舱先说个很容易混淆的点Harness 不是一个模型甚至不绑定某个具体的模型。你可以在它里面接 DeepSeek、接 Claude、接本地部署的开源模型模型只负责理解文本并生成文本而 Harness 负责的是模型之外的所有工程问题。如果模型是一台发动机Harness 就是驾驶舱油门、刹车、仪表盘、方向盘。你不可能靠一台发动机自己开到目的地同理也不能靠一个模型自己把工程做完。这两年社区里很火的 deepseek harness、claudecode 实战 harness 工程之道还有 CodeBuddy 做的一些 harness engineering 完整案例本质上都是在做同一个动作把跟 AI 对话写代码升级为在 AI 上搭建一层可控工程外壳。区别只在于底层模型不同、封装的深度不同。但不管谁来做要解决的核心问题始终是那三个上下文管理、工具权限、过程可观测。下面一个个说。2.1 直接对话模式为什么撑不起一个工程我们之前都是这么干的打开 AI 聊天窗口把需求贴进去它给你代码你复制到文件里。这个流程对单个功能没问题但对一个工程是撑不起来的。最直接的体现是上下文窗口的浪费。模型的上下文是有限资源你不可能把整个项目的所有文件都塞进去。直接对话时AI 的记忆完全依赖你每次提问时贴多少内容、以及聊天窗口里还残留多少历史记录。聊天记录越来越长模型就开始注意不到早期的重要约束于是出现前面说的那种——上次说好不用 ORM这次它又给你写了一个 ORM 的封装。更麻烦的是工具权限。真实工程不是生成代码就完了还要跑测试、查报错、改文件、做静态检查。你在聊天里拿到一段代码然后自己手动跑测试、手动把报错贴回去给 AI整个过程非常绕。Harness 这类封装做的事情就是让 AI 具备在项目内部执行受控操作的能力它能自己读取项目结构、能运行测试命令、能看到运行结果并据此修改代码但它不能乱跑、不能访问不该访问的路径、不能越过预设的边界执行危险命令。注意关键词是受控不是全放开。全放开更可怕模型什么时候抽风你根本拦不住。2.2 驾驶舱里的三个核心仪表上下文、权限、回放我自己搭 Harness 实践下来感觉最值得关注的三个仪表分别是上下文管理。Harness 需要维护四个层级的上下文全局规则比如项目禁止使用 ORM所有函数需要类型标注、项目规范比如API 路径统一以 /api/v1 开头、当前任务上下文用户正在改什么功能、历史决策记录上一次为什么从 JSON 换成了 SQLite。这四个层级叠加后AI 的每次生成都是站在完整信息之上而不是一穷二白地瞎猜。这也是为什么规则设定和提示词工程在 Harness 里那么重要——因为它们就是这部分上下文的主要来源。工具权限。Harness 把模型能调用的工具分成若干档只读工具读文件、搜代码、写入工具改文件、建文件、执行工具跑测试、跑 lint。合理的设计是把默认档位设在只读需要写出和执行时再打开对应权限把每个工具的调用记录到日志里。这样万一 AI 改错了文件你能从日志里精准定位是哪一步导致的而不是面对一团混乱。过程回放。这是 Harness 比直接对话强最多的地方。每次 AI 执行的任务、修改的文件、跑过的命令、得到的报错、最后的通过状态全部被记录下来。下次任务开始时这部分历史可以作为经验注入上下文。更棒的是你可以回放整个修改过程相当于 AI 替你做了完整的 commit message 和变更说明。有了回放AI 的产出就不再是一次性代码而变成了可持续演进的过程资产。这三个仪表装好之后后面才能谈工作流的搭建。3. 一套可以直接抄作业的 SDD Harness 工作流理论讲完直接上我实际跑通的工作流。这套流程我从一个两周的个人项目里验证过也在一个小团队的协作里试用过核心就是五步写 spec → 解析为任务 → 约束内实现 → 自动验证 → 回放与回顾。每一步都不复杂难的是把每一步固定成习惯并且用配置文件、规范文件把它固化下来而不是靠口头约定。3.1 第一步写一份可执行的 spec而不是一段抽象的描述SDD 的第一步也是绝大多数人做错的一步就是把 spec 写成抽象描述。比如需要实现一个用户注册功能——这就等于没写。AI 拿到这句话能干的事太多了它可能给你做邮箱注册也可能给你做手机号注册可能做验证码也可能不做可能把注册信息存 MySQL也可能存文本文件。你看着它跑偏了还得骂它笨其实是你的 spec 没给它约束。一份可执行的 spec 至少要包含这几个部分功能目标一句话说清楚干什么接口契约输入、输出、错误码如果是 API 项目明细到请求体和响应体数据约束字段类型、是否可空、唯一性约束边界条件超时怎么处理、重复提交怎么处理、没有权限怎么处理验收标准跑什么命令、查什么接口、得到什么结果就算通过。我习惯把它写成 Markdown 文件放在项目根目录的specs/文件夹里一个功能一个文件。文件名要包含模块名比如specs/user-registration.md。写 spec 的时候有个技巧如果是改现有代码先贴一段现状说明再写目标否则 AI 不知道从哪下手。比如你写把用户信息存储从 JSON 文件改成 SQLite这是目标但 AI 还需要知道现在代码在哪个文件、数据格式是什么、有哪些调用点依赖这个 JSON。把这些现状信息放进 spec 的背景小节AI 就不会像无头苍蝇一样到处找。我踩过这个坑一开始 spec 只写目标不写现状AI 生成出来的代码把原有接口全改了然后把锅甩给了按 spec 实现实际上是我 spec 没写完整——这种教训一次就够。3.2 第二步规则与提示词的分层把约束注入每个生成环节有了 spec接下来要做的是把规则和提示词分层设置。很多人以为提示词工程就是写一个超级长的万能提示词那是对它的误解。在 Harness 体系里提示词是分层出现的系统级规则对所有 AI 生成行为生效、项目级规范只在当前项目生效、任务级指令只在当前任务生效。三层叠加之后模型收到的不是一个庞大的提示词文件而是一组有优先级的上下文片段。系统级规则我会写得很短像技术栈为 Python 3.12 FastAPI禁止在没有权限的情况下修改测试文件代码需包含类型注解这种铁律。项目级规范稍微长一点包含项目独有的约定比如数据库迁移文件放在 migrations/ 目录日志统一使用项目的 logger 而不是 print。任务级指令就是每次和 AI 对话时自然形成的输入比如请基于 specs/user-registration.md 实现注册接口先读取现有 auth 模块的代码。一个很实用的做法把这些规则分别写进rules/system.md、rules/project.md然后通过 Harness 的插件机制在每次会话启动时自动加载。这里要提醒一句规则不是越多越好。我见过有人写了三四十条规则AI 反而更容易犯低水平错误——因为上下文被规则挤满了反而稀释了关键信息的权重。我的经验是系统级规则控制在 8 条以内项目级规范控制在 15 条以内每条都要能通过一句话说清楚说不清楚的先放一边等真实遇到再补。3.3 第三步用 skill 和工作流插件把流程固化下来规则是静态的工作流是动态的。这一步就是热词里大家老在问的deepseek harness 工作流插件和harness skill到底是什么。用我的话说skill 就是一组预定义好的任务模板告诉 Harness遇到这类任务按这个流程来。比如我可以定义一个implement-feature的 skill读取 spec → 分析现有代码结构 → 生成实现 → 运行测试 → 若失败则读取报错并修复 → 回写变更日志。这样做的好处是每次实现新功能AI 走的是同一条路径不会这次先写测试、下次先写代码完全看心情。工作流插件的原理也类似差别在于它往往还绑定了一些外部工具调用比如跑 lint、格式化、更新接口文档。配置上我用一个简单的 YAML 描述流程节点整体思路其实很像 CI/CD 的 pipeline 定义只不过执行者从 Jenkins 换成了 AI。下面是个简化示例workflow: name: implement-feature steps: - read_spec: specs/{ticket_id}.md - analyze: 读取 specs/{ticket_id}.md 中列出的现状文件 - implement: 根据 spec 和项目规范生成代码 - verify: command: pytest -m {ticket_id} on_fail: 将报错返回给模型要求修复后重跑 - review: 输出变更清单等待人工确认这个流程在 Harness 里跑起来之后AI 的产出第一次有了可预期的节奏。它不再是一个让你不知道什么时候给你什么答案的黑箱而是一个每个节点都明确的执行器。我经常跟团队说这种工作流不是为了限制 AI而是为了让你能信任它。你只有知道它在每个阶段会做什么、在什么条件下停下来请你确认你才敢把手里的生产代码交给它去改。4. 踩坑实录从 harness failed to load plugins 讲起的完整排查这节讲我在实际部署 Harness 过程里遇到的那些问题。为什么单开一章因为网上教程大多只告诉你怎么安装怎么用没人告诉你装完之后第一次启动就报错的感受。热词里那个高频问题 harness failed to load plugins web boot: 2 entries did not activate 我实实在在遇到过不夸张地说这个报错卡了我两天。下面我把完整排查链路复盘一遍这个思路对所有类似问题都通用。4.1 一条真实报错的排查链路逐层往下拆先描述一下现场。我按照安装文档把 harness 装好后启动 web 界面控制台立刻弹出一行提示harness failed to load plugins web boot: 2 entries did not activate。第一次看到这行报错我下意识想法是插件系统坏了。但我告诉自己先别急着找插件目录先把报错看全。很多时候我们只盯着第一行错误其实后面几行日志才点名了真实原因。我做的第一件事是打开日志。启动服务时用--debug参数跑让日志输出到前台。结果发现在 2 entries did not activate 的上一行有一条关于插件入口文件加载失败的警告指向某个插件目录里的__init__.py不存在。这就有意思了不是整个插件系统坏了而是有两个插件条目没有成功激活但其中一条的前置依赖报错被系统吞掉直到最后统一汇报时才合并成这一条提示。接着我查了插件加载机制。Harness 的插件系统在启动时会扫描指定目录下的插件包每个插件包需要有一个入口文件通常是一个 Python 模块它向外暴露一个激活函数。如果入口文件缺失、依赖库没装、或者激活函数内部抛异常这个插件就会静默失败最后汇总成一个 entries did not activate。知道这个机制后排查方向就很明确了去看那 2 个未激活插件分别是谁再逐个看它们缺什么。我用了一个很土的排查方式把插件目录里除了核心插件外的包全部临时移走只保留一个重启看还报不报错。这样二分定位很快发现其中一个插件引用了pydantic的parse_obj_as而这个函数在版本 2 里被移除了另一个插件是配置里写错了入口文件路径导致加载时根本找不到模块。两个问题都不是大问题但表现形式都是同一个报错。修复分别是将 pydantic 版本降到兼容版本或者改代码用新版 API以及修正配置里的入口路径。整个过程最大的收益不是解决了这一个报错而是我彻底搞懂了这类问题的共同规律failed to load plugins 是一条汇总错误真实原因藏在它前面的 warning 日志里。以后再遇到我会第一时间看插件加载相关的 warning而不是对着汇总信息发呆。4.2 安装与本地部署里最常见的三类失败除了上面那个加载问题安装部署阶段还有三类高频问题我按踩坑概率排个序。第一类是依赖版本冲突。Harness 这类封装通常同时依赖多个库直接pip install时很容易和项目里已有的包打架。最典型的就是 pydantic、typing-extensions、httpx 这几位常客。个人经验是强烈建议用独立的环境安装不要图省事往系统环境里塞。如果你有 Conda 或 uv给 Harness 单独建一个环境哪怕多占几百兆磁盘也比依赖冲突折磨两小时划算。我在 Linux 服务器上部署时曾因为系统自带的 Python 3.9 和某个依赖要求 Python 3.11 直接装不上换成 uv 建的隔离环境后一条命令跑通这种问题属于环境管理问题跟代码本身无关。第二类是插件市场不可达造成安装失败。有些插件需要从远程仓库拉取如果你的部署环境没有相关网络访问能力插件初始化就会卡很久然后报超时或 TLS 错误。这不是安装包的问题而是访问策略的问题解决办法是把插件提前下载好离线安装或者在能访问的环境中装完再把插件目录复制过去。要说一句我看到网上有人在某个基于 Debian 的发行版上安装也遇到过一类奇怪的坑其实跟发行版关系不大多半还是 Python 版本和依赖对齐问题用隔离环境都能解决。第三类是模型 API 配置没生效。装好 Harness 后它需要连接一个模型后端无论是 DeepSeek 还是本地部署的开源模型。很多人在这里漏了配置环境变量导致 Harness 启动正常但一发起任务就报连接失败。检查顺序我建议是先确认 API key 有没有写进环境变量或配置文件再确认模型名称是否是服务商 API 里真实存在的模型名最后确认网络和端口没问题。前两步占 90% 的失败比例我踩过几次之后已经把检查这三件事练成了肌肉记忆。下面把这三类问题整理成一张对照表方便你排错时快速定位失败类型典型表现最常见原因处理思路依赖版本冲突安装时报错或启动即崩溃全局环境中有同名包版本不兼容使用隔离环境锁定 requirements插件源不可达插件初始化卡住最后报超时部署环境无法访问远程插件源离线打包插件或先装再复制目录模型配置问题服务启动正常但任务即报错API key、模型名、端口环境变量未设按 key → 模型名 → 网络顺序排查4.3 规则设计里的三个暗坑别等到代码库变大才后悔部署问题说完了说说规则设计上的坑——这不是软件配置是工程实践里的坑但同样能让你项目陷入混乱。第一个坑规则写得像倡议书。比如尽量保持代码简洁注意代码质量——这类规则对 AI 毫无约束力因为它不可验证。AI 看到尽量就知道你没给它硬约束。你真正要写的是函数超过 50 行必须拆分新增公共函数必须写 docstring包含参数说明和返回值说明禁止在业务代码里 import 测试包。每一条都可以通过静态检查验证的规则才是好规则。我后来还引入了一组 lint 规则跟项目规则绑定AI 生成的代码不通过 lint 就算失败让验证从人的感觉变成机器的判定。第二个坑规则没有做版本管理。工程的代码是经常变化的规范也会跟着演进但古早年代的规则如果没有被更新、删除就会一直留在规则库里影响新的 AI 生成。我第一次没注意这个项目从 SQLite 切到 PostgreSQL 之后系统规则里还留着数据库默认使用 SQLite结果 AI 迭代新功能时仍然按 SQLite 写连接代码导致测试环境一堆怪问题。从那以后我把rules/目录跟代码一起放进 Git 管理任何规则的修改都要走一次 review老规则除非确认废弃否则不修改而是新增一条覆盖规则并在变更记录里写明原因。第三个坑规则全部堆到任务级指令里。有人不习惯用系统级和项目级规则把所有要求每次都在对话里写一遍。结果一是又臭又长浪费上下文二是容易写漏不同任务之间行为不一致。你要把稳定的、普适的约束下沉到系统级或项目级规则里任务级指令只保留本次任务的特异信息比如这次要改哪几个文件、验收标准是什么。层次一清晰上下文利用率和 AI 行为的稳定性都会明显改善。5. 质量闭环从AI 写的也能跑到AI 写的敢上线最后一个环节也是我从自己玩得爽跨越到敢给项目用的关键就是质量闭环。氛围编码阶段大家只关心代码能不能跑但在工程化之后我们要的是可验证、可回滚、可复盘。这个闭环里有三个我实践下来最有效的动作。5.1 让 AI 生成测试但人类来定通过标准很多人让 AI 写测试就把测试标准和生成都交给 AI最后得到一个所有测试都绿的虚假安全感。因为 AI 生成测试时常常会挑容易过的路径写不敢碰那些会导致自己代码失败的边界。比如它实现了一个注册接口写的测试全是正常注册成功没有人试过重复用户名、空字段、非法格式——而这些才是最容易出事故的场景。我的做法是spec 里明确写出测试要求谁来生成都行但通过标准必须由人来定义。我会在 spec 末尾列一个验收测试清单用自然语言描述各类场景正常路径、异常路径、边界条件、安全相关然后让 Harness 工作流把它转成测试用例并执行。如果 AI 改了实现让某条验收用例挂掉那它不是把 bug 修好了而是把契约破坏了——这条用例必须立刻恢复到通过状态否则不允许进入下一步。这个机制保证了 AI 的自由发挥不会牺牲你已经定好的行为承诺。5.2 小步提交 过程回放AI 工作的每条轨迹都算数工程 AI 最怕的另一件事是突进式修改——AI 一次性改动了几十个文件表面上功能都正常但没有人说得清每个改动为什么发生。我的对策是强制小步提交每个 spec 只对应一个改动批次每个批次完成后必须先通过测试、再生成变更摘要最后人看完摘要确认才允许提交。这个流程对模型是有点烦但它换来的东西极其重要每次改动都是可解释、可回退的。出问题时你只需要回退最近一个批次而不是在几十个文件里玩大逃杀。回放功能在这里特别有用。Harness 会把一次工作中调用的工具、读写的文件、执行的命令全部记录下来。我常在 review 时直接看回放日志而不是 diff因为 diff 只告诉你结果回放日志能告诉你 AI 当时的决策路径。比如它为什么把某个函数从一个文件移到另一个文件回放里通常能看到它先试了哪种写法、报了什么错、才改成另一种。这种透明度在传统编码里根本不存在但在 AI 原生工程里它是你唯一能让工作成果变得可审计的抓手。5.3 一套可以用最小成本启动的实践建议如果你看完这篇文章觉得有道理但又不想一来就上全套流程我的建议是从一个最小的组合开始挑一个不太重要的内部工具项目建好specs/和rules/目录装好一个带工作流能力的 Harness 封装然后严格按写 spec → 实现 → 自动验证 → 人工确认的顺序跑一周。一周后你会直观感受到一个差异AI 生成的代码开始变得有条理了因为每个功能都是对着同样的规范、同样的流程生成的它不再像一群散兵游勇而像一个被同一个排长带出来的兵。任何代码库最重要的从来不是代码本身有多漂亮而是每一个改动都能被解释、被验证、被回滚。我个人的体会是SDD 规范驱动和 Harness 工程 AI 这套组合真正改变的不是写代码的效率而是对代码的信任方式。氛围编码让我们信任 AI 的速度工程化让我们重新把信任建立在一套可验证的过程之上。这中间有摩擦也有反复踩坑的沮丧但走通之后AI 从偶尔惊艳的实习生变成了稳定可靠的老同事——这个转变值得楼下那些踩坑的时间。
返回列表