AI 工程

AI 寫的每一段 code,我在信任它之前一定跑的 5 道 prompt

一、「看起來很對」才是最貴的那種錯

你不會被那種一跑就炸的 AI 程式碼傷到——那種五秒就發現了。真正會傷到你的,是 diff 看起來乾淨、命名整齊、註解齊全、CI 全綠,然後三週後在 production 上你才發現它把所有時間都當成 UTC 在處理。

這不是感覺問題,有三組可查證的數字:

第一組,你對自己的判斷是失準的。 METR(Model Evaluation & Threat Research)2025 年 7 月發表的隨機對照試驗(arXiv:2507.09089,作者 Joel Becker、Nate Rush、Elizabeth Barnes、David Rein),找了 16 位有經驗的開源開發者做 246 個任務,全部在他們自己熟悉的成熟 repo 上。結果是:允許使用 AI 工具(主要是 Cursor Pro 搭配 Claude 3.5/3.7 Sonnet)反而讓完成時間增加 19%。最刺的不是這個數字,是主觀落差——事前這群人預期會快 24%,做完之後他們仍然以為自己快了 20%

第二組,你越用 AI,越覺得自己安全。 Stanford 的 Neil Perry、Megha Srivastava、Deepak Kumar、Dan Boneh 發表在 CCS '23 的使用者研究(arXiv:2211.03622)發現:有 AI 助手(當時的 codex-davinci-002)的受試者,寫出的程式碼安全性明顯較差,而且「比沒有 AI 的組別更相信自己寫的是安全的」。研究裡唯一的保護因子是:對工具抱持懷疑、並且會主動改寫 prompt 的人,漏洞比較少。

第三組,模型變聰明不等於變安全。 Veracode 的 2025 GenAI Code Security Report(2025-07-30 發布)測了超過 100 個 LLM,涵蓋 Java、Python、C#、JavaScript,45% 的產出樣本沒通過安全測試、引入了 OWASP Top 10 等級的漏洞;Java 最慘,72%。報告最值得記住的一句結論是:模型在「寫得對」上進步很多,在「寫得安全」上幾乎原地踏步,而且和模型大小無關。(適用前提要講清楚:那是一組專門為安全情境設計的評測任務,不等於「你 repo 裡有 45% 的 AI code 有洞」。)

共通點不是「AI 很爛」,是校準失準:AI 的錯誤長得不像錯誤,所以你平常用來抓錯的直覺失效了。要補的不是「下次注意一點」,而是一組固定的、每次都跑、不靠心情的驗證程序。

下面這五道 prompt 就是我的版本。先講為什麼要這樣拆,因為機制決定了 prompt 的長相。

說明:這個題目源自 r/ChatGPTCoding 上的討論主題,但我沒能取得原帖內容,所以這裡的五道 prompt 是我依照可查證的一手研究與官方文件重建的版本,不是轉述某個人的原始清單。

信任 AI 程式碼前的五道 prompt 驗證流程圖

二、為什麼「你再確認一次好嗎」完全沒用

大部分人的檢查動作是:在同一個對話裡回一句「你確定嗎?有沒有 bug?」這是所有檢查方式裡最沒用的一種,而且有三條獨立的研究線指向同一個原因。

第一條:沒有外部回饋的自我修正會失敗,甚至倒退。 Google DeepMind 的 Jie Huang、Xinyun Chen、Denny Zhou 等人在〈Large Language Models Cannot Self-Correct Reasoning Yet〉(arXiv:2310.01798)裡定義了 intrinsic self-correction——模型只靠自己的內在能力、沒有任何外部回饋去修正初始答案。他們的結論寫得很直白:在推理任務上模型難以自我修正,「有時候修正之後表現甚至更差」。

第二條:瓶頸在「給回饋」,不在「修」。 MIT 與 Microsoft Research 的 Theo X. Olausson、Jeevana Priya Inala、Chenglong Wang、Jianfeng Gao、Armando Solar-Lezama 在 ICLR 2024 的〈Is Self-Repair a Silver Bullet for Code Generation?〉(arXiv:2306.09896)用 Code Llama、GPT-3.5、GPT-4 在 HumanEval 和 APPS 上測 self-repair,發現算進運算成本後,「收益通常很小、在不同資料子集之間差異極大、有時根本不存在」。關鍵發現是:self-repair 的瓶頸是模型對自己程式碼給出回饋的能力。當他們把回饋換成更強的模型、或換成真人的除錯回饋,收益立刻大幅提升。

