Azure OpenAI client library for .NET
Azure OpenAI Service へのアクセスは、REST API で行えますが、JSON 文字列の要求の作成、応答の解析を行うのが手間になります。そこで、REST API をラップした SDK として、Azure OpenAI client library が提供されています。.NET での開発では、Azure OpenAI client library for .NET が利用できます。
一方で、REST API での開発と比べて、どのような HTTP 要求 / 応答が行われているか分からないためデバッグ時に問題となることがあります。このような時に、Azure OpenAI client library のログを有効にすることで、ライブラリ経由での HTTP 要求 / 応答を確認することができるようになります。
ロギングの有効化
Azure OpenAI client library は、Azure SDK としてリリースされているので、Azure SDK で提供されている方法で、ロギングを有効化できます。
具体的には、Azure.Core.Diagnostics.AzureEventSourceListener クラスのインスタンスを生成することで、ロギングを有効化できます。 AzureEventSourceListener のファクトリ メソッド CreateConsoleLogger, CreateTraceLogger を使用してインスタンスを生成することで、コンソール、トレースへのログ出力ができます。
// コンソールへの出力
using AzureEventSourceListener consoleListener =
AzureEventSourceListener.CreateConsoleLogger(EventLevel.Verbose);
// トレースへの出力
using AzureEventSourceListener traceListener =
AzureEventSourceListener.CreateTraceLogger(EventLevel.Verbose);
Visual Studio のデバッグ出力へのログ出力は、CreateTraceLogger によって有効になります。
ログ出力のコード例
ここで、Azure OpenAI client library を使用して簡単なコンソールアプリを書いてログ出力の例を示します。 エントリポイントの最初で、CreateConsoleLogger, CreateTraceLogger を使用してログ出力を有効化しています。引数には、出力するログレベルを設定しています。
using Azure.AI.OpenAI;
using Azure.Core.Diagnostics;
using System.Diagnostics.Tracing;
namespace LoggingDemoConsoleApp001;
internal class Program
{
async static Task Main(string[] args)
{
// コンソールへの出力
using AzureEventSourceListener consoleListener =
AzureEventSourceListener.CreateConsoleLogger(EventLevel.Verbose);
// トレースへの出力
using AzureEventSourceListener traceListener =
AzureEventSourceListener.CreateTraceLogger(EventLevel.Verbose);
var endpoint = "<AOAI エンドポイント>";
var apiKey = "<AOAI API キー>";
var deploymentModelName = "<AOAI モデル名>";
OpenAIClient openAIClient = new(new Uri(endpoint), new Azure.AzureKeyCredential(apiKey));
ChatCompletionsOptions chatCompletionsOptions = new()
{
DeploymentName = deploymentModelName,
Messages =
{
new ChatRequestSystemMessage("あなたは和歌の名手です。与えられた句を丁寧に解説してください。さらに、より良い句にするための添削を行い、添削のポイントを丁寧に解説してください。"),
new ChatRequestUserMessage("この世をば わが世とぞ思ふ 望月の 欠けたることも なしと思へば"),
},
};
var response = await openAIClient.GetChatCompletionsAsync(chatCompletionsOptions);
Console.WriteLine(response.Value.Choices[0].Message.Content);
Console.ReadLine();
}
}
このコンソール アプリの実行時のコンソール出力は以下のようになります。
[Informational] Azure-Core: Request [f19b3a4c-a10b-44bd-80e0-3ee5a390c4cd] POST https://<endpoint>.openai.azure.com/openai/deployments/gpt-35-turbo-16k/chat/completions?api-version=2023-12-01-preview
Accept:application/json
Content-Type:application/json
x-ms-client-request-id:f19b3a4c-a10b-44bd-80e0-3ee5a390c4cd
x-ms-return-client-request-id:true
User-Agent:azsdk-net-AI.OpenAI/1.0.0-beta.13 (.NET 8.0.1; Microsoft Windows 10.0.22621)
api-key:REDACTED
client assembly: Azure.AI.OpenAI
[Informational] Azure-Core: Response [f19b3a4c-a10b-44bd-80e0-3ee5a390c4cd] 200 OK (06.7s)
Cache-Control:no-cache, must-revalidate
Access-Control-Allow-Origin:REDACTED
apim-request-id:REDACTED
Strict-Transport-Security:REDACTED
X-Content-Type-Options:REDACTED
x-ms-region:REDACTED
x-ratelimit-remaining-requests:REDACTED
x-ratelimit-remaining-tokens:REDACTED
x-accel-buffering:REDACTED
X-Request-ID:REDACTED
x-ms-client-request-id:f19b3a4c-a10b-44bd-80e0-3ee5a390c4cd
azureml-model-session:REDACTED
Date:Sun, 18 Feb 2024 13:04:56 GMT
Content-Length:2922
Content-Type:application/json
[Warning] Azure-Core: Response [f19b3a4c-a10b-44bd-80e0-3ee5a390c4cd] took 06.7s
「この世をば わが世とぞ思う 望月の 欠けたることも なしと思えば」という句は、「この世界を私の世界と思えば、望月の欠けたることもない」という意味です。この 句は、「この世界全体を私のものと考えれば、望月の欠けた部分も何もない」という、一種の宇宙観を表現しています。句の中で「ば」という助詞が使われていることから、 作者の心情や思想が含まれていると考えられます。
この句にはいくつかの改善点があります。まず、「ば」という助詞は古風な言い回しであり、現代の和歌にはあまり使用されません。また、「思ふ」という古代仮名遣いは「 思う」という現代仮名遣いで置き換えることができます。さらに、「望月の」の表現は少し古風な印象がありますので、より具体的な言葉を使うことでイメージが鮮明になる でしょう。
改善版として、以下のような句が考えられます。
この世を 私の世と思えば 月は満ち その光射す場所 亡くはないと知る
改善版では「この世をば」を「この世を」としたことで、より現代的な印象となりました。また、「わが世とぞ思ふ」を「私の世と思えば」と書き換えることで、「思ふ」の 古さを解消し、自然な言い回しになりました。さらに、「望月の 欠けたることも」という表現を「月は満ち その光射す場所」と置き換えることで、月の満ち欠けではなく 、光の場所に焦点を当てたより具体的な表現になりました。
このように、句を改善する際のポイントは、古風な表現や言葉の選び方に注意しながら、より具体的でイメージの鮮明な言葉を使い、現代の感覚に合った表現にすることです 。また、作者が表現したい思いや情景を丁寧に読み取り、それをできるだけ直接的に表現するように心がけると良いでしょう。
[Informational] のプレフィックスで、Azure OpenAI client library からの HTTP リクエスト/レスポンス、ヘッダーが出力されていることが分かります。
HTTP Body のログ出力
ここまでのコードでは、HTTP リクエスト / レスポンスの Body がログに出力されません。これまでのコードに加えて OpenAIClient のインスタンス生成時に、OpenAIClientOptions
オプションの Diagnostics.IsLoggingContentEnabled
プロパティを設定することで、HTTP Body をログに出力できます。
以下は、HTTP Body ログ出力を有効化したコードになります。
Diagnostics.LoggedContentSizeLimit
は、ログ出力サイズの制限となります。デフォルトでは、4k Byte となります。会話履歴としてコンテキストを保持する場合や多くのプロンプトを設定する場合は、値を多めに設定しておくと良いと思います。
using Azure.AI.OpenAI;
using Azure.Core.Diagnostics;
using System.Diagnostics.Tracing;
namespace LoggingDemoConsoleApp001;
internal class Program
{
async static Task Main(string[] args)
{
// コンソールへの出力
using AzureEventSourceListener consoleListener =
AzureEventSourceListener.CreateConsoleLogger(EventLevel.Verbose);
// トレースへの出力
using AzureEventSourceListener traceListener =
AzureEventSourceListener.CreateTraceLogger(EventLevel.Verbose);
var endpoint = "<AOAI エンドポイント>";
var apiKey = "<AOAI API キー>";
var deploymentModelName = "<AOAI モデル名>";
OpenAIClientOptions openAIClientOptions = new()
{
Diagnostics =
{
// コンテント出力の有効化
IsLoggingContentEnabled = true,
// コンテント出力サイズ制限の設定。デフォルト 4096。
LoggedContentSizeLimit = 16 * 1024,
},
};
OpenAIClient openAIClient = new(new Uri(endpoint), new Azure.AzureKeyCredential(apiKey), openAIClientOptions);
ChatCompletionsOptions chatCompletionsOptions = new()
{
DeploymentName = deploymentModelName,
Messages =
{
new ChatRequestSystemMessage("あなたは和歌の名手です。与えられた句を丁寧に解説してください。さらに、より良い句にするための添削を行い、添削のポイントを丁寧に解説してください。"),
new ChatRequestUserMessage("この世をば わが世とぞ思ふ 望月の 欠けたることも なしと思へば"),
},
};
var response = await openAIClient.GetChatCompletionsAsync(chatCompletionsOptions);
Console.WriteLine(response.Value.Choices[0].Message.Content);
Console.ReadLine();
}
}
このコンソール アプリの実行時のコンソール出力は以下のようになります。
[Informational] Azure-Core: Request [6bf17f51-a77b-49b7-ba7e-2addee532f9f] POST https://<endpoint>.openai.azure.com/openai/deployments/gpt-35-turbo-16k/chat/completions?api-version=2023-12-01-preview
Accept:application/json
Content-Type:application/json
x-ms-client-request-id:6bf17f51-a77b-49b7-ba7e-2addee532f9f
x-ms-return-client-request-id:true
User-Agent:azsdk-net-AI.OpenAI/1.0.0-beta.13 (.NET 8.0.1; Microsoft Windows 10.0.22621)
api-key:REDACTED
client assembly: Azure.AI.OpenAI
[Verbose] Azure-Core: Request [6bf17f51-a77b-49b7-ba7e-2addee532f9f] content: {"messages":[{"content":"\u3042\u306A\u305F\u306F\u548C\u6B4C\u306E\u540D\u624B\u3067\u3059\u3002\u4E0E\u3048\u3089\u308C\u305F\u53E5\u3092\u4E01\u5BE7\u306B\u89E3\u8AAC\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u3055\u3089\u306B\u3001\u3088\u308A\u826F\u3044\u53E5\u306B\u3059\u308B\u305F\u3081\u306E\u6DFB\u524A\u3092\u884C\u3044\u3001\u6DFB\u524A\u306E\u30DD\u30A4\u30F3\u30C8\u3092\u4E01\u5BE7\u306B\u89E3\u8AAC\u3057\u3066\u304F\u3060\u3055\u3044\u3002","role":"system"},{"content":"\u3053\u306E\u4E16\u3092\u3070\u3000\u308F\u304C\u4E16\u3068\u305E\u601D\u3075\u3000\u671B\u6708\u306E\u3000\u6B20\u3051\u305F\u308B\u3053\u3068\u3082\u3000\u306A\u3057\u3068\u601D\u3078\u3070","role":"user"}],"model":"gpt-35-turbo-16k"}
[Informational] Azure-Core: Response [6bf17f51-a77b-49b7-ba7e-2addee532f9f] 200 OK (05.7s)
Cache-Control:no-cache, must-revalidate
Access-Control-Allow-Origin:REDACTED
apim-request-id:REDACTED
Strict-Transport-Security:REDACTED
X-Content-Type-Options:REDACTED
x-ms-region:REDACTED
x-ratelimit-remaining-requests:REDACTED
x-ratelimit-remaining-tokens:REDACTED
x-accel-buffering:REDACTED
X-Request-ID:REDACTED
x-ms-client-request-id:6bf17f51-a77b-49b7-ba7e-2addee532f9f
azureml-model-session:REDACTED
Date:Sun, 18 Feb 2024 13:45:04 GMT
Content-Length:2634
Content-Type:application/json
[Verbose] Azure-Core: Response [6bf17f51-a77b-49b7-ba7e-2addee532f9f] content: {"id":"chatcmpl-8tblT1hSRsjXvR5f721Eqv125ZgWr","object":"chat.completion","created":1708263899,"model":"gpt-35-turbo-16k","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"解説: \nこの句は、自分自身の人生がとても尊いものであるという思いを表現しています。望月の欠けたることもな しと思えるほど、この世が自分にとって完璧なものだと思っている様子が伝わってきます。\n\n添削: \nこの世をば わが世とぞ思ふ 望月の\nこの句は、主語がなく抽象的な表現が多いため、具体的なイメージが伝わりにくいです。また、「ば」や「ぞ」といった助詞や助動詞も使用されていますが、これらは古語であり 、現代の和歌にはあまり使用しないです。よりシンプルな言葉を使い、具体的なイメージを持たせるように改善しましょう。\n\n添削例: \nこの身こそ 宝と思いけむ 欠け たる 望月もあらじ と信じきれば\n\n添削のポイント: \n1. 具体的な言葉を使用する: 「わが世」という表現では、具体的にどのような感情や思いが込められているのかが分かりにくいです。具体的な表現を使って感情や 思いを明確に伝えましょう。\n2. 古語を避ける: 「ば」「ぞ」などの古語は、現代の和歌であまり使用されない表現です。できるだけシンプルな言葉を用いるようにしましょう。\n3. 対象物を明確にする: 「望月の欠けたることもなし」と いう表現は、どのような状況や具体的なイメージを指しているのかがわかりにくいです。対象物を具体的に説明することで、読者にもっと具体的なイメージが伝わります。\n4. 言葉の響きやリズムにこだわる: 和歌は音やリズムにも美しさが求められます。語感や響きを考慮して、より美しい言葉や音の組み合わせを探しましょう。"},"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"usage":{"prompt_tokens":124,"completion_tokens":673,"total_tokens":797}}
[Warning] Azure-Core: Response [6bf17f51-a77b-49b7-ba7e-2addee532f9f] took 05.7s
解説:
この句は、自分自身の人生がとても尊いものであるという思いを表現しています。望月の欠けたることもなしと思えるほど、この世が自分にとって完璧なものだと思っている 様子が伝わってきます。
添削:
この世をば わが世とぞ思ふ 望月の
この句は、主語がなく抽象的な表現が多いため、具体的なイメージが伝わりにくいです。また、「ば」や「ぞ」といった助詞や助動詞も使用されていますが、これらは古語であり、現代の和歌にはあまり使用しないです。よりシンプルな言葉を使い、具体的なイメージを持たせるように改善しましょう。
添削例:
この身こそ 宝と思いけむ 欠けたる 望月もあらじ と信じきれば
添削のポイント:
1. 具体的な言葉を使用する: 「わが世」という表現では、具体的にどのような感情や思いが込められているのかが分かりにくいです。具体的な表現を使って感情や思いを明確に伝えましょう。
2. 古語を避ける: 「ば」「ぞ」などの古語は、現代の和歌であまり使用されない表現です。できるだけシンプルな言葉を用いるようにしましょう。
3. 対象物を明確にする: 「望月の欠けたることもなし」という表現は、どのような状況や具体的なイメージを指しているのかがわかりにくいです。対象物を具体的に説明することで、読者にもっと具体的なイメージが伝わります。
4. 言葉の響きやリズムにこだわる: 和歌は音やリズムにも美しさが求められます。語感や響きを考慮して、より美しい言葉や音の組み合わせを探しましょう。
今回のコードでは、[Verbose] のプレフィックスで、Azure OpenAI client library からの HTTP リクエスト / レスポンス Body が出力されていることが分かります。ただし、要求の日本語部分が Unicode エスケープシーケンスになってしまっています。これを改善するためには、カスタムのリスナーを作成する必要があります。この方法は、別途説明したいと思います。
また、[Verbose] からわかるように、ログレベルが Verbose で設定されているので、CreateConsoleLogger, CreateTraceLogger の引数で、EventLevel.Verbose
か、EventLevel.LogAlways
を設定しないと Body 部は、出力されません。引数なしでコールすると、EventLevel.Informational
となるので注意が必要です。
以上、参考までに。
コメント (0)
コメントの投稿