這個場景值得一眼就認出來,所以我們從它開始。您剛寫完一個遊戲,重新啟動後分數就歸零,於是您問代理:
重啟後為什麼分數會消失?代理的回答平靜而有條理:
專案已經會儲存分數,只是寫入功能沒有在任何地方啟用。
只要改一個設定,分數在重啟後就會保留下來。您去檢查。專案裡既沒有這個設定,也沒有儲存分數的功能,從來就沒有過 - 一行都沒有,連點影子都沒有。這個回答讀起來像是在分析您的程式碼,其實只是在推測這類專案通常是怎麼寫的。
而且在這之前,它既沒有遲疑,也沒有加上任何但書。語氣和前面那些後來證明正確的回答一模一樣。工具本身並沒有故障,模型也沒有比昨天差。
原因在於回答本身是怎麼產生的。模型並不是從某個儲存庫裡取出現成的事實 - 它是一段接一段地挑選您這段文字最可能的後續。而最可能的和正確的是兩回事,尤其是談到您那個模型從沒看過的專案時。這種捏造出來卻很像真的細節,一般稱為幻覺。

接下來會談到陷阱藏在哪裡、為什麼錯誤總是用平靜的語氣說出來,以及哪三項檢查能涵蓋大部分這類情況。
模型在您的請求中看到了什麼
這裡的陷阱是所有陷阱中最簡單的一個:您會以為模型在看您的專案。實際上它只看得到傳給它的內容,而傳給它的是這些:
- 內建知識。訓練時固定在模型參數裡的規律。不是附有事實和出處的卡片,而是一堆數字,所以就算是熟悉的事實,模型也可能重現得不精確。而且這些知識有時間界線:對於較晚才發布的函式庫,模型知道的就只有別人給它看過的那些。
- 目前的對話。您的請求、對話紀錄、代理讀過的檔案、它執行過的指令的輸出。
- 應用程式的記憶。存在另外的檔案裡、並在下一個工作階段開始時放進對話的內容。在 Claude Code 中,這指的是您自己撰寫的
CLAUDE.md,以及代理自行寫下筆記的 auto memory。 - 外部來源。文件、搜尋、資料庫、專案檔案 - 所有訓練資料裡可能沒有、或之後已經變動過的東西。
後三者都走同一條路:要影響回答,它們必須出現在目前的對話中。模型本身並不會去看您的專案。
由此得出一個值得在腦中調整一次的結論。沒有人打開過的檔案,對模型來說就不存在。在它的內容進入對話之前,"我們的授權是在哪裡處理的"這個問題,比較誠實的讀法是"這類專案通常把這種檔案叫什麼名字"。兩種情況下您都會得到回答,而且看起來一模一樣。
旁邊還有一個小一點、但很讓人氣惱的陷阱。讀過的檔案會以讀取當下的樣子留在對話裡。您在自己的編輯器裡改了一行,對話紀錄裡留著的卻還是舊版本。Claude Code 會註明檔案已經變更,但代理要不要重新讀取,就由模型決定了。在對話進行到一半時手動改了檔案,比較保險的做法是直接請它重新讀一次。
Claude Code 會先讀專案、執行指令,之後才回答。回答裡通常看得到它打開了哪些檔案、執行了什麼。如果關於您程式碼的回答在代理讀任何東西之前就出現了,您看到的是對典型專案的推測,而不是對您專案的分析。
為什麼措辭會改變結果
在所有進入對話的內容中,您的請求是唯一由您親手寫的。所以請求裡的小細節才會起決定作用:調換幾個字、多加一句說明,回答就往另一個方向走了。這不是工具在耍脾氣。
這裡很容易搞混,所以我單獨說清楚。同一個請求送兩次,也會得到不同的回答:輸出的不一定是最可能的那個後續,生成過程有一些設定,決定它多願意選機率低一點的選項。自然會冒出一個問題:既然回答本來就會飄,我的用字又有什麼關係?差別在於飄的是什麼。相同請求之間的差異,是繞著同一個選擇的雜訊:措辭會不一樣,方向不變。而請求裡的用字推動的是方向本身,而且推得很穩定。
含糊的請求效果最差。它把選擇權留給模型,而模型會選整體上最可能的那個 - 也就是它看過的所有內容平均出來的結果。拿簡單的"把文字縮短"來說。縮短多少 - 一個百分比、三分之一、一半?什麼可以刪,什麼不能動?請求裡沒有回答任何一個問題,所以會由模型來回答,而且形式上它並沒有錯。"把說明縮短 30%"或"把說明縮短到 300 個字元",就不會在該由您決定的地方留給它選擇。
拿兩個講同一件事的請求比一比就很清楚。"修好排序"把一切都留成未知:哪個列表、依哪個欄位、什麼才算壞掉。第二種寫法則幾乎什麼都沒留下:
訂單列表中的日期是從舊到新排列,需要反過來。
先指出排序順序是在哪個檔案、哪一行設定的,再提出修改建議。它比第一種長出來的部分,剛好就是第一種需要靠猜的部分。順帶還替您做了一半的檢查:您還沒問,代理就已經把程式碼中的位置找出來了。
結論很簡單:請求裡的自由越少,回答裡的猜測就越少。這不是什麼神奇咒語,而是很平常的道理 - 範圍要嘛由您來定,要嘛由別人替您選。
自信的語氣不等於事實
最主要的陷阱來了。模型沒有一種專屬於"這個我確定知道"的語氣,也沒有另一種專屬於"這是我剛剛補上的"的語氣:兩句話用同一種方式產生,聽起來一樣平靜。兩者之間也不是完全沒有關聯,只是派不上用場:模型的自信永遠偏高,所以您從語氣分辨不出哪些經過查證、哪些是補出來的。
在一般文字裡,奇怪的說法會讓人覺得刺眼,您自己就會放慢速度。到了程式碼裡,這層防護就失靈了:名稱看起來沒錯,結構很眼熟,函式庫真正的方法和照著類比拼出來的方法,除非您把那個函式庫背得滾瓜爛熟,否則用肉眼分不出來。所以失誤往往不是在閱讀時浮現,而是在執行時。
再說,名稱很少是完全憑空捏造的。有一份分析檢查了生成的程式碼中指向不存在函式庫的引用,其中超過三分之一的名稱是兩個真實名稱拼起來的。可信的感覺就是這樣來的:名稱的各個部分您都在哪裡見過,只是沒見過這種組合。
以下四種情況,只要知道要找什麼,第一次就認得出來:
| 看起來是什麼樣子 | 實際發生了什麼 | 怎麼抓出來 |
|---|---|---|
| 替指令寫了一個它根本沒有的旗標 | 模型照慣用的格式拼出這行指令,所以旗標看起來很真 | 指令的說明或它的文件 |
| 提到專案裡不存在的檔案、函式或設定欄位 | 這個名稱在這類專案中很常見,但沒有人讀過這個專案 | 在專案中搜尋、請它指出那一行 |
| 篤定地描述您的專案是怎麼架構的 | 對話中連一個專案檔案都沒有,所以描述的是典型的專案 | 問它回答之前打開了哪些檔案 |
| 說"完成了",但指令根本沒有執行 | 報告是根據任務描述拼出來的,不是根據執行結果 | 自己執行並查看輸出 |
四種情況的共同點只有一個:回答聽起來像是工作的成果,背後卻沒有任何工作。
最後一種情況值得單獨說明,因為它看起來像欺騙,其實並不是。代理會在工作看起來已經完成時停下來。如果沒有東西可以讓它執行,"看起來已經完成"就是它唯一能用的訊號,負責把關的就變成了您。
這件事的代價可以量化。在一份代理日誌的分析中,假的"完成"佔了將近一半的失敗。而在獨立於代理自身報告去檢查狀態的情境裡,這類情況幾乎消失。下一節第二項檢查的意義就在這裡:它把報告換成您親眼看得到的東西。
剩下一個問題:模型為什麼寧可把答案補完,也不承認不知道。這跟它的訓練和評分方式有關。想像一場選擇題考試,交白卷保證零分,隨便猜一個還有機會得分。在這種制度下,保持沉默永遠不划算,只要有一點不確定就作答才是合理的。人類之後會在真實的任務中重新學習,因為在那裡答錯的代價比老實說"我不知道"更高。模型沒有經過這種重新學習。
請模型在不確定時誠實地提醒您,確實有幫助 - 但這也是由同一套機制產生的文字。當作額外的訊號可以,當作檢查的替代品不行。
工具能縮小盲區
這裡最好先把兩個平常聊天時常被混為一談的詞分開。模型只會做一件事:接收對話,產生後續。代理則是包在模型外面的程式,它擁有工具:讀取檔案、執行指令、修改專案。
接下來這個細節,決定了錯誤究竟會出在哪裡。選擇工具的不是程式。模型在自己的回答中要求打開某個檔案或執行某個指令,程式則檢查這個要求、執行它,再把結果送回對話中。做決定的是模型,執行動作的是程式。也就是說,決定本身也可能出錯:開錯檔案、下錯指令、跑錯測試。
即使如此,差別還是很大。檔案還沒打開時,關於它的回答是憑著對類似專案的記憶拼出來的。檔案一讀、測試一跑,對話裡就出現了事實:這是內容,這是輸出。這已經不是回憶,而是查證,之後回答的品質明顯不同。
由此得出一個技巧,只要在對話開頭寫一行:
先找出並指給我看它在專案中的位置,然後再解釋。盲區會因此縮小,但不會消失。代理可能打開隔壁的檔案而不是該看的那個、用自己的方式解讀指令的輸出,或是停在第一個聽起來合理的解釋上,沒有找到真正的原因。這些我都見過,而且不只一次。所以外部訊號 - 測試的輸出、文件的內容、檔案的內容 - 比最有把握卻沒有依據的推論更有份量。
三項只花一分鐘的檢查
好消息是,要抓出這類失誤幾乎不費力氣。一切都歸結到一個分岔點。
這裡要看的不是寫得多有說服力,而是回答裡有沒有可以抓住的東西:具體的檔案、具體的那一行、附上輸出的指令。沒有依據,就表示您眼前的還只是假設,不是結果。很多回答一開始都是這樣,問題只在於您接下來怎麼做。
接下來是三項檢查。每一項都花不到一分鐘,而且很少需要三項同時做。
- 請它指出位置。不是"再解釋詳細一點",而是"給我看檔案、行號和那段程式碼本身"。如果根本沒有這個位置,馬上就會知道。
- 親自打開或執行結果。在瀏覽器中打開頁面、在終端機中執行指令、在專案中跑測試。關於工作的報告和工作本身不是同一回事。
- 拿工具本身來核對關於工具的說法。指令有內建的說明,函式庫有文件。提到的旗標或設定鍵要嘛存在,要嘛不存在,半分鐘就能確認。函式庫的方法要更謹慎:文件不一定收錄了所有內容,所以查不到只是追問的理由,不是定論。
與其和回答爭論,不如直接要它拿出依據:
給我看描述這件事的檔案和那一行。
如果專案裡沒有這個位置,就直接說這是推測。這兩行值得放在手邊。第一行讓代理去專案裡找,而不是憑著對類似專案的記憶回答。第二行給它一條退路:說"沒有這個位置",比把回答補成一個聽起來合理的樣子更划算。
面對"完成了"的報告,也是同樣的要求,只是換成執行:"給我看你執行的指令和完整的輸出"。
這不代表有了保證:"這是推測"這句話也是由同一套機制產生的。它能降低失誤的機率,但取代不了檢查。
還要馬上補充一點,免得留下錯誤的印象。檢查不等於不信任工具。您自己的 diff,在 commit 之前也會再讀一遍,即使那是您親手寫的。這裡也是一樣,只是理由稍微充分一點。
這對使用 Claude Code 代表什麼
這一切落到鍵盤前,就是這幾點:
- 附上檔案、行號或指令輸出的回答,幾秒鐘就能查證。沒有這些的回答則無從查起:只能再讀一遍,然後接受;
- 在提問之前,先讓代理讀過需要的內容。多這一步,就能排除一整類失誤;
- 不能憑自信程度判斷:不管話語背後是查證還是猜測,它都一樣偏高;
- "聽起來對"和"執行起來對"是兩回事,只有實際執行才分得出來;
- 錯誤的代價越高,外部的東西就應該越早出現在對話中:測試、文件、真實的檔案。小修改的話,看一眼 diff 就夠了;
- 代理出錯時,用事實告訴它,而不是用評價。"專案裡沒有這個函式,資料夾裡有的是這些"能讓工作往前推進,"你又在亂編了"則什麼也推不動。還要記住:面對篤定的反駁,代理比較可能順著您改口,而不是和您爭論,即使原本對的是它。代理同意您,並不代表您是對的。
這套機制偶爾會篤定地出錯,但依然非常有用。了解它的界限,正是為了不把時間花在驚訝上,而是直接開始檢查。
GO TO FULL VERSION