引言
在生成式 AI 應用中,Prompt(提示)是與大型語言模型(LLM)交互的核心輸入格式。Prompt 的設計不僅決定了模型理解任務的準確度,還直接影響生成結果的風格、長度、結構與可控性。隨著模型能力和應用場景的不斷豐富,Prompt 的結構也從最初的簡單文本演進為支持多角色區分、占位符模板化、工具調用等高級特性。本文將系統梳理 Prompt 的演變歷程、角色(Role)分類及職責、Spring AI 中 Prompt API 的詳細使用方法,以及 PromptTemplate 的模板化實踐,并在每個小節提供詳盡的文字說明和代碼示例。
一、Prompt 的演變歷程
Prompt 的演進大致可分為三個階段:
1.1 初始階段:簡單字符串
背景與特點
-
實現方式:直接將用戶意圖用自然語言表述為一條字符串,傳入模型。
-
示例:
Tell me a joke about cats.
-
優點:上手門檻低,無需額外框架或結構。
-
局限:無法提供上下文區分,也難以對輸出進行精細控制,適用于單輪、簡單的任務。
使用場景
- 問答類 Chatbot 的一次性查詢
- 單條文本生成,如自動撰寫短消息或標語
1.2 占位符與參數化
為什么要參數化?
- 當同一類任務需要多次調用時,硬編碼不同 Prompt 會導致維護成本高。
- 參數化模板能夠通過動態替換變量,提高 Prompt 的復用性和可維護性。
基本示例
String template = "Tell me a {adjective} joke about {topic}.";
String adjective = "silly";
String topic = "robots";
// 使用簡單替換實現參數化
String prompt = template.replace("{adjective}", adjective).replace("{topic}", topic);
System.out.println(prompt);
// 輸出:Tell me a silly joke about robots.
詳細說明
- 模板定義:使用?
{}
?包裹的占位符表示可替換的變量位置。 - 動態賦值:在運行時根據業務邏輯提供不同的變量值。
- 字符串替換:示例中使用?
String.replace
?實現,適合簡單場景,但無法處理復雜數據類型(如列表、條件邏輯)。
參數化模板的優勢
- 高復用性:一份模板可支持多種輸入組合。
- 易于維護:模板與參數分離,修改模板無需改動調用代碼。
- 結構化:在模板中明確標識可變部分,增強可讀性。
1.3 多角色消息流與上下文管理
角色化的必要性
隨著對話式應用需求的增長,僅靠單條字符串難以維護多輪對話上下文,也無法區分系統指令與用戶輸入。引入角色(Role)后,每條消息都可攜帶角色標簽,明確其在對話中的作用。
角色類型
- SYSTEM:設定對話基調、行為規范或全局約束。
- USER:真實用戶的提問或指令。
- ASSISTANT:模型的回復,用于記錄對話歷史和保持連貫性。
- TOOL:外部工具或函數調用的輸入/輸出信息。
示例代碼
List<Message> messages = new ArrayList<>();// 系統角色:初始化對話規則
messages.add(new SystemMessage("You are a professional travel planner. Provide concise and accurate travel advice."));// 用戶角色:用戶發起需求
messages.add(new UserMessage("I want to visit Xinjiang in autumn. What do you recommend?"));// 模型角色:占位符,由模型生成后再添加到列表
// messages.add(new AssistantMessage(modelOutput));// 下一輪用戶角色:繼續提問
// messages.add(new UserMessage("What is the weather forecast there for the next 3 days?"));
詳細說明
- SystemMessage:通過?
new SystemMessage(...)
?創建,內容為對模型的全局指令。 - UserMessage:通過?
new UserMessage(...)
?創建,內容為用戶輸入。 - AssistantMessage:模型生成的回復,由程序接收并添加,用于多輪對話。
- Role 的作用:模型在生成回復時會根據角色區分系統指令與用戶對話,確保響應符合預期。
二、Prompt 中的角色(Role)詳解
在 Spring AI 中,MessageType
?枚舉將角色明確定義為四種類型:
public enum MessageType {SYSTEM("system"), // 系統指令USER("user"), // 用戶輸入ASSISTANT("assistant"), // 模型回復TOOL("tool"); // 工具調用
}
2.1 系統角色(System Role)
-
主要職責:向模型提供身份設定、業務規則或回答風格。
-
典型指令:
- “You are a helpful assistant that speaks formally.”
- “Only answer questions related to legal advice.”
代碼示例
Message system = new SystemMessage("You are a financial advisor. Provide concise and accurate investment suggestions in JSON format."
);
擴展說明
- 可以通過添加元數據(metadata)為系統消息攜帶額外信息,如時間戳或優先級。
- 多個?
SystemMessage
?可按順序添加,以分階段設定對話規則。
2.2 用戶角色(User Role)
- 主要職責:傳達用戶的具體需求或問題。
- 設計要點:用戶消息應清晰、簡潔,避免含糊不清的描述。
代碼示例
Message user = new UserMessage("Please recommend three budget-friendly hotels in Tokyo for a family of four."
);
擴展說明
- 可在用戶消息中列出多步操作,模型會嘗試按順序執行。
- 對于復雜查詢,建議在一條消息中明確所有必要參數,如時間、人數、預算等。
2.3 助手角色(Assistant Role)
- 主要職責:承載模型的回復內容,同時可觸發工具調用。
- 觸發工具調用:當模型需要執行計算或外部操作時,會在?
AssistantMessage
?中返回?function_call
?字段,表明后續需調用對應工具。
代碼示例
// 模型生成的回復示例
Message assistant = new AssistantMessage("The weather in Xinjiang for the next 3 days is:\nDay1: 18°C, sunny\nDay2: 20°C, partly cloudy\nDay3: 17°C, light rain"
);
擴展說明
- 在接收到?
AssistantMessage
?后,程序應檢查是否存在工具調用請求,并根據?tool_name
?和?arguments
?調用相應工具。 - 回復中可包含結構化內容(如 JSON、表格等),便于后續解析。
2.4 工具/功能角色(Tool/Function Role)
- 主要職責:傳遞外部工具執行結果,如 API 調用、數據庫查詢或數學運算。
- 示例場景:天氣查詢、匯率轉換、訂單查詢等。
代碼示例
// 工具調用返回示例
Message tool = new ToolMessage("{ \"location\": \"Xinjiang\", \"forecast\": [18,20,17] }"
);
擴展說明
ToolMessage
?的?content
?通常為 JSON 格式,程序解析后將結果注入下一次 Prompt。- 多個工具調用可串聯使用,形成復雜工作流。
三、Spring AI 的 Prompt API 深入
Spring AI 提供了?Prompt
?和?Message
?等核心類,簡化 Prompt 構建與調用流程。以下示例演示完整調用鏈路,并對關鍵方法進行詳細解讀。
3.1 構建 Prompt 實例
// 1. 導入依賴:確保已引入 spring-ai 相關庫// 2. 構建消息列表
List<Message> messages = new ArrayList<>();
messages.add(system);
messages.add(user);// 3. 創建 Prompt 對象
Prompt prompt = new Prompt(messages)// 4. 可選:設置 ChatOptions,如溫度、最大 Token.options(ChatOptions.builder().temperature(0.5).maxTokens(500).build());
詳細說明
- 消息列表初始化:使用?
ArrayList
?存儲各角色消息,順序決定 Prompt 的上下文順序。 - Prompt 構造函數:
new Prompt(List<Message>)
?將消息列表復制到內部。 - 鏈式調用:通過?
options(...)
?方法設置模型參數,如溫度(temperature)控制隨機性,maxTokens 控制最大生成長度。
3.2 調用 ChatModel
// 5. 使用 ChatModel 或 ChatClient 發起調用
ChatResponse response = chatModel.call(prompt);// 6. 解析響應
String reply = response.getResult().getOutput().getContent();
System.out.println("Assistant: " + reply);
詳細說明
- ChatModel.call():接收?
Prompt
,發送給底層 LLM 服務,并返回?ChatResponse
。 - ChatResponse:封裝模型生成的所有輸出,包括文本、工具調用請求、Usage 信息等。
- getResult().getOutput().getContent():提取生成文本內容。
3.3 動態添加消息
在多輪對話中,可根據上一輪回復動態添加消息:
// 上一輪回復添加為 AssistantMessage
messages.add(new AssistantMessage(reply));// 下一輪用戶輸入
messages.add(new UserMessage("Thank you. Can you also suggest local cuisines?"));// 重新構建并調用 Prompt
Prompt nextPrompt = new Prompt(messages);
ChatResponse nextResp = chatModel.call(nextPrompt);
詳細說明
- 將模型回復添加到消息列表,保證上下文連貫性。
- 新的用戶消息與歷史消息一起傳入,模型能夠參考整個對話歷史。
四、PromptTemplate:高級模板化設計
當 Prompt 邏輯復雜、參數眾多時,手動拼接字符串或管理消息列表容易出錯。PromptTemplate
?基于 StringTemplate 引擎提供了靈活的模板化方案。
public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {private final String template;public PromptTemplate(String template) {this.template = template;}// render/create 方法由接口提供,具體由 StringTemplate 引擎實現
}
4.1 渲染為純文本
// 無參數模板
PromptTemplate staticTpl = new PromptTemplate("List three facts about the moon.");
String staticPrompt = staticTpl.render();
// staticPrompt == "List three facts about the moon."// 帶參數模板
PromptTemplate paramTpl = new PromptTemplate("List three {adjective} facts about the {subject}.");
String dynamicPrompt = paramTpl.render(Map.of("adjective", "interesting","subject", "moon"
));
// dynamicPrompt == "List three interesting facts about the moon."
詳細說明
- render():直接輸出模板內容,適用于靜態 Prompt。
- render(Map):根據鍵值對替換占位符,適用于簡單參數化需求。
4.2 生成 Message 對象
// 僅文本消息
Message msg1 = paramTpl.createMessage(Map.of("adjective", "funny","subject", "penguins"
));
// msg1.getContent() -> "List three funny facts about the penguins."
// 默認角色為 USER,可通過重載指定其他角色
詳細說明
- createMessage(Map):將渲染后的文本封裝為?
UserMessage
,簡化消息構建流程。 - 也可使用重載方法傳入媒體列表(圖片、音頻等),擴展多模態能力。
4.3 生成完整 Prompt
// 創建帶系統和用戶消息的模板
String tpl = "<system>You are {role}.</system>" +"<user>Summarize the following text: {text}</user>";
PromptTemplate fullTpl = new PromptTemplate(tpl);Map<String,Object> data = Map.of("role", "an academic writer","text", "Artificial intelligence is transforming industries..."
);// 生成 Prompt 并設置選項
Prompt fullPrompt = fullTpl.create(data).options(ChatOptions.builder().maxTokens(200).build());ChatResponse fullResp = chatModel.call(fullPrompt);
System.out.println(fullResp.getResult().getOutput().getContent());
詳細說明
- 模板標簽:使用?
<system>
、<user>
?標簽將渲染內容分配給不同角色。 - create(Map):渲染并拆分消息為多條?
Message
,并封裝為?Prompt
。 - options(...):為該 Prompt 單獨設置模型參數。
五、進階用法與最佳實踐
-
分層組織 Prompt:
- 將不同職責的消息(系統、用戶、助手、工具)分層組織,保持清晰的調用鏈路。
- 例如,先添加所有系統級指令,再添加用戶級查詢,最后再執行工具調用。
-
語義化占位符:
- 使用有意義的占位符名稱,如?
{userName}
、{startDate}
,提高模板可讀性和可維護性。
- 使用有意義的占位符名稱,如?
-
模板管理:
- 將常用 PromptTemplate 存儲在配置文件或數據庫中,支持在線更新和版本控制。
- 結合 CI/CD 流程,實現模板自動化測試與回滾。
-
工具調用預留:
- 在模板中預先定義工具調用點,如?
Please fetch current weather via function_call.
- 模型在生成時會自動觸發?
function_call
,程序接收后執行相應工具。
- 在模板中預先定義工具調用點,如?
-
性能與成本控制:
- 緩存靜態 Prompt 或已渲染模板,減少重復渲染開銷。
- 控制消息列表長度,定期對歷史對話進行摘要或清理,避免超出模型上下文窗口。
-
安全與合規:
- 對用戶輸入進行校驗和清洗,避免注入惡意指令。
- 記錄 Prompt 與響應日志,確保可審計、可追溯。
六、總結
Prompt 設計是生成式 AI 應用的基礎,直接影響模型的行為和輸出質量。通過理解 Prompt 的演變歷程、合理劃分角色、熟練使用 Spring AI 提供的 Prompt API 與 PromptTemplate 工具,開發者可以構建出靈活、高效且易維護的對話系統。結合最佳實踐,您將能夠在各類業務場景中充分發揮 LLM 的潛力,為用戶帶來流暢、智能的交互體驗。
本文示例基于 Spring AI 框架及 StringTemplate 引擎,旨在為開發者提供系統的 Prompt 設計指導。