一、Promt提示詞
Promt提示是引導 AI 模型生成特定輸出的輸入,?提示的設計和措辭會顯著影響模型的響應。
在 Spring AI 中與 AI 模型交互的最低層級,處理提示有點類似于在 Spring MVC 中管理”視圖”。 這涉及創建帶有動態內容占位符的大段文本。 這些占位符隨后會根據用戶請求或應用程序中的其他代碼進行替換。 另一個類比是包含某些表達式占位符的 SQL 語句。
隨著 Spring AI 的發展,它將引入更高級別的與 AI 模型交互的抽象。 本節描述的基礎類在其角色和功能上可以類比為 JDBC。 例如,ChatModel
?類類似于 JDK 中的核心 JDBC 庫。?ChatClient
?類類似于?JdbcClient
,它構建在?ChatModel
?之上,并通過?Advisor
?提供更高級的構造,能夠考慮與模型的歷史交互、用額外的上下文文檔增強提示,并引入代理行為。
提示的結構在 AI 領域內隨著時間的推移而演變。 最初,提示只是簡單的字符串。 隨著時間的推移,它們發展為包含特定輸入占位符,如”USER:“,AI 模型能夠識別。 OpenAI 通過在處理前將多條消息字符串分類為不同角色,為提示引入了更多結構。
prompt()這個無參數方法讓您開始使用流暢 API,允許您構建用戶、系統和其他提示部分。prompt(Prompt prompt)這個方法接受 Prompt 參數,讓您傳入使用 Prompt 的非流暢 API 創建的 Prompt 實例。prompt(String content)這是一個類似于前一個重載的便捷方法。它接受用戶的文本內容。
二、Prompt? API 概述
2.1 、Prompt
通常會使用?ChatModel
?的?call()
?方法,該方法接受一個?Prompt
?實例并返回一個?ChatResponse
。
Prompt
?類作為有序?Message
?對象序列和請求?ChatOptions
?的容器。 每個?Message
?在提示中都具有獨特的角色,內容和意圖各不相同。 這些角色可以包含多種元素,從用戶提問到 AI 生成的響應,再到相關的背景信息。 這種安排使與 AI 模型的交互變得復雜且詳細,因為提示由多個消息構建,每個消息在對話中扮演特定角色。
下面是 Prompt 類的簡化版本,省略了構造函數和工具方法:
public class Prompt implements ModelRequest<List<Message>> {private final List<Message> messages;private ChatOptions chatOptions;
}
2.2、Message
Message
?接口封裝了提示的文本內容、元數據屬性集合和稱為?MessageType
?的分類。
public interface Content {String getContent();Map<String, Object> getMetadata();
}public interface Message extends Content {MessageType getMessageType();
}
多模態消息類型還實現了?MediaContent
?接口,提供?Media
?內容對象列表。
public interface MediaContent extends Content {Collection<Media> getMedia();}
Message
?接口有多種實現,對應于 AI 模型可以處理的不同類別的消息。 模型根據對話角色區分消息類別。
2.3 、?MessageType
?消息角色
這些角色由?MessageType
?有效映射。每條消息都被分配了一個特定角色。 這些角色對消息進行分類,為 AI 模型澄清每個提示片段的上下文和目的。 這種結構化方法增強了與 AI 的交流的細致性和有效性,因為提示的每一部分在交互中都扮演著獨特且明確定義的角色。
系統角色
指導 AI 的行為和響應風格,設置參數或規則,規定 AI 如何解釋和回復輸入。類似于在開始對話前為 AI 提供指令。
用戶角色
代表用戶的輸入——他們對 AI 的問題、命令或陳述。這個角色是基礎,因為它構成了 AI 響應的依據。
助手角色
AI 對用戶輸入的響應。不僅僅是答案或反應,對于維持對話的流暢性至關重要。 通過跟蹤 AI 先前的響應(其”助手角色”消息),系統確保交互的連貫性和上下文相關性。 助手消息還可能包含函數工具調用請求信息。 這就像 AI 的一個特殊功能,在需要時用于執行特定功能,如計算、獲取數據或其他超出對話的任務。
工具/函數角色
工具/函數角色專注于響應工具調用助手消息時返回額外信息。
角色在 Spring AI 中以枚舉方式表示
public enum MessageType {USER("user"),ASSISTANT("assistant"),SYSTEM("system"),TOOL("tool");...
}
2.4、PromptTemplate
Spring AI 中提示模板的關鍵組件是?PromptTemplate
?類,旨在便于創建結構化提示,然后將其發送給 AI 模型進行處理。
public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {// 其他方法后續討論
}
該類使用?TemplateRenderer
?API 渲染模板。默認情況下,Spring AI 使用基于 Terence Parr 開發的開源?StringTemplate?引擎的?StTemplateRenderer
?實現。模板變量由?{}
?語法標識,但您也可以配置分隔符以使用其他語法。
public interface TemplateRenderer extends BiFunction<String, Map<String, Object>, String> {@OverrideString apply(String template, Map<String, Object> variables);}
Spring AI 使用?TemplateRenderer
?接口處理變量到模板字符串的實際替換。 默認實現使用 StringTemplate。 如果需要自定義邏輯,您可以提供自己的?TemplateRenderer
?實現。 對于不需要模板渲染的場景(例如模板字符串已完整),可以使用提供的?NoOpTemplateRenderer
。
PromptTemplate promptTemplate = PromptTemplate.builder().renderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build()).template("""告訴我 5 部由 <composer> 作曲的電影名稱。""").build();String prompt = promptTemplate.render(Map.of("composer", "John Williams"));
該類實現的接口支持提示創建的不同方面:
-
PromptTemplateStringActions
?專注于創建和渲染提示字符串,代表最基本的提示生成形式。 -
PromptTemplateMessageActions
?針對通過生成和操作?Message
?對象進行提示創建。 -
PromptTemplateActions
?旨在返回?Prompt
?對象,可傳遞給?ChatModel
?以生成響應。
雖然這些接口在許多項目中可能不會被廣泛使用,但它們展示了提示創建的不同方法。
實現的接口如下:
public interface PromptTemplateStringActions {String render();String render(Map<String, Object> model);}
PromptTemplateStringActions 方法
public interface PromptTemplateMessageActions {Message createMessage();Message createMessage(List<Media> mediaList);Message createMessage(Map<String, Object> model);}
PromptTemplateMessageActions 方法
public interface PromptTemplateActions extends PromptTemplateStringActions {Prompt create();Prompt create(ChatOptions modelOptions);Prompt create(Map<String, Object> model);Prompt create(Map<String, Object> model, ChatOptions modelOptions);}
PromptTemplateActions 方法
Prompt create():生成不帶外部數據輸入的 Prompt 對象,適用于靜態或預定義的提示。
Prompt create(ChatOptions modelOptions):生成不帶外部數據輸入且帶有特定聊天請求選項的 Prompt 對象。
Prompt create(Map<String, Object> model):擴展提示創建能力以包含動態內容,接受 Map<String, Object>,每個 map 條目是提示模板中的占位符及其關聯的動態值。
Prompt create(Map<String, Object> model, ChatOptions modelOptions):擴展提示創建能力以包含動態內容,接受 Map<String, Object>,每個 map 條目是提示模板中的占位符及其關聯的動態值,并帶有特定的聊天請求選項。
2.5、示例用法
PromptTemplate promptTemplate = new PromptTemplate("Tell me a {adjective} joke about {topic}");Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic));return chatModel.call(prompt).getResult();
三、構建提示(Prompt)的三種模式
提示是引導模型輸出的關鍵,ChatClient支持三種構建方式,滿足不同復雜度需求:
3.1、逐層構建(復雜場景)
顯式添加系統消息、用戶消息,支持動態參數替換:
String response = chatClient.prompt() ?.system(s -> s.text("以{style}風格回答").param("style", "古風")) ?// 系統消息含占位符 ?.user("解釋什么是人工智能") ?// 用戶消息 ?.option(OpenAiChatOptions.builder().temperature(0.8).build()) ?// 模型參數(如OpenAI特有參數) ?.call() ?.content(); ?
優勢:細粒度控制消息結構,支持多輪對話歷史拼接。
3.2、預構建Prompt對象(批量處理)
適用于提示模板化場景,提前組裝消息列表:
// 定義提示模板 ?
Prompt promptTemplate = Prompt.builder() ?.systemMessage("你是一個幽默的助手") ?.userMessage("講一個{topic}相關的笑話") ?.build(); ?// 運行時填充參數 ?
Prompt dynamicPrompt = promptTemplate.replaceParams(Map.of("topic", "程序員")); ?
ChatResponse response = chatClient.prompt(dynamicPrompt).call().chatResponse(); ?
最佳實踐:將常用提示模板存入數據庫或配置中心,實現動態加載。
3.3、快捷方式(極簡場景)
單行代碼完成用戶消息提交,適用于簡單問答:
String answer = chatClient.prompt("如何煮咖啡").call().content(); ?
注意:此模式默認無系統消息,模型行為依賴其基礎訓練數據。
四、ChatClient響應處理:從文本到結構化數據
4.1、 獲取完整元數據(性能監控和Token消耗計算)
通過chatResponse()獲取包含令牌消耗、生成結果列表的完整響應:
ChatResponse response = chatClient.prompt() ?.user("計算1+1等于多少") ?.call() ?.chatResponse(); ?System.out.println("總令牌數:" + response.getMetadata().getUsage().getTotalTokens()); ?
System.out.println("生成結果:" + response.getResults().get(0).getOutput()); ?
關鍵元數據:
-
totalTokens:請求+響應的總令牌數(影響調用成本)。
-
completionTokens:響應生成的令牌數。
-
promptTokens:提示內容的令牌數。
4.2、 自動映射Java對象(結構化輸出)
通過entity()方法將模型輸出轉為自定義實體,需確保輸出格式符合JSON規范:
// 定義目標實體 ?
record Recipe(String dish, List<String> ingredients) {} ?// 生成食譜并映射 ?
Recipe salad = chatClient.prompt() ?.user("生成一份蔬菜沙拉食譜,以JSON格式輸出") ?.call() ?.entity(Recipe.class); ?// 自動解析JSON為對象 ?System.out.println("菜名:" + salad.dish()); ?
System.out.println("食材:" + salad.ingredients()); ?
進階技巧:結合StructuredOutputConverter自定義解析邏輯,處理非標準格式。