🪁🍁 希望本文能給您帶來幫助,如果有任何問題,歡迎批評指正!🐅🐾🍁🐥
文章目錄
- 一、前言
- 二、提示詞前置知識
- 2.1 提示詞要素
- 2.2 設計提示詞的通用技巧
- 2.2.1 從簡單開始
- 2.2.2 指令
- 2.2.3 具體性
- 2.2.4 避免不明確
- 2.3 Spring AI Prompt 位置圖
- 三、Prompt 工程技術
- 3.1 零樣本提示(Zero-shot Prompting)
- 3.2 少樣本提示(Few-shot Prompting)
- 3.3 系統、情境和角色提示
- 3.3.1 系統提示
- 3.3.2 角色提示
- 3.3.3 情景提示
- 3.4 后退提示(Step-Back Prompting)
- 3.5 鏈式思考提示(Chain-of-Thought Prompting)
- 3.6 自洽性(Self-Consistency)
- 3.7 思維樹(ToT)
- 3.8 自動提示工程(Automatic Prompt Engineering)
- 3.9 代碼提示(Code Generation Prompting)
- 四、Prompt API概述
- 4.1 Message
- 4.2 Prompt
- 4.3 Prompt Template
- 五、Prompt Template使用示例
- 5.1 使用自定義模板渲染器
- 5.2 使用資源而不是字符串
- 六、總結
- 七、參考資料
導航參見:
Spring AI實戰:SpringBoot項目結合Spring AI開發——ChatClient API詳解
Spring AI實戰:SpringBoot項目結合Spring AI開發——提示詞(Prompt)技術與工程實戰詳解
一、前言
在前面的文章中,介紹了Chat Client API的基本用法,這篇文章來介紹一下提示詞相關的內容。Prompt 是引導 AI 模型生成特定輸出的輸入格式,Prompt 的設計和措辭會顯著影響模型的響應,這也是為什么有的人在使用大模型時,效率非常高,很容易就能獲得自己想要的答案,而有的人需要和模型對話幾輪才能得到自己想要的答案。本文將從提示詞原理到Spring AI中提示詞開發實戰做一個詳細的介紹,希望本文對您能有所幫助。
二、提示詞前置知識
我們通常在提問時,就是給大模型提問題,遵循以下格式:
<問題>?
或
<指令>
除上面之外,我們還可以將其格式化為問答(QA)格式,這在許多問答數據集中是標準格式,如下所示:
Q: <問題>?
A:
2.1 提示詞要素
提示詞可以包含以下任意要素:
- 指令: 想要模型執行的特定任務或指令。
- 上下文: 包含外部信息或額外的上下文信息,引導語言模型更好地響應。
- 輸入數據: 用戶輸入的內容或問題。
- 輸出指示:指定輸出的類型或格式。
為了更好地演示提示詞要素,下面是一個簡單的提示,旨在完成文本分類任務:
請將文本分為中性、否定或肯定
文本:我覺得食物還可以。
情緒:
在上面的提示示例中,指令是“將文本分類為中性、否定或肯定”。輸入數據是“我認為食物還可以”部分,使用的輸出指示是“情緒:”。請注意,此基本示例不使用上下文,但也可以作為提示的一部分提供。例如,此文本分類提示的上下文可以是作為提示的一部分提供的其他示例,以幫助模型更好地理解任務并引導預期的輸出類型。
注意,提示詞所需的格式取決于您想要語言模型完成的任務類型,并非所有以上要素都是必須的。
2.2 設計提示詞的通用技巧
2.2.1 從簡單開始
在開始設計提示詞時,你應該記住,這實際上是一個迭代過程,需要大量的實驗才能獲得最佳結果。你可以從簡單的提示詞開始,并逐漸添加更多元素和上下文(因為你想要更好的結果)。因此,在這個過程中不斷迭代你的提示詞是至關重要的。
2.2.2 指令
可以使用命令來指示模型執行各種簡單任務,例如“寫入”、“分類”、“總結”、“翻譯”、“排序”等,從而為各種簡單任務設計有效的提示。請記住,你還需要進行大量實驗以找出最有效的方法。以不同的關鍵詞(keywords
),上下文(contexts
)和數據(data
)試驗不同的指令(instruction
),看看什么樣是最適合你特定用例和任務的。通常,上下文越具體和跟任務越相關則效果越好。
有些人建議將指令放在提示的開頭。另有人則建議是使用像“###”這樣的清晰分隔符來分隔指令和上下文。
例如:
提示:
### 指令 ###
將以下文本翻譯成西班牙語:文本:“hello!”輸出:?Hola!
2.2.3 具體性
要非常具體地說明你希望模型執行的指令和任務。提示越具描述性和詳細,結果越好。特別是當你對生成的結果或風格有要求時,這一點尤為重要。不存在什么特定的詞元(tokens)或關鍵詞(tokens)能確定帶來更好的結果。更重要的是要有一個具有良好格式和描述性的提示詞。事實上,在提示中提供示例對于獲得特定格式的期望輸出非常有效。
在設計提示時,還應注意提示的長度,因為提示的長度是有限制的。想一想你需要多么的具體和詳細。包含太多不必要的細節不一定是好的方法。這些細節應該是相關的,并有助于完成手頭的任務。
例如:讓我們嘗試從一段文本中提取特定信息的簡單提示。
提示:
提取以下文本中的地名。所需格式:
地點:<逗號分隔的公司名稱列表>輸入:“雖然這些發展對研究人員來說是令人鼓舞的,但仍有許多謎團。里斯本未知的香帕利莫德中心的神經免疫學家 Henrique Veiga-Fernandes 說:“我們經常在大腦和我們在周圍看到的效果之間有一個黑匣子。”“如果我們想在治療背景下使用它,我們實際上需要了解機制。””輸出:地點:里斯本,香帕利莫德中心
2.2.4 避免不明確
給定上述關于詳細描述和改進格式的建議,很容易陷入陷阱:想要在提示上過于聰明,從而可能創造出不明確的描述。通常來說,具體和直接會更好。這里的類比非常類似于有效溝通——越直接,信息傳達得越有效。
例如,你可能有興趣了解提示工程的概念。你可以嘗試這樣做:
解釋提示工程的概念。保持解釋簡短,只有幾句話,不要過于描述。
從上面的提示中不清楚要使用多少句子以及什么風格。盡管你可能仍會從上述提示中得到較好的響應,但更好的提示應當是非常具體、簡潔并且切中要點的。例如:
使用 2-3 句話向高中學生解釋提示工程的概念。
2.3 Spring AI Prompt 位置圖
在大模型處理過程中,提示詞位于輸入端,對模型響應具有重要決定作用。
三、Prompt 工程技術
3.1 零樣本提示(Zero-shot Prompting)
零樣本提示(Zero-shot Prompting)
是指要求人工智能在不提供任何示例的情況下執行任務。這種方法測試模型從零開始理解和執行指令的能力。大型語言模型在海量文本語料庫上進行訓練,使其能夠在沒有明確演示的情況下理解“翻譯”、“摘要”或“分類”等任務的含義。
零樣本訓練非常適合一些簡單的任務,因為模型在訓練過程中很可能已經見過類似的樣本,而且您希望盡量縮短提示長度。然而,其性能可能會因任務復雜度和指令的制定方式而有所不同。
public void pt_zero_shot(ChatClient chatClient) {enum Sentiment {POSITIVE, NEUTRAL, NEGATIVE}Sentiment reviewSentiment = chatClient.prompt("""將電影評論分類為“正面”、“中性”或“負面”。評論:“《她》是一部發人深省的研究,揭示了如果人工智能不受控制地持續進化,人類將走向何方。真希望有更多這樣的杰作。”情感分類:""").options(ChatOptions.builder().model("claude-3-7-sonnet-latest").temperature(0.1).maxTokens(5).build()).call().entity(Sentiment.class);System.out.println("Output: " + reviewSentiment);
}
3.2 少樣本提示(Few-shot Prompting)
少樣本提示(Few-shot Prompting)
為模型提供了一個或多個示例,以幫助指導其響應,這對于需要特定輸出格式的任務特別有用。通過向模型展示所需輸入-輸出對的示例,它可以學習該模式并將其應用于新的輸入,而無需顯式更新參數。
單樣本訓練僅提供單個樣本,當樣本成本高昂或模式相對簡單時非常有用。少樣本訓練則使用多個樣本(通常為 3-5 個),以幫助模型更好地理解更復雜任務中的模式,或展示正確輸出的不同變體。
public void pt_one_shot_few_shots(ChatClient chatClient) {String pizzaOrder = chatClient.prompt("""解析用戶的披薩訂單并生成有效JSON示例1:我要一個小號披薩,加奶酪、番茄醬和意大利辣香腸。JSON響應:```{"size": "small","type": "normal","ingredients": ["cheese", "tomato sauce", "pepperoni"]}```示例2:請給我一個大號披薩,加番茄醬、羅勒葉和馬蘇里拉奶酪。JSON響應:```{"size": "large","type": "normal","ingredients": ["tomato sauce", "basil", "mozzarella"]}```現在,我要一個大號披薩,前半部分加奶酪和馬蘇里拉,另一半加番茄醬、火腿和菠蘿。""").options(ChatOptions.builder().model("claude-3-7-sonnet-latest").temperature(0.1).maxTokens(250).build()).call().content();
}
對于需要特定格式、處理邊緣情況,或在沒有示例的情況下任務定義可能含糊不清的任務,小樣本提示尤其有效。示例的質量和多樣性會顯著影響性能。
3.3 系統、情境和角色提示
3.3.1 系統提示
系統提示
設定了語言模型的整體背景和目的,定義了模型應該做什么的“總體情況”。它為模型的響應建立了行為框架、約束條件和高級目標,并與具體的用戶查詢區分開來。
系統提示在整個對話過程中充當著持續的“使命”,允許您設置全局參數,例如輸出格式、語氣、道德界限或角色定義。與專注于特定任務的用戶提示不同,系統提示框定了所有用戶提示的解讀方式。
public void pt_system_prompting_1(ChatClient chatClient) {String movieReview = chatClient.prompt().system("將電影評論分類為“正面”(POSITIVE)、“中性”(NEUTRAL)或“負面”(NEGATIVE),且僅返回大寫的分類標簽。").user("""評論:“《她》是一部發人深省的研究,揭示了如果人工智能不受控制地持續進化,人類將走向何方。真希望有更多這樣的杰作。”情感分類:""").options(ChatOptions.builder().model("claude-3-7-sonnet-latest").temperature(1.0).topK(40).topP(0.8).maxTokens(5).build()).call().content();
}
系統提示與 Spring AI 的實體映射功能結合使用時尤其強大:
@Data
public class MovieReviews{private String name;private Sentiment sentimentEnum;}public void pt_system_prompting_1(ChatClient chatClient) {MovieReviews movieReviews = chatClient.prompt().system("將電影評論分類為“正面”(POSITIVE)、“中性”(NEUTRAL)或“負面”(NEGATIVE),返回有效的json數據。").user("""評論:“《她》是一部發人深省的研究,揭示了如果人工智能不受控制地持續進化,人類將走向何方。真希望有更多這樣的杰作。”情感分類:""").options(ChatOptions.builder().model("claude-3-7-sonnet-latest").temperature(1.0).topK(40).topP(0.8).maxTokens(5).build()).call().content(MovieReviews.class);
}
系統提示對于多輪對話尤其有價值,可確保跨多個查詢的一致行為,并建立適用于所有響應的格式約束(如 JSON 輸出)。
3.3.2 角色提示
角色提示
會指示模型采用特定的角色或人物,這會影響其生成內容的方式。通過為模型分配特定的身份、專業知識或視角,您可以影響其響應的風格、語氣、深度和框架。
角色提示利用模型模擬不同專業領域和溝通風格的能力。常見角色包括專家(例如,“您是一位經驗豐富的數據科學家”)、專業人士(例如,“充當導游”)或風格人物(例如,“像莎士比亞一樣解釋”)。
public void pt_role_prompting_1(ChatClient chatClient) {String travelSuggestions = chatClient.prompt().system("""你是一個在java開發領域非常資深的專家,我將請教你一些java編程時需要注意的地方。""").user("""我在用java開發有關于redis和mysql相關的代碼時,需要注意哪些地方?""").call().content();
}
可以通過樣式說明來增強角色提示:
public void pt_role_prompting_1(ChatClient chatClient) {String travelSuggestions = chatClient.prompt().system("""你是一個在java開發領域非常資深的專家,我將請教你一些java編程時需要注意的地方,你在回答我的問題的時候,專業中最好夾雜些幽默。""").user("""我在用java開發有關于redis和mysql相關的代碼時,需要注意哪些地方?""").call().content();
}
這種技術對于專業領域知識特別有效,可以在響應中實現一致的語氣,并與用戶創建更具吸引力、個性化的互動。
3.3.3 情景提示
情境提示通過傳遞情境參數為模型提供額外的背景信息
。這項技術豐富了模型對具體情境的理解,使其能夠提供更相關、更有針對性的響應,而不會擾亂主要指令。
通過提供上下文信息,您可以幫助模型理解與當前查詢相關的特定領域、受眾、約束或背景事實。這可以帶來更準確、更相關、更恰當的響應。
public void pt_contextual_prompting(ChatClient chatClient) {// 調用大模型生成文章主題建議String articleSuggestions = chatClient.prompt()// 定義用戶提示(User Prompt)// 普通Prompt,通用內容,無領域聚焦 .user(u -> u.text("""要求生成3個文章主題及描述Context: {context}""")// 注入上下文參數// 上下文Prompt,專業化、符合特定場景需求 .param("context", "你正在撰寫自己java領域的博客專欄。"))// 執行調用.call()// 獲取模型生成的純文本響應.content();
}
Spring AI 使用 param() 方法注入上下文變量,使上下文提示更加清晰。當模型需要特定領域知識、根據特定受眾或場景調整響應,以及確保響應符合特定約束或要求時,此技術尤為有用。
適用場景:
-
內容創作輔助
:為特定領域的博客/自媒體快速生成選題。 -
上下文感知任務
:需要模型根據特定背景生成回答(如垂直行業、品牌調性等)。 -
動態參數注入
:通過占位符{context}靈活切換上下文(如更換為你正在撰寫自己python領域的博客專欄)。
3.4 后退提示(Step-Back Prompting)
后退提示法(Step-Back Prompting)
通過先獲取背景知識,將復雜的請求分解成更簡單的步驟。這種技術鼓勵模型先從當前問題“后退一步”,思考更廣泛的背景、基本原理或與問題相關的常識,然后再處理具體的問題。
通過將復雜問題分解為更易于管理的部分并首先建立基礎知識,該模型可以對難題提供更準確的答案。
public void javaOptimizationExample(ChatClient.Builder chatClientBuilder) {// 配置模型參數(針對技術性內容調優)ChatClient chatClient = chatClientBuilder.defaultOptions(ChatOptions.builder().model("claude-3-opus") // 使用更高階模型處理技術問題.temperature(0.3) // 低隨機性確保技術準確性.maxTokens(512).build()).build();// 第一步:Step-Back(抽象核心優化維度)String optimizationPrinciples = chatClient.prompt("""作為Java性能優化專家,請列出5個最關鍵的高性能Java代碼優化方向,每個方向需包含:1) 技術術語 2) 適用場景 3) 典型收益""").call().content();// 第二步:應用生成(具體優化方案)String optimizedCode = chatClient.prompt().user(u -> u.text("""請為以下Java方法提供優化方案,要求:1. 使用{principles}中的至少3個優化方向2. 展示優化前后的代碼對比3. 解釋性能提升關鍵點待優化方法:```javapublic List<String> processData(List<Data> input) {List<String> result = new ArrayList<>();for (Data d : input) {String s = expensiveCalculation(d);if (s != null) {result.add(s);}}return result;}```""").param("principles", optimizationPrinciples)).call().content();
}
預期輸出解析
第一步輸出示例(抽象原則)
1. **并行流處理(Parallel Streams)** - 場景:CPU密集型批量數據處理 - 收益:多核利用率提升3-5倍 2. **方法內聯(Method Inlining)** - 場景:高頻調用的小方法 - 收益:減少棧幀開銷約20% 3. **預分配集合大小** - 場景:已知容量的集合初始化 - 收益:避免擴容拷貝耗時 4. **空值模式(Null Object Pattern)** - 場景:頻繁的空值檢查 - 收益:消除分支預測失敗 5. **熱點代碼緩存** - 場景:重復計算的確定性結果 - 收益:時間復雜度從O(n)→O(1)
將第一階段輸出的抽象概念作為上下文注入,引導模型生成專業優化代碼。例如:
// 優化后代碼(應用了并行流、預分配、空值模式)
public List<String> processData(List<Data> input) {// 預分配大小(基于輸入規模預測)int estimatedSize = Math.min(input.size(), 1000); return input.parallelStream() // 并行流.map(d -> cachedCalculation(d)) // 緩存熱點.filter(Objects::nonNull) // 空值過濾.collect(Collectors.toCollection(() -> new ArrayList<>(estimatedSize)));
}// 使用緩存裝飾器(偽代碼)
private String cachedCalculation(Data d) {return Cache.getOrCompute(d, this::expensiveCalculation);
}
退后提示對于復雜的推理任務、需要專業領域知識的問題以及當您想要更全面、更周到的回應而不是立即得到答案時特別有效。Step-Back分步生成的專業性強,符合領域規范;通過中間概念能夠精確控制方向;可以審查中間概念確保合理性,總的來說,就是輸出質量、可控性以及可解釋性更加好。
Step-Back Prompt模板:
1. 先問"作為[領域]專家,列出影響[目標]的N個關鍵因素"
2. 再問"基于{factors},請具體解決[問題]"
3.5 鏈式思考提示(Chain-of-Thought Prompting)
思路鏈(Chain-of-Thought Prompting, CoT)提示
鼓勵模型逐步推理問題,從而提高復雜推理任務的準確性。通過明確要求模型展示其工作成果或以邏輯步驟思考問題,您可以顯著提高需要多步驟推理的任務的性能。
CoT 的工作原理是鼓勵模型在得出最終答案之前生成中間推理步驟,類似于人類解決復雜問題的方式。這使得模型的思維過程更加清晰,并有助于其得出更準確的結論。
// 零樣本思維鏈(Zero-shot CoT)
// 通過魔法短語"Let's think step by step"激活模型的逐步推理能力
//模型內部行為:
// 1.計算初始年齡差:3歲時 partner = 3×3 = 9歲 → 年齡差 = 9-3 = 6歲
// 2.當前年齡推算:20歲時 partner = 20 + 6 = 26歲
public void pt_chain_of_thought_zero_shot(ChatClient chatClient) {String output = chatClient.prompt("""當我3歲的時候,我朋友年齡是我的3倍大。現在,我20歲了,請問我朋友年齡多大? 請讓我們一步一步思考并解決這個問題。""").call().content();
}// 單樣本少樣本思維鏈(Few-shot CoT)
// 提供完整推理模板(示例中展示如何計算年齡差并應用)
public void pt_chain_of_thought_singleshot_fewshots(ChatClient chatClient) {String output = chatClient.prompt("""Q: 當我的朋友2歲的時候,我的年齡是他的2倍。現在我40歲了,請問我的朋友年齡多大?請一步一步思考并解決這個問題。A: 當我的朋友2歲的時候,2×2=4,那么那個時候我是4歲,我比他年齡大2歲。現在我40歲, 40-2=38,因此答案是38歲,我的朋友現在38歲。Q: 當我3歲的時候,我朋友年齡是我的3倍大。現在,我20歲了,請問我朋友年齡多大? 請讓我們一步一步思考并解決這個問題。A:""").call().content();
}
兩種方法最終都應得到正確結果(26歲),但技術路徑不同:
對于初始年齡計算,Zero-shot CoT
依賴模型自主理解"3 times",Few-shot CoT
嚴格遵循示例中的乘法模式;對于年齡差處理,Zero-shot CoT
隱性推導差值,Few-shot CoT
顯式復制示例的差值計算邏輯;對于錯誤魯棒性,Zero-shot CoT
可能跳過步驟導致錯誤,而Few-shot CoT
強制分步降低錯誤率。
關鍵詞“讓我們一步一步思考”會觸發模型展示其推理過程。CoT 對于數學問題、邏輯推理任務以及任何需要多步推理的問題尤其有用。它通過明確中間推理來幫助減少錯誤。
選擇策略:
- 簡單問題 → Zero-shot CoT(更簡潔)
- 復雜問題 → Few-shot CoT(更可靠)
3.6 自洽性(Self-Consistency)
自洽性(Self-Consistency)
是指多次運行模型并匯總結果以獲得更可靠的答案。該技術通過對同一問題進行不同的推理路徑采樣,并通過多數表決選出最一致的答案,解決了 LLM 輸出結果的差異性問題。
通過生成具有不同溫度或采樣設置的多條推理路徑,然后聚合最終答案,自洽性可以提高復雜推理任務的準確性。它本質上是一種針對 LLM 輸出的集成方法。
public void pt_self_consistency(ChatClient.Builder chatClientBuilder) {// 配置模型(技術類問題建議低隨機性)var chatClient = chatClientBuilder.defaultOptions(ChatOptions.builder().model("claude-3-opus").temperature(0.7) // 適度隨機性.maxTokens(512).build()).build();// 待優化代碼String inputCode = """public String buildSQL(List<Filter> filters) {String sql = "SELECT * FROM data WHERE ";for (Filter f : filters) {sql += f.getField() + " = '" + f.getValue() + "' AND ";}return sql.substring(0, sql.length() - 5);}""";// 自洽性驗證:生成5種優化方案record OptimizationSuggestion(String approach, String code, String reasoning) {}List<OptimizationSuggestion> suggestions = new ArrayList<>();for (int i = 0; i < 5; i++) {OptimizationSuggestion suggestion = chatClient.prompt().user(u -> u.text("""分析以下Java字符串拼接的性能問題,并提供最高效的優化方案要求:1.解釋所選的優化方法2.展示優化后的代碼3.避免非線程安全的解決方案待優化代碼:{code}""").param("code", inputCode)).call().entity(OptimizationSuggestion.class);suggestions.add(suggestion);}// 多數投票選擇最佳方案Map<String, Integer> approachVotes = new HashMap<>();suggestions.forEach(s -> approachVotes.merge(s.approach(), 1, Integer::sum));String bestApproach = approachVotes.entrySet().stream().max(Map.Entry.comparingByValue()).get().getKey();// 輸出最終推薦方案System.out.println("【最佳方案】" + bestApproach);suggestions.stream().filter(s -> s.approach().equals(bestApproach)).findFirst().ifPresent(best -> {System.out.println("優化代碼:\n" + best.code());System.out.println("理論依據:" + best.reasoning());});
}
模型生成的5種方案
方案 | 投票數 | 特點 |
---|---|---|
StringBuilder | 3 | 線程安全,O(n)時間復雜度 |
StringJoiner | 1 | 更語義化但性能稍低 |
String.format() | 1 | 可讀性好但性能最差 |
最終選擇的結果
【最佳方案】StringBuilder
優化代碼:
public String buildSQL(List<Filter> filters) {StringBuilder sql = new StringBuilder("SELECT * FROM data WHERE ");for (Filter f : filters) {sql.append(f.getField()).append(" = '").append(f.getValue()).append("' AND ");}return sql.substring(0, sql.length() - 5);
}
理論依據:
1. StringBuilder比字符串"+"拼接減少90%的對象創建開銷
2. 預分配緩沖區避免多次擴容(capacity=初始SQL長度+平均filter長度×數量)
3. 線程安全(方法內局部變量)
技術優勢對比
方法 | 單次生成風險 | 自洽性技術優勢 |
---|---|---|
準確性 | 可能推薦StringJoiner等次優方案 | 排除偶然性錯誤(如線程不安全建議) |
性能認知 | 只考慮單一優化維度 | 綜合評估時間復雜度、內存開銷等 |
可解釋性 | 單一理由 | 多角度論證(如3票都提到O(n)優化) |
對于高風險決策、復雜推理任務以及需要比單一響應更可靠答案的情況,自洽性尤為重要。但其弊端是由于多次 API 調用會增加計算成本和延遲。
Java開發中的典型應用場景:
-
代碼審查:對AI生成的修復建議進行多次驗證
-
API設計:比較多種設計模式的推薦頻率
-
并發編程:檢測線程安全建議的一致性
-
性能調優:如連接池配置、緩存策略選擇
3.7 思維樹(ToT)
思路樹 (ToT)
是一種高級推理框架,它通過同時探索多條推理路徑來擴展思路鏈。它將問題解決視為一個搜索過程,模型會生成不同的中間步驟,評估其可行性,并探索最具潛力的路徑。
這種技術對于具有多種可能方法的復雜問題或解決方案需要探索各種替代方案才能找到最佳路徑的情況特別有效。
public void pt_tree_of_thoughts_game(ChatClient chatClient) {// 待優化場景描述String requirements = """需求特征:1. 需要存儲10萬條用戶交易記錄2. 高頻操作:按ID查詢(>1000次/秒)、批量插入(每5分鐘1萬條)3. 線程安全要求4. 內存占用需<500MB""";// 第一階段:生成候選方案(溫度=0.7激發多樣性)String candidates = chatClient.prompt("""根據以下需求,生成3種Java集合類型的選擇方案:{requirements}每個方案需包含:1. 集合類型全類名2. 適用性分析3. 性能評分(1-10)""").param("requirements", requirements).options(ChatOptions.builder().temperature(0.7).build()).call().content();/* 示例輸出:1. java.util.concurrent.ConcurrentHashMap // 側重查詢- 優點:線程安全,查詢O(1)- 缺點:批量插入需加鎖- 評分:82. java.util.Collections.synchronizedList(new ArrayList<>()) // 側重讀多寫少- 優點:內存緊湊- 缺點:查詢O(n)- 評分:53. com.google.common.cache.CacheBuilder.newBuilder().build() // 緩存方案- 優點:自動LRU淘汰- 缺點:GC壓力大- 評分:6*/// 第二階段:評估候選方案String bestChoice = chatClient.prompt("""評估以下集合方案的適用性:{candidates}根據以下維度加權評分:1. 查詢性能(權重40%)2. 插入性能(權重30%)3. 內存效率(權重20%)4. 線程安全(權重10%)返回綜合得分最高的方案及詳細分析。""").param("candidates", candidates).call().content();// 第三階段:推演使用場景String usageScenario = chatClient.prompt("""基于選定的{bestChoice},推演以下場景:1. 10萬數據初始化時的內存占用2. 高并發查詢時的線程競爭處理3. 批量插入時的優化策略給出具體代碼示例和監控指標建議。""").param("bestChoice", bestChoice).call().content();
}
技術優勢對比
方法 | 單路徑推理 | 思維樹(ToT) |
---|---|---|
決策質量 | 易陷局部最優 | 全局最優概率提升3-5倍 |
可解釋性 | 單一解釋路徑 | 完整決策樹可視化 |
容錯性 | 單點失敗導致全錯 | 多路徑冗余保障 |
典型應用場景:
-
金融投資:
- 生成多種投資組合 → 評估風險收益 → 推演市場反應
-
醫療診斷:
- 列出疑似病癥 → 分析檢查指標 → 模擬治療方案效果
-
供應鏈優化:
- 提出運輸方案 → 評估成本時效 → 預測突發事件影響
其他Java開發適用場景:
-
API設計決策
-
生成:REST vs GraphQL
-
評估:開發效率/性能/可維護性
-
推演:長期擴展成本
-
-
并發模式選擇
-
生成:ThreadPool vs ForkJoin
-
評估:任務類型匹配度
-
推演:資源爭用模擬
-
-
架構選型
-
生成:微服務 vs 單體
-
評估:團隊能力/業務需求
-
推演:運維復雜度分析
-
自我一致性提示和思維樹提示的差異:
自我一致性提示(Self-Consistency)和思維樹提示(Tree of Thoughts, ToT)雖然都涉及多路徑推理,但它們在技術目標、實現方式和適用場景上存在本質差異,下面表格是總結出的差異對比:
維度 | 自我一致性提示 | 思維樹提示 |
---|---|---|
主要目標 | 通過多次采樣降低隨機性誤差 | 通過結構化搜索找到最優決策路徑 |
輸出要求 | 選擇高頻出現的答案(投票機制) | 構建可解釋的推理樹并選擇全局最優解 |
問題類型 | 封閉式問題(如分類、計算) | 開放式復雜決策(如策略規劃、多步推理) |
3.8 自動提示工程(Automatic Prompt Engineering)
自動提示工程(Automatic Prompt Engineering)
利用人工智能生成并評估備選提示。這項元技術利用語言模型本身來創建、改進和基準測試不同的提示變體,以找到特定任務的最佳方案。通過系統地生成和評估提示語的變化,APE 可以找到比人工設計更有效的提示語,尤其是在處理復雜任務時。這是利用 AI 提升自身性能的一種方式。
public void pt_automatic_prompt_engineering(ChatClient chatClient) {// 生成相同請求的變體String orderVariants = chatClient.prompt("""我們運營一個樂隊周邊T恤的電商網站,為了訓練客服聊天機器人,需要針對訂單語句"一件Metallica樂隊S碼T恤" 生成10種語義相同但表達不同的變體。""").options(ChatOptions.builder().temperature(1.0) // 高溫激發創造力.build()).call().content();// 利用高溫參數(temperature=1.0)使模型突破常規表達模式,生成如// 1. "我要一件S號的Metallica樂隊T恤" // 2. "Metallica印花T恤S碼來一件" // 3. "下單:金屬樂隊黑色T恤,尺寸小號(S)" ...// 評估并選擇最佳變體String output = chatClient.prompt().user(u -> u.text("""請對以下文本變體進行BLEU(雙語評估替代指標)質量評估:----{variants}----選擇評估分數最高的指令候選方案""").param("variants", orderVariants)).call().content();// 選擇得分最高且符合商業要求的表達,如:"請給我發一件Metallica的S號T恤"// 約束生成范圍// 記錄用戶實際query補充變體庫
}
APE 對于優化生產系統的提示、解決手動提示工程已達到極限的挑戰性任務以及系統地大規模提高提示質量特別有價值。
技術優勢對比
方法 | 人工設計提示詞 | 自動提示工程 |
---|---|---|
覆蓋度 | 依賴個人經驗,有限 | 系統性生成數十種變體 |
響應率 | 可能遺漏用戶表達方式 | 提高 chatbot 理解率30%+* |
迭代效率 | 每次修改需重新設計 | 自動批量生成評估 |
典型問題場景
用戶可能這樣下單:
-
“Metallica的S碼來一件”
-
“黑色金屬樂隊T恤小號”
-
“我要買那個M開頭的樂隊T恤S”
通過自動化生成的提示詞變體:
-
訓練數據增強
:提升NLU模型識別準確率 -
應答模板優化
:客服機器人響應更自然 -
搜索Query擴展
:提高商品搜索命中率
通過模型自動生成同義提示詞訓練模型,來提高模型的泛化能力;生成的變體直接作為搜索Query同義詞庫,使得搜索命中率提升;客服機器人能理解并引導非標準表達,增強對話式購物體驗。據不完全統計,因為自動提示工程在這些場景的應用,行業應用實例Amazon客服系統通過此技術將"尺寸困惑"導致的退貨率降低19%,SHEIN跨境電商支持中英混雜查詢(如"哥特風L碼 dress")使歐美轉化率提升34%。
3.9 代碼提示(Code Generation Prompting)
代碼提示是指針對代碼相關任務的專門技術。
這些技術利用法學碩士 (LLM) 理解和生成編程語言的能力,使他們能夠編寫新代碼、解釋現有代碼、調試問題以及在語言之間進行轉換。
有效的代碼提示通常包含清晰的規范、合適的上下文(庫、框架、代碼規范),有時還會包含類似代碼的示例。為了獲得更確定的輸出,溫度設置通常較低(0.1-0.3)
。
public void pt_code_prompting_writing_code(ChatClient chatClient) {String springBootCode = chatClient.prompt("""為一個Spring Boot應用生成完整的REST控制器代碼,要求:1. 實現GET /api/users/{id} 端點2. 使用JPA從數據庫查詢User實體3. 包含異常處理(404未找到)4. 返回標準JSON響應格式""").options(ChatOptions.builder().temperature(0.1) // 低隨機性確保語法正確.call().content();// 生成結果示例:/*@RestController@RequestMapping("/api/users")public class UserController {@Autowiredprivate UserRepository userRepository;@GetMapping("/{id}")public ResponseEntity<?> getUser(@PathVariable Long id) {return userRepository.findById(id).map(user -> ResponseEntity.ok(user)).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"));}}*/
}public void pt_code_prompting_explaining_code(ChatClient chatClient) {String springBootCode = """@RestController@RequestMapping("/api/users")public class UserController {@Autowiredprivate UserRepository userRepository;@GetMapping("/{id}")public ResponseEntity<?> getUser(@PathVariable Long id) {return userRepository.findById(id).map(user -> ResponseEntity.ok(user)).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"));}}"""String explanation = chatClient.prompt("""解釋以下Spring Boot控制器的設計要點和技術細節:```java{code}```""").param("code", springBootCode).call().content();// 輸出示例:/*1. 注解驅動:- @RestController 組合了@Controller和@ResponseBody- @RequestMapping 定義基礎路徑2. JPA集成:- 自動注入UserRepository實現數據庫操作3. 響應處理:- 使用Optional優雅處理空值- 通過ResponseStatusException返回404狀態碼4. RESTful規范:- 符合HTTP語義(GET方法+路徑變量)*/
}public void pt_code_prompting_translating_code(ChatClient chatClient) {String springBootCode = """@RestController@RequestMapping("/api/users")public class UserController {@Autowiredprivate UserRepository userRepository;@GetMapping("/{id}")public ResponseEntity<?> getUser(@PathVariable Long id) {return userRepository.findById(id).map(user -> ResponseEntity.ok(user)).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"));}}"""String kotlinCode = chatClient.prompt("""將以下Java Spring Boot代碼轉換為Kotlin版本:{code}""").param("code", springBootCode).options(ChatOptions.builder().temperature(0.2) // 適度創造性處理語法差異.call().content();// 輸出示例:/*@RestController@RequestMapping("/api/users")class UserController @Autowired constructor(private val userRepository: UserRepository) {@GetMapping("/{id}")fun getUser(@PathVariable id: Long): ResponseEntity<User> =userRepository.findById(id).map { ResponseEntity.ok(it) }.orElseThrow {ResponseStatusException(HttpStatus.NOT_FOUND, "User not found")}}*/
}
這些技術已在Google的Project IDX等工具中實際應用,可提升Java開發效率40%以上。代碼提示對于自動化代碼文檔、原型設計、學習編程概念以及編程語言間的轉換尤其有用。將其與少樣本提示或思路鏈等技術結合使用,可以進一步提升其有效性。
參數設計對比
場景 | Temperature | 輸出要求 |
---|---|---|
代碼生成 | 0.1-0.3 | 語法準確性 > 創造性 |
代碼解釋 | 0.5-0.7 | 可讀性 > 技術術語精確性 |
代碼轉譯 | 0.2-0.4 | 語言特性適配 > 字面直譯 |
應用場景:
-
快速生成部署腳本/自動化工具
-
減少樣板代碼編寫時間
-
新人快速理解項目架構
-
技術文檔自動生成
-
多語言項目遷移
-
開發者快速學習新語言語法
四、Prompt API概述
Prompt 最開始只是簡單的字符串,隨著時間的推移,prompt 逐漸開始包含特定的占位符,例如 AI 模型可以識別的 “USER:”、“SYSTEM:” 等。阿里云通義模型可通過將多個消息字符串分類為不同的角色,然后再由 AI 模型處理,為 prompt 引入了更多結構。每條消息都分配有特定的角色,這些角色對消息進行分類,明確 AI 模型提示的每個部分的上下文和目的。這種結構化方法增強了與 AI 溝通的細微差別和有效性,因為 prompt 的每個部分在交互中都扮演著獨特且明確的角色。
Prompt 中的主要角色(Role)包括:
系統角色(System Role)
:指導 AI 的行為和響應方式,設置 AI 如何解釋和回復輸入的參數或規則。這類似于在發起對話之前向 AI 提供說明。用戶角色(User Role)
:代表用戶的輸入 - 他們向 AI 提出的問題、命令或陳述。這個角色至關重要,因為它構成了 AI 響應的基礎。助手角色(Assistant Role)
:AI 對用戶輸入的響應。這不僅僅是一個答案或反應,它對于保持對話的流暢性至關重要。通過跟蹤 AI 之前-的響應(其“助手角色”消息),系統可確保連貫且上下文相關的交互。助手消息也可能包含功能工具調用請求信息。它就像 AI 中的一個特殊功能,在需要執行特定功能(例如計算、獲取數據或不僅僅是說話)時使用。工具/功能角色(Tool/Function Role)
:工具/功能角色專注于響應工具調用助手消息返回附加信息。
4.1 Message
我們先來看Message,Message
接口封裝了一個提示文本
、一組元數據屬性
以及一個稱為 MessageType
的分類。
public interface Message extends Content {MessageType getMessageType();
}public interface Content {String getText();Map<String, Object> getMetadata();
}
Message 接口的各種實現對應 AI 模型可以處理的不同類別的消息。模型根據對話角色
區分消息類別。
與上面提到的提示詞中重要角色相對應,每個Message都被分配一個特定的角色,也就是每個Message有不同的實現類,定義了不同的類型的消息,AbstractMessage實現類中定義了四種角色類型,Message 接口的各種實現對應于 AI 模型可以處理的不同類別的消息。
4.2 Prompt
Prompt 類充當有組織的一系列 Message 對象和請求 ChatOptions 的容器。
每條消息在提示中都體現了獨特的角色,其內容和意圖各不相同。這些角色可以包含各種元素,從用戶查詢到 AI 生成的響應再到相關背景信息。這種安排可以實現與 AI 模型的復雜而詳細的交互,因為提示是由多條消息構成的,每條消息都被分配了在對話中扮演的特定角色。
下面是 Prompt 類的截斷版本,還是很容易能夠看清楚,Prompt 是Message 的一層封裝:
Prompt 類除了封裝了Message 對象之外,還封裝了ChatOptions ,ChatOptions 留在后續文章中專門講解。
4.3 Prompt Template
Spring AI 中用于提示模板的關鍵組件是 PromptTemplate
類,其旨在促進結構化提示的創建,然后將其發送到 AI 模型進行處理。
PromptTemplate 類使用 TemplateRenderer API
來渲染模板,默認情況下,Spring AI 使用 StTemplateRenderer
作為模板渲染器,它是基于 Terence Parr 開發的開源 StringTemplate
引擎。它默認使用花括號{}作為占位符的分隔符,除此,在Spring AI Alibaba的M8版本中PromptChatMemoryAdvisor等組件會對提示模板進行二次渲染,可能導致包含特殊字符(如JSON中的花括號)的內容拋出IllegalArgumentException異常。
Spring AI 使用 TemplateRenderer 接口來處理將變量替換到模板字符串中。 默認實現使用StringTemplate
。 如果需要自定義邏輯,可以提供自己的 TemplateRenderer 實現。 對于不需要模板渲染的場景(例如,模板字符串已經完整),可以使用提供的 NoOpTemplateRenderer
。
PromptTemplate 類實現的接口支持創建不同方面的提示詞:
PromptTemplateStringActions
專注于創建和呈現提示字符串,代表提示生成的最基本形式。PromptTemplateMessageActions
專門用于通過生成和操作 Message 對象來創建提示。PromptTemplateActions
旨在返回 Prompt 對象,該對象可以傳遞給 ChatModel 以生成響應。
String render()
:將提示模板渲染為最終字符串格式,無需外部輸入,適用于沒有占位符或動態內容的模板。String render(Map<String, Object> model)
:增強渲染功能以包含動態內容。它使用 Map<String, Object>,其中映射鍵是提示模板中的占位符名稱,值是要插入的動態內容。
-
Prompt create()
:生成不帶外部數據輸入的 Prompt 對象,非常適合靜態或預定義提示。 -
Prompt create(ChatOptions modelOptions)
:生成一個 Prompt 對象,無需外部數據輸入,但具有聊天請求的特定選項。 -
Prompt create(Map<String, Object> model)
:擴展提示創建功能以包含動態內容,采用 Map<String, Object>,其中每個映射條目都是提示模板中的占位符及其關聯的動態值。 -
Prompt create(Map<String, Object> model, ChatOptions modelOptions)
:擴展提示創建功能以包含動態內容,采用 Map<String, Object>,其中每個映射條目都是提示模板中的占位符及其關聯的動態值,以及聊天請求的特定選項。
-
Message createMessage()
:創建一個不帶附加數據的 Message 對象,用于靜態或預定義消息內容。 -
Message createMessage(List mediaList)
:創建一個帶有靜態文本和媒體內容的 Message 對象。 -
Message createMessage(Map<String, Object> model)
:擴展消息創建以集成動態內容,接受 Map<String, Object>,其中每個條目代表消息模板中的占位符及其對應的動態值。
五、Prompt Template使用示例
下面是用PromptTemplate來創建提示詞的簡單示例
@RestController
public class ChatController {private final ChatClient chatClient;@Resourceprivate ChatClient deepSeekChatClient;public ChatController(ChatClient.Builder builder) {this.chatClient = builder.build();}@GetMapping("/testPrompt/chat")public ChatResponse testPrompt(@RequestParam(value = "animeName") String animeName,@RequestParam(value = "style") String style) {PromptTemplate promptTemplate = new PromptTemplate("請以{style}的方式給我介紹動漫{animeName}的主要劇情");Prompt prompt = promptTemplate.create(Map.of("style", style, "animeName", animeName));return this.deepSeekChatClient.prompt(prompt).call().chatResponse();}
}
測試結果
下面是結合系統消息和用戶消息之后的混合提示詞演示:
@GetMapping("/testPrompt1/chat")
public ChatResponse testPrompt1(@RequestParam(value = "name") String name,@RequestParam(value = "style") String style) {String userText = """靈籠2動漫的主要故事情節是什么?""";Message userMessage = new UserMessage(userText);String systemText = """你是一個幫助動漫愛好者快速了解動漫劇情的 AI 助手。你的名字是 {name}你應該用你的名字回復用戶的請求,并且用 {style} 的風格。""";SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemText);Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "style", style));Prompt prompt = new Prompt(List.of(userMessage, systemMessage));return this.deepSeekChatClient.prompt(prompt).call().chatResponse();
}
測試結果
5.1 使用自定義模板渲染器
在開發中,我們可以通過實現TemplateRenderer
接口并將其傳遞給PromptTemplate
構造函數。我們也可以繼續使用默認的StTemplateRenderer
,但具有自定義配置。
默認情況下,模板變量由{}
語法標識。如果計劃在提示詞中包含 JSON,可能希望使用不同的語法以避免與 JSON 語法沖突。例如,可以使用 < 和 > 分隔符
。
@GetMapping("/testPrompt2/chat")
public ChatResponse testPrompt2(@RequestParam("actor") String actor) {PromptTemplate promptTemplate = PromptTemplate.builder().renderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build()).template("""告訴我 5 部由 <actor> 主演的電視劇名稱。""").build();String prompt = promptTemplate.render(Map.of("actor", actor));return this.deepSeekChatClient.prompt(prompt).call().chatResponse();
}
5.2 使用資源而不是字符串
Spring AI 支持 org.springframework.core.io.Resource
抽象,因此可以將提示詞數據放在文件中,可以直接在 SystemPromptTemplate
中使用文件。 例如,可以在 Spring 管理的組件中用@Value()
注解來檢索 Resource
。
首先在resource下定義一個system-message.st
文件,用于存放系統提示詞
然后直接將該資源傳遞給 SystemPromptTemplate
。
@Value("classpath:/prompts/system-message.st")
private org.springframework.core.io.Resource systemResource;@GetMapping("/testPrompt3/chat")
public ChatResponse testPrompt3(@RequestParam("actor") String actor) {SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "style", style));Prompt prompt = new Prompt(systemMessage);return this.deepSeekChatClient.prompt(prompt).call().chatResponse();
}
測試結果
六、總結
本文最初是介紹了提示詞工程基本概念以及構建提示詞的工程技術,然后介紹了Spring AI 提供的優雅的 Java API。通過將這些技術與 Spring 強大的實體映射和流暢的 API 相結合,開發人員可以使用簡潔、可維護的代碼構建復雜的 AI 應用。最有效的方法通常需要結合多種技術——例如,將系統提示與少量樣本示例結合使用,或將思路鏈與角色提示結合使用。希望本文對您能有所幫助,下文將介紹更多關于Spring AI的技術。
七、參考資料
- Spring AI 官方文檔
- Prompt Engineering Guide