Mackerel Advent Calendar 2024 の21日目です。
20日目は
id:lufiabb さんの 意思決定のためにOpenTelemetry Metricsを利用する でした。
こんにちは、Mackerel開発チームWebアプリケーションエンジニアの
id:yohfee です。
今年の冬はあまり寒くなくない?と、まだ仕事部屋の暖房をつけていなかったのですが、ここのところは画像生成やSLMなどAIの利用や学習などでGPUが元気に働いて部屋を暖めてくれていたのでした。
その様子は mackerel-plugin-nvidia-smi を利用した次のグラフから観察することができます。

さて前回はその一環として、MackerelのドキュメントをRAGとしてSLMから参照するってことをやってました。
Semantic KernelのメトリックやトレースはOpenTelemetry形式で取れるとのことなので、今回はメトリックをMackerelに投稿してみます。 トレースはVaxilaの準備をしてなかったのでまた今度…
> dotnet add package OpenTelemetry --version 1.10.0 > dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol --version 1.10.0 > dotnet add package Microsoft.SemanticKernel --version 1.32.0 > dotnet add package Microsoft.SemanticKernel.Connectors.Onnx --version 1.32.0-alpha
前回との差分として、SLMモデルを Phi-3.5-mini-instruct-onnx に更新してGPUから利用できるようにしたり、RAGのEmbeddingを省略したりなどありますが、基本的にSemantic Kernel方面のコードに変更はありません。 ちなみにPhi-3.5を使っているのは実質無料だからで、速度や精度が重要な場合はSemantic KernelのバックエンドをOpenAIなどに差し替えるのも簡単です。
OpenTelemetryの計装はMeterProvider を組み立ててExporterにMackerelのエンドポイントとAPIキーを設定するだけです。
いずれも環境変数で設定できるけど今回は素朴に。
アプリケーションの方に手を入れなくてもいいのはお手軽でいいですね。
.NETランタイムなど組み込みのメトリックを追加したい場合は .AddRuntimeInstrumentation() を足すだけなのも便利です。
open System open System.IO open Microsoft.SemanticKernel open Microsoft.SemanticKernel.ChatCompletion open Microsoft.SemanticKernel.Connectors.OpenAI open OpenTelemetry open OpenTelemetry.Metrics open OpenTelemetry.Resources AppContext.SetSwitch("Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnosticsSensitive", true) let resourceBuilder = ResourceBuilder.CreateDefault() |> _.AddService("OtelSkSample") let meterProvider = Sdk.CreateMeterProviderBuilder() |> _.SetResourceBuilder(resourceBuilder) |> _.AddMeter("Microsoft.SemanticKernel*") |> _.AddOtlpExporter(fun b -> b.Endpoint <- Uri "https://otlp.mackerelio.com:4317" b.Headers <- "Mackerel-Api-Key=****************************") |> _.Build() let kernel = Kernel.CreateBuilder() |> _.AddOnnxRuntimeGenAIChatCompletion( "phi-3.5", Path.Join("path", "to", "gpu-int4-awq-block-128") ) |> _.Build() let chatCompletionService = kernel.GetRequiredService<IChatCompletionService>() let executionSettings = OpenAIPromptExecutionSettings(MaxTokens = 200) while true do Console.ForegroundColor <- ConsoleColor.White Console.Write "\n>> " let arguments = KernelArguments executionSettings arguments.Add("input", Console.ReadLine()) let response = kernel.InvokePromptStreamingAsync("Question: {{$input}}", arguments) Console.ForegroundColor <- ConsoleColor.Cyan Console.Write "\n> " task { let enumerator = response.GetAsyncEnumerator() while! enumerator.MoveNextAsync() do Console.Write enumerator.Current } |> Async.AwaitTask |> Async.RunSynchronously Console.WriteLine()
実行しておしゃべりを楽しんだ後、メトリックエクスプローラーを覗いてみるとこんな感じでした。


前掲のドキュメントによるとカーネル関数の呼び出しごとに実行時間やトークン数が計測されるようです。 このままのグラフだとあまり役に立たないので、関数名あたりで集計するといい感じになりそうです。
ところで、最初はメトリックの投稿に失敗していたのですが、 OTEL_DIAGNOSTICS.json として次のようなファイルをプロジェクトのルートディレクトリに置いておくとOpenTelemetry.NET SDK自体のログが吐き出されるのがとても便利でした。
{ "LogDirectory": ".", "FileSize": 1024, "LogLevel": "LogAlways" }
余談ですが、はてなブログのAIタイトルアシスト機能で提案されたものがおもしろかったので供養しておきます。
- Mackerelの開発チームが暖房をつけない理由
- 冬の暖房不要説に挑戦!Mackerel開発者のグラフ観察奮戦記
- 冬のGPU活用術!部屋を暖めるAIの力
どうして…
Mackerel Advent Calendar 2024 の22日目は k-masuyaさんです。