DotPrompt 是一個簡單的庫,它允許您使用基於配置的語法建立提示,而無需將它們嵌入到您的應用程式中。它支援透過 Fluid 模板語言對提示進行模板化,讓您重複使用相同的提示並在執行時間傳遞不同的值。
提示檔案就是任何以.prompt
副檔名結尾的檔案。實際文件本身是一個 YAML 配置文件,擴展名允許庫快速識別該文件以實現其預期目的。
.prompt
檔案有一個已知問題,會導致 Rider 和 IntelliJ 等工具出現異常行為。您可以透過停用終端插件或使用不同的編輯器修改檔案來解決此問題。
提示文件的內容包含一些頂級標識屬性,後面是配置信息,最後是提示。
完整的提示文件如下圖所示。
名稱:範例型號:gpt-4oconfig:輸出格式:文本 溫度:0.9 最大代幣數:500 輸入:參數:主題:字串樣式? 您是一位有用的研究助理,將為給定主題提供描述性答覆以及它如何影響社會用戶: | 解釋一下 {{ topic }} 對我們作為一個社會如何參與科技的影響 {% if style -%} 你能用 {{ style }} {% endif -%}fewShots 的風格回答嗎: - 使用者:什麼是藍牙 回覆:藍牙是一種短距離無線技術標準,用於固定和行動裝置之間短距離交換資料以及建置個人區域網路。 - 使用者:機器學習與傳統程式設計有何不同? - 使用者:您能提供一個日常生活中人工智慧的例子嗎?
該name
在配置中是可選的,如果未提供,則名稱取自檔案名稱減去副檔名。因此,名為gen-lookup-code.prompt
的檔案將會獲得名稱gen-lookup-code
。這在提示本身的生成中不起作用(儘管將來的更新可能會起作用),但允許您在記錄時識別提示來源,並從提示管理員中選擇提示。
如果使用此屬性,則在載入檔案時,名稱將轉換為小寫,並且空格將替換為連字符。因此My cool Prompt
的名稱將變為my-cool-prompt
。這樣做是為了確保可以從程式碼中輕鬆存取該名稱。
這是配置中的另一個可選項目,但它向提示檔案的使用者提供應使用哪個模型(或 Azure Open AI 的部署)的資訊。由於如果未指定,該值可能為空,因此消費者應確保在使用前進行檢查。例如:
var model =提示檔.Model ?? “我的預設”;
不過,使用此選項可以讓提示工程師非常明確地了解他們打算使用哪種模型來提供最佳結果。
config
部分有一些頂級項目,供客戶在其 LLM 呼叫中使用,以設定每個調用的選項。 outputFormat
屬性採用text
或json
值,取決於 LLM 回應請求的方式。如果指定json
,則某些 LLM 需要係統或使用者提示來聲明預期輸出也是 JSON。如果庫在提示中沒有偵測到術語JSON
,那麼它將在系統提示中附加一個小語句,請求回應採用 JSON 格式。
input
部分包含有關提供給提示的參數的詳細資訊。這些不是必需的,您可以建立根本不傳遞任何值的提示。但如果你這樣做了,那麼這些就是你所需要的。
input
下方是parameters
部分,其中包含鍵值對列表,其中鍵是參數的名稱,值是參數的類型。如果您在參數名稱後面加上問號(例如style?
),則它被視為可選參數,並且如果您不為其提供值,則不會出錯。
支援的類型有:
參數類型 | 點網類型 | C# 等效項 |
---|---|---|
細繩 | 系統字串 | 細繩 |
布林值 | 系統布林值 | 布林值 |
日期時間 | 系統日期時間偏移 | 系統日期時間偏移 |
數位 | 系統位元組 系統.SByte 系統.UInt16 系統.Int16 系統.UInt32 系統.Int32 系統.UInt64 系統.Int64 系統.單一 系統.雙 系統.十進制 | 位元組 斯位元組 超短 短 單位 整數 烏龍 長的 漂浮 雙 小數 |
目的 | 系統對象 | 目的 |
前 4 個以提供的方式使用。傳遞給提示的物件將呼叫其ToString
方法以在提示中使用。
datetime
類型可以使用預設的ToString
表示形式顯示,也可以使用 Fluid 的篩選器來指定其格式、變更時區等。
如果您為參數提供的值不符合指定的類型,則會引發錯誤。
input
中還有default
部分。此部分允許您指定任何參數的預設值。因此,如果您的應用程式中未提供該參數,則將使用預設值。
prompts
部分包含系統和使用者提示的範本。雖然需要使用者提示,但您不需要指定係統提示。
system
和user
提示都是字串值,可以用 YAML 支援的任何方式定義。上面的範例使用多行字串,其中保留回車符。
YAML 透過區塊標量對多行字串值提供了很好的支援。有了這些,它支援文字字串和折疊字串。對於文字字串,輸入字串中的新行字元將被保留,並且字串將完全保持原樣。折疊後,新行字符會折疊並替換為空格字符,從而允許您在多行上寫入很長的字串。使用折疊時,如果使用兩個換行符,則會將換行符新增至字串。
# Foldedexample: > 船懸掛在天空中的方式與磚塊的方式大致相同# Produces:# 船懸掛在天空中的方式與磚塊的方式大致相同
# 文字範例: | 船懸掛在天空中的方式與磚塊幾乎相同#產生:#船懸掛在天空中的方式與磚塊幾乎相同#
提示的語法使用 Fluid 模板語言,語言本身基於 Shopify 創建的 Liquid。這種模板語言允許我們定義使用者提示,這些提示可以根據傳遞到模板解析器的值而變化。
在上面的範例中,您可以看到{{ topic }}
這是傳入值的佔位符,將直接替換到範本中。還有{% if style -%} ... {% endif -%}
部分,它告訴解析器僅在style
參數具有值時才包含此部分。標記末尾的-%}
包含連字符,它告訴解析器應該折疊空白行。
在線有一個關於使用 Fluid 編寫模板的很棒的教程。
當您產生提示時,它不會取代模板,只會為您提供產生的輸出。這意味著您可以使用不同的輸入值多次產生提示。
fewShots
是一個允許提示編寫者為解決方案提供少鏡頭提示技術的部分。在建立提示時,您將包括這些提示以及系統提示,然後是使用者提示,這提供了有關 LLM 應如何回應使用者提示的範例。如果您使用的是 OpenAI 或 Azure OpenAI,那麼您可以使用擴充方法(請參閱下文),這將為您建立所有訊息。
可以直接存取提示文件。如果您只有幾個文件或想要快速測試它們,那麼這是一種相當簡單的方法。
使用 DotPrompt;var PromptFile = PromptFile.FromFile("path/to/prompt-file.prompt");var systemPrompt = PromptFile.GetSystemPrompt(null);var userPrompt = PromptFile.GetUserPrompt(new Diction>string,{ciction> topic", "藍牙" },{ "風格", "二手車推銷員" }});
如果提示檔案包含上面的範例,那麼它將產生以下內容。
System Prompt:
You are a helpful research assistant who will provide descriptive responses for a given topic and how it impacts society
User Prompt:
Explain the impact of bluetooth on how we engage with technology as a society
Can you answer in the style of a used car salesman
這可能會導致法學碩士的回覆如下(抱歉)
女士們先生們,聚集在一起,讓我告訴你們現代科技的奇蹟,它徹底改變了我們與電子設備的連接方式——我說的是藍牙!藍牙是無名英雄,是讓我們的生活變得更方便、更互聯、絕對更高科技的秘密武器。想像一下:您喜愛的裝置之間的無縫、無線通訊。不再有纏結的電線,不再有混亂。這就像擁有未來前排的 VIP 通行證!
…
提示管理器是處理提示檔案的首選方法。它允許您從某個位置加載它們,然後按名稱訪問,然後在您的應用程式中使用它們。
提示管理器的預設設定是存取本機prompts
夾中的文件,但您可以根據需要指定不同的路徑。
// 從 `prompts` 目錄的預設位置載入 varpromptManager = new PromptManager();varpromptFile =promptManager.GetPromptFile("example");// 使用不同的資料夾varpromptManager = new PromptManager("another-loop); = PromptManager.GetPromptFile("example");// 列出所有載入的提示var PromptNames = PromptManager.ListPromptFileNames();
提示管理器實作了IPromptManager
接口,因此如果您想透過 DI 容器或 IoC 模式使用它,那麼您可以輕鬆提供模擬版本進行測試。
提示管理器也可以採用IPromptStore
實例,該實例可讓您建置可能不是基於檔案的自訂儲存空間(請參閱建立自訂提示儲存)。這也允許提供模擬接口,以便您可以編寫不依賴儲存機制的單元測試。
使用提示管理器讀取提示,然後在對 Azure OpenAI 終結點的呼叫中使用它。
注意:此範例假設有一個包含可用提示檔案的prompts
目錄。
使用 System.ClientModel;使用 Azure.AI.OpenAI;使用 DotPrompt;var openAiClient = new(new Uri("https://endpoint"), new ApiKeyCredential("abc123"));var PromptManager = new PromptManager();var PromptFile = PromptManager.GetPromptFile("example");// 系統提示和使用者提示方法採用包含範本所需值的字典。如果不需要,您可以簡單地傳入null.var systemPrompt = PromptFile.GetSystemPrompt(null);var userPrompt = PromptFile.GetUserPrompt(new Dictionary<string, object>{{ "topic", "bluetooth" },{ "styletooth" },{ "style " , "二手車銷售員" }});var client = openAiClient.GetChatClient(promptFile.Model ?? "default-model");var finish = wait client.CompleteChatAsync([new SystemChatMessage(systemPrompt),new UserChatMessage(ChatAsync([new SystemChatMessage(systemPrompt),new userChatMessage(ChatProPrompt )] ,new ChatCompletionOptions(ResponseFormat = PromptFile.OutputFormat == OutputFormat.Json ? ChatResponseFormat.JsonObject : ChatResponseFormat.Text,Temperature = PromptFile.Config.Temperature,MaxResponseFormat.Text,Temperature = PromptFile.Config.Temperature,MaxResponseFormat.Text,Temperature = PromptFile.Config。
或者,使用 OpenAI 提供的擴充方法。
使用 System.ClientModel;使用 Azure.AI.OpenAI;使用 DotPrompt;使用 DotPrompt.Extensions.OpenAi;var openAiClient = new(new Uri("https://endpoint"), new ApiKeyCredential("abc123"));fvar PromptManager = new PromptManager();var PromptFile = PromptManager.GetPromptFile("example");var PromptValues = new Dictionary<string, object>{{ "topic", "bluetooth" },{ "style", "二手車銷售員" } };var client = openAiClient.GetChatClient(promptFile.Model ?? "預設模型");varcompletion =等待client.CompleteChatAsync(promptFile.ToOpenAiChatMessages(promptValues),promptFile.ToOpenpenAiChattional.com( .WriteLine(response.Content[0].Text);
現在,如果我們需要修改提示符,我們可以簡單地更改提示符檔案並保留我們的程式碼(假設參數不變)。
上面顯示如何使用 DotPrompt 從磁碟讀取提示檔。但是,如果您希望提示位於更中心的位置(例如雲端儲存服務或資料庫),該怎麼辦?那麼提示管理器可以將IPromptStore
實例作為參數。在上面的所有範例中,它都使用包含的FilePromptStore
,但您也可以建立自己的。只需要實作該介面即可。
舉個例子,以下是一個簡單的實現,它使用 Azure 儲存體表儲存體來保存提示詳細資訊。
/// <summary>/// Azure 儲存表的 IPromptStore 實作/// </summary>public class AzureTablePromptStore : IPromptStore{/// <summary>/// 從資料儲存載入提示/// < /summary>public IEnumerable<PromptFile> Load(){var tableClient = GetTableClient();var PromptEntities = tableClient.Query<PromptEntity>(e => e.PartitionKey == "DotPromptTest");var PromptFiles = Prompt>Entities.Select = Entities. ToPromptFile()).ToList();return PromptFiles;}/// <summary>/// 取得表格用戶端/// </summary>private static TableClient GetTableClient(){// 替換設定項此處輸入您的值或切換到using// 基於Entra 的驗證var client = new TableServiceClient(new Uri($"https://{Configuration.StorageAccountName}.table.core.windows.net/"),new TableSharedKeyCredential(Configuration.StorageAccountName , Configuration.StorageAccountKey));var tableClient = client.GetTableClient("prompts");tableClient.CreateIfNotExists();return tableClient;}}/// <summary>/// 表示儲存表中儲存的一筆記錄/// </summary>public class PromptEntity : ITableEntity{/// <summary>/// 取得、設定記錄的分區鍵/// </summary>public string PartitionKey { get;放; } = string.Empty;/// <summary>/// 取得、設定記錄的行鍵/// </summary>public string RowKey { get;放; } = string.Empty;/// <summary>/// 取得、設定條目的時間戳記/// </summary>public DateTimeOffset?時間戳{獲取;設定; }/// <summary>/// 取得、設定記錄ETag值/// </summary>public ETag ETag { get;放; }/// <summary>/// 取得、設定要使用的模型/// </summary>公共字串?模型{得到;放; }/// <summary>/// 取得、設定輸出格式/// </summary>public string OutputFormat { get;放; } = string.Empty;/// <summary>/// 取得、設定最大token數量/// </summary>public int MaxTokens { get;放; }/// <summary>/// 取得、設定以 JSON 字串值儲存的參數資訊/// </summary>public stringParameters { get;放; } = string.Empty;/// <summary>/// 取得、以 JSON 字串值形式儲存的預設值/// </summary>public string Default { get;設定; } = string.Empty;/// <summary>/// 取得、設定係統提示範本/// </summary>public string SystemPrompt { get;放; } = string.Empty;/// <summary>/// 取得、設定使用者提示範本/// </summary>public string UserPrompt { get;放; } = string.Empty;/// <summary>/// 將提示實體記錄回傳至 <see cref="PromptFile"/> 實例中/// </summary>/// <returns></returns>public PromptFile ToPromptFile(){varparameters = new Dictionary<string, string>();var defaults = new Dictionary<string, object>();//如果有參數值則將其轉換為字典if (!string.IsNullOrEmpty(IsNullOrEmpty(參數)){varEntityParameters =(JsonObject)JsonNode.Parse(Parameters)! ;foreach(var(prop,propType)inentityParameters){parameters.Add(prop,propType?.AsValue().ToString()??字串。 ; }}// 如果有預設值則轉成字典if (!string.IsNullOrEmpty(Default)){varEntityDefaults = (JsonObject)JsonNode.Parse(Default)!;foreach(var(prop,defaultValue) inentityDefaults){defaults. Add(prop, defaultValue?.AsValue().GetValue<object>() ?? string.Empty);}}//產生新的提示檔varpromptFile = new PromptFile{Name = RowKey,Model =模型,Config = new PromptConfig {OutputFormat = Enum.Parse<OutputFormat>(OutputFormat,true),MaxTokens = MaxTokens,Input = new InputSchema{Parameters = 參數,預設= 預設值}},Prompts = new Prompts{System = SystemPrompt,使用者= UserPrompt};返回提示檔;}}
然後要使用它,我們將執行以下操作
varpromptManager=newPromptManager(newAzureTablePromptStore());varpromptFile=promptManager.GetPromptFile(“範例”);
這裡仍有工作空間,我們正在研究的一些項目包括
附加配置選項
額外的提示技巧
開放回饋。有什麼想看的嗎?讓我們知道