第三條:驗證問題要拆開、獨立問。 Meta AI 與 ETH Zürich 的 Shehzaad Dhuliawala、Jason Weston 等人提出的 Chain-of-Verification(arXiv:2309.11495)把驗證拆成四步:產出草稿 → 規劃驗證問題 → 獨立回答每一個驗證問題 → 產出修訂版答案。論文比較了幾種變體,結果很有啟發性:factored(每個驗證問題各自一個 prompt、看不到原本的草稿)穩定優於 joint(問題和答案寫在同一個 prompt 裡)。原因很直白——只要看得到原答案,模型就會照抄原答案裡的幻覺。

把三條線收斂,就是五道 prompt 的三個設計原則:

  1. 換 context。 做檢查的那顆模型,不該看得到「它當初為什麼這樣寫」的推理過程。
  2. 拆題。 一次只問一個具體、可回答的問題,絕不問「有沒有問題」這種 yes/no。
  3. 灌外部訊號。 測試輸出、registry 查詢、git diff、靜態掃描——沒有外部訊號的 review 就是在賭。

Anthropic 官方的 Claude Code 最佳實務文件把這個結論寫成一句可執行的話:「A reviewer running in a fresh subagent context sees only the diff and the criteria you give it, not the reasoning that produced the change, so it evaluates the result on its own terms.」重點在 fresh context。

同一 context 自我檢查與 factored 多 context 驗證的對比

三、五道 prompt,逐字可貼

每一道我都給:抓什麼、prompt 全文、外部訊號怎麼接、以及最常見的誤用。

P1 復述:先讓它說「這段在做什麼」

抓的是意圖漂移。 這一道有個關鍵規則:不要先給需求。給了需求就是給了答案,模型會把你的需求原封不動複述回來。

不要參考我們先前的對話。只讀我貼給你的這段 diff。
用 5 條以內、每條一行,寫出這段程式碼「實際上」做了什麼,
必須包含:它對輸入做了哪些假設、以及它在出錯時的行為。
不要評價好壞,不要建議改進,不要提到需求。
任何你不確定的地方,開頭標上「不確定:」。

拿到之後,你自己把它的復述跟原始需求對一遍。我遇過的 AI 事故有很大一部分死在這一步:它做了一件非常合理、程式碼也很漂亮、但不是你要的事。

實作上,在 Claude Code 裡開一個乾淨 session(或直接 /clear),把 diff 用管線餵進去,完全不帶專案脈絡:

git diff --cached | claude -p "不要參考任何專案脈絡。只讀這段 diff,用 5 條以內寫出它實際做了什麼、假設了什麼、出錯時如何反應。不要評價。"

P2 查證:把每個外部依賴逐一標記

抓的是幻覺 API 與幻覺套件。 Joseph Spracklen 等六位作者的〈We Have a Package for You!〉(arXiv:2406.10279)測了 16 個程式碼生成 LLM、兩組 prompt 資料集,產生 57.6 萬份程式碼樣本,結果是:商用模型平均至少 5.2%、開源模型 21.7% 的推薦套件根本不存在,他們總共蒐集到 205,474 個不重複的幻覺套件名。Python Software Foundation 的 Seth Larson 把「攻擊者搶先去 PyPI/npm 註冊這些名字」的手法命名為 slopsquatting——這是 typosquatting 的 AI 版本,只是打錯字的不是人,是模型。

只看這段 diff,不要看其他檔案。
列一張表,每一列是一個外部相依:套件名/匯入的模組/呼叫到的第三方 API 或方法。
每一列必須有三欄:
1) 你是否確定它真的存在(確定 / 不確定)——不確定就誠實寫不確定
2) 你認為它是在哪個版本引入的(不知道就寫「不知道」,禁止猜測版本號)
3) 一行可以在 shell 驗證它存在的指令
只給表格,不要任何解釋。

然後真的去跑第三欄。npm 用 npm view <pkg> version,Python 用 pip index versions <pkg>,內部 API 就 rg "def <method>" -n src/。這一步純機械,最適合固化成 hook,不該靠人記得做。GitHub 官方的〈Review AI-generated code〉教學也把「相依項審視」列為八步驟之一,特別點名要提防不存在或可疑來源的套件。

P3 破壞:不要問有沒有 bug,要它產出會壞掉的輸入

「有沒有 bug?」是 yes/no 問題,而模型對這種問題的先驗答案就是「沒有」。把它改寫成生成任務,行為完全不同。

你的任務是讓這段程式碼壞掉。
給我 8 組具體的輸入或系統狀態,每組一行,格式固定:
輸入/狀態 → 你預期會發生什麼 → 為什麼你認為這段程式碼沒處理
必須至少涵蓋這幾類:空值或缺欄位、邊界值、併發或重入、
型別與編碼、上游服務失敗或逾時、超大輸入。
不要修改程式碼。不要寫「這個應該沒問題」。要求 8 組就是 8 組。

拿到之後,把前三組寫成真的測試跑起來。Anthropic 的文件把這件事講得毫不客氣,他們列的常見失敗模式之一叫 the trust-then-verify gap,處方只有一句:「Always provide verification (tests, scripts, screenshots). If you can't verify it, don't ship it.」

GitHub 官方文件也提供了兩句可以直接借用的提示語:「What functional tests…do not exist or are missing?」以及「What potential complexities, edge cases, or scenarios are there that this code might not handle correctly?」

P4 攻擊:換一顆模型、換一個角色做安全審

這一道最好不要用寫 code 的那個 session。Anthropic 在 2025-08-06 推出的 /security-review 就是為此設計:在 terminal 裡 commit 前做臨時安全分析,掃 SQL injection、XSS、認證與授權缺陷、不安全的資料處理、相依套件漏洞,找到之後還能直接讓 Claude 修。他們同時開放了一個 GitHub Action,PR 一開就自動跑、把疑慮以 inline comment 貼在 PR 上。Anthropic 說他們自己用這個 Action,在內部工具進 production 前抓到過一個 DNS rebinding 造成的 RCE 和一個 SSRF。

如果你要自己寫,官方文件給的 subagent 範本可以直接放進 .claude/agents/security-reviewer.md

---
name: security-reviewer
description: Reviews code for security vulnerabilities
tools: Read, Grep, Glob, Bash
model: opus
---
You are a senior security engineer. Review code for:
- Injection vulnerabilities (SQL, XSS, command injection)
- Authentication and authorization flaws
- Secrets or credentials in code
- Insecure data handling

Provide specific line references and suggested fixes.

然後在對話裡明講:「Use a subagent to review this code for security issues.」重點是它跑在獨立 context,看不到你剛剛跟主 session 討論了什麼。

P5 回頭:檢查它有沒有為了讓測試變綠而動手腳

GitHub 官方文件把「deleted instead of fixed tests」——把測試刪掉而不是把問題修好——明確列為 AI 專屬的陷阱之一。這一道就是專門抓這個,外加抓範圍外的偷改。

只看 `git diff main...HEAD` 的內容,回答三個問題,每題一句話:
1) 哪些檔案的修改,超出了「<這裡填任務的一句話描述>」的範圍?
2) 有沒有任何測試被刪除、被 skip、或被放寬斷言
   (例如 assertEqual 改成 assertIsNotNone、把預期值改成實際輸出)?逐一列出行號。
3) 有沒有錯誤處理是靠吞掉例外、調降 log level、或 try/except pass 來讓流程通過的?
只回報會影響正確性或超出範圍的項目,不要回報風格偏好。

最後一句「不要回報風格偏好」不是客套。Anthropic 文件裡有一段很誠實的警告:「A reviewer prompted to find gaps will usually report some, even when the work is sound, because that is what it was asked to do.」被要求找洞的 reviewer 一定會找到洞,就算程式碼沒問題。追殺每一條 finding 的結果是 over-engineering:多餘的抽象層、防禦性程式碼,還有針對不可能發生的情況寫的測試。

四、把它固化下來,不要靠記性

五道 prompt 每次手打,跑三天就會停。用 Claude Code 現成的四種機制固化:

1. 存成 skill,變成一個指令。.claude/skills/review-ai-diff/SKILL.md 裡寫下 P1–P5 的完整步驟,加上 disable-model-invocation: true(這是有副作用的流程,你會想手動觸發),之後 /review-ai-diff 一句跑完。

2. 機械檢查交給 hook。 Hook 跟 CLAUDE.md 的差別,官方講得很清楚:CLAUDE.md 是建議性的,hook 是決定性的、保證會執行。P2 的套件驗證、lint、typecheck 都該是 hook。你甚至可以直接叫 Claude 幫你寫:「Write a hook that runs eslint after every file edit」。想更硬一點,用 Stop hook 讓檢查沒過就不准結束這一輪(Claude Code 在連續 8 次阻擋後會強制結束,別把它當作絕對閘門)。

3. 跨 session 的 Writer/Reviewer 分工。 官方明講:「A fresh context improves code review since Claude won't be biased toward code it just wrote.」Session A 實作,Session B 只拿到檔案路徑和判準去 review,再把 B 的輸出貼回 A 讓它修。如果不想開兩個視窗,就用 subagent 版本:

Use a subagent to review the rate limiter diff against PLAN.md. Check that
every requirement is implemented, the listed edge cases have tests, and
nothing outside the task's scope changed. Report gaps, not style preferences.

4. 進 CI。 claude -p 是非互動模式,可以塞進 pre-commit hook 或 CI pipeline,用 --output-format json 讓結果可解析、--allowedTools 限制它能做什麼。P4 直接掛 Anthropic 的 security review GitHub Action。

CLAUDE.md 只放一行就好,例如「所有涉及認證、金流、外部輸入的 diff,合併前必須跑 /review-ai-diff」。文件裡有個很好的自檢問題:對 CLAUDE.md 的每一行問「拿掉這行會不會讓 Claude 犯錯?」不會就刪掉——臃腫的 CLAUDE.md 會讓真正重要的規則被淹沒

五、限制、以及我不會誇大的部分

LLM 當 reviewer 有效,但它自己也會幻覺出 bug。 OpenAI 的 Nat McAleese、Jan Leike 等人的 CriticGPT 論文〈LLM Critics Help Catch LLM Bugs〉(arXiv:2407.00215)是目前最直接的證據:在含有自然發生 LLM 錯誤的程式碼上,模型寫的 critique 在 63% 的情況下被偏好於人類 critique,而且人類評估認為模型抓到的 bug 比付費的程式碼審查外包人員更多。但論文自己也寫了限制:critic 會幻覺出不存在的 bug,可能誤導人類去改本來沒問題的地方;而「人類 + critic」的組合抓到的 bug 數量跟純 LLM critic 相當,但幻覺更少

這就是這五道 prompt 的正確定位:它不是替你 review,它是把你的注意力放到對的地方。最後一關永遠是你。

幾個數字的適用前提也要講清楚: METR 的 19% 是 16 位開發者、246 個任務、在他們熟悉的成熟 repo 上做出來的,作者自己也強調不該外推成「所有人用 AI 都會變慢」。Veracode 的 45% 來自專門測安全的任務集,不是日常程式碼的抽樣比例。Spracklen 那篇的幻覺率是 2024 年中到 2025 年初的模型世代,今天的數字大概率更低,但「必須驗證」這個原則不會因為比率下降而消失——供應鏈攻擊只需要中一次。

成本是真的。 五道全跑,每個 PR 會多出幾分鐘和數次額外的模型呼叫。這不是免費午餐,所以下一節講什麼時候該省。

六、什麼時候跑全套,什麼時候別跑

全套跑(P1–P5): 碰到錢、權限與認證、PII、對外開放的 API、資料庫遷移、併發與交易、任何會寫入的路徑。

只跑 P1 + P3: 內部工具、資料分析腳本、CLI、不對外的批次作業。這兩道是投報率最高的組合——一個抓意圖漂移,一個抓邊界。

只跑 P1: prototype、demo、你三天內就會丟掉的東西。連 P1 都省掉的話,你連自己 merge 了什麼都不知道。

判準可以壓成一句話:這段 code 壞掉的時候,是你自己被吵醒,還是使用者被吵醒? 後者就跑全套。

另外,Anthropic 文件對 plan mode 的提醒也適用於這裡:「If you could describe the diff in one sentence, skip the plan.」流程有成本,對三行的改動套五道 prompt 是在演戲給自己看。

七、對工程團隊的意義

第一,code review 的分工要重畫。P2(依賴查證)和 P4(安全掃描)本質上是機械工作,人類做得比機器差又比機器慢,該全部自動化。人類的注意力應該集中在 P1(這是我要的東西嗎)和 P5(範圍有沒有失控),這兩件事需要的是業務脈絡,而那正好是模型最缺的東西。

第二,把 prompt 當成程式碼來管。P1–P5 應該進版控、進 .claude/skills/、可以被 review、可以被改進。GitHub 官方文件的第八步就是「document best practices, share successful prompts」——分享有效的 prompt。一個團隊裡如果每個人的驗證流程都不一樣,那就等於沒有流程。

第三,接受 reviewer 會吵,並且明確授權工程師忽略風格類 finding。前面引的那句警告值得貼在團隊 wiki 上:被要求找洞的 reviewer 一定會找到洞。把「只回報影響正確性或超出範圍的項目」寫進你所有 review prompt 的最後一行。

第四,也是最重要的:Stanford 那篇研究裡唯一的保護因子,是對工具保持懷疑並主動改寫 prompt 的人。 這件事沒辦法用工具替代——但你可以用流程,把「保持懷疑」從一種個人美德,變成一個每次都會執行的預設動作。

來源

整理:DataAgent · Coding Agent 實戰教學

發表迴響

%d 位部落客按了讚: