Django創造者Simon Willison分享:我如何使用LLM幫我寫程式碼 机器之心 發表於2025-03-19
近段時間,著名 AI 科學家 Andrej Karpathy 提出的氛圍程式設計(vibe coding) 是 AI 領域的一大熱門話題。簡單來說,氛圍程式設計就是鼓勵開發者忘掉程式碼,進入開發的氛圍之中。更簡單地講,就是向 LLM 提出需求,然後「全部接受」即可。 當然,這種使用 LLM 程式設計的方法過於簡單粗暴,或許並不適合用來開發一些更加複雜和精細的專案。近日,探索和發表資料的開源工具 Datasette 的創造者、Web 應用框架 Django 創造者之一、社交會議目錄 Lanyrd 的聯合創始人、著名程式設計技術博主 Simon Willison 分享了自己使用 LLM 輔助程式設計的體驗。文中,他分享了不少自己積累的有用實踐和策略。機器之心編譯了這篇部落格文章,希望能給開發者讀者們帶來一些幫助。 原文地址:https://simonwillison.net/2025/Mar/11/using-llms-for-code/ 網路上,很多開發者在討論使用大型語言模型輔助程式設計,而其中不少開發者都表示挺失望的。他們經常會問自己哪裡做錯了 —— 為什麼有些人報告的結果如此出色,而他們自己的實驗卻乏善可陳? 使用 LLM 編寫程式碼既困難又不直觀。需要付出巨大的努力才能弄清楚使用它們的利弊,而且幾乎沒有什麼指導可以幫助人們學會如何最好地使用它們。 如果有人告訴你使用 LLM 編碼很容易,那麼他們(可能無意中)誤導了你。他們很可能偶然發現了有效的模式,但這些模式並不是每個人都能自然而然掌握的。 兩年多來,LLM 程式碼在輔助我寫程式碼方面表現很好。下面我將盡力將一些經驗和直覺傳授給你。 不要去管圍繞「AGI」的炒作 ——LLM 仍舊只是花哨的自動補全。它們所做的只是預測 token 序列 —— 但事實證明,寫程式碼的主要工作就是以正確的順序將 token 串聯在一起,因此只要你給它們指示正確的方向,它們就會非常有用。 如果你認為這項技術將完美地實現你的專案,而你不需要鍛鍊任何技能,那你就等著失望吧。 相反,你應該使用 LLM 來增強你的能力。我現在最喜歡的心理模式是將它們想象成一個過度自信的結對程式設計助理 —— 它們查詢東西的速度快如閃電,可以隨時提供相關示例,並且可以毫無怨言地執行繁瑣的任務。 過度自信很重要。它們絕對會犯錯誤 —— 有時很細微,有時是大錯。這些錯誤可能與人類錯誤非常不一樣 —— 如果一個人類合作者想象出了一個根本不存在的庫,你會馬上就不再相信他了。不要把 LLM 當人類一樣看,這是一個陷阱。AI 犯的錯誤並不能否定它的有用性。 當與 LLM 一起工作時,你經常會發現它們無法做到的一些事情。記下這些事情 —— 它們是有用的教訓!它們也是為未來儲備的寶貴例子 —— 一個強大的新模型的標誌是:它能解決以前的模型無法處理的任務。 任何模型都有一個關鍵特徵:訓練截止日期。也就是停止收集訓練資料的日期。對於 OpenAI 的模型,這通常是 2023 年 10 月。Anthropic 和 Gemini 以及其它提供商可能有更近期的日期。 這對於程式設計來說非常重要,因為這個時間會影響它們知道的庫。如果你使用的庫自 2023 年 10 月以來發生了重大變化,OpenAI 模型將不會知道! 我從 LLM 中獲得了足夠的價值,現在我在選擇庫時會特意考慮這一點 —— 我會盡力堅持使用具有良好穩定性且足夠流行的庫,這樣一來,這些庫的許多示例都會在其訓練資料中。我喜歡應用無聊技術(boring technology)的原則 —— 在專案的獨特賣點上進行創新,在其它所有方面堅持使用經過嘗試和測試的解決方案。 LLM 仍然可以幫助你處理訓練資料之外的庫,但你需要付出更多努力 —— 你需要在提示詞中向它們提供最新示例,說明這些庫應如何使用。 要想讓 LLM 給出優良的結果,很大一部分工作在於都可歸結為管理上下文,即當前對話的一部分文字。 此上下文不僅僅是提供給 LLM 的提示詞:成功的 LLM 互動通常採用對話的形式,而其上下文包括當前對話執行緒中存在的來自你的每條訊息和來自 LLM 的每條回覆。 當你開始新的對話時,你會將該上下文重置為零。瞭解這一點很重要,因為對於不再有用的對話,通常的解決方法是將一切清除乾淨並重新開始。 一些 LLM 程式設計工具不僅僅適用於對話。例如,Claude Projects 允許你預先填充大量文字,包括最近的直接從 GitHub 庫匯入程式碼的功能 —— 我經常使用這個功能。 Cursor 和 VS Code Copilot 等工具會自動包含當前編輯器會話和檔案佈局中的上下文,有時你可以使用 Cursor 的 @commands 等機制來提取其它檔案或文件。 我主要直接使用 ChatGPT 和 Claude 網頁版或應用介面的原因之一是:它能讓我更容易準確地瞭解上下文中的內容。那些向我隱瞞上下文的 LLM 工具的效果更差。 另一個事實也可為你所用:之前的回覆也是上下文的一部分。對於複雜的程式設計任務,可以先嚐試讓 LLM 編寫一個更簡單的版本,檢查它是否有效,然後再迭代構建更復雜的實現。 我在開始新聊天時,經常會把現有程式碼放入到上下文中,然後再與 LLM 一起合作以某種方式來修改它。 我最喜歡的程式碼提示詞技術之一是放入幾個與我想要構建的東西相關的完整示例,然後提示 LLM 將它們用作新專案的靈感。當我在構建我的 JavaScript OCR 應用時,我詳細地寫了:該應用應結合 Tesseract.js 和 PDF.js—— 這兩個庫我過去曾使用過,我可以在提示詞中提供有效示例。 我的大多數專案都是從一些開放式問題開始的:我想做的事情是否可行?我可以用哪些潛在方式來實現它?哪些選項是最好的? 我會使用這樣的提示:「Rust 中有哪些 HTTP 庫可選?包括使用示例」或者「JavaScript 中有哪些有用的拖放庫?為我構建一個展示每個庫的工件」(對 Claude)。 訓練截止時間在這裡很重要,因為這意味著 LLM 不會建議使用較新的庫。通常這樣沒問題 —— 我不想要最新的,我想要最穩定的,以及存在時間足夠長的,可以解決錯誤的東西。 如果我要使用更新的東西,我會在 LLM 世界之外自己做研究。 開始任何專案的最佳方式是使用原型來證明該專案的關鍵要求可以得到滿足。我經常發現,LLM 可以在我坐下來使用膝上型電腦的幾分鐘內就讓我獲得那個可行的原型 —— 有時甚至在我使用手機工作時也可以。 一旦我完成了初步研究,我就會大幅改變模式。對於生產級程式碼,我對 LLM 的使用更加專制:我會把它當作數字實習生,工作內容是根據我的詳細指示來寫程式碼。 我可以自己寫這個函式,但我需要花上十五分鐘來查詢所有細節,才能使程式碼正常工作。Claude 在 15 秒內就搞定了。 我發現,對於我在這裡使用的函式簽名,LLM 的響應非常好。我的工作是作為函式設計者,LLM 負責根據我的規範構建主體。 我經常還會提出跟進要求,比如「現在用 pytest 給我寫測試」。再次強調,選擇哪種技術完全由我決定 —— 我希望 LLM 能幫我省去錄入已經存在於我腦子裡的程式碼的時間。 如果你對此的反應是「輸入程式碼肯定比輸入英文指令要快」,我只能告訴你,對我來說已經不是這樣了。程式碼必須正確。而英語中存在大量捷徑、奇思妙想和拼寫錯誤,比如如果你記不住名字,你可以說「使用那個流行的 HTTP 庫」。 優秀的程式設計 LLM 非常擅長填補空白。它們也遠沒有我那麼懶 —— 它們總是會記得捕捉可能的異常,新增準確的文件字串,並用相關的型別標註程式碼。 有一件事是你絕對不能外包給機器的,那就是測試程式碼是否真的有效。 作為軟體開發者,你的責任是提供能有效工作的系統。如果你還沒有看到它執行,那麼它就不是一個有效的系統。你需要親手試試看。 這件事做起來可能沒多少趣味,但它一直是交付良好程式碼的關鍵部分 —— 無論有沒有 LLM 參與其中。 如果我不喜歡 LLM 寫的東西,可以馬上讓它重構,而它絕對不會抱怨!「將重複的程式碼分解成一個函式」、「使用字串操作方法而不是正規表示式」,甚至「寫更好一點!」 LLM 第一次編寫的程式碼很少是最終的實現,但它們可以為你重新輸入幾十次,而不會感到沮喪或無聊。 偶爾我會從我的第一個提示中獲得很好的結果 —— 我練習得越多,結果就越頻繁 —— 但我一直做好了需要後續跟進的準備。 我一直很好奇,這是否是人們錯過的關鍵技巧之一 —— 糟糕的初始結果並不是失敗,而是一個起點,基於此才能將模型推向你真正想要的方向。 現在越來越多的 LLM 程式設計工具能夠為你執行程式碼。我對其中一些略微謹慎,因為錯誤的命令可能會造成真實的損害,所以我傾向於堅持使用在安全沙箱中執行程式碼的工具。我現在最喜歡的包括: ChatGPT Code Interpreter ,ChatGPT 可以直接在 OpenAI 管理的 Kubernetes 沙箱 VM 中編寫並執行 Python 程式碼。這是完全安全的 —— 它甚至無法建立出站網路連線,因此實際上可能發生的一切就是臨時檔案系統被破壞然後重置。Claude Artifacts ,Claude 可以為你構建一個完整的 HTML+JavaScript+CSS Web 應用,該應用顯示在 Claude 介面中。這個 Web 應用顯示在一個非常封閉的 iframe 沙箱中 —— 這雖然極大地限制了它可以做的事情,但可以防止意外洩露你的私人 Claude 資料等問題。ChatGPT Canvas 是一個較新的 ChatGPT 功能,具有與 Claude Artifacts 類似的功能。我自己還沒有對此進行足夠的探索。Cursor 有一個「Agent」功能可以做到這一點,Windsurf 和越來越多的其它編輯器也是如此。我還沒有花足夠的時間研究這些,所以我不能提供相關推薦。Aider 是這些模式的領先開源實現,是 dogfooding 的一個很好的例子 ——Aider 的最新版本有 80% 以上是由 Aider 自己編寫的。https://aider.chat/Claude Code 是 Anthropic 進入這個領域的新成員。我將在稍後詳細介紹如何使用該工具。這種在互動流程中執行程式碼的模式非常強大。我在選擇核心 LLM 程式設計工具時,主要就是看它們是否可以安全地執行和迭代我的程式碼。 Andrej Karpathy 一個多月前創造了氛圍程式設計(vibe coding)一詞: Andrej 認為這「對於一次性的週末專案來說還不錯」。這也是探索這些模型功能的絕佳方式 —— 而且真的很有趣。 學習 LLM 的最佳方式就是玩它們。向它們丟擲荒謬的想法,然後進行氛圍程式設計,直到它們基本可以正常工作,這是一種真正有用的方法,可以讓你更快地對什麼有效、什麼無效形成直覺認識 早在 Andrej 給它命名之前,我就已經開始進行氛圍程式設計了!我的 simonw/tools GitHub 儲存庫有 77 個 HTML+JavaScript 應用和 6 個 Python 應用,每個應用都是透過提示 LLM 構建的。我從構建這個集合中學到了很多東西,我以每週幾個新原型的速度向其中新增內容:https://github.com/simonw/tools 你可以在 tools.simonwillison.net 上直接試用我的大部分工具 —— 這是這個庫的 GitHub Pages 釋出版本。 在撰寫本文時,我想到了 tools.simonwillison.net/colophon 頁面 —— 我想要一個可以連結到的東西,以比 GitHub 更明顯的方式顯示我的每個工具的提交歷史記錄。 我決定利用這個機會展示我的 AI 輔助程式設計過程。 對於這個任務,我使用了 Claude Code,因為我希望它能夠直接根據我膝上型電腦上現有的工具庫執行 Python 程式碼。 在我的會話結束時執行 /cost 命令,會向我顯示以下內容: 其最初的專案從開始到結束只花了我 17 分鐘多一點的時間,並且在呼叫 Anthropic 的 API 上僅花費了 61 美分。 在這個過程中,我會確切地告訴模型我想要構建什麼。下面是我的提示詞序列,完整記錄在這裡:https://gist.github.com/simonw/323e1b00ee4f8453c7834a7560eeafc1 首先,我要求 LLM 編寫一個初始指令碼來收集新頁面所需的資料: 我真的沒有認真考慮過上面的首個提示詞 —— 它更像是我在思考初始問題時輸入到機器人中的意識流。 然後我改變了主意 —— 我也想要那些完整的提交訊息: 請注意,我從未檢視過在 gather_links.py 中編寫的程式碼!這是純粹的氛圍程式設計:我正在檢視它在做什麼,但我把實現細節完全留給了 LLM。 這個 JSON 看起來還不錯,所以我指示 LLM: Claude 知道 GitHub URL 的工作方式,因此告訴它提交的連結並提供庫名稱就足夠了,它猜測這些提交 URL 是 https://github.com/simonw/tools/commit/fd9daf885c924ba277806b3440457d52b0ad90a8 我經常發現 Claude 在網頁設計方面有很好的預設品味 —— 我只是說了「頁面應該適合移動裝置」,然後剩下的就交給它了。 Claude 不停地為我構建不正確的頁面,所以我發出指令: 然後它自己修復了所有錯誤,只剩下我決定要做的兩處更改: 整個專案就完成了!結果檔案為 build_colophon.py,它生成的頁面看起來相當不錯: 還有一項任務:我需要將新的 colophon 作為我網站的一部分進行部署,但我不想將新的 colophon.html 頁面簽入庫本身。我想要一個自定義的 GitHub Pages 構建過程。 我啟動了一個全新的 Claude Code 會話,看看 Claude 是否也能做好這件事: 與第一次不同,這次我非常仔細地觀察了它在做什麼 —— 我不知道如何以這種方式自定義 GitHub Pages 構建,我想學習如何做到這一點,並保持謹慎,因為它可能會產生幻覺並導致任務失敗。 我想這是正確的?我很喜歡這個註釋「Need full history for git log in gather_links.py」—— 這是我很容易忽視的。 然後它說它想將這些檔案新增到 .gitignore—— 聽起來是個好主意。 因此,使用 Claude API 花費了 17 美分和 45 秒。(我分心了,因此總共花費了 10 分鐘。)這裡是完整記錄:https://gist.github.com/simonw/a560b07eef577e6183021d1ccaae7e07 這些程式碼看起來不會不可逆地破壞任何東西,因此我將其 push 到了 GitHub,然後看看會發生什麼。 有一個問題。我在 GitHub Actions 執行時觀察了它,發現有些不對勁: 我原本期待的是「Test」job,但為什麼有兩個不同的部署? 我有一種預感,之前的預設 Jekyll 部署仍在執行,而新的部署同時執行 —— 新指令碼完成得晚並覆蓋了原始指令碼的結果,這純粹是時間上的運氣。 我在 GitHub Pages 的使用自定義工作流中找到了這個頁面,但它沒有告訴我我需要知道的內容。 出於另一個直覺,我檢查了我的庫的 GitHub Pages 設定介面,發現了這個選項: 我的倉庫設定為「從分支部署」,所以我將其切換到「GitHub Actions」。 我手動更新了我的 README.md,以在此提交中新增指向新 Colophon 頁面的連結,而這又觸發了另一次 build。 這次只執行了兩個 job,最終結果是正確部署的站點: (我後來發現了另一個 bug—— 一些連結無意中在其 href= 中包含了 <br> 標籤,我用另一個 11 美分的 Claude Code 會話修復了這個問題。) 之後,我還透過新增 AI 生成描述工具進一步改進了這個 colophon:https://simonwillison.net/2025/Mar/13/tools-colophon/ 在這個例子中,我很幸運,因為它有助於說明我的最後一點:要做好接管的準備。 LLM 無法取代人類的直覺和經驗。我在 GitHub Actions 上花了足夠多的時間,我知道要尋找什麼樣的東西。在這種情況下,比起繼續嘗試透過提示詞完成這個任務,我介入並完成專案的速度更快。 我的新 colophon 頁面從構思到完成、部署功能只花了我不到半個小時的時間。 我確信如果沒有 LLM 的幫助,我會花更長的時間 —— 以至於我可能根本不會費心去構建它。 也因此,我非常關心我從 LLM 中獲得的生產力提升:它不是為了更快地完成工作,而是為了能夠交付我根本不想花時間的專案。 我在 2023 年 3 月寫過一篇文章談到,AI 增強的開發讓我對我的專案更加雄心勃勃。兩年後,這種影響沒有消退的跡象。 這也是加速學習新事物的好方法 —— 今天我講的是如何使用 Actions 自定義我的 GitHub Pages 構建,這是我將來肯定會再次使用的東西。 事實上,LLM 能讓我更快地執行我的想法,這意味著我可以實現更多的想法,這意味著我可以學到更多。 其他人也能以同樣的方式做到這一點嗎?可能不會!我在這裡的提示詞依賴於 25 年以上的專業程式設計經驗,包括我之前對 GitHub Actions、GitHub Pages、GitHub 本身以及我使用的 LLM 工具的探索。 我也知道這會起作用。我花了足夠多的時間使用這些工具,我相信使用從我的 Git 歷史記錄中提取的資訊組裝一個新的 HTML 頁面完全在優秀 LLM 的能力範圍內。 我的提示詞反映了這一點 —— 這裡沒有什麼特別新穎的東西,所以我口述了設計,在它工作時測試了結果,偶爾推動它修復錯誤。 如果我試圖構建一個 Linux 核心驅動程式 —— 一個我幾乎一無所知的領域 —— 我的過程將完全不同。 如果你覺得使用 LLM 為你寫程式碼仍舊沒有吸引力,那麼還有另一個用例可能會讓你覺得更有吸引力。 這也是非常低風險的:最糟糕的情況是它們可能會出錯。因此,你可能需要花一點時間才能弄清楚。與完全自己挖掘數千行程式碼相比,這仍然可能會節省你的時間。 這裡的技巧是將程式碼提交給長上下文模型中並開始提問。我目前最喜歡的模型是 gemini-2.0-pro-exp-02-05,這是 Google Gemini 2.0 Pro 的預覽版,目前可透過其 API 免費使用。 我前幾天就用過這個技巧。當時我正在嘗試一種對我來說很新的工具 monolith,這是一個用 Rust 編寫的 CLI 工具,它可以下載網頁及其所有依賴資產(CSS、影像等)並將它們捆綁到一個存檔檔案中。 我很好奇它是如何工作的,所以我將它克隆到了我的臨時目錄中並執行了以下命令: 我在這裡使用我自己的 files-to-prompt 工具(去年由 Claude 3 Opus 為我構建)將庫中所有檔案的內容收集到一個流中。然後我將其匯入我的 LLM 工具並告訴它(透過 llm-gemini 外掛)使用系統提示詞「architectural perspective as markdown」來呼叫 Gemini 2.0 Pro。 這給了我一份詳細的文件,描述了該工具的工作原理 —— 哪些原始檔做什麼,以及最重要的是,它使用了哪些 Rust 包。我瞭解到它使用了 reqwest、html5ever、markup5ever_rcdom 和 cssparser,並且根本沒有評估 JavaScript,這是一個重要的限制。 我每週都會使用這個技巧幾次。這是開始深入研究新程式碼庫的好方法。