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