規(guī)則設(shè)計(jì))
我最近做代碼評(píng)審看到一份非常典型的 AI 生成 MR支付回調(diào)里加了日志邏輯挑不出大毛病但它順手把整個(gè) header 對(duì)象打了出來(lái)。如果合入access token 會(huì)跟著日志一起流進(jìn)日志系統(tǒng)。這不是個(gè)例而是 AI 編碼時(shí)代的典型翻車(chē)方式——AI 生成的代碼越來(lái)越“像樣”但它不會(huì)主動(dòng)告訴你它改了哪些文件、加了哪些依賴、埋下了哪些自己察覺(jué)不到的問(wèn)題。所以我想認(rèn)真聊一個(gè)很多人忽略的話題工程級(jí) AI 編碼邊界與防護(hù)規(guī)則設(shè)計(jì)。如果你正在用 AI 寫(xiě)代碼或者正準(zhǔn)備把 AI 編碼工具引入正式項(xiàng)目這篇文章適合你。我會(huì)結(jié)合自己踩過(guò)的坑講清楚為什么邊界比效率更重要再給出一套可以直接拿走的防護(hù)規(guī)則設(shè)計(jì)從規(guī)則文件到 Git Hook、再到 CI 流水線盡量做到每一步都可落地。1. 為什么 AI 代碼總會(huì)“暗度陳倉(cāng)”失控場(chǎng)景復(fù)盤(pán)1.1 主路徑正確邊緣條件一碰就碎讓 AI 寫(xiě)一個(gè)用戶上傳頭像的功能它生成的文件處理函數(shù)看著漂漂亮亮文件名處理、格式校驗(yàn)都有。但仔細(xì)一檢查能發(fā)現(xiàn)三個(gè)問(wèn)題文件名直接用原始文件名拼進(jìn)路徑?jīng)]有做歸一化路徑穿越風(fēng)險(xiǎn)MIME 校驗(yàn)用了includes(image)image/svgxml也能混進(jìn)來(lái)如果 SVG 里塞了腳本就成了存儲(chǔ)型 XSS文件大小檢查放在讀入內(nèi)存之后一個(gè)超大文件能把內(nèi)存打滿。這不是個(gè)例。AI 的訓(xùn)練數(shù)據(jù)讓它特別擅長(zhǎng)模仿“正常代碼的長(zhǎng)相”但項(xiàng)目特有的威脅模型和邊界條件它沒(méi)有感知。它寫(xiě)出來(lái)的代碼主流程基本能跑一到異常分支、惡意輸入、極端參數(shù)就開(kāi)始露餡。這些代碼混進(jìn)主干之后不是“有個(gè) bug 沒(méi)修”這么簡(jiǎn)單而是直接給系統(tǒng)開(kāi)了一個(gè)口子。1.2 AI“一本正經(jīng)”編造 API 和配置更讓人頭疼的是 AI 的“幻覺(jué)”。我問(wèn)過(guò) AI 某個(gè)第三方 SDK 的用法它給我寫(xiě)出了一個(gè)根本不存在的函數(shù)簽名參數(shù)名、返回類型看起來(lái)都合理一編譯就報(bào)錯(cuò)。還有一次它在一個(gè) Node 項(xiàng)目里自行添加了一個(gè) npm 包沒(méi)做任何版本鎖定理由是“這個(gè)場(chǎng)景用這個(gè)庫(kù)比較方便”。這種“自信地犯錯(cuò)”比不會(huì)寫(xiě)更危險(xiǎn)。人工寫(xiě)代碼時(shí)作者通常會(huì)查文檔、跑測(cè)試確認(rèn)一下AI 生成代碼后如果開(kāi)發(fā)者在 review 時(shí)也沒(méi)多想這些不存在的 API、未鎖版本的依賴就會(huì)直接進(jìn)入代碼庫(kù)。等部署環(huán)境一換或者依賴一更新問(wèn)題才會(huì)集中爆發(fā)。1.3 失控的真正代價(jià)責(zé)任和信任都模糊了代碼是資產(chǎn)也是負(fù)債。AI 生成的代碼在沒(méi)有邊界約束時(shí)會(huì)變成一種特殊的負(fù)債——你不知道它在什么場(chǎng)景下會(huì)失效也不知道它背后的設(shè)計(jì)依據(jù)是什么。人工寫(xiě)代碼時(shí)作者至少踩過(guò)哪些坑、為什么繞開(kāi)某個(gè)方案都會(huì)有印象AI 代碼沒(méi)有這些上下文。出問(wèn)題后你不得不從需求反推重新理解一遍代碼邏輯甚至重寫(xiě)。更麻煩的是責(zé)任模糊。團(tuán)隊(duì)一旦養(yǎng)成“AI 寫(xiě)、我隨便看”的習(xí)慣review 就會(huì)流于形式。到時(shí)候出了線上事故誰(shuí)都覺(jué)得不是自己的問(wèn)題但誰(shuí)都無(wú)法快速定位問(wèn)題。結(jié)果就是信任崩塌一部分人開(kāi)始把 AI 編碼拉黑另一部分人繼續(xù)無(wú)邊界地依賴 AI團(tuán)隊(duì)割裂成兩個(gè)極端。2. 工程級(jí) AI 編碼邊界設(shè)計(jì)三層邊界模型與工作流改造2.1 動(dòng)手前先回答三個(gè)問(wèn)題我建議你在給 AI 編碼立規(guī)矩之前先認(rèn)真回答三個(gè)問(wèn)題第一你允許 AI 寫(xiě)哪些文件哪些文件是絕對(duì)不讓 AI 碰的比如鑒權(quán)模塊、支付邏輯、部署腳本這些改動(dòng)一旦出錯(cuò)影響的是賬號(hào)和錢(qián)。第二每一段 AI 代碼進(jìn)入主干前要經(jīng)過(guò)哪幾道驗(yàn)證單測(cè)、lint、安全掃描、人工 review缺了哪一道都不算完整閉環(huán)。第三萬(wàn)一 AI 改錯(cuò)了你能在多長(zhǎng)時(shí)間內(nèi)回滾影響范圍到哪里這三個(gè)問(wèn)題看著簡(jiǎn)單但絕大多數(shù)團(tuán)隊(duì)答不上來(lái)。答不上來(lái)說(shuō)明邊界是模糊的。別讓這種模糊延續(xù)到代碼庫(kù)里。2.2 入口邊界任務(wù)拆到多細(xì)才算穩(wěn)入口邊界解決的是“給 AI 什么活兒”的問(wèn)題。很多開(kāi)發(fā)者習(xí)慣一句話丟給 AI“幫我重構(gòu)這個(gè)模塊”。任務(wù)太大AI 的自由發(fā)揮空間就大產(chǎn)出不可控性也大。工程級(jí)的做法是把需求拆成小而明確的“任務(wù)單元”比如“把 utils/format.ts 里的手機(jī)號(hào)格式化函數(shù)拆成兩個(gè)函數(shù)并補(bǔ)充單元測(cè)試”這個(gè)粒度就比“重構(gòu)整個(gè) utils 模塊”穩(wěn)得多。我給團(tuán)隊(duì)的提示詞模板大概是這樣的先說(shuō)明目標(biāo)任務(wù)再限定文件范圍再列出約束條件。比如“只修改 src/utils/date.ts 文件不要新增依賴不要改變現(xiàn)有函數(shù)簽名不要格式化無(wú)關(guān)代碼提交前跑一遍相關(guān)測(cè)試”。這些約束看起來(lái)啰嗦但對(duì) AI 來(lái)說(shuō)約束越明確它的行為就越可控。2.3 過(guò)程邊界文件范圍、變更粒度和可觀測(cè)性過(guò)程邊界解決的是“AI 干活時(shí)怎么限制它”的問(wèn)題。我習(xí)慣從三個(gè)維度去卡文件范圍用規(guī)則文件把關(guān)鍵目錄拉黑比如src/auth/**、deploy/**凡是 AI 生成的 diff 碰了這些目錄直接攔截。變更粒度AI 一次只能提交一個(gè)任務(wù)單元對(duì)應(yīng)的改動(dòng)不允許夾帶無(wú)關(guān)重構(gòu)。判斷方法很簡(jiǎn)單看 diff 里有沒(méi)有跟當(dāng)前任務(wù)無(wú)關(guān)的改行??捎^測(cè)性AI 生成的提交必須在提交信息里帶[ai]或類似標(biāo)簽比如feat: add phone formatter [ai]。標(biāo)簽的作用不是區(qū)別對(duì)待而是讓評(píng)審者心里多一道校驗(yàn)線也讓日后的代碼考古能夠快速定位這段代碼的來(lái)源。2.4 出口邊界沒(méi)有驗(yàn)證就不許合并出口邊界解決的是“AI 寫(xiě)的東西怎么驗(yàn)收”的問(wèn)題。我規(guī)定 AI 相關(guān) MR 必須滿足三個(gè)條件才能合并單測(cè)和 lint 全綠密鑰掃描和依賴審計(jì)通過(guò)至少一個(gè)負(fù)責(zé)該模塊的人做了 review。如果有 AI 改動(dòng)的文件屬于關(guān)鍵模塊比如鑒權(quán)、支付、權(quán)限模型我會(huì)要求兩個(gè)人 review其中一個(gè)人專門(mén)看安全影響。這個(gè)環(huán)節(jié)不能省。很多項(xiàng)目出事都是因?yàn)?AI 寫(xiě)著順手review 看著也順手結(jié)果上線后一打就碎。2.5 把 AI 編碼改成“時(shí)間流式”工作流現(xiàn)在很多 AI 編碼工具都強(qiáng)調(diào)“一鍵生成”但工程上我更推薦“時(shí)間流式”推進(jìn)。所謂時(shí)間流就是把開(kāi)發(fā)過(guò)程看作一系列按時(shí)間順序排列的可回放步驟每個(gè)步驟只做一件事做完就提交提交就能回滾。舉個(gè)例子讓 AI 重構(gòu)一個(gè)父組件我不會(huì)讓它一次性輸出完整的新文件。我會(huì)拆成三個(gè)時(shí)間片第一個(gè)時(shí)間片把被內(nèi)聯(lián)的子組件抽成獨(dú)立組件第二個(gè)時(shí)間片替換父組件的調(diào)用方式第三個(gè)時(shí)間片跑測(cè)試并清理死代碼。每個(gè)時(shí)間片單獨(dú)提交、單獨(dú) review。這樣就算第二個(gè)時(shí)間片的方案有問(wèn)題也不會(huì)把第一個(gè)時(shí)間片的好代碼拖下水。工具類型上現(xiàn)代 AI 代碼編輯器、命令行 Agent、自動(dòng)評(píng)審機(jī)器人可以組合成一條流水線。哪怕只是先用免費(fèi)的基礎(chǔ)工具也應(yīng)該讓它們遵循同一套工作流而不是各干各的、最后人工補(bǔ)救。AI 代碼編程工具的效率差異其實(shí)沒(méi)有想象中大真正拉開(kāi)差距的是“輸出有沒(méi)有被有效校驗(yàn)和控制”。3. 防護(hù)規(guī)則落地規(guī)則文件、Git Hook 與 CI 強(qiáng)校驗(yàn)3.1 規(guī)則要防的不是 AI而是五類高發(fā)風(fēng)險(xiǎn)規(guī)則不是用來(lái)防 AI 的防的是 AI 放大后的工程風(fēng)險(xiǎn)。從我實(shí)際看到的上線事故來(lái)看有五類風(fēng)險(xiǎn)優(yōu)先級(jí)最高風(fēng)險(xiǎn)類型典型表現(xiàn)防護(hù)手段敏感信息泄露硬編碼密鑰、日志打印 token 和 header密鑰掃描、正則黑名單危險(xiǎn) API 調(diào)用eval、child_process.exec、shell 拼接危險(xiǎn)調(diào)用黑名單依賴失控AI 自行新增第三方包、不鎖版本依賴白名單、lock 文件檢查權(quán)限過(guò)寬文件權(quán)限 0o666、越權(quán)寫(xiě)操作代碼風(fēng)格檢查、人工審核非冪等操作數(shù)據(jù)庫(kù)遷移腳本被重復(fù)執(zhí)行、批量寫(xiě)操作單獨(dú)人工審核AI 的很多錯(cuò)誤是“單個(gè)點(diǎn)看著沒(méi)問(wèn)題整體破壞系統(tǒng)穩(wěn)定性”。所以規(guī)則要偏保守寧可用黑白名單也不要讓 AI 在關(guān)鍵路徑上自由探索。3.2 用一份規(guī)則文件統(tǒng)轄所有檢查我習(xí)慣把防護(hù)規(guī)則收斂到一份 YAML 文件里比如ai-code-rules.yml。這份文件同時(shí)供本地腳本和 CI 讀取避免在兩處各寫(xiě)一套規(guī)則、最后不一致的問(wèn)題。一個(gè)比較典型的文件長(zhǎng)這樣version: 1.0 project: demo-web blocked_paths: - src/auth/** - src/payment/** - deploy/** - .github/workflows/** allowed_paths: - src/components/** - src/utils/** - src/pages/** forbidden_patterns: - (AKIA[0-9A-Z]{16}) - eval\\( - password\\s*\\s*[\][^\][\] dangerous_calls: - child_process.execSync - child_process.exec - shell: true dependency_policy: mode: whitelist allow: - lodash - axios - react deny: [] check_level: strict review_required: true這份文件里blocked_paths是保護(hù)名單allowed_paths是鼓勵(lì) AI 發(fā)揮的范圍forbidden_patterns是正則黑名單dependency_policy則是依賴白名單。實(shí)際操作中要根據(jù)項(xiàng)目情況調(diào)整但核心思路一致把規(guī)則寫(xiě)成代碼而不是寫(xiě)在團(tuán)隊(duì)協(xié)作說(shuō)明里。3.3 本地第一道網(wǎng)pre-commit 掃描腳本本地防護(hù)的最佳位置是pre-commithook。我寫(xiě)過(guò)一個(gè) Node 腳本核心邏輯是讀取當(dāng)前暫存的 diff然后依次檢查剛才那份規(guī)則文件里的條目。以下是一個(gè)簡(jiǎn)化版#!/usr/bin/env node // scripts/guard-ai-diff.js const { execSync } require(child_process); const fs require(fs); const yaml require(yaml); const rules yaml.parse(fs.readFileSync(ai-code-rules.yml, utf8)); const diff execSync(git diff --cached, { encoding: utf-8 }); const problems []; // 1. 檢查是否觸碰未允許修改的文件 rules.blocked_paths.forEach((pattern) { if (diff.includes(diff --git a/${pattern})) { problems.push(禁止修改文件: ${pattern}); } }); // 2. 檢查危險(xiǎn)正則 rules.forbidden_patterns.forEach((pattern) { const re new RegExp(pattern, gm); if (re.test(diff)) { problems.push(檢測(cè)到危險(xiǎn)模式: ${pattern}); } }); if (problems.length) { console.error(AI 防護(hù)規(guī)則攔截到以下問(wèn)題:); problems.forEach((p) console.error( -, p)); process.exit(1); }這里有一個(gè)很實(shí)際的坑掃描 diff 時(shí)要過(guò)濾掉刪除行。如果你刪掉一行壞代碼正則卻從刪除的內(nèi)容里命中了問(wèn)題就會(huì)誤報(bào)。更穩(wěn)妥的做法是解析 diff只掃描新增行。另外YAML 解析和 JSON 解析一樣加錯(cuò)一個(gè)空格就會(huì)掛所以規(guī)則文件建議配一個(gè)rules.schema.json做校驗(yàn)或者用 TypeScript 寫(xiě)規(guī)則讓編譯期幫你看住類型。3.4 CI 做強(qiáng)制關(guān)卡密鑰掃描、依賴審計(jì)、diff 校驗(yàn)本地 hook 可以被繞過(guò)真正強(qiáng)制的是 CI。我一般在流水線里放四類任務(wù)先跑 lint 和單元測(cè)試再跑依賴漏洞檢查接著跑密鑰掃描最后跑剛才那份規(guī)則文件里的 diff 校驗(yàn)。GitHub Actions 的簡(jiǎn)版配置大概是這樣的name: ai-guard on: [pull_request] jobs: ai-guard: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: 20 - run: npm ci - run: npm run lint - run: npm test - run: npm audit --audit-levelhigh - run: npx gitleaks detect --source . --redact - run: node scripts/guard-ai-diff.jsgitleaks是目前比較普及的密鑰掃描工具對(duì)常見(jiàn)云廠商密鑰格式都有默認(rèn)規(guī)則npm audit則負(fù)責(zé)依賴漏洞。兩者配合 diff 校驗(yàn)基本能覆蓋我前面列的五類高發(fā)風(fēng)險(xiǎn)中的四類。關(guān)鍵是CI 里的規(guī)則必須和本地腳本用同一份配置否則本地測(cè)完 CI 又?jǐn)r一道體驗(yàn)會(huì)很割裂。3.5 人的 review 升級(jí)成“決策點(diǎn)抽查”加入防護(hù)規(guī)則后人工 review 的價(jià)值沒(méi)有降低而是變化了。要求 reviewer 一行行重讀 AI 生成的幾百行代碼不現(xiàn)實(shí)也沒(méi)有必要。我更推薦把 review 重心放在三個(gè)決策點(diǎn)上第一AI 為什么做這個(gè)設(shè)計(jì)有沒(méi)有被忽略的更簡(jiǎn)單方案第二邊界條件覆蓋了嗎尤其是輸入校驗(yàn)、日志脫敏、依賴變化。第三AI 是不是順手改多了有沒(méi)有夾帶與當(dāng)前任務(wù)無(wú)關(guān)的重構(gòu)這三個(gè)決策點(diǎn)一看就是“人在回路”才會(huì)關(guān)注的單純靠自動(dòng)化工具判斷不了。Reviewer 不需要把 AI 當(dāng)成不信任對(duì)象而是把它當(dāng)成一個(gè)經(jīng)驗(yàn)豐富但偶爾冒進(jìn)的協(xié)作伙伴你要做的是把住設(shè)計(jì)層面的關(guān)鍵關(guān)口。4. 實(shí)操記錄給前端 Node 項(xiàng)目加一道 AI 代碼安全閘門(mén)4.1 項(xiàng)目背景和規(guī)則目標(biāo)我在一個(gè)由 3 人維護(hù)的 Node 前端項(xiàng)目上完整跑過(guò)這套方案。項(xiàng)目有幾十個(gè)頁(yè)面核心業(yè)務(wù)集中在幾個(gè)大模塊里團(tuán)隊(duì)三個(gè)人都在用 AI 編碼工具。剛開(kāi)始大家覺(jué)得 AI 效率很高直到有一天在日志里發(fā)現(xiàn)測(cè)試環(huán)境密鑰被打了出來(lái)才意識(shí)到?jīng)]有邊界的 AI 編碼有多危險(xiǎn)。這次整改的目標(biāo)很簡(jiǎn)單第一防止敏感信息進(jìn)倉(cāng)庫(kù)第二保護(hù) auth 和支付這類核心目錄不被 AI 隨意改動(dòng)第三讓 AI 新增依賴必須有意識(shí)、有記錄第四保證所有 AI 生成代碼都經(jīng)過(guò) review。4.2 第一步建立 ai-code-rules.yml 并鎖死我先在倉(cāng)庫(kù)根目錄建了ai-code-rules.yml并規(guī)定這個(gè)文件本身不允許 AI 修改改動(dòng)必須走人工提交。然后做了三個(gè)設(shè)置把src/auth、src/payment、deploy目錄加入blocked_paths因?yàn)橘~號(hào)和錢(qián)相關(guān)的模塊一旦被改壞影響不可控把src/components、src/utils加進(jìn)allowed_paths在這個(gè)范圍內(nèi)允許 AI 自由發(fā)揮在forbidden_patterns里放入密鑰正則、eval、child_process.exec和密碼硬編碼規(guī)則。依賴策略我用了白名單模式新依賴必須人工確認(rèn)后加進(jìn)allow列表。這個(gè)文件建好之后我會(huì)在團(tuán)隊(duì)里同步一份說(shuō)明文檔把每個(gè)規(guī)則的解釋寫(xiě)清楚。因?yàn)橐?guī)則如果不解釋大家只會(huì)覺(jué)得“我改了代碼結(jié)果被機(jī)器人攔了”不知道攔的是什么、為什么攔。4.3 第二步寫(xiě)本地 pre-commit 防護(hù)我按照前面的示例寫(xiě)了scripts/guard-ai-diff.js掛在pre-commithook 上。腳本做三件事檢查 blocked_paths 是否出現(xiàn)在暫存的 diff 里掃描新增代碼中的危險(xiǎn)模式檢查 package.json 里的新增依賴是否在白名單內(nèi)。如果發(fā)現(xiàn)問(wèn)題腳本直接輸出錯(cuò)誤并退出進(jìn)程提交被中斷。第一周跑下來(lái)誤報(bào)率確實(shí)不低比如eval的正則會(huì)命中測(cè)試代碼里的字符串。后來(lái)我按文件類型做了分級(jí)測(cè)試文件和數(shù)據(jù)模擬文件走寬松規(guī)則業(yè)務(wù)代碼走嚴(yán)格規(guī)則。誤報(bào)降下去之后團(tuán)隊(duì)對(duì)這個(gè)腳本的態(tài)度從“煩人”變成了“還挺靠譜”。4.4 第三步在 CI 里跑同一套規(guī)則本地 hook 只是緩解CI 才是強(qiáng)制關(guān)卡。我把項(xiàng)目接到 GitHub Actions在 PR 合并前跑了npm run lint、npm test、npm audit、gitleaks最后再執(zhí)行一次node scripts/guard-ai-diff.js。develop 分支設(shè)為保護(hù)分支merge 前必須全部通過(guò)。這里一個(gè)重要經(jīng)驗(yàn)是不要讓本地和 CI 各實(shí)現(xiàn)一套邏輯。我之前見(jiàn)過(guò)有團(tuán)隊(duì)本地 Shell 腳本、CI 用插件結(jié)果兩邊規(guī)則不同步本地通過(guò)、CI 報(bào)錯(cuò)或者反過(guò)來(lái) CI 通過(guò)、本地?cái)r截非常浪費(fèi)時(shí)間。我直接把本地腳本復(fù)用到 CI這樣只要規(guī)則文件更新全鏈路立刻生效。4.5 第四步兩個(gè)星期的實(shí)測(cè)數(shù)據(jù)運(yùn)行兩周后我看到一些比較有意思的數(shù)據(jù)防護(hù)腳本在合并前攔下了 2 次密鑰泄露攔下 1 次對(duì) auth 目錄的越權(quán)修改有 3 個(gè) PR 因?yàn)樾略鲆蕾嚊](méi)走白名單被打回。這個(gè)結(jié)果直接證明規(guī)則攔截的“潛在事故”數(shù)量是真實(shí)的而不是空轉(zhuǎn)。更微妙的變化是團(tuán)隊(duì)對(duì) AI 編碼的信任度提升了。以前大家心里沒(méi)底不知道 AI 有沒(méi)有埋雷現(xiàn)在規(guī)則跑完一遍大家默認(rèn)“只要過(guò)了規(guī)則大概率沒(méi)問(wèn)題”。信任體系建立起來(lái)之后AI 編碼的效率紅利才真正釋放出來(lái)。5. AI 防護(hù)規(guī)則避坑指南誤報(bào)、繞過(guò)與團(tuán)隊(duì)阻力5.1 誤報(bào)太多規(guī)則會(huì)被“內(nèi)部和諧”第一次上規(guī)則的時(shí)候不要試圖把所有檢查調(diào)到最強(qiáng)。我第一周收集了大量誤報(bào)發(fā)現(xiàn)若干正則寫(xiě)得太寬比如密鑰正則把代碼里示例 token 當(dāng)成真密鑰危險(xiǎn)調(diào)用正則把注釋里的字符串也算了進(jìn)去。處理方式是按文件類型分規(guī)則文檔、測(cè)試文件、業(yè)務(wù)代碼分開(kāi)配置嚴(yán)苛度不同。還有一個(gè)技巧是給開(kāi)發(fā)者留顯式的豁免注釋比如在代碼行尾寫(xiě)// ai-guard:ignore-line告知規(guī)則這里已經(jīng)人工確認(rèn)過(guò)。這樣既不會(huì)卡住正常開(kāi)發(fā)也能保證豁免行為有記錄、可追溯。如果豁免次數(shù)太多就要回頭看看是不是規(guī)則本身有問(wèn)題而不是開(kāi)發(fā)者想繞規(guī)則。5.2 本地 Hook 能繞過(guò)CI 必須兜底git hooks 可以被繞過(guò)。git commit --no-verify一用本地腳本直接失效。所以我要強(qiáng)調(diào)這個(gè)原則CI 才是最后的兜底。我見(jiàn)過(guò)不少團(tuán)隊(duì)把心思都花在給開(kāi)發(fā)者裝 hook 上結(jié)果繞過(guò)之后毫無(wú)感知。更好的做法是本地 hook 做體驗(yàn)優(yōu)化CI 做強(qiáng)制校驗(yàn)兩者各司其職。如果團(tuán)隊(duì)用企業(yè)級(jí)代碼托管平臺(tái)建議把主干分支設(shè)為保護(hù)分支要求 PR 合并前必須全部檢查通過(guò)。這樣就算有人強(qiáng)行 push也會(huì)被攔截在服務(wù)器端而不是靠個(gè)人自覺(jué)。5.3 AI 喜歡順手改沒(méi)關(guān)的代碼怎么治AI 編碼工具最普遍的問(wèn)題就是“順手優(yōu)化”。它改一個(gè)函數(shù)時(shí)會(huì)把相鄰代碼也重排一遍順手改掉幾個(gè)縮進(jìn)、幾個(gè)變量名然后讓整個(gè) MR 看起來(lái)像個(gè)大重構(gòu)。這種情況不僅增加 review 負(fù)擔(dān)還會(huì)掩蓋真正有風(fēng)險(xiǎn)的改動(dòng)。我的處理方式有兩個(gè)第一在提示詞里顯式要求“只修改與任務(wù)相關(guān)的區(qū)域不要格式化無(wú)關(guān)代碼”第二在 diff 檢查里增加行數(shù)閾值A(chǔ)I 相關(guān) MR 改動(dòng)超過(guò) 400 行時(shí)自動(dòng)標(biāo)記為“需要人工拆分”。同時(shí)Reviewer 的檢查單里要有“刪除與本任務(wù)無(wú)關(guān)的改動(dòng)”這一項(xiàng)從制度上逼著 AI 的產(chǎn)出更聚焦。5.4 規(guī)則文件本身被改動(dòng)等于保護(hù)失效規(guī)則文件本身也要被保護(hù)這一點(diǎn)最容易忽略。如果 AI 可以順手修改ai-code-rules.yml和 CI 配置那它相當(dāng)于解鎖了所有限制防護(hù)形同虛設(shè)。我會(huì)把這些文件加進(jìn)blocked_paths并且要求對(duì)規(guī)則文件的任何改動(dòng)走單獨(dú)的 MR說(shuō)明變更原因由負(fù)責(zé)人確認(rèn)后再合入。另外規(guī)則文件最好放在.ai/目錄下讓所有 AI 工具和所有團(tuán)隊(duì)成員都讀取同一份配置。不要靠某個(gè)成員在個(gè)人提示詞里“約定俗成”那個(gè)東西沒(méi)法審計(jì)也沒(méi)法傳承。5.5 團(tuán)隊(duì)覺(jué)得審查負(fù)擔(dān)重怎么把阻力降到最低任何新流程都會(huì)遇到阻力防護(hù)規(guī)則也一樣。我最常聽(tīng)到的抱怨是“審查太多了本來(lái) AI 是為了解放人現(xiàn)在又要搭進(jìn)去時(shí)間”。我的經(jīng)驗(yàn)是不要把防護(hù)做成對(duì)人的考核而是把它做成對(duì)代碼的體檢。在落地時(shí)我會(huì)明確傳達(dá)一個(gè)原則這套規(guī)則是幫助開(kāi)發(fā)者在交付前發(fā)現(xiàn)問(wèn)題的不是事后追責(zé)的依據(jù)。然后把絕大多數(shù)檢查自動(dòng)化讓 AI 生成的代碼自動(dòng)跑掃描只有掃描結(jié)果異常時(shí)才需要人工介入。這樣團(tuán)隊(duì)成員感受到的就不是流程變重而是安全感變強(qiáng)AI 寫(xiě)的代碼有人幫忙把關(guān)自己可以放手試。我在實(shí)踐里最有用的一句話是“這套規(guī)則不是不讓你用 AI是讓 AI 的產(chǎn)出不會(huì)在深夜上線的時(shí)候反咬你一口?!蔽易约旱捏w會(huì)是AI 編碼邊界不是對(duì) AI 的不信任而是對(duì)軟件交付這件事的尊重。AI 工具只會(huì)越來(lái)越強(qiáng)但工程系統(tǒng)的價(jià)值從來(lái)不是由“生成速度”決定的而是由“可預(yù)測(cè)性”決定的。你給 AI 設(shè)置邊界、設(shè)計(jì)防護(hù)規(guī)則本質(zhì)上是把對(duì)交付質(zhì)量的控制權(quán)重新拿回自己手里。最后分享一個(gè)小技巧把這份規(guī)則文件放到倉(cāng)庫(kù)的.ai/目錄下讓所有 AI 工具和所有成員都讀取同一份配置團(tuán)隊(duì)就不需要靠口口相傳來(lái)約定 AI 使用規(guī)范。規(guī)則會(huì)過(guò)時(shí)記得每?jī)芍?review 一次跟項(xiàng)目一起演進(jìn)。