DotPrompt は、アプリケーションに埋め込まなくても、構成ベースの構文を使用してプロンプトを作成できるシンプルなライブラリです。 Fluid テンプレート言語によるプロンプトのテンプレート化をサポートしているため、同じプロンプトを再利用して、実行時に異なる値を渡すことができます。
プロンプト ファイルは、拡張子.prompt
で終わるファイルです。実際のファイル自体は YAML 構成ファイルであり、拡張子により、ライブラリは意図された目的のファイルを迅速に識別できます。
.prompt
ファイルには、Rider や IntelliJ などのツールで異常な動作を引き起こす既知の問題があります。この問題を回避するには、ターミナル プラグインを無効にするか、別のエディタを使用してファイルを変更します。
プロンプト ファイルの内容には、いくつかのトップレベルの識別プロパティが含まれており、その後に構成情報が含まれ、最後にプロンプトが続きます。
完全なプロンプト ファイルは次のようになります。
名前: 例モデル: gpt-4oconfig: 出力形式: テキスト 温度: 0.9 最大トークン: 500 入力:パラメータ: トピック: 文字列スタイル?: 文字列デフォルト: トピック: ソーシャルメディアプロンプト: システム: | あなたは、特定のトピックとそれが社会ユーザーにどのような影響を与えるかについて説明的な回答を提供する、役に立つ研究アシスタントです。 社会としてテクノロジーとどのように関わるかに対する {{ topic }} の影響について説明してください {% if style -%} {{ style }} {% endif -%}fewShots のスタイルで答えていただけますか: - ユーザー: Bluetooth とは何ですか: Bluetooth は、固定デバイスとモバイル デバイス間で短距離でデータを交換し、パーソナル エリア ネットワークを構築するために使用される短距離無線技術標準です。 - ユーザー: 機械学習は従来のプログラミングとどのように異なりますか?回答: 機械学習を使用すると、明示的にプログラムしなくてもアルゴリズムがデータから学習し、時間の経過とともに改善されます。 - ユーザー: 日常生活における AI の例を教えてください。回答: AI は、音声コマンドを理解して応答する Siri や Alexa などの仮想アシスタントで使用されています。
name
は構成でオプションです。指定しない場合、名前はファイル名から拡張子を除いたものから取得されます。したがって、 gen-lookup-code.prompt
というファイルの名前はgen-lookup-code
になります。これはプロンプト自体の生成には影響しません (将来の更新で影響を受ける可能性があります) が、ログ記録時にプロンプト ソースを識別したり、プロンプト マネージャーからプロンプトを選択したりできるようになります。
このプロパティを使用すると、ファイルがロードされるときに名前が小文字に変換され、スペースはハイフンに置き換えられます。したがって、 My cool Prompt
の名前はmy-cool-prompt
になります。これは、コードから名前に簡単にアクセスできるようにするために行われます。
これは構成内のもう 1 つのオプション項目ですが、プロンプト ファイルのユーザーにどのモデル (または Azure Open AI のデプロイメント) を使用する必要があるかという情報を提供します。これが指定されていない場合は null になる可能性があるため、消費者は使用前に必ず確認する必要があります。例えば:
var モデル = プロンプトファイル.モデル ?? "私のデフォルト";
ただし、このオプションを使用すると、プロンプト エンジニアは、最良の結果を提供するためにどのモデルを使用するつもりであるかを明確に示すことができます。
config
セクションには、クライアントが各呼び出しでオプションを設定するために LLM 呼び出しで使用するために提供されるトップレベルの項目がいくつかあります。 outputFormat
プロパティは、LLM が要求にどのように応答するかに応じて、 text
またはjson
のいずれかの値を受け取ります。 json
を指定する場合、一部の LLM では、システムまたはユーザーのプロンプトに、期待される出力も JSON であることを示す必要があります。ライブラリがプロンプト内でJSON
という用語を検出しない場合、応答が JSON 形式であることを要求する短いステートメントをシステム プロンプトに追加します。
input
セクションには、プロンプトに提供されるパラメーターに関する詳細が含まれます。これらは必須ではないため、値がまったく渡されないプロンプトを作成することもできます。しかし、そうするなら、これらが必要なものです。
input
の下には、キーと値のペアのリストが含まれるparameters
セクションがあり、キーはパラメータの名前、値はパラメータのタイプです。パラメータ名の末尾に疑問符 (例: style?
) を付けると、そのパラメータはオプションのパラメータとみなされ、値を指定しなくてもエラーは発生しません。
サポートされているタイプは次のとおりです。
パラメータの種類 | ドットネットタイプ | C# と同等 |
---|---|---|
弦 | System.String | 弦 |
ブール | System.Boolean | ブール |
日時 | System.DateTimeOffset | System.DateTimeOffset |
番号 | システム.バイト System.SByte System.UInt16 System.Int16 System.UInt32 System.Int32 System.UInt64 System.Int64 System.Single System.Double System.Decimal | バイト エスバイト ウーショート 短い 単位 整数 ウーロン 長さ フロート ダブル 10進数 |
物体 | システム.オブジェクト | 物体 |
最初の 4 つは提供されたとおりに使用されます。プロンプトに渡されるオブジェクトには、プロンプトで使用するためにToString
メソッドが呼び出されます。
datetime
型は、デフォルトのToString
表現で表示することも、Fluid のフィルターを使用して形式を指定したり、タイムゾーンを変更したりすることもできます。
指定された型に準拠しないパラメータの値を指定すると、エラーがスローされます。
input
にはdefault
セクションもあります。このセクションでは、任意のパラメータのデフォルト値を指定できます。したがって、アプリケーションにパラメータが指定されていない場合は、代わりにデフォルト値が使用されます。
prompts
セクションには、システム プロンプトとユーザー プロンプトのテンプレートが含まれています。ユーザー プロンプトは必須ですが、システム プロンプトを指定する必要はありません。
system
とuser
プロンプトは両方とも文字列値であり、YAML がサポートする任意の方法で定義できます。上の例では、キャリッジ リターンが保持される複数行の文字列を使用しています。
YAML は、ブロック スカラーを介して複数行の文字列値を強力にサポートしています。これらを使用すると、リテラル文字列と折り畳まれた文字列の両方がサポートされます。リテラル文字列の場合、入力文字列内の改行文字は維持され、文字列は書き込まれたとおりのままになります。折りたたむと、改行文字が折りたたまれてスペース文字に置き換えられるため、非常に長い文字列を複数行にまたがって書くことができます。折りたたまれた状態で 2 つの改行文字を使用すると、文字列に改行が追加されます。
# Foldedexample: > 船はレンガがぶら下がっていないのとほぼ同じ方法で空にぶら下がっています# 生成物:# 船はレンガがぶら下がっていないのとほぼ同じ方法で空にぶら下がっています
# リテラルの例: | 船はレンガがぶら下がっているのと同じように空にぶら下がっています# 生産物:# 船はレンガがぶら下がっているのとほぼ同じように空にぶら下がっています#
プロンプトの構文は Fluid テンプレート言語を使用します。この言語自体は Shopify によって作成された Liquid に基づいています。このテンプレート言語を使用すると、テンプレート パーサーに渡される値に応じて変化するユーザー プロンプトを定義できます。
上の例では、渡される値のプレースホルダーである{{ topic }}
がわかります。これはテンプレートに直接置き換えられます。また、 style
パラメータに値がある場合にのみこのセクションを含めるようパーサーに指示する{% if style -%} ... {% endif -%}
セクションもあります。マーカーの末尾の-%}
には、空白行を折りたたむ必要があることをパーサーに指示するハイフン記号が含まれています。
Fluid を使用してテンプレートを作成するための優れたチュートリアルがオンラインで入手できます。
プロンプトを生成しても、テンプレートは置き換えられず、生成された出力のみが提供されます。これは、異なる入力値を使用してプロンプトを何度でも生成できることを意味します。
fewShots
は、プロンプト作成者がソリューションに少数ショット プロンプト手法を提供できるようにするセクションです。プロンプトを作成するときは、システム プロンプト、次にユーザー プロンプトとともにこれらを含めます。これは、LLM がユーザー プロンプトにどのように応答するかについての例を示します。 OpenAI または Azure OpenAI を使用している場合は、すべてのメッセージを作成する拡張メソッド (後述) を使用できます。
プロンプトファイルには直接アクセスできます。ファイルが数個しかない場合、またはそれらをすぐにテストしたい場合、これは非常に簡単な方法です。
DotPrompt を使用する;var promptFile = PromptFile.FromFile("path/to/prompt-file.prompt");var systemPrompt = プロンプトファイル.GetSystemPrompt(null);var userPrompt = プロンプトファイル.GetUserPrompt(new Dictionary<string, object>{{ "トピック", "Bluetooth" },{ "スタイル", "中古車セールスマン" }});
プロンプト ファイルに上記の例が含まれている場合、次のように生成されます。
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
これにより、LLM から次のような応答が返される可能性があります (申し訳ありません)。
紳士淑女の皆様、お集まりください。私たちのガジェットとの接続方法に革命をもたらした現代テクノロジーの奇跡についてお話しましょう。私は Bluetooth について話しています。 Bluetooth は縁の下の力持ちであり、私たちの生活をより便利に、よりつながり、そして間違いなくよりハイテクにする秘密のソースです。これを想像してください。お気に入りのデバイス間のシームレスなワイヤレス通信。コードが絡まったり、混乱したりすることはもうありません。未来の最前列への VIP パスを手に入れたようなものです。
...
プロンプト マネージャーは、プロンプト ファイルを処理するための推奨される方法です。これにより、それらを場所からロードし、名前でアクセスして、アプリケーションで使用することができます。
プロンプト マネージャーのデフォルトでは、ローカルprompts
フォルダー内のファイルにアクセスしますが、必要に応じて別のパスを指定することもできます。
// `prompts` ディレクトリのデフォルトの場所からロードしますvar promptManager = new PromptManager();var promptFile = promptManager.GetPromptFile("example");// 別のフォルダーを使用しますvar promptManager = new PromptManager("another-location"); var promptFile = promptManager.GetPromptFile("example");// すべてのプロンプトを一覧表示しますloadvarpromptNames = プロンプトマネージャー.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プロンプトファイル = プロンプトマネージャー.GetPromptFile("example");// システム プロンプト メソッドとユーザー プロンプト メソッドは、必要な値を含む辞書を受け取ります。 // テンプレート。何も必要ない場合は、単純に null.var systemPrompt = PromptFile.GetSystemPrompt(null);var userPrompt = PromptFile.GetUserPrompt(new Dictionary<string, object>{{ "topic", "bluetooth" },{ "style" を渡すだけで済みます。 , "中古車営業マン" }});var client = openAiClient.GetChatClient(promptFile.Model ?? "default-model");var completed = await client.CompleteChatAsync([new SystemChatMessage(systemPrompt),new UserChatMessage(userPrompt)],new ChatCompletionOptions(ResponseFormat = promptFile.OutputFormat == OutputFormat.Json ? ChatResponseFormat.JsonObject : ChatResponseFormat.Text,Temperature = promptFile.Config.Temperature ,MaxTokens =プロンプトファイル.Config.MaxTokens));
または、OpenAI が提供する拡張メソッドを使用します。
System.ClientModel を使用する;Azure.AI.OpenAI を使用する;DotPrompt を使用する;DotPrompt.Extensions.OpenAi を使用する;var openAiClient = new(new Uri("https://endpoint"), new ApiKeyCredential("abc123"));var promptManager = 新しい PromptManager();var プロンプトファイル = promptManager.GetPromptFile("example");var PromptValues = new Dictionary<string, object>{{ "トピック", "bluetooth" },{ "スタイル", "中古車セールスマン" }};var client = openAiClient.GetChatClient(promptFile.Model ?? "default-model" );var 完了 = 待機しますclient.CompleteChatAsync(promptFile.ToOpenAiChatMessages(promptValues),promptFile.ToOpenAiChatCompletionOptions());var response = completed.Value;Console.WriteLine(response.Content[0].Text);
これで、プロンプトを変更する必要がある場合は、プロンプト ファイルを変更するだけでコードをそのまま残すことができます (パラメーターが変更されないと仮定して)。
上記は、DotPrompt を使用してディスクからプロンプト ファイルを読み取る方法を示しています。しかし、クラウド ストレージ サービスやデータベースなど、より中心的な場所にプロンプトを表示したい場合はどうすればよいでしょうか?プロンプト マネージャーは、 IPromptStore
インスタンスを引数として受け取ることができます。上記のすべての例では、含まれているFilePromptStore
を使用していますが、独自のものを構築することもできます。インターフェースを実装するだけで完了です。
例として、Azure Storage Table Store を使用してプロンプトの詳細を保持する簡単な実装を次に示します。
/// <summary>/// Azure Storage Tables 用の 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 = promptEntities.Select(pe => pe.ToPromptFile()).ToList();return PromptFiles;}/// <summary >/// テーブル client を取得します/// </summary>private static TableClient GetTableClient(){// ここの構成項目を実際の値に置き換えるか、 // Entra ベースの認証の使用に切り替えますvar client = new TableServiceClient(new Uri($"https://{Configuration.StorageAccountName}.table.core.windows.net/"),new TableSharedKeyCredential(Configuration.StorageAccountName, Configuration.StorageAccountKey) );var tableClient = client.GetTableClient("プロンプト");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>public string?モデル { 取得;セット; }/// <summary>/// 出力形式を取得、設定します/// </summary>public string OutputFormat { get;セット; } = string.Empty;/// <summary>/// トークンの最大数を取得、設定します/// </summary>public int MaxTokens { get;セット; }/// <summary>/// JSON 文字列値として保持されるパラメータ情報を取得、設定します/// </summary>public string Parameters { 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>();vardefaults = new Dictionary<string, object>();// パラメータ値がある場合は、それらを辞書に変換しますif (!string.IsNullOrEmpty(Parameters)){varentityParameters = (JsonObject)JsonNode.Parse(Parameters)!;foreach (var (prop, propType) inentityParameters){parameters.Add(prop, propType?.AsValue().ToString () ?? string.Empty);}}// デフォルト値がある場合は、それらを辞書に変換しますif (!string.IsNullOrEmpty(Default)){varentityDefaults = (JsonObject)JsonNode.Parse(Default)!;foreach (var (prop,defaultValue)inentityDefaults){defaults.Add(prop,defaultValue?.AsValue().GetValue <object>() ?? string.Empty);}}// 新しいプロンプト ファイルを生成しますvar protected File = new PromptFile{Name = RowKey,Model = Model,Config = new PromptConfig{OutputFormat = Enum.Parse<OutputFormat>(OutputFormat, true),MaxTokens = MaxTokens,Input = new InputSchema{Parameters = パラメータ,Default = デフォルト}},Prompts = 新しいプロンプト{システム= SystemPrompt,User = UserPrompt}};プロンプトファイルを返す;}}
そして、これを使用するには、次のようにします
varプロンプトマネージャー = 新しいプロンプトマネージャー(新しい AzureTablePromptStore()); var プロンプトファイル = プロンプトマネージャー.GetPromptFile("例");
ここにはまだ作業の余地があり、私たちが検討している項目のいくつかは次のとおりです。
追加の構成オプション
追加のプロンプト手法
フィードバックを歓迎します。何か見たいものはありますか?お知らせください