全到工程化交付:SDD+Harness構(gòu)建可控AI開(kāi)發(fā)流水線)
從AI 自動(dòng)補(bǔ)全到AI 工程化交付中間隔著一條巨大的管理鴻溝。過(guò)去半年我一直在折騰 SDDSpecification-Driven Development規(guī)范驅(qū)動(dòng)開(kāi)發(fā) Harness 這套組合目的只有一個(gè)把失控的、碎片化的 AI 輔助編碼變成一條可規(guī)劃、可執(zhí)行、可驗(yàn)收、可回退的工程流水線。這篇文章不是科普AI 有多強(qiáng)而是分享我怎么搭一套可控化 AI 輔助開(kāi)發(fā)體系以及這條路上踩過(guò)的坑、繞過(guò)的彎。先說(shuō)結(jié)論Harness 和普通 IDE 里的 AI 插件有本質(zhì)區(qū)別——它不是幫你生成更多代碼而是約束 AI 在給定范圍內(nèi)正確地產(chǎn)出。SDD 則負(fù)責(zé)把模糊的人類需求翻譯成機(jī)器可執(zhí)行的規(guī)格鏈。兩者加起來(lái)才勉強(qiáng)算得上駕馭工程 AI而不是被 AI 牽著走。1. 從AI 輔助到AI 工程的那道坎我為什么轉(zhuǎn)向 SDD Harness1.1 失控的典型癥狀如果你已經(jīng)用 AI 編程超過(guò)三個(gè)月大概率遇到過(guò)這幾個(gè)場(chǎng)景AI 生成了一段能跑的代碼但和需求文檔里寫(xiě)的業(yè)務(wù)邏輯完全不是一回事上下文一長(zhǎng)AI 開(kāi)始遺忘你半小時(shí)前定下的約束自作主張引入新的依賴代碼能編譯、單測(cè)能過(guò)但代碼風(fēng)格、目錄結(jié)構(gòu)、接口命名和團(tuán)隊(duì)規(guī)范背道而馳更頭疼的是AI 在某個(gè)文件里靈機(jī)一動(dòng)改了無(wú)關(guān)函數(shù)而你壓根沒(méi)注意到這次改動(dòng)。這些癥狀的根因不是模型不夠聰明而是我們壓根沒(méi)給 AI 一個(gè)可被校驗(yàn)的邊界。普通聊天的上下文窗口太脆弱一次刷新、一次會(huì)話切換就丟光了狀態(tài)。而 Harness 式的工作流核心思路是把 AI 的生成過(guò)程變成有狀態(tài)、可追蹤、可回滾的工程動(dòng)作。1.2 提示詞工程救不了長(zhǎng)期項(xiàng)目很多人第一反應(yīng)是我把提示詞寫(xiě)好不就完了。早期的我也這么干后來(lái)發(fā)現(xiàn)提示詞工程在一次性生成任務(wù)里很有效但放到一個(gè)迭代周期長(zhǎng)、多文件耦合、需要持續(xù)演進(jìn)的項(xiàng)目里它會(huì)迅速失效。原因很直白提示詞本質(zhì)是一次性的輸入指令它不攜帶項(xiàng)目級(jí)的歷史決策記錄也無(wú)法感知工作區(qū)里其他文件的真實(shí)狀態(tài)。AI 補(bǔ)全代碼時(shí)它看的是你的提示詞和它自己訓(xùn)練出來(lái)的常識(shí)而不是你項(xiàng)目里此時(shí)此刻的真實(shí)約束。要讓 AI 在長(zhǎng)期項(xiàng)目里可控必須把約束外置——外置到規(guī)格文件里、外置到工作區(qū)的狀態(tài)文件里、外置到可執(zhí)行的驗(yàn)證腳本里。這正是 SDD Harness 組合存在的意義。1.3 什么是 SDD 和 Harness 的正確分工我個(gè)人的理解是SDD 解決做什么的問(wèn)題把需求拆成有優(yōu)先級(jí)的、可驗(yàn)收的規(guī)格條目Harness 解決怎么做和怎么管的問(wèn)題負(fù)責(zé)調(diào)度模型、維護(hù)執(zhí)行狀態(tài)、提供回退機(jī)制、串起驗(yàn)證步驟。打個(gè)生活化比方——SDD 是建筑施工圖Harness 是工程監(jiān)理。圖紙定義了墻要多厚、窗戶開(kāi)在哪監(jiān)理負(fù)責(zé)確保施工隊(duì)按圖紙干活干錯(cuò)了能砸掉重來(lái)。沒(méi)有圖紙監(jiān)理再嚴(yán)格也不知道該管什么沒(méi)有監(jiān)理圖紙畫(huà)得再細(xì)也可能被施工隊(duì)自由發(fā)揮。2. 把讓 AI 寫(xiě)代碼變成按規(guī)格造輪子SDD 規(guī)范化拆解的核心做法2.1 三級(jí)規(guī)格拆解需求規(guī)格、任務(wù)規(guī)格、驗(yàn)收規(guī)格我一上來(lái)就把整個(gè)項(xiàng)目拆成三級(jí)規(guī)格分別放在獨(dú)立目錄里維護(hù)。這套結(jié)構(gòu)是為了讓 AI 在任意一個(gè)執(zhí)行節(jié)點(diǎn)都能只看眼前而不迷失全局。第一級(jí)是需求規(guī)格RQ-SPEC對(duì)應(yīng)產(chǎn)品側(cè)的原始訴求。它不需要寫(xiě)技術(shù)實(shí)現(xiàn)只描述業(yè)務(wù)規(guī)則、用戶場(chǎng)景、邊界條件。比如用戶可以用郵箱和密碼登錄連續(xù)錯(cuò)誤 5 次鎖定賬號(hào) 30 分鐘這是原始需求。第二級(jí)是任務(wù)規(guī)格TK-SPEC由我或架構(gòu)師角色把需求翻譯成可執(zhí)行任務(wù)。任務(wù)規(guī)格必須包含涉及的模塊、需要的接口簽名、依賴項(xiàng)、硬性約束、允許改動(dòng)的文件列表、禁止觸碰的文件列表。第三級(jí)是驗(yàn)收規(guī)格AC-SPEC每條任務(wù)對(duì)應(yīng)一組可執(zhí)行的驗(yàn)證條件。比如調(diào)用 /api/auth/login 時(shí)參數(shù)缺失必須返回 422 錯(cuò)誤碼字段這不僅僅是描述后面會(huì)變成自動(dòng)化測(cè)試。2.2 從需求到任務(wù)規(guī)格的具體寫(xiě)法示例拿登錄模塊舉例。在任務(wù)規(guī)格里我會(huì)明確寫(xiě)任務(wù)編號(hào): TK-102 關(guān)聯(lián)需求: RQ-004 目標(biāo): 實(shí)現(xiàn)郵箱密碼登錄接口 改動(dòng)范圍: - src/modules/auth/ - src/utils/password.py 禁止改動(dòng): - src/database/migrations/ - config/production.yaml 接口約束: POST /api/auth/login 參數(shù): { email: string, password: string } 成功響應(yīng): { token: string, expires_in: 3600 } 失敗響應(yīng): { error: invalid_credentials } 邊界條件: - 郵箱不存在時(shí)返回 401與密碼錯(cuò)誤時(shí)不區(qū)分提示 - 用戶被鎖定時(shí)返回 423 account_locked這樣一份任務(wù)規(guī)格AI 在執(zhí)行時(shí)就不需要猜業(yè)務(wù)意圖了。它只需要像是照著填空題一樣把行為補(bǔ)齊。就算它補(bǔ)得不夠完美我能基于規(guī)格逐條驗(yàn)收而不是像以前那樣靠肉眼 review 兩三百行生成的代碼。2.3 規(guī)格評(píng)審環(huán)節(jié)不可跳過(guò)有一個(gè)很容易被忽略的動(dòng)作給 AI 執(zhí)行之前規(guī)格本身必須先過(guò)一遍評(píng)審。我通常直接用一個(gè)小模型或者讓另一個(gè) AI 扮演評(píng)審角色檢查任務(wù)規(guī)格是否有歧義、是否覆蓋邊界條件、改動(dòng)范圍是否收窄。這個(gè)環(huán)節(jié)極其重要。實(shí)測(cè)下來(lái)一個(gè)存在二義性的規(guī)格會(huì)讓 AI 產(chǎn)生大量無(wú)意義發(fā)揮。比如你寫(xiě)完善登錄邏輯AI 可能去改密碼重置流程但如果你寫(xiě)僅修改 TK-102 改動(dòng)范圍內(nèi)文件的登錄接口行為它就安分得多。規(guī)格評(píng)審不花多少時(shí)間但能省掉后面大量的返工成本。3. Harness 具體怎么駕馭模型工作區(qū)、上下文與執(zhí)行回退的約束機(jī)制3.1 Harness 不是 IDE 插件而是運(yùn)行環(huán)境剛開(kāi)始我看到 DeepSeek Harness 這類詞時(shí)以為它是一個(gè)普通插件。實(shí)際用過(guò)之后我傾向于把它理解為一套AI 執(zhí)行沙箱 狀態(tài)控制器。它會(huì)為每次任務(wù)建立一個(gè)隔離的工作區(qū)把規(guī)格文件、相關(guān)代碼、既有測(cè)試全部掛載進(jìn)去然后才讓 AI 開(kāi)始生成代碼。這樣一來(lái)AI 看到的不是整個(gè)項(xiàng)目的龐雜文件樹(shù)而是本次任務(wù)真正需要感知的最小集合。上下文切片這個(gè)設(shè)計(jì)特別關(guān)鍵——它比把所有代碼塞進(jìn)對(duì)話更可控也大幅降低了 AI 產(chǎn)生跨文件誤操作的概率。3.2 Harness 和 Agent 的區(qū)別到底在哪搜索熱度里不少人糾結(jié) Harness 和 Agent 的區(qū)別。我用自己的話概括Agent 是AI 自主行動(dòng)的能力單元它有多步規(guī)劃、能自己決定下一步做什么Harness 是約束 Agent 行為的運(yùn)行框架它規(guī)定 Agent 每一步能做什么、不能做什么、做完必須產(chǎn)出什么。如果說(shuō) Agent 是手腳Harness 就是韁繩和導(dǎo)航儀。單獨(dú)用 Agent你看到的是它很能干但不可預(yù)期配上 Harness你看到的是它能干且基本不跑偏。實(shí)際工程里我不會(huì)讓 AI 以純 Agent 模式自由發(fā)揮而是讓它在這個(gè) harness 定義的狀態(tài)機(jī)里一格一格推進(jìn)。3.3 執(zhí)行軌跡與回退機(jī)制的設(shè)計(jì)在 Harness 工作流里每次代碼生成都會(huì)記錄執(zhí)行軌跡trace。這個(gè)軌跡包含AI 讀了哪些文件、改了哪些文件、生成時(shí)依據(jù)了哪條規(guī)格條目、執(zhí)行了哪些驗(yàn)證命令。這個(gè)設(shè)計(jì)給我的實(shí)際價(jià)值是代碼回退不再是一刀切。以前用普通 AI 編程改壞了只能整文件 revertAI 自己也不會(huì)記得更早的版本。有了軌跡之后我可以定位到某一次錯(cuò)誤的生成動(dòng)作精準(zhǔn)回退那一步的 diff而不是丟掉整塊的改動(dòng)。這跟我手動(dòng)用 git 配合有很大區(qū)別——git 告訴我改了什么Harness 告訴我為什么這么改、按什么理由改。4. 一條可落地的 SDD Harness 開(kāi)發(fā)流水線從任務(wù)拆解到驗(yàn)收回退4.1 理想流水線的六個(gè)環(huán)節(jié)綜合我自己的實(shí)踐一套標(biāo)準(zhǔn)流程大致是需求入庫(kù)產(chǎn)品側(cè)的需求先落到 RQ-SPEC規(guī)格拆解架構(gòu)師/資深開(kāi)發(fā)者把 RQ-SPEC 拆成 TK-SPEC任務(wù)派遣Harness 按 TK-SPEC 調(diào)動(dòng)模型在工作區(qū)執(zhí)行生成自動(dòng)驗(yàn)收?qǐng)?zhí)行 AC-SPEC 對(duì)應(yīng)的單元測(cè)試、接口測(cè)試、lint 檢查差異評(píng)審人類審查關(guān)鍵 diff結(jié)合 trace 判斷是否放行合并回退通過(guò)則合入主干不通過(guò)則基于 trace 回退到最近可用狀態(tài)。不要小看差異評(píng)審這一環(huán)。它必須由人來(lái)做而且只 review diff 和 trace不用像以前那樣通讀全部生成代碼。這既避免了人力過(guò)載又保留了人的判斷權(quán)。4.2 把本地驗(yàn)證接入 HarnessAI 生成的代碼能否并入主干不能看它說(shuō)能跑得看驗(yàn)證腳本的真實(shí)輸出。我習(xí)慣在 Harness 的配置里把驗(yàn)證命令編排好# 在 Harness 工作區(qū)內(nèi)執(zhí)行驗(yàn)證 python -m pytest src/modules/auth/tests/ -q python -m mypy src/modules/auth/ python -m ruff check src/modules/auth/這些命令執(zhí)行失敗時(shí)Harness 會(huì)把失敗信息反饋給 AI讓 AI 繼續(xù)修復(fù)或者標(biāo)記為過(guò)度生成并觸發(fā)回退。實(shí)測(cè)下來(lái)這個(gè)反饋閉環(huán)是保證質(zhì)量最有效的一步本質(zhì)上就是給 AI 裝了一個(gè)驗(yàn)收儀表盤。4.3 內(nèi)網(wǎng)部署與團(tuán)隊(duì)協(xié)作的注意點(diǎn)如果你和我一樣有把整套體系部署在內(nèi)網(wǎng)服務(wù)器的需求那要注意幾個(gè)點(diǎn)大模型權(quán)重要做防護(hù)。本地部署時(shí)模型是通過(guò)內(nèi)網(wǎng) API 暴露的harness 側(cè)只要配置 base_url 指向內(nèi)網(wǎng)地址即可工作區(qū)目錄建議放在共享存儲(chǔ)上方便多人復(fù)用規(guī)格和執(zhí)行軌跡權(quán)限上要區(qū)分誰(shuí)能提交規(guī)格、誰(shuí)能修改 harness 配置、誰(shuí)能放行合并否則團(tuán)隊(duì)里人人都能改動(dòng)約束流水線就崩了。4.4 用 Harness 串起 RPA 落地場(chǎng)景順帶提一句現(xiàn)在熱詞里也有harness rpa 落地實(shí)現(xiàn)。我理解這個(gè)方向是把同樣的規(guī)格驅(qū)動(dòng)邏輯應(yīng)用到 RPA 流程編排上——RPA 機(jī)器人執(zhí)行的每一步也用規(guī)格文檔約束用 harness 去管理流程版本和觸發(fā)條件。這樣同事改 RPA 流程時(shí)不用再靠 Excel 表格來(lái)回傳而是直接改一條規(guī)格記錄由 harness 負(fù)責(zé)更新與回退。思路和代碼工程完全一致只是執(zhí)行體從模型生成代碼換成了機(jī)器人執(zhí)行操作步驟。5. 多模型協(xié)作與本地化部署的現(xiàn)實(shí)取舍5.1 不同任務(wù)用不同模型的策略同一套 Harness 體系下我不建議所有任務(wù)都用同一個(gè)最強(qiáng)模型。模型選擇應(yīng)該跟著任務(wù)難度走簡(jiǎn)單函數(shù)生成、正則、測(cè)試腳手架用一個(gè)快速的小模型就夠成本低、延遲低跨模塊重構(gòu)、接口設(shè)計(jì)、復(fù)雜邊界推導(dǎo)用更強(qiáng)的模型規(guī)格評(píng)審、需求歧義檢查反而適合用一個(gè)挑剔的模型專門挑刺。這個(gè)思路對(duì)應(yīng)了實(shí)踐中常見(jiàn)的多 AI 協(xié)作場(chǎng)景。不是讓多個(gè) AI 同時(shí)寫(xiě)代碼——那只會(huì)產(chǎn)出災(zāi)難——而是讓它們各司其職各自負(fù)責(zé)一個(gè)可驗(yàn)證的環(huán)節(jié)。5.2 DeepSeek 系列模型在 Harness 中的適配在我實(shí)際用的模型譜系里DeepSeek 系列是性價(jià)比非常高的選擇。比如用 deepseek 模型做 RQ-SPEC 拆分時(shí)它可以給出比較全面的邊界條件清單而做代碼執(zhí)行任務(wù)時(shí)只要給定清晰的約束和格式要求它的產(chǎn)出質(zhì)量也相當(dāng)穩(wěn)定。如果你也想試安裝和接入流程并不復(fù)雜把模型的 API 地址配置到 harness 的模型路由里然后針對(duì)不同任務(wù)配置不同的 model 字段。這里有一個(gè)關(guān)鍵提醒——模型切換時(shí)格式約束必須保持一致。比如要求輸出 JSON 就都要求 JSON否則 harness 的后續(xù)解析器很容易斷掉。5.3 提示詞優(yōu)化插件的實(shí)際作用熱搜里一直有deepseek harness 提示詞優(yōu)化插件這個(gè)詞。我試過(guò)這類插件之后的理解是它的工作是在把任務(wù)規(guī)格傳給模型之前先對(duì)指令做一次結(jié)構(gòu)強(qiáng)化——比如把人工寫(xiě)的含糊描述改寫(xiě)成更嚴(yán)密的指令鏈。真實(shí)的使用反饋它對(duì)一次性任務(wù)是錦上添花對(duì)長(zhǎng)鏈路任務(wù)幫助不小。因?yàn)樵陂L(zhǎng)鏈路里模型每執(zhí)行一步提示詞的微小歧義都會(huì)被放大。建議你把提示詞優(yōu)化插件當(dāng)作輸入端的保險(xiǎn)絲而不是替代規(guī)格拆分。規(guī)格拆分是設(shè)計(jì)問(wèn)題提示詞優(yōu)化只是表達(dá)問(wèn)題兩者不在一個(gè)層面上。6. 這半年踩過(guò)的坑Harness 不是銀彈邊界比能力更重要6.1 上下文過(guò)載讓 AI 一口氣處理整個(gè)模塊第一次實(shí)戰(zhàn)時(shí)我把一個(gè)大模塊的所有規(guī)格、所有歷史 trace 全部掛進(jìn)一次任務(wù)結(jié)果 AI 直接在中間崩了輸出了前后矛盾的結(jié)構(gòu)。后來(lái)我學(xué)乖了每次任務(wù)只掛載當(dāng)前 TK-SPEC 所需的最少上下文。Harness 的切片機(jī)制本來(lái)就是干這個(gè)的別自己貪心把它繞過(guò)去。建議的做法是如果一個(gè)需求涉及超過(guò) 5 個(gè)文件就主動(dòng)拆成兩到三個(gè)任務(wù)讓 AI 分步執(zhí)行每步只動(dòng)一小片。拆得越小回退粒度越精確找錯(cuò)越容易。6.2 規(guī)格寫(xiě)得太像需求描述等于沒(méi)寫(xiě)我犯過(guò)的最典型的錯(cuò)誤是把 TK-SPEC 寫(xiě)成了產(chǎn)品需求說(shuō)明書(shū)。比如優(yōu)化登錄體驗(yàn)這種描述放進(jìn)任務(wù)規(guī)格AI 當(dāng)然自由發(fā)揮。正確的做法是規(guī)格里只保留可驗(yàn)證項(xiàng)和硬性邊界不給 AI 留解釋空間。有個(gè)自檢方法很好用拿任務(wù)規(guī)格去問(wèn)一個(gè)初次接觸項(xiàng)目的人他是否能不看項(xiàng)目代碼就說(shuō)出代碼必須做什么、絕對(duì)不能做什么。如果答案模棱兩可說(shuō)明規(guī)格沒(méi)寫(xiě)到位。6.3 回退粒度與AI 盲信Harness 提供了回退能力但如果你只回退到上一版 git commit很多無(wú)關(guān)改動(dòng)仍然會(huì)被混進(jìn)主干。我現(xiàn)在的習(xí)慣是要求 Harness 把每次 AI 生成產(chǎn)出一個(gè)獨(dú)立 patch 文件回退時(shí)只撤銷那個(gè) patch 涉及的 diff不動(dòng)其他任何文件。這樣多個(gè)任務(wù)并發(fā)時(shí)互不污染。還有一點(diǎn)必須強(qiáng)調(diào)別盲信 AI 的自述。AI 會(huì)告訴你測(cè)試都過(guò)了但我親眼見(jiàn)過(guò)它虛構(gòu)測(cè)試結(jié)果。因此 Harness 工作流里所有的驗(yàn)證結(jié)果必須來(lái)自真實(shí)命令輸出而不是來(lái)自模型描述。驗(yàn)證環(huán)節(jié)絕對(duì)不能省這個(gè)底線守不住后面的可控都是紙糊的。6.4 人機(jī)協(xié)作的節(jié)奏Harness 管效率人管判斷用了一段時(shí)間之后我最大的體會(huì)是Harness 不是讓人閑著而是把人的精力從盯過(guò)程轉(zhuǎn)移到抓關(guān)鍵。以前我是全程盯著 AI 輸出生怕它跑偏現(xiàn)在我只關(guān)心三個(gè)時(shí)間點(diǎn)規(guī)格評(píng)審時(shí)、關(guān)鍵 diff 審查時(shí)、驗(yàn)收失敗時(shí)。這種節(jié)奏下我一個(gè)人可以同時(shí)推進(jìn)三到四個(gè)模塊的開(kāi)發(fā)質(zhì)量反而比以前更高。原因很簡(jiǎn)單失控的自由發(fā)揮被約束了AI 生成代碼的可預(yù)期性大幅提升。最后再分享一個(gè)讓我很受用的經(jīng)驗(yàn)SDD Harness 這套體系一開(kāi)始落地時(shí)會(huì)覺(jué)得寫(xiě)規(guī)格比寫(xiě)代碼還麻煩但堅(jiān)持一兩個(gè)迭代之后你會(huì)發(fā)現(xiàn)自己團(tuán)隊(duì)的返工率明顯下降因?yàn)殄e(cuò)誤在更早的環(huán)節(jié)就被攔截了。如果你也因?yàn)?AI 代碼不可控而頭疼強(qiáng)烈建議從這個(gè)組合入手試試別指望靠換一個(gè)更強(qiáng)的模型解決問(wèn)題——模型的智商不是瓶頸流程是否可控才是。