提示詞
提示詞是引導 AI 模型生成特定輸出的輸入。這些提示詞的設計和措辭顯著影響模型的響應。
在 Spring AI 中與 AI 模型互動的最底層,Spring AI 處理提示詞的方式有點類似於在 Spring MVC 中管理“檢視(View)”。這涉及到建立包含動態內容佔位符的大量文字。然後根據使用者請求或應用程式中的其他程式碼替換這些佔位符。另一個類比是包含特定表示式佔位符的 SQL 語句。
隨著 Spring AI 的發展,它將引入更高級別的抽象來與 AI 模型互動。本節描述的基礎類在作用和功能上可以類比於 JDBC。例如,`ChatModel` 類類似於 JDK 中的核心 JDBC 庫。`ChatClient` 類可以類比於 `JdbcClient`,它構建在 `ChatModel` 之上,並透過 `Advisor` 提供更高階的構造,以便考慮模型過去的互動,用額外的上下文文件增強提示詞,並引入代理行為。
提示詞的結構在 AI 領域隨時間演變。最初,提示詞是簡單的字串。隨著時間的推移,它們開始包含特定輸入的佔位符,例如 AI 模型可以識別的 "USER:"。OpenAI 透過將多個訊息字串分類為不同的角色,然後在 AI 模型處理之前進一步增加了提示詞的結構。
API 概覽
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;
}
Message
`Message` 介面封裝了 `Prompt` 的文字內容、一組元資料屬性以及稱為 `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 模型可以處理的不同類別的訊息。模型根據對話角色區分訊息類別。

正如我們在下文討論的那樣,這些角色由 `MessageType` 有效地對映。
角色
每條訊息都被分配了特定的角色。這些角色對訊息進行分類,為 AI 模型闡明提示詞各部分的上下文和目的。這種結構化方法增強了與 AI 通訊的細微差別和有效性,因為提示詞的每個部分在互動中都扮演著獨特且明確的角色。
主要角色是
-
系統角色:指導 AI 的行為和響應風格,設定 AI 如何解釋和回覆輸入的引數或規則。這類似於在開始對話之前向 AI 提供指令。
-
使用者角色:代表使用者的輸入——他們對 AI 的問題、命令或陳述。這個角色是基礎性的,因為它構成了 AI 響應的基礎。
-
助手角色:AI 對使用者輸入的響應。它不僅僅是答案或反應,對於維持對話流程至關重要。透過跟蹤 AI 之前的響應(其“助手角色”訊息),系統確保互動連貫且上下文相關。助手訊息也可能包含函式工具呼叫請求資訊。這就像 AI 中的一個特殊功能,在需要執行特定功能時使用,例如計算、獲取資料或除了交談之外的其他任務。
-
工具/函式角色:工具/函式角色的重點是響應工具呼叫助手訊息返回附加資訊。
角色在 Spring AI 中表示為列舉,如下所示
public enum MessageType {
USER("user"),
ASSISTANT("assistant"),
SYSTEM("system"),
TOOL("tool");
...
}
PromptTemplate
Spring AI 中用於提示詞模板化的關鍵元件是 `PromptTemplate` 類,旨在促進建立結構化的提示詞,然後將其傳送給 AI 模型進行處理
public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {
// Other methods to be discussed later
}
該類使用 `TemplateRenderer` API 來渲染模板。預設情況下,Spring AI 使用基於 Terence Parr 開發的開源 StringTemplate 引擎的 `StTemplateRenderer` 實現。模板變數由 {}
語法標識,但您也可以配置分隔符以使用其他語法。
public interface TemplateRenderer extends BiFunction<String, Map<String, Object>, String> {
@Override
String apply(String template, Map<String, Object> variables);
}
Spring AI 使用 `TemplateRenderer` 介面來處理將變數實際替換到模板字串中。預設實現使用 [StringTemplate]。如果您需要自定義邏輯,可以提供自己的 `TemplateRenderer` 實現。對於不需要模板渲染的場景(例如,模板字串已經完整),您可以使用提供的 `NoOpTemplateRenderer`。
PromptTemplate promptTemplate = PromptTemplate.builder()
.renderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build())
.template("""
Tell me the names of 5 movies whose soundtrack was composed by <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);
}
方法 `String render()`:將提示詞模板渲染成最終的字串格式,無需外部輸入,適用於不包含佔位符或動態內容的模板。
方法 `String render(Map<String, Object> model)`:增強渲染功能以包含動態內容。它使用一個 `Map<String, Object>`,其中 Map 鍵是提示詞模板中的佔位符名稱,值是要插入的動態內容。
public interface PromptTemplateMessageActions {
Message createMessage();
Message createMessage(List<Media> mediaList);
Message createMessage(Map<String, Object> model);
}
方法 `Message createMessage()`:建立一個不帶附加資料的 `Message` 物件,用於靜態或預定義的訊息內容。
方法 `Message createMessage(List<Media> mediaList)`:建立一個包含靜態文字和媒體內容的 `Message` 物件。
方法 `Message createMessage(Map<String, Object> model)`:擴充套件訊息建立功能以整合動態內容,接受一個 `Map<String, Object>`,其中每個條目代表訊息模板中的一個佔位符及其對應的動態值。
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);
}
方法 `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 條目是提示詞模板中的一個佔位符及其關聯的動態值,以及聊天請求的特定選項。
用法示例
下面展示了一個來自 AI Workshop on PromptTemplates 的簡單示例。
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();
下面展示了另一個來自 AI Workshop on Roles 的示例。
String userText = """
Tell me about three famous pirates from the Golden Age of Piracy and why they did.
Write at least a sentence for each pirate.
""";
Message userMessage = new UserMessage(userText);
String systemText = """
You are a helpful AI assistant that helps people find information.
Your name is {name}
You should reply to the user's request with your name and also in the style of a {voice}.
""";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemText);
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
List<Generation> response = chatModel.call(prompt).getResults();
這展示瞭如何透過使用 `SystemPromptTemplate` 建立帶有系統角色的 `Message` 並傳入佔位符值來構建 `Prompt` 例項。然後將具有 `user` 角色的訊息與具有 `system` 角色的訊息結合起來形成提示詞。最後將提示詞傳遞給 `ChatModel` 以獲得生成響應。
使用自定義模板渲染器
您可以透過實現 `TemplateRenderer` 介面並將其傳遞給 `PromptTemplate` 建構函式來使用自定義模板渲染器。您也可以繼續使用預設的 `StTemplateRenderer`,但使用自定義配置。
預設情況下,模板變數由 {}
語法標識。如果您打算在提示詞中包含 JSON,您可能需要使用不同的語法來避免與 JSON 語法衝突。例如,您可以使用 <
和 >
分隔符。
PromptTemplate promptTemplate = PromptTemplate.builder()
.renderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build())
.template("""
Tell me the names of 5 movies whose soundtrack was composed by <composer>.
""")
.build();
String prompt = promptTemplate.render(Map.of("composer", "John Williams"));
使用資源代替原始字串
Spring AI 支援 `org.springframework.core.io.Resource` 抽象,因此您可以將提示詞資料放在檔案中,該檔案可以直接在 `PromptTemplate` 中使用。例如,您可以在 Spring 管理的元件中定義一個欄位來檢索 `Resource`。
@Value("classpath:/prompts/system-message.st")
private Resource systemResource;
然後直接將該資源傳遞給 `SystemPromptTemplate`。
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
提示詞工程
在生成式 AI 中,建立提示詞對於開發者來說是一項關鍵任務。這些提示詞的質量和結構顯著影響 AI 輸出的有效性。投入時間和精力設計周密的提示詞可以大大提高 AI 的結果。
在 AI 社群中,分享和討論提示詞是一種常見的做法。這種協作方法不僅創造了一個共享的學習環境,還有助於識別和使用高效的提示詞。
在這一領域的研究通常涉及分析和比較不同的提示詞,以評估它們在各種情況下的有效性。例如,一項重要研究表明,以“Take a deep breath and work on this problem step by step”(深呼吸並一步一步解決這個問題)開頭提示詞顯著提高了解決問題的效率。這突顯了精心選擇的語言對生成式 AI 系統性能的影響。
掌握提示詞最有效的使用方法,特別是隨著 AI 技術的快速發展,是一個持續的挑戰。您應該認識到提示詞工程的重要性,並考慮利用社群和研究的見解來改進提示詞建立策略。
建立有效的提示詞
開發提示詞時,整合幾個關鍵元件對於確保清晰和有效至關重要
-
指令:向 AI 提供清晰直接的指令,類似於您與人交流的方式。這種清晰度對於幫助 AI“理解”預期內容至關重要。
-
外部上下文:必要時包含相關的背景資訊或針對 AI 響應的特定指導。這種“外部上下文”為提示詞提供了框架,並幫助 AI 掌握整體場景。
-
使用者輸入:這是直接的部分——使用者的直接請求或問題構成了提示詞的核心。
-
輸出指示器:這一方面可能很棘手。它涉及指定 AI 響應所需的格式,例如 JSON。但是,請注意,AI 可能不會總是嚴格遵守此格式。例如,它可能會在實際的 JSON 資料之前加上“這是您的 JSON”之類的短語,或者有時會生成一個不準確的類似 JSON 的結構。
在編寫提示詞時,向 AI 提供預期問題和答案格式的示例會非常有益。這種做法有助於 AI“理解”您查詢的結構和意圖,從而獲得更精確和相關的響應。雖然本文件並未深入探討這些技術,但它們為進一步探索 AI 提示詞工程提供了起點。
以下是供進一步研究的資源列表。
高階技術
-
零樣本學習 (Zero-shot), 少樣本學習 (Few-shot Learning):
使模型能夠在極少甚至沒有特定問題型別先驗示例的情況下做出準確的預測或響應,利用學到的泛化能力理解和處理新任務。 -
思維鏈 (Chain-of-Thought):
將多個 AI 響應連結起來,建立連貫且上下文感知的對話。這有助於 AI 保持討論的主線,確保相關性和連續性。 -
ReAct (思考 + 行動):
在此方法中,AI 首先分析(思考)輸入,然後確定最合適的行動方案或響應。它將理解與決策相結合。
Microsoft Guidance
-
提示詞建立與最佳化框架:
Microsoft 提供了一種結構化的方法來開發和最佳化提示詞。此框架指導使用者建立能夠從 AI 模型中獲得所需響應的有效提示詞,最佳化互動的清晰度和效率。
Token(標記)
Token 在 AI 模型處理文字的方式中至關重要,它充當一個橋樑,將我們理解的詞語轉換為 AI 模型可以處理的格式。這種轉換分兩個階段發生:詞語在輸入時轉換為 Token,然後這些 Token 在輸出時再轉換回詞語。
分詞(Tokenization)是將文字分解為 Token 的過程,是 AI 模型理解和處理語言的基礎。AI 模型使用這種 Token 化格式來理解和響應提示詞。
為了更好地理解 Token,可以將它們視為詞語的一部分。通常,一個 Token 代表詞語的大約四分之三。例如,莎士比亞的全部作品,總計約 900,000 個詞,將轉換為大約 120 萬個 Token。
嘗試使用 OpenAI Tokenizer UI 來檢視詞語如何轉換為 Token。
Token 除了在 AI 處理中的技術作用外,還具有實際意義,尤其是在計費和模型能力方面
-
計費:AI 模型服務通常根據 Token 使用量計費。輸入(提示詞)和輸出(響應)都計入總 Token 數,因此較短的提示詞更具成本效益。
-
模型限制:不同的 AI 模型有不同的 Token 限制,定義了它們的“上下文視窗”——它們一次可以處理的最大資訊量。例如,GPT-3 的限制是 4K Token,而像 Claude 2 和 Meta Llama 2 這樣的其他模型有 100K Token 的限制,一些研究模型可以處理高達 100 萬個 Token。
-
上下文視窗:模型的 Token 限制決定了其上下文視窗。超出此限制的輸入不會被模型處理。傳送用於處理的最小有效資訊集至關重要。例如,在查詢“哈姆雷特”時,無需包含莎士比亞所有其他作品的 Token。
-
響應元資料:AI 模型響應的元資料包含使用的 Token 數量,這是管理使用量和成本的關鍵資訊。