繁體
  • 简体中文
  • 繁體中文

熱門資訊> 正文

告別無效提示!開發者必學的AI協作提示框架全解析

2025-06-28 08:00

神譯局是36氪旗下編譯團隊,關注科技、商業、職場、生活等領域,重點介紹國外的新技術、新觀點、新風向。

編者按:開發者們越來越依賴AI編程助手來加速日常工作流程,但AI編程助手輸出質量取決於提示詞水平。本文系統拆解調試、重構、開發三大場景的提示工程框架,並附上可複用的模板及對比案例,助開發者釋放AI協作潛能。文章來自編譯。

開發者們越來越依賴AI編程助手來加速日常工作流程。這些工具可以自動補全函數、建議修復bug,甚至生成整個模塊或最小可行產品(MVP)。不過,正如我們許多人已經瞭解到的那樣,AI輸出的質量在很大程度上取決於你提供的提示(prompt)的質量。換句話説,提示工程已經成為一項必備技能。一個措辭不當的請求可能會得到無關或泛泛的答案,而一個精心設計的提示則可能生成深思熟慮、準確甚至富有創意的代碼解決方案。本文旨在從實用角度探討如何系統地為常見開發任務設計有效的提示。

AI結對編程工具很強大,但並非魔法——它們缺乏有關你的項目或意圖的先驗知識,只知道你告訴它們或作為上下文所包含的內容。你提供的信息越豐富,輸出結果就越好。我們將提煉出關鍵的提示模式、可複用的框架以及讓開發者產生共鳴的難忘示例。你將看到好提示與壞提示及其實際AI響應的對比,並附有評論説明為什麼一個成功了而另一個卻失敗了。以下是一份入門速查指南:

高效編碼提示基礎

向AI編碼工具發出提示,有點像是跟一位非常刻板(有時候還知識淵博)的合作者溝通。要想獲得有用的結果,你需要清晰設定場景,並引導AI瞭解你想要什麼以及你想要的方式。

以下是支撐本指南所有示例的基本原則:

  • 提供豐富的上下文。始終假設AI除了你提供的信息外,對你的項目一無所知。要提供相關細節,如編程語言、框架和庫,以及有問題的具體函數或代碼片段。如果有錯誤,提供確切的錯誤信息並描述代碼本應實現的功能。具體性和上下文決定了你得到的是含糊的建議還是精確、可操作的解決方案。在實踐中,這意味着你的提示可能要有一個大概的設定,比方説:「我有一個使用Express和Mongoose的Node.js函數,它應該通過ID獲取用户,但它拋出了一個TypeError。這是代碼和錯誤信息……」。你提供的設置信息越多,AI需要猜測的部分就越少。

  • 明確你的目標或問題。模糊的查詢會導致模糊的答案。與其問「為什麼我的代碼工作不了?」,不如明確指出你需要什麼樣的洞察。比方説:「這個JavaScript函數返回的是undefined,而不是預期的結果。根據下面的代碼,你能幫我找出原因以及如何修復嗎?」 這樣更有可能得到有用的答案。一個用於調試的提示公式是:「預期行為是[預期行為],但當輸入[示例輸入]時,實際行為卻是[當前行為]。問題出在哪里?」。同樣地,如果你想做優化,請具體説明需要哪種優化(比方説:「如何改進這個排序函數在處理1萬個項目時的運行時性能?」)。明確性可以引導AI該關注哪里。

  • 分解複雜任務。在實現新功能或處理多步驟問題時,不要即將整個問題塞進一個宂長的提示里面。更有效的做法通常是將工作拆分成更小的部分並進行迭代。比方説,「首先,為產品列表頁面生成一個React組件骨架。接下來,我們將添加狀態管理。然后,我們將集成API調用。」 每個提示都建立在前一個的基礎上。通常不建議一次性要求實現整個大功能;相反,從一個高級目標開始,然后迭代地請求實現每一部分。這種方法不僅讓AI的響應更專注、更易於管理,也是人類逐步構建解決方案的體現。

  • 把輸入/輸出或預期行為的示例放進去。如果你能用例子説明你想要什麼,那就這麼做。比方説,「給定數組 [3,1,4],這個函數應該返回 [1,3,4]。」 在提示中提供一個具體的例子有助於AI理解你的意圖並減少歧義。這就像給初級開發人員一個快速的測試用例——可明確要求。在提示工程術語中,這有時候被稱為「少樣本提示」(few-shot prompting),即你向AI展示一個要遵循的模式。即便是一個正確行為的例子也能為模型的響應起到很好的引導作用。

  • 利用角色或人設。在許多廣泛傳播的提示示例里,流行着一個強大技巧,就是要求AI「扮演」(act as)某個特定的人設或角色。這可以影響答案的風格和深度。比方説,「扮演一位資深React開發者,審查我的代碼是否存在潛在錯誤」或「你是一位JavaScript性能專家。優化以下函數。」 通過設定一個角色,可引導助手採取相關的語氣——比如嚴格的代碼審查員、樂於幫助初級開發者的老師,還是尋找漏洞的安全分析師等。社區共享的提示已經證明了這種方法的成功,比方説「扮演一個JavaScript錯誤處理程序,幫我調試這個函數。API調用並沒有正確渲染數據。」。我們自己在使用時,仍然需要提供代碼和問題細節,但這種角色扮演提示可以得到更結構化的輸出和專家級的指導。

  • 迭代優化對話。提示工程是一個互動過程,不是一錘子買賣。開發者通常需要審查AI的首次回答,然后提出后續問題或進行修正。如果解決方案不太合適,你可以説:「那個解決方案用了遞歸,但我更喜歡迭代方法——你能不用遞歸再試一次嗎?」 或者,「很好,現在你能優化一下變量名並添加註釋嗎?」 AI會記住聊天會話中的上下文,因此你可以逐步將其引導至期望的結果。關鍵是把AI看作一個你可以指導的夥伴——追求進步,而非第一次就要求完美。

  • 保持代碼清晰度和一致性。最后這條原則不是那麼直截了當,但對於處理你代碼上下文的工具來説非常重要。即便在使用AI之前,也要編寫乾淨、結構良好的代碼和註釋。有意義的函數和變量名、一致的格式以及文檔字符串(docstrings)不僅讓你的代碼更容易被人理解,也能為AI瞭解你在做什麼提供更強烈的線索。如果你展示出一致的模式或風格,AI會延續的。將這些工具視為極其專注的初級開發者——它們會從你的代碼和註釋中獲取所有提示。

牢記這些基本原則后,讓我們深入探討具體場景。我們將從調試開始,這可能是最直接的用例:你的代碼行為異常,你想讓AI幫忙找出原因。

調試代碼的提示模式

調試天然適合AI助手。這就像擁有一個橡皮鴨,它不僅會聽你説話,還會提出建議。不過,成功與否很大程度上取決於你如何向AI呈現問題。以下是系統性地請求幫助查找和修復bug的方法:

  1. 清晰描述問題和症狀。首先在提示中描述哪里出了問題,以及代碼本應實現什麼功能。務必包含確切的錯誤信息或錯誤行為。比方説,與其只是説「我的代碼不工作」,你可以這樣寫提示:「我有一個JavaScript函數,它應該計算一個數字數組的總和,但它返回的是NaN(非數字)而不是實際的總和。代碼如下:[包含代碼]。對於像[1,2,3]這樣的數字數組,它應該輸出一個數字(總和),但我得到的是NaN。這個bug可能是什麼原因造成的?」 這個提示明確了編程語言、預期行為、觀察到的錯誤輸出,並提供了代碼上下文——所有這些都是關鍵信息。提供結構化的上下文(代碼 + 錯誤 + 預期結果 + 你嘗試過的方法)為AI奠定了堅實的基礎。相比之下,像「為什麼我的函數不工作?」這種泛泛的問題收效甚微——模型在沒有上下文的情況下只能提供最籠統的猜測。

  2. 對棘手的bug採用分步或逐行方法。對於更復雜的邏輯錯誤(沒有拋出明顯的錯誤消息但輸出錯誤),你可以提示AI逐步執行代碼。比方説:「逐行分析這個函數,並在每一步跟蹤`total`的值。這個值累加不正確——邏輯是在哪里出錯的?」 這是一個橡皮鴨調試提示的示例——本質上是在要求AI模擬人類用打印語句或調試器進行的調試過程。這類提示常常能揭示一些微妙的問題,比如變量未重置或條件邏輯錯誤,因為AI會詳細説明每一步的狀態。如果你懷疑代碼的某個特定部分,可以放大關注點:「解釋一下這里的`filter`調用在做什麼,它排除掉的項是否比預期的要多?」 讓AI扮演解釋者的角色,可以在解釋過程中暴露bug。

  3. 儘可能提供最小可復現示例。有時候你的實際代碼庫很大,但bug可以在一個小片段中復現。如果能提取或簡化仍然能復現問題的代碼,就把它提供給AI。這不僅讓AI更容易集中注意力,也迫使你澄清問題(這本身通常就是一次有用的練習)。比方説,如果你在一個深度嵌套的函數調用中得到了一個TypeError,嘗試用幾行可分享的代碼來複現它。目標是用最少的代碼隔離掉bug,對問題所在做出假設,進行測試並迭代。你可以讓AI參與這個過程:「這是一個簡化后的示例,它仍然會觸發錯誤[包含代碼片段]。為什麼會出現這個錯誤?」 通過簡化,可消除干擾,幫助AI精確定位問題。(這種技術反映了許多資深工程師的建議:如果不能立即找到bug,就簡化問題空間。如果你向AI呈現一個更小的案例,它可以協助進行分析。)

  4. 提出有針對性的問題和后續跟進。在提供上下文后,直接詢問你需要的內容通常很有效,比方説:「可能導致這個問題的原因是什麼,以及我該如何修復它?」。這會促使AI既進行診斷又提出解決方案。如果AI的首次回答不清楚或只部分有用,不要猶豫,繼續追問。你可以説:「這個解釋有道理。你能告訴我如何修復代碼嗎?請提供修正后的代碼。」 在聊天設置中,AI是掌握對話歷史的,因此它可以直接輸出修改后的代碼。如果你使用的是像VS Code中的Copilot或Cursor這樣的內聯工具(沒有聊天功能),你可能需要在代碼上方寫一條註釋,如`// BUG: returns NaN, fix this function`,然后看看它如何自動補全——但通常來説,互動式聊天能產生更詳盡的解釋。另一種跟進模式:如果AI給出了修復方案但你不理解原因,就問:「你能解釋一下為什麼這個改動能解決問題嗎?」 這樣你就能學到東西以備下次使用,同時也複覈了AI的推理是否合理。

現在,讓我們用一個具體示例來説明這些調試提示原則,展示一個糟糕提示與改進提示的對比以及AI響應的差異:

調試示例:糟糕提示 vs 改進提示

想象我們有一個簡單的Node.js函數,目的是將用户對象列表轉換為按用户ID索引的查找映射(lookup map)。但是,它拋出了一個錯誤。以下是包含bug的代碼:

// Buggy function: converts array of users to a map by ID

function mapUsersById(users) {

  const userMap = {};

  for (let i = 0; i <= users.length; i++) { 

    // Note: using <= will go out of bounds on the last iteration

    const user = users[i];

    userMap[user.id] = user;

  }

  return userMap;

}

 

// Example usage:

const result = mapUsersById([{ id: 1, name: "Alice" }]);

這段代碼應該遍歷用户數組並構建一個對象,其鍵是用户ID。不過,存在一個bug:循環使用了`i <= users.length`而不是`i < users.length`。這會導致在最后一次迭代時`users[i]`是undefined,很可能引發運行時錯誤(比方説,嘗試訪問undefined的屬性`id`)。讓我們看看AI助手可能如何迴應關於此問題的兩個不同提示。

❌ 糟糕提示:「為什麼我的mapUsersById函數不工作?」

AI對糟糕提示的響應(模擬):

> *沒有更多信息很難説。你確定函數是用一個有效的用户數組調用的嗎?可能是`users`為空或不是數組,導致循環無法正常執行。也可能是在`userMap`上設置屬性時出錯。確保每個用户都有`id`屬性。如果`users`是undefined或null,也會導致它不工作。*

分析:這個答案相當含糊。提示只提供了函數名,沒有任何細節,所以AI在猜測一些常見原因(「可能數組為空或不是數組」、「確保每個用户都有id」)。它沒有識別出實際的bug。當問題描述不足時,你就會得到這種迴應——模型只能複述常見的調試技巧。作為開發者,這並沒什麼幫助,因為它沒有解決真正的錯誤。提示中缺乏上下文和具體性導致得出泛泛的答案(本質上相當於AI在説「你試過關機再開機嗎?」)。這印證了社區的建議:模糊的提示會導致無關或籠統的答案。

✅ 改進提示:「我有一個JavaScript函數`mapUsersById`,它應該將用户對象數組轉換為按用户ID索引的映射(對象)。但是,當我運行它時會拋出錯誤。比方説,當我傳入`[ {id: 1, name: "Alice"} ]`時,我得到`TypeError: Cannot read property 'id' of undefined`。函數代碼如下:

function mapUsersById(users) {

  const userMap = {};

  for (let i = 0; i <= users.length; i++) { 

    const user = users[i];

    userMap[user.id] = user;

  }

  return userMap;

}

 它應該返回`{ "1": {id: 1, name: "Alice"} }`。bug是什麼?如何修復?」

AI對改進提示的響應(模擬):

> *錯誤`Cannot read property 'id' of undefined`表明在某個時刻`user`是undefined,然后你嘗試訪問`user.id`。查看你的循環,問題出在循環邊界上。你使用了`i <= users.length`。如果`users.length`是1(數組里有一個用户),循環會在i=0和i=1時運行。當i為1時,`users[1]`是undefined(因為數組中唯一的索引是0)。因此`user`在這一次迭代中變成了undefined,`user.id`就會拋出TypeError。修復方法是將循環條件改為`i < users.length`,這樣i的範圍是從0到`users.length-1`。比方説:*

for (let i = 0; i < users.length; i++) {

  const user = users[i];

  userMap[user.id] = user;

}

> *這樣,對於第一個(也是唯一一個)元素,i將是0,然后循環停止。經過此修改,你的函數將不會再遇到undefined的user,並且應該按預期工作,對於給定的輸入返回`{"1": {id: 1, name: "Alice"}}`。*

分析:這個響應直接指出了bug(在循環中使用了`<=`),並解釋了它為什麼導致`user`變成undefined。然后它提供了正確的代碼解決方案(`i < users.length`)並確認了預期結果。改進提示之所以成功,是因為它遵循了我們的原則:明確了語言(JavaScript),描述了函數目的,包含了確切的錯誤消息和示例輸入,甚至還提供了相關的代碼片段。注意AI是如何利用錯誤消息作為線索,將注意力集中在循環邊界上的——一個有明確目標的提示使AI能夠進行真正的問題解決,有效地模擬了人類調試者的思考方式:「undefined可能從哪里來?很可能來自循環索引」。這具體展示了詳細提示的好處。

額外的調試策略:除了識別明顯的bug,你還可以利用提示工程輔助進行更深層次的調試:

  • 詢問潛在原因。如果你真的卡住了,可以稍微拓展一下問題:「這段代碼中出現`TypeError: cannot read property 'foo' of undefined`的一些可能原因是什麼?」,同時提供代碼。模型可能會列出幾種情況(比方説對象未初始化、競態條件、錯誤的變量作用域等)。這可以為你提供一些未曾考慮的調查角度。就像與同事進行頭腦風暴。

  • 「問問橡皮鴨」——即向AI解釋你的代碼。這可能聽起來違反直覺(為什麼要向助手解釋?),但寫下解釋的行為可以澄清你自己的理解,然后你可以讓AI驗證或批評它。比方説:「我來解釋一下這個函數在做什麼:[你的解釋]。基於此,我的推理正確嗎?它是否揭示了bug在哪里?」 AI可能會發現你解釋中的一個缺陷,而這個缺陷指向了實際的bug。這種技術將AI利用為一個主動的橡皮鴨,它不僅願意聽還會迴應。

  • 讓AI創建測試用例。你可以問:「你能提供幾個可能破壞這個函數的測試用例(輸入)嗎?」 助手可能會提出你沒想到的邊界情況(空數組、極大的數字、null值等)。這對於調試和生成未來增強健壯性的測試都很有用。

  • 扮演代碼審覈員。作為直接「調試這個」提示的替代方案,你可以説:「扮演一位代碼審覈員。這是一個沒有按預期工作的代碼片段。請審查它並指出任何可能導致問題的錯誤或不良實踐:[代碼]」。這會讓AI進入批判模式。許多開發者發現,將請求表述為代碼審查會生成非常詳盡的分析,因為模型會評論代碼的每個部分(通常在這個過程中,它就能發現bug)。事實上,一個提示工程技巧就是明確要求AI表現得像一個一絲不苟的審查員。這不僅可以揭示手頭的bug,還可能發現其他問題(比方説潛在的空值檢查缺失),這可能會很有用。

總之,在使用AI助手進行調試時,細節和方向性是關鍵。提供場景、症狀,然后提出有針對性的問題。正如我們上面所看到那樣,慌亂無措地提示「它不工作了,救命!」和一個精準的手術式調試提示之間有着天壤之別。接下來,我們將轉向另一個主要用例:重構和改進現有代碼。

重構與優化的提示模式

重構代碼——在不改變其功能的前提下使其更清晰、更快或更符合語言習慣——是AI助手可以大放異彩的領域。它們基於海量代碼進行訓練,里面就有很多結構良好、優化解決方案的示例。不過,要有效利用這些知識,你的提示必須明確説明在你的情況下「更好」意味着什麼。以下是如何為重構任務編寫提示:

1、明確説明你的重構目標。 光説「重構這段代碼」就太過開放了。你是想提高可讀性?降低複雜度?優化性能?使用不同的範式或庫?AI需要一個目標。一個好的提示會框定任務,比方説:「重構以下函數以提高其可讀性和可維護性(減少重複,使用更清晰的變量名)。」或者「優化這個算法來提高速度——它在處理大型輸入時太慢了。」通過説明具體目標,你幫助模型決定應用哪些轉換。比方説,告訴它你關心性能可能會讓它使用更高效的排序算法或緩存,而關注可讀性則可能會讓它將一個函數拆分成更小的函數或添加註釋。如果你有多個目標,把它們列出來。來自Strapi指南的一個提示模板甚至建議列舉問題:「我想解決的問題:1) [性能問題], 2) [代碼重複], 3) [過時的API使用]。」 這樣,AI就知道具體要修復什麼。記住,它本身並不知道你覺得代碼中存在的問題是什麼——你必須告訴它。

2、提供必要的代碼上下文。 重構時,你通常會在提示中納入需要改進的代碼片段。重要的是把你想要重構的函數或代碼完整放進來,有時候如果相關的話,還需要把周邊的上下文放進來(比如函數的用法或相關代碼,這可能會影響你重構的方式)。同時要介紹語言和框架,因為「符合語言習慣」的代碼在不同環境(比方説,符合Node.js習慣 vs 符合Deno習慣,或者React類組件 vs 函數組件)下是不一樣的。比方説:「我有一個用類寫的React組件。請將其重構為使用Hooks的函數組件。」然后AI會應用典型的步驟(使用useState, useEffect等)。如果你只是説「重構這個React組件」而沒有説明風格,AI可能不知道你特指想要Hooks。

  • 如果相關,把版本或環境細節信息放進來。 比方説,「這是一個Node.js v14的代碼庫」或「我們使用的是ES6模塊」。這會影響AI是否使用某些語法(如import/export vs require),這是正確重構的一部分。如果你想確保它不會引入不兼容的東西,請説明你的約束條件。

3、鼓勵附帶代碼的解釋。 從AI主導的重構中學習(並驗證其正確性)的一個好方法是要求對更改進行解釋。比方説:「請建議一個重構后的代碼版本,並解釋你所做的改進。」這甚至被內置在我們引用的提示模板里面:「……建議重構后的代碼並附上對更改的解釋。」 當AI提供解釋時,你可以評估它是否理解了代碼並滿足了你的目標。解釋可能會是這樣:「我將兩個相似的循環合併爲一個以減少重複,並且我使用了一個字典來加速查找」,等等。如果解釋有任何聽起來不對勁的地方,那就是需要仔細檢查代碼的危險信號。簡而言之,利用AI的解釋能力作為保障——這就像讓AI對其自身的重構進行代碼審查。

4、 利用角色扮演設定高標準。如前所述,要求AI扮演代碼審查員或資深工程師可能非常有效。對於重構,你可以説:「扮演一位經驗豐富的TypeScript專家,重構這段代碼以符合最佳實踐和現代標準。」結果往往不僅是膚淺的更改,還會帶來更有見地的改進,因為AI試圖符合「專家」的形象。提示指南有個流行的例子是讓AI扮演導師:「扮演一位經驗豐富的Python開發者指導初級開發者。提供解釋並編寫文檔字符串(docstrings)。重寫代碼來優化。」 在那個案例里,結果是AI使用了更高效的數據結構(用集合set去重),併爲原本使用循環的函數提供了一行式的解決方案。角色扮演不僅幫助它進行了重構,還解釋了為什麼新方法更好(在那種情況下,使用集合是一種衆所周知的用於唯一性的優化)。

現在,讓我們通過一個重構示例來看看提示是如何影響結果的。我們將使用一個JavaScript (Node.js)場景,里面有一段不太理想的代碼,我們想改進它。

重構示例:糟糕提示 vs 改進提示

假設我們有一個函數,它進行兩次數據庫調用並進行一些處理。函數能工作,但不夠優雅——有重複的代碼且難以閲讀。我們想重構它來提升清晰度和效率。以下是原始代碼:

// Original function: Fetches two lists and processes them (needs refactoring)

async function getCombinedData(apiClient) {

  // Fetch list of users

  const usersResponse = await apiClient.fetch('/users');

  if (!usersResponse.ok) {

    throw new Error('Failed to fetch users');

  }

  const users = await usersResponse.json();

  // Fetch list of orders

  const ordersResponse = await apiClient.fetch('/orders');

  if (!ordersResponse.ok) {

    throw new Error('Failed to fetch orders');

  }

  const orders = await ordersResponse.json();

  // Combine data (match user to orders)

  const result = [];

  for (let user of users) {

    const userOrders = orders.filter(o => o.userId === user.id);

    result.push({ user, orders: userOrders });

  }

  return result;

}

 

這段代碼的問題:我們看到獲取用户和訂單的邏輯重複(可以重構為輔助函數或循環),錯誤信息缺乏細節(只是通用錯誤消息),並且可以並行執行獲取操作以提高速度。讓我們看看兩種提示方法:

❌ **提示1(描述不足):「重構上面的getCombinedData函數。」**

AI對提示1的響應:

> *好的。以下是getCombinedData的重構版本:*

async function getCombinedData(apiClient) {

  // Fetch users and orders in parallel

  const [usersResponse, ordersResponse] = await Promise.all([

    apiClient.fetch('/users'),

    apiClient.fetch('/orders')

  ]);

  if (!usersResponse.ok || !ordersResponse.ok) {

    throw new Error('Failed to fetch data');

  }

  const [users, orders] = await Promise.all([

    usersResponse.json(),

    ordersResponse.json()

  ]);

  // Map orders by userId for efficient lookup

  const ordersByUser = {};

  for (const order of orders) {

    if (!ordersByUser[order.userId]) {

      ordersByUser[order.userId] = [];

    }

    ordersByUser[order.userId].push(order);

  }

  // Combine data

  return users.map(user => ({

    user,

    orders: ordersByUser[user.id] || []

  }));

}

 

> *我並行獲取了用户和訂單以提高性能。我還合併了錯誤處理,並使用了一個映射(ordersByUser)來按用户分組訂單,這使得組合步驟更簡潔且可能更快。*

分析:這實際上是一個相當好的重構,實現了多項改進:並行獲取、按用户分組訂單以提高效率、使用`.map`進行更簡潔的組合並默認為空數組。AI也解釋了它的做法。**那麼為什麼我們將此提示標記為描述不足或「差」?** 在這個案例中,AI碰巧做出了好的選擇,但由於提示是開放式的,它做了很多假設。它假設性能應該被改進(並行請求,這是一個聰明的改動,但如果在這里並行不安全呢?),並且它將錯誤處理改成了一個通用的消息「Failed to fetch data」,這**丟失了**具體是哪個調用失敗的**特異性**。

使用通用的「重構這個」提示的風險在於,AI可能以非預期方式做出改變行為的更改。在這里,合併錯誤處理可能可以接受,也可能不行,這取決於需求。助手還做了一個結構性更改(使用字典分組訂單)——這沒問題,可能更好,但如果我們沒有告知特定的約束(比如想要流式傳輸結果或保持低內存使用),AI是不知道的。所以,雖然結果看起來不錯,但AI自己對「重構」的理解恰好與我們想要的相符,這多少有些運氣成分。這說明了描述不足的提示有時候在簡單案例上能得到好結果,但對於複雜代碼,它可能會偏離期望的方向或遺漏你關心的某些改進。

✅ **提示2(目標導向):「重構上面的getCombinedData函數,目標是消除重複代碼並提高性能。具體來説:(1) 避免重複獲取用户和訂單的邏輯——也許可以利用輔助函數或一併獲取它們。(2) 如果可能,並行獲取兩個列表。(3) 保留每個獲取操作的錯誤處理(我們想知道哪個調用失敗了)。(4) 改進數據的組合方式,可能可以使用更高效的結構進行查找,而不是嵌套循環。提供重構后的代碼,並附帶註釋解釋更改。」**

AI對提示2的響應:

> *好的!以下是按照要求重構后的改進版getCombinedData:*

async function getCombinedData(apiClient) {

  // 1. Fetch users and orders in parallel to improve performance

  const [usersResponse, ordersResponse] = await Promise.all([

    apiClient.fetch('/users'),

    apiClient.fetch('/orders')

  ]);

 

  // 2. Handle errors separately to know which call failed

  if (!usersResponse.ok) {

    throw new Error('Failed to fetch users');

  }

  if (!ordersResponse.ok) {

    throw new Error('Failed to fetch orders');

  }

 

  // Parse JSON responses in parallel

  const [users, orders] = await Promise.all([

    usersResponse.json(),

    ordersResponse.json()

  ]);

 

  // 3. Use a lookup map for orders by userId for efficient matching

  const ordersByUser = orders.reduce((map, order) => {

    if (!map[order.userId]) {

      map[order.userId] = [];

    }

    map[order.userId].push(order);

    return map;

  }, {});

 

  // 4. Combine data using the lookup map

  return users.map(user => ({

    user,

    orders: ordersByUser[user.id] || []  // empty array if no orders for user

  }));

}

 

> *更改説明:*

> *並行獲取:我使用Promise.all同時獲取用户和訂單(第1點)。*

> *保留單獨的錯誤處理:我保留了針對usersResponse.ok和ordersResponse.ok的單獨檢查,以便任何錯誤都能清楚地表明哪個獲取操作失敗了(第2點)。*

> *並行JSON解析:也對response.json()調用使用了Promise.all,這是一個小改進。*

> *高效組合:我沒有使用嵌套循環,而是創建了一個ordersByUser映射(第3點)。這通過單次遍歷將訂單按userId分組。然后我遍歷users構建結果,在O(1)時間內從映射中提取每個用户的訂單(第4點)。這更高效且簡潔。*

分析:這個響應直接處理了**所有**指定的目標。代碼被重構得更清晰、更快,並且按要求保留了單獨的錯誤消息。AI的解釋確認了我們列出的每一點,這表明它仔細遵循了提示指令。這是一個很好的結果,因為作為提示者,我們定義了在這個上下文中「重構」意味着什麼。如此,我們引導AI產生了一個符合我們需求的解決方案,讓來回溝通最小化。如果AI忽略了其中一點(比如説它仍然合併了錯誤處理),我們很容易就可以再次提示:「看起來不錯,但請確保區分用户和訂單的錯誤消息。」——不過,在這個案例中,因為我們的提示很詳盡,所以不需要這樣做。

這個例子展示了一個關鍵教訓:**當你知道你想要改進什麼時,要明確說出來。** AI擅長遵循指令,但它不會讀心術。一句寬泛的「讓它更好」可能適用於簡單的事情,但對於複雜的代碼,通過列舉「更好」對你意味着什麼,才能獲得最佳結果。這符合社區的見解:清晰、結構化的提示能顯著改善結果。

額外的重構技巧:

  • 分步重構:如果代碼量很大或者你有很長的更改列表,你可以一次處理一條。比方説,先讓AI「重構以提高可讀性」(側重於重命名、拆分函數),然后再「優化這個函數中的算法」。這可以防止一次性給模型太多指令而造成負擔,並讓你逐步驗證每個更改。

  • 要求替代方法:也許AI的第一次重構有效,但你對不同的角度感到好奇。你可以問:「你能用另一種方式重構它嗎?也許使用函數式編程風格(比方説,使用數組方法代替循環)?」或者「在這里使用遞歸代替迭代方法怎麼樣?只是爲了比較一下?」這樣,你可以評估不同的解決方案。這就像與同事一起頭腦風暴多個重構選項。

  • 將重構與解釋結合來學習模式:我們提到過這一點,但值得再強調一次——將AI用作學習工具。如果它以巧妙的方式重構了代碼,請研究它的輸出和解釋。你可能會發現一個你以前從未使用過的新的API或技術(比如使用reduce構建映射)。這是要求解釋的原因之一:它將答案變成了一個迷你教程,強化你對最佳實踐的理解。

  • 驗證和測試:在任何AI生成的重構之后,務必運行你的測試或用示例輸入嘗試代碼。AI可能會無意中引入一些很小的錯誤,特別是如果提示沒有指定重要的約束條件的話。比方説,在我們的重構中,如果原始代碼爲了日誌記錄而有意分開獲取操作的錯誤,但我們沒有提到日誌記錄,那麼合併的錯誤可能用處不大。在審查中發現這是我們的責任。AI也可以通過編寫測試來提供幫助——你可以要求「為重構后的函數生成幾個單元測試」,以確保它在預期輸入下的行為與之前相同。

至此,我們已經講完了調試和重構——對現有代碼的改進。下一個合乎邏輯的步驟是使用AI輔助來實現新功能或生成新代碼。我們將探討如何有效地為這種場景編寫提示。

現代的調試場景

**React Hook依賴項問題**

❌ **糟糕提示:** 「我的useEffect工作不正常」

✅ **增強提示:**

I have a React component that fetches user data, but it's causing infinite re-renders. Here's my code:

 const UserProfile = ({ userId }) => {

  const [user, setUser] = useState(null);

  const [loading, setLoading] = useState(true);

   useEffect(() => {

    fetchUser(userId).then(setUser).finally(() => setLoading(false));

  }, [userId, setUser, setLoading]); // Problem is here

 

  return loading ? <div>Loading...</div> : <div>{user?.name}</div>;

};

 

預期行為:應在userId變化時獲取一次用戶數據

實際行為:組件無限重新渲染

控制檯錯誤:"Warning: Maximum update depth exceeded"

是什麼導致了這個無限循環?如何修復依賴項數組?

為什麼有效:提供了確切的代碼、錯誤消息、預期與實際行為,並聚焦於一個常被誤解的特定React模式。

現代的狀態架構

❌ **糟糕提示:** 「為我的Next.js電商應用構建狀態管理」

✅ **增強提示:**

我正在構建一個Next.js 14電商應用,需要設計狀態管理架構。以下是我的要求:

組件

*   產品列表頁(需要:products[], 篩選器, 分頁)

*   購物車(需要:購物車商品, 總計, 配送信息)

*   用户認證(需要:用户資料, 認證狀態, 偏好設置)

*   實時通知(需要:toast消息, 錯誤狀態)

技術約束

*   Next.js 14 帶App Router和服務器組件

*   TypeScript嚴格模式

*   服務端數據獲取(用於SEO)

*   購物車/用户操作的客户端交互性

*   狀態應在導航間保持

我應該使用:

*   每個域(cart, auth, notifications)使用獨立的Zustand store

*   React Query/TanStack Query用於服務器狀態 + Zustand用於客户端狀態

*   一個帶切片(slices)的Zustand store

請提供一個推薦的架構,並附帶代碼示例,展示如何構建store以及如何與Next.js App Router模式集成。

為什麼有效:真實場景,包含具體技術棧、需求清晰,並要求提供帶有實現細節的架構指導。

實現新功能的提示模式

AI 代碼助手最令人興奮的用途之一,就是幫助你從頭開始編寫新代碼,或者將新功能集成到現有代碼庫中。這可能包括生成 React 組件的樣板代碼,或者在 Express 應用中編寫一個新的 API 端點。這里的挑戰通常在於這些任務是開放式的——實現一個功能有很多種方法。用於代碼生成的提示工程,就是引導 AI 生成符合你需求和風格的代碼。以下是一些策略:

1.  從高階指令開始,然后逐步深入。先用通俗語言概述你想開發什麼,可能的話將其分解成更小的任務(類似於我們之前關於分解複雜任務的建議)。比方説,假設你想在現有的 Web 應用當中添加一個搜索欄功能。你可以先提示:「寫一個計劃概述,在我的 React 應用里面添加一個按名稱過濾產品列表的搜索功能。產品數據是從 API 獲取的。」

AI 可能會給你一個分步計劃:「1. 添加一個輸入框用於搜索查詢。2. 添加狀態來保存查詢。3. 根據查詢過濾產品列表。4. 確保不區分大小寫,等等。」一旦你有了這個計劃(可以在 AI 的幫助下完善它),你就可以通過針對性的提示來搞定每一條。

比方説:「好的,實現第 1 步:創建一個 SearchBar 組件,要有一個能更新 searchQuery 狀態的輸入框。」之后,「實現第 3 步:給定 searchQuery 和一個產品數組,過濾產品(按名稱進行不區分大小寫的匹配)。」通過將功能分解,你可以確保每個提示都是具體的,並且響應是易於管理的。這也是迭代開發過程的體現——你可以在開發的時候測試每一部分。

2.  提供相關上下文或參考代碼。如果你要將功能添加到現有項目中,向 AI 展示項目的類似功能是如何實現的會很有幫助。比方説,如果你已經有了一個與你想要實現的組件相似的組件,你可以説:「這是一個現有的 UserList 組件(代碼……)。現在創建一個類似的 ProductList 組件,但要包含一個搜索欄。」

AI 會識別模式(可能你用了某些庫或風格約定)並加以應用。在提示中打開相關文件或引用之,可以提供上下文信息,從而得到更符合項目需求、更一致的代碼建議。另一個技巧是:如果你的項目使用了特定的編碼風格或架構(比如用 Redux 管理狀態或某個 CSS 框架),一定要提一下。「我們使用了 Redux 進行狀態管理——請將搜索狀態集成到 Redux store 中。」

訓練有素的模型就會生成符合 Redux 模式等的代碼。本質上,你是在向 AI 介紹你的項目環境,這樣它才能定製化輸出。有些助手甚至可以將你的整個代碼庫作為上下文來源;如果使用這類助手,確保你將其指向代碼庫中相似的模塊或文檔。

  • 如果是開始一個新項目,但你偏好某個方法,你也可以提出來:「我想用函數式編程風格來實現這個(無外部狀態,使用數組方法)。」或者,「確保遵循 MVC 模式,把邏輯放在控制器里,而不是視圖里。」這些都是高級工程師可能會提醒初級工程師注意的細節,在這里,你就是那個告訴 AI 的高級工程師。

3.  使用註釋和 TODO 作為內聯提示。如果是直接在帶有 Copilot 的 IDE 來開發,一個有效的工作流是:寫一條描述你接下來所需要的代碼塊的註釋,然后讓 AI 自動補全它。比方説,在 Node.js 后端,你可以寫:`// TODO: 驗證請求負載(確保提供了姓名和郵箱)`,然后開始新的一行。Copilot 通常都能理解你的意圖,並生成執行該驗證的代碼塊。這之所以有效,是因為你的註釋本質上就是一個自然語言提示。不過,如果 AI 理解錯了,你要準備好編輯生成的代碼——並一如既往地,驗證其正確性。

4.  提供預期的輸入/輸出或用法示例。跟我們之前討論過的情況類似,如果你要求 AI 實現一個新函數,請包含一個它將如何被使用的快速示例或一個簡單的測試用例。比方説:「用 JavaScript 實現一個函數 `formatPrice(amount)`,它將接收一個數字(比如 2.5),並返回一個格式化為美元貨幣的字符串(比如 `$2.50`)。比方説,`formatPrice(2.5)` 應該返回 `'$2.50'`。」

通過提供這個例子,你就約束了 AI 去生成一個與之相符的函數。沒有這個例子,AI 可能會假設使用其他格式或貨幣。雖然感覺差別不大但很重要。另一個在 Web 上下文中的例子:「實現一個 Express 中間件來記錄請求。比方説,一個到 `/users` 的 GET 請求應該在控制檯記錄 `‘GET /users’`。」 這清楚地表明瞭輸出應該是什麼樣子。在提示中包含預期行為相當於給 AI 一個它要努力通過的測試。

5.  當結果不是你想要的時,用更多細節或約束重寫提示。第一次嘗試生成新功能時常常不盡如人意,這很常見。可能代碼能運行但不地道,或者遺漏了某個需求。與其沮喪,不如把 AI 當作一個提交了初稿的初級開發者對待——現在你需要給出反饋。比方説,「這個方案可行,但我更希望你使用內置的數組 `filter` 方法,而不是 `for` 循環。」或者,「你能重構生成的組件,使用 React Hooks 來管理狀態,而不是類組件嗎?我們的代碼庫都是函數式組件。」你還可以添加新的約束:「另外,確保函數在 O(n) 時間複雜度或更好的情況下運行,因為 n 可能很大。」這種迭代式的提示非常強大。一個真實場景:一位開發者要求 LLM 生成使用 JS Canvas 庫繪製冰淇淋甜筒的代碼,但它一直給出不相關的輸出,直到他們用更多具體細節和上下文優化了提示。教訓是,不要一次嘗試后就放棄。找出提示中缺少或被誤解的內容,並澄清它。這就是提示工程的精髓——每一次調整都能引導模型更接近你的設想。

讓我們通過一個示例場景來説明功能實現的提示:

功能實現示例:在 AI 輔助下開發 React 組件

假設我們有一個 React 應用,我們想添加一個新組件:一個帶有搜索過濾器和排序下拉框的產品列表。這在 Web 應用中是一個相當典型的功能。為簡潔起見,我們將重點放在為該組件內的搜索功能進行提示。

**原先提示(功能請求):** 「創建一個名為 `ProductList` 的 React 函數組件,它顯示一個產品列表,幷包含一個文本輸入框用於按名稱過濾產品。該組件應該:從 `/api/products` 獲取產品數組(假設它返回 `{id, name, ...}` 的 JSON 數組),將它們存儲在狀態中,並允許用户在搜索框中輸入內容以按名稱過濾顯示的產品(不區分大小寫匹配)。在一個簡單的 `<ul>` 列表中顯示過濾后的產品名稱。包含基本的錯誤處理(比方説,如果 API 調用失敗則顯示錯誤信息)以及獲取數據時的加載狀態。」

這是一個很長的提示,但很具體。假設我們在聊天中把這個提示給了一個 LLM。

AI 的響應(可能生成的摘要):AI 很可能會輸出一個類似下面這樣的 `ProductList` 組件:

import React, { useState, useEffect } from 'react';

 function ProductList() {

  const [products, setProducts] = useState([]);

  const [search, setSearch] = useState('');

  const [loading, setLoading] = useState(true);

  const [error, setError] = useState(null);

   useEffect(() => {

    async function fetchProducts() {

      try {

        const res = await fetch('/api/products');

        if (!res.ok) throw new Error('Network response was not ok');

        const data = await res.json();

        setProducts(data);

      } catch (err) {

        console.error('Failed to fetch products:', err);

        setError('Failed to load products.');

      } finally {

        setLoading(false);

      }

    }

    fetchProducts();

  }, []);

 

  const handleSearchChange = (e) => {

    setSearch(e.target.value);

  };

   // Filter products by search query (case-insensitive)

  const filteredProducts = products.filter(product =>

    product.name.toLowerCase().includes(search.toLowerCase())

  );

   if (loading) return <div>Loading products...</div>;

  if (error) return <div>{error}</div>;

 

  return (

    <div className="product-list">

      <input

        type="text"

        placeholder="Search products"

        value={search}

        onChange={handleSearchChange}

      />

      <ul>

        {filteredProducts.map(prod => (

          <li key={prod.id}>{prod.name}</li>

        ))}

      </ul>

    </div>

  );

}

 

export default ProductList;

 

(如果我們提出要求,代碼中可能還會包含一些解釋各部分的註釋。)

現在,我們來分析這個結果,並考慮如何優化提示:

  • 假設上面這段代碼返回了,但我們的項目使用 Context 或全局存儲(如 Redux)來進行數據獲取,而不是在組件內部獲取。這是我們沒有明確指出的。AI 選擇了在 `useEffect` 中獲取數據。如果我們更傾向於使用自定義 Hook 或 Context,我們應該在提示中暗示這一點。因此我們可以優化提示:**「實際上,在我們的應用中,我們使用一個自定義 Hook `useProducts()` 來處理數據獲取。請重構組件,使用 `useProducts` Hook,而不是直接調用 fetch。」** AI 隨后會進行調整,可能會假設 `useProducts` 返回 `{ products, loading, error }`,並相應地簡化組件。

  • 另一個優化點:也許我們意識到還想要一個排序下拉框(這是我們最初沒有提到的)。我們現在可以擴展對話:**「很好,現在添加一個下拉框,用於按名稱排序產品(A-Z 或 Z-A)。下拉框應讓用户選擇升序或降序,列表在過濾的基礎上還應相應排序。」** 因為 AI 有現有代碼的上下文,它可以插入一個排序狀態並調整渲染。我們提供了一個清晰的新需求,它會嘗試完成這個需求,可能會添加類似下面的代碼:

const [sortOrder, setSortOrder] = useState('asc');

// ... a select input for sortOrder ...

// and sort the filteredProducts before rendering:

const sortedProducts = [...filteredProducts].sort((a, b) => {

  if (sortOrder === 'asc') return a.name.localeCompare(b.name);

  else return b.name.localeCompare(a.name);

});

  • (以及下拉框的 UI 代碼)。

  • 通過像這樣逐個功能地迭代,我們模擬了一個與 AI 協作的開發周期。這比一開始就試圖用單個提示獲取包含所有功能的整個複雜組件有效得多。這種做法減少了錯誤,並允許在需求變得更清晰時進行中途修正。

如果 AI 犯了一個小錯誤(比如它忘記了讓搜索過濾器不區分大小寫),我們只需指出:**「讓搜索不區分大小寫。」** 它會調整過濾器使用小寫比較(在我們的偽代碼輸出中它已經這麼做了,但如果沒有,它會修復)。

這個例子表明,用 AI 實現功能的核心在於漸進式開發和提示優化。一篇 Twitter 帖子可能會驚歎於某人如何通過持續、針對每一部分向 LLM 發出提示來構建一個小型應用——這本質上就是這種方法:開發、審查、優化、擴展。每個提示就像是開發過程中的一次提交(commit)。

實現功能的額外技巧:

*   讓 AI 搭建腳手架,然后你填充細節:有時候讓 AI 生成一個粗略的結構,然后你再進行調整是很有用的。比方説,「生成一個用於用户註冊的 Node.js Express 路由的骨架,包含驗證和錯誤處理。」它可能會生成一個帶有佔位符的通用路由。然后你可以填入特定於你的應用程序的實際驗證規則或數據庫調用。AI 省去了你編寫樣板代碼的麻煩,而你可以處理那些敏感的自定義邏輯。

*   要求處理邊界情況:在生成功能時,你可以提示 AI 考慮邊界情況:**「我們應該為這個功能考慮哪些邊界情況(並且你能在代碼中處理它們嗎)?」** 比方説,在搜索示例中,邊界情況可能是「如果用户輸入時產品數據還沒加載完怎麼辦?」(儘管我們的代碼通過加載狀態處理了)或者「如果兩個產品同名怎麼辦」(不是什麼大問題,但也許可以提一下)。AI 可能會提到空結果處理、非常大的列表(搜索輸入可能需要防抖)等。這是利用 AI 對常見陷阱訓練的一種方式。

*   文檔驅動開發:有些人的做法很妙,就是先寫一個文檔字符串(docstring)或用法示例,然后讓 AI 實現與之匹配的函數。比方説:

/**

 * Returns the nth Fibonacci number.

 * @param {number} n - The position in Fibonacci sequence (0-indexed).

 * @returns {number} The nth Fibonacci number.

 *

 * Example: fibonacci(5) -> 5  (sequence: 0,1,1,2,3,5,…)

 */

function fibonacci(n) {

  // ... implementation

}

  •  如果你寫了上面的註釋和函數簽名,LLM 可能會正確地幫你填充實現,因為註釋準確地描述了要做什麼,甚至給出了一個例子。這種技術確保你先用文字闡明功能(這本身也是很好的實踐),然后 AI 將其作爲規範來編寫代碼。

在介紹了用於調試、重構和新代碼生成的提示策略之后,讓我們把注意力轉向編碼提示工程中的一些常見陷阱和反模式。理解這些將幫助你避免在無效的交互上浪費時間,並在 AI 沒有給出你需要的東西時快速調整。

常見提示反模式及應對策略

不是所有提示的有效性都一樣的。我們已經看過許多高效提示的案例,但識別反模式——那些導致AI表現不佳的常見錯誤——同樣具有指導意義。

以下是幾種常見的提示失誤及其改進方法:

  • 反模式:模糊提示。典型表現是"它不工作了,請修復"或"寫個實現X功能的東西"這種缺乏細節的指令。當"為什麼我的函數不工作?"這種問題得到的是無用的回覆時就是例子。模糊提示迫使AI去猜測上下文,通常導致泛泛而談的建議或不相關代碼。改進方法很簡單:補充上下文和具體信息。如果你發現自己的提問得到像魔法八號球那樣的迴應("你檢查過X嗎?"),請暫停並補充細節重構問題(錯誤信息、代碼片段、預期與實際結果等)。建議自問一下:"這個問題能套用到幾十種不同場景嗎?"如果答案是肯定的,説明提示太含糊了。要讓提示具體到只適用於你的獨特場景。

  • 反模式:超載提示。這是另一個極端:要求AI同時處理的任務太多。比方説"生成完整的Node.js應用,包含身份驗證、React前端和部署腳本",或者規模小點的,類似"一次性修復這5個bug並添加3個新功能"。AI可能會做處理嘗試,但結果往往很混亂,殘缺不全,或會忽略部分需求。即便全部處理,回覆也會很宂長難驗證。解決方案是拆分任務:像之前強調的那樣,每次專注一件事。這樣更易發現錯誤並保持模型專注。當發現指令段落包含多個"並且"時,就應考慮拆分為獨立提示或分步執行。

  • 反模式:缺失明確訴求。 有時候用户提供大量信息卻未説明需求。比方説丟出大段代碼只說"這是我的代碼"。這會讓AI困惑——它不知道你要什麼。務必包含明確指令,如"找出上方代碼的bug"、"解釋代碼功能"或"完成代碼中的TODO"。每個提示都應有目的。若僅提供文本而無指令,AI可能產生錯誤預判(比如總結代碼而非修復)。確保AI明白你展示代碼的意圖。簡單加上"這段代碼有什麼問題?"或"請繼續實現這個函數"就能指明方向。

  • 反模式:模糊成功標準。 這個更隱蔽——要求優化改進時未定義成功標準。比方説"讓這個函數更快",但"更快"的標準是什麼?若AI不知道性能約束,可能優化無關緊要的部分,或採用理論更快但實際無感的方案。"讓代碼更整潔"中的"整潔"也是主觀的。我們應明確目標如"減少重複"或"改進變量命名"。改進方法是量化或限定優化方向,比方説"優化此函數至線性時間複雜度(當前為平方級)"或"重構以移除全局變量改用類"。本質上要闡明重構或功能具體解決什麼問題,否則AI可能解決非目標問題。

  • 反模式:忽視AI的澄清請求。 AI有時候會迴應澄清性問題或假設,比方説"您使用的是React類組件還是函數組件?"或"假設輸入是字符串——請確認"。若忽略這些直接重複請求,就錯過了優化提示的機會。AI在暗示需要更多信息。務必迴應問題或在提示中補充細節。另外,若AI輸出明顯偏離(如誤解問題),不要原封不動重試相同提示。花時間調整措辭:可能提示存在歧義或遺漏關鍵點。像在跟人對話那樣——如果對方誤解,你會換種方式解釋;對AI也應如此。

  • 反模式:風格反覆無常。 若頻繁變換提問方式或混用多種格式,模型可能困惑。比方説指令中第一人稱和第三人稱切換,或偽代碼與真實代碼混淆不清。單個提示中應保持風格統一。提供示例時確保明確區分(用Markdown三引號標記代碼,引號標註輸入輸出示例等)。一致性幫助模型正確解析意圖。若有偏好風格(如ES6與ES5語法),需始終明確説明,否則模型可能在不同提示中會給出不同方案。

  • 反模式:模糊指代(如'上方代碼')。在聊天中使用"上方函數"或"之前的輸出"時,確保指代明確。若對話較長時説"重構上方代碼",AI可能跟丟或選錯代碼片段。更安全的做法是重新引用代碼或具體命名要重構的函數。模型注意力有限,儘管多數LLM能回溯對話,但重新提供明確上下文可避免混淆。若距離代碼展示已過較長時間(或經歷多輪對話),這點尤為重要。

當出現問題時的提示重寫策略:

  • 先定位AI回覆中缺失或錯誤的部分:是否解決了錯誤問題?是否產生錯誤或不符合要求的方案?比方説要求TypeScript方案卻得到普通JavaScript,或明確要求迭代方案卻給出遞歸實現。精準定位差異點。

  • 在新提示中補充或強調該要求。比方説:"方案需用TypeScript而非JavaScript,請包含類型註解",或"我明確要求迭代方案——請避免遞歸改用循環"。有時候直接使用"注意:"或"重要:"等短語突顯關鍵約束很有效(模型雖無情緒,但會權衡短語重要性)。比方説:"重要:請勿使用任何外部庫"或"注意:代碼需在瀏覽器運行,故不可用Node專屬API"。

  • 必要時進一步分解請求。若AI在複雜需求上反覆失敗,先嚐試請求更小的模塊。或提出澄清性問題:"你理解我説的X是指什麼嗎?"模型可能複述其理解,你可據此糾正錯誤。這種元提示法(討論提示本身)有時候能消除誤解。

  • 對話卡住時考慮重啟。多次嘗試后對話可能陷入混亂狀態。新建會話(或暫時清空聊天記錄)后,基於先前失敗經驗重構更精確的提示重新開始會很有幫助。模型不介意重複,而新上下文能消除之前積累的混淆信息。

瞭解這些反模式及解決方案后,調整實時提示就能更快。對開發者而言,提示工程本質是迭代的、反饋驅動的過程(如同所有編程任務!)。好消息是你現在擁有豐富的模式案例可供參考。

結論

提示工程既是藝術也是科學——正如我們所見,它正迅速成為開發者使用AI編程助手的必備技能。通過構建清晰、上下文豐富的提示,你本質上是在教導AI你的需求,如同引導新團隊成員或向同事解釋問題。本文系統探討了如何構建用於調試、重構和功能實現的提示:

  • 我們學會向AI提供求助同事時的同類信息:代碼預期功能、異常表現、相關代碼片段等——從而獲得更精準的幫助。

  • 我們見證了與AI迭代協作的力量:無論是逐行梳理函數邏輯,還是通過多輪提示優化方案(如將遞歸方案轉為迭代方案再改進變量命名)。耐心迭代使AI成為真正的結對編程夥伴,而非一次性代碼生成器。

  • 我們運用角色扮演提升回覆質量——將AI視為代碼審查員、導師或特定技術棧的專家。這通常可生成更嚴謹、解釋更豐富的輸出,不僅能解決問題,還在此過程中給予我們啓發。

  • 重構優化時我們強調定義"優秀"標準(更快/更簡潔/更地道等),而AI展示了在引導下應用最佳實踐的能力(如並行調用、消除重複、正確處理錯誤)。這如同獲得了無數代碼審查員的集體智慧——但需通過正確提問來激活。

  • 我們還演示了在AI協助下逐步構建新功能,表明即便複雜任務也能分解為單次提示處理。AI可搭建基礎框架、建議實現方案,甚至在被提示時強調邊界情況——如同隨時待命的資深協作者。

  • 過程中我們識別了需規避的陷阱:提示既不過於模糊也不超載、始終明確意圖和約束、隨時準備在AI偏離目標時調整。通過具體反面案例,我們看到微小改動(如添加錯誤信息或預期輸出)如何顯著提升結果。

將這些技巧融入工作流后,你會發現AI協作變得更直觀。你將逐步掌握獲取最佳結果的表達方式,以及模型偏離時如何引導。記住AI是其訓練數據的產物——它見過無數代碼和問題解決案例,但由你決定哪些案例與當前相關。本質上,你設定上下文,AI負責執行。

值得注意的是,提示工程是不斷發展的實踐。開發者社區持續發現新技巧——一個聰明的單行提示或結構化模板可能突然在社交媒體爆火,因為它解鎖了人們未曾意識到的能力。關注這些討論(如Hacker News、Twitter等)可激發個人技術靈感。同時勇於自我實驗:將AI視為靈活工具——若有想法(如"讓它用ASCII字符畫架構圖會怎樣?"),儘管嘗試。結果可能令你驚喜,即便失敗也無妨——你由此瞭解了模型的侷限或需求。

總之,提示工程可賦能開發者更高效利用AI助手。有沒有提示工程,就是挫敗體驗("這工具沒用,盡説廢話")與高效體驗("如同專家結對編程幫我寫基礎代碼")之別。應用本文的策略手冊——從提供完整上下文到引導AI風格思路——你就能將這些代碼導向的AI工具轉化為開發流程的真正延伸。最終結果不僅是編碼提速,更常在過程中收穫新見解和模式(當AI解釋或建議替代方案時),從而提升自身技能水平。

最后一個關鍵點:記住提示是一場循序漸進的對話。要像跟其他工程師溝通時那麼清晰、耐心和嚴謹地對待這場對話。如此你將發現AI助手能極大增強你的能力——助你更快地調試、更智能地重構、更輕松地實現功能。

祝你享受編寫提示和代碼的過程!

譯者:boxi。

風險及免責提示:以上內容僅代表作者的個人立場和觀點,不代表華盛的任何立場,華盛亦無法證實上述內容的真實性、準確性和原創性。投資者在做出任何投資決定前,應結合自身情況,考慮投資產品的風險。必要時,請諮詢專業投資顧問的意見。華盛不提供任何投資建議,對此亦不做任何承諾和保證。