次の方法で共有


テレメトリのより高度なシナリオ

Note

この記事では、 Aspire Dashboard を使用して説明します。 他のツールを使用する場合は、セットアップ手順で使用しているツールのドキュメントを参照してください。

関数の自動呼び出し

自動関数呼び出しは、モデルが関数呼び出しで応答したときにカーネルが関数を自動的に実行し、結果をモデルに返すセマンティック カーネル機能です。 この機能は、クエリで関数呼び出しを複数回繰り返して最終的な自然言語応答を取得する必要があるシナリオに役立ちます。 詳細については、GitHub サンプルを参照してください。

Note

関数呼び出しは、すべてのモデルでサポートされているわけではありません。

ヒント

"ツール" と "ツール呼び出し" という用語は、"関数" と "関数呼び出し" と同じ意味で使用される場合があります。

前提条件

  • 関数呼び出しをサポートする Azure OpenAI チャット完了デプロイ。
  • Docker
  • オペレーティング システム用の最新の .Net SDK
  • 関数呼び出しをサポートする Azure OpenAI チャット完了デプロイ。
  • Docker
  • Python 3.10、3.11、または 3.12 コンピューターにインストールされています。

Note

セマンティック カーネルの可観測性は、Java ではまだ使用できません。

セットアップ

新しいコンソール アプリケーションを作成する

ターミナルで次のコマンドを実行して、C# で新しいコンソール アプリケーションを作成します。

dotnet new console -n TelemetryAutoFunctionCallingQuickstart

コマンドの完了後、新しく作成されたプロジェクト ディレクトリに移動します。

必要なパッケージをインストールする

  • セマンティック カーネル

    dotnet add package Microsoft.SemanticKernel
    
  • OpenTelemetry コンソール エクスポーター

    dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
    

セマンティック カーネルを使用して単純なアプリケーションを作成する

プロジェクト ディレクトリから、お気に入りのエディターで Program.cs ファイルを開きます。 セマンティック カーネルを使用してチャット完了モデルにプロンプトを送信する単純なアプリケーションを作成します。 既存のコンテンツを次のコードに置き換え、 deploymentNameendpoint、および apiKeyに必要な値を入力します。

using System.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

namespace TelemetryAutoFunctionCallingQuickstart
{
    class BookingPlugin
    {
        [KernelFunction("FindAvailableRooms")]
        [Description("Finds available conference rooms for today.")]
        public async Task<List<string>> FindAvailableRoomsAsync()
        {
            // Simulate a remote call to a booking system.
            await Task.Delay(1000);
            return ["Room 101", "Room 201", "Room 301"];
        }

        [KernelFunction("BookRoom")]
        [Description("Books a conference room.")]
        public async Task<string> BookRoomAsync(string room)
        {
            // Simulate a remote call to a booking system.
            await Task.Delay(1000);
            return $"Room {room} booked.";
        }
    }

    class Program
    {
        static async Task Main(string[] args)
        {
            // Endpoint to the Aspire Dashboard
            var endpoint = "http://localhost:4317";

            var resourceBuilder = ResourceBuilder
                .CreateDefault()
                .AddService("TelemetryAspireDashboardQuickstart");

            // Enable model diagnostics with sensitive data.
            AppContext.SetSwitch("Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnosticsSensitive", true);

            using var traceProvider = Sdk.CreateTracerProviderBuilder()
                .SetResourceBuilder(resourceBuilder)
                .AddSource("Microsoft.SemanticKernel*")
                .AddOtlpExporter(options => options.Endpoint = new Uri(endpoint))
                .Build();

            using var meterProvider = Sdk.CreateMeterProviderBuilder()
                .SetResourceBuilder(resourceBuilder)
                .AddMeter("Microsoft.SemanticKernel*")
                .AddOtlpExporter(options => options.Endpoint = new Uri(endpoint))
                .Build();

            using var loggerFactory = LoggerFactory.Create(builder =>
            {
                // Add OpenTelemetry as a logging provider
                builder.AddOpenTelemetry(options =>
                {
                    options.SetResourceBuilder(resourceBuilder);
                    options.AddOtlpExporter(options => options.Endpoint = new Uri(endpoint));
                    // Format log messages. This is default to false.
                    options.IncludeFormattedMessage = true;
                    options.IncludeScopes = true;
                });
                builder.SetMinimumLevel(LogLevel.Information);
            });

            IKernelBuilder builder = Kernel.CreateBuilder();
            builder.Services.AddSingleton(loggerFactory);
            builder.AddAzureOpenAIChatCompletion(
                deploymentName: "your-deployment-name",
                endpoint: "your-azure-openai-endpoint",
                apiKey: "your-azure-openai-api-key"
            );
            builder.Plugins.AddFromType<BookingPlugin>();

            Kernel kernel = builder.Build();

            var answer = await kernel.InvokePromptAsync(
                "Reserve a conference room for me today.",
                new KernelArguments(
                    new OpenAIPromptExecutionSettings {
                        ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
                    }
                )
            );

            Console.WriteLine(answer);
        }
    }
}

上記のコードでは、まず、 FindAvailableRoomsAsyncBookRoomAsyncの 2 つの機能を持つモック会議室予約プラグインを定義します。 次に、プラグインをカーネルに登録する単純なコンソール アプリケーションを作成し、必要に応じて自動的に関数を呼び出すようにカーネルに依頼します。

新しい Python 仮想環境を作成する

python -m venv telemetry-auto-function-calling-quickstart

仮想環境をアクティブにします。

telemetry-auto-function-calling-quickstart\Scripts\activate

必要なパッケージをインストールする

pip install semantic-kernel opentelemetry-exporter-otlp-proto-grpc

セマンティック カーネルを使用して単純な Python スクリプトを作成する

新しい Python スクリプトを作成し、お気に入りのエディターで開きます。

New-Item -Path telemetry_auto_function_calling_quickstart.py -ItemType file

セマンティック カーネルを使用してチャット完了モデルにプロンプトを送信する単純な Python スクリプトを作成します。 既存のコンテンツを次のコードに置き換え、 deployment_nameendpoint、および api_keyに必要な値を入力します。

import asyncio
import logging
from typing import Annotated

from opentelemetry._logs import set_logger_provider
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.metrics import set_meter_provider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.metrics.view import DropAggregation, View
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.semconv.resource import ResourceAttributes
from opentelemetry.trace import set_tracer_provider

from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.functions.kernel_function_decorator import kernel_function


class BookingPlugin:
    @kernel_function(
        name="find_available_rooms",
        description="Find available conference rooms for today.",
    )
    def find_available_rooms(self,) -> Annotated[list[str], "A list of available rooms."]:
        return ["Room 101", "Room 201", "Room 301"]

    @kernel_function(
        name="book_room",
        description="Book a conference room.",
    )
    def book_room(self, room: str) -> Annotated[str, "A confirmation message."]:
        return f"Room {room} booked."


# Endpoint to the Aspire Dashboard
endpoint = "http://localhost:4317"

# Create a resource to represent the service/sample
resource = Resource.create({ResourceAttributes.SERVICE_NAME: "telemetry-aspire-dashboard-quickstart"})


def set_up_logging():
    exporter = OTLPLogExporter(endpoint=endpoint)

    # Create and set a global logger provider for the application.
    logger_provider = LoggerProvider(resource=resource)
    # Log processors are initialized with an exporter which is responsible
    # for sending the telemetry data to a particular backend.
    logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter))
    # Sets the global default logger provider
    set_logger_provider(logger_provider)

    # Create a logging handler to write logging records, in OTLP format, to the exporter.
    handler = LoggingHandler()
    # Add filters to the handler to only process records from semantic_kernel.
    handler.addFilter(logging.Filter("semantic_kernel"))
    # Attach the handler to the root logger. `getLogger()` with no arguments returns the root logger.
    # Events from all child loggers will be processed by this handler.
    logger = logging.getLogger()
    logger.addHandler(handler)
    logger.setLevel(logging.INFO)


def set_up_tracing():
    exporter = OTLPSpanExporter(endpoint=endpoint)

    # Initialize a trace provider for the application. This is a factory for creating tracers.
    tracer_provider = TracerProvider(resource=resource)
    # Span processors are initialized with an exporter which is responsible
    # for sending the telemetry data to a particular backend.
    tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
    # Sets the global default tracer provider
    set_tracer_provider(tracer_provider)


def set_up_metrics():
    exporter = OTLPMetricExporter(endpoint=endpoint)

    # Initialize a metric provider for the application. This is a factory for creating meters.
    meter_provider = MeterProvider(
        metric_readers=[PeriodicExportingMetricReader(exporter, export_interval_millis=5000)],
        resource=resource,
        views=[
            # Dropping all instrument names except for those starting with "semantic_kernel"
            View(instrument_name="*", aggregation=DropAggregation()),
            View(instrument_name="semantic_kernel*"),
        ],
    )
    # Sets the global default meter provider
    set_meter_provider(meter_provider)


# This must be done before any other telemetry calls
set_up_logging()
set_up_tracing()
set_up_metrics()


async def main():
    # Create a kernel and add a service
    kernel = Kernel()
    kernel.add_service(AzureChatCompletion(
        api_key="your-azure-openai-api-key",
        endpoint="your-azure-openai-endpoint",
        deployment_name="your-deployment-name"
    ))
    kernel.add_plugin(BookingPlugin(), "BookingPlugin")

    answer = await kernel.invoke_prompt(
        "Reserve a conference room for me today.",
        arguments=KernelArguments(
            settings=PromptExecutionSettings(
                function_choice_behavior=FunctionChoiceBehavior.Auto(),
            ),
        ),
    )
    print(answer)


if __name__ == "__main__":
    asyncio.run(main())

上記のコードでは、まず、 find_available_roomsbook_roomの 2 つの機能を持つモック会議室予約プラグインを定義します。 次に、プラグインをカーネルに登録する単純な Python スクリプトを作成し、必要に応じて自動的に関数を呼び出すようにカーネルに依頼します。

環境変数

カーネルが AI コネクタのスパンを出力できるようにするために必要な環境変数を設定する方法の詳細については、この article を参照してください。

Note

セマンティック カーネルの可観測性は、Java ではまだ使用できません。

アスパイア ダッシュボードを開始する

ここに 指示に従って ダッシュボードを起動します。 ダッシュボードが実行されたら、ブラウザーを開き、 http://localhost:18888 に移動してダッシュボードにアクセスします。

[ファイル名を指定して実行]

次のコマンドを使用してコンソール アプリケーションを実行します。

dotnet run

次のコマンドを使用して Python スクリプトを実行します。

python telemetry_auto_function_calling_quickstart.py

Note

セマンティック カーネルの可観測性は、Java ではまだ使用できません。

次のような出力が表示されます。

Room 101 has been successfully booked for you today.

テレメトリ データを検査する

アプリケーションを実行したら、ダッシュボードに進み、テレメトリ データを調べます。

Traces タブでアプリケーションのトレースを見つけます。トレースには 5 つのスパンが必要です。

TracesAdvancedScenarioDotNet

これら 5 つのスパンは、自動関数呼び出しが有効になっているカーネルの内部操作を表します。 最初にモデルを呼び出し、関数呼び出しを要求します。 その後、カーネルは関数 FindAvailableRoomsAsync を自動的に実行し、結果をモデルに返します。 その後、モデルは予約を行うために別の関数呼び出しを要求し、カーネルは関数 BookRoomAsync を自動的に実行し、結果をモデルに返します。 最後に、モデルはユーザーに自然言語の応答を返します。

最後のスパンをクリックし、 gen_ai.content.prompt イベントでプロンプトを探すと、次のような内容が表示されます。

[
  { "role": "user", "content": "Reserve a conference room for me today." },
  {
    "role": "Assistant",
    "content": null,
    "tool_calls": [
      {
        "id": "call_NtKi0OgOllJj1StLkOmJU8cP",
        "function": { "arguments": {}, "name": "FindAvailableRooms" },
        "type": "function"
      }
    ]
  },
  {
    "role": "tool",
    "content": "[\u0022Room 101\u0022,\u0022Room 201\u0022,\u0022Room 301\u0022]"
  },
  {
    "role": "Assistant",
    "content": null,
    "tool_calls": [
      {
        "id": "call_mjQfnZXLbqp4Wb3F2xySds7q",
        "function": { "arguments": { "room": "Room 101" }, "name": "BookRoom" },
        "type": "function"
      }
    ]
  },
  { "role": "tool", "content": "Room Room 101 booked." }
]

これは、モデルとして構築され、カーネルが相互に対話するチャット履歴です。 これは、自然言語の応答を取得するために、最後のイテレーションでモデルに送信されます。

Traces タブでアプリケーションのトレースを見つけます。トレース内の 5 つのスパンは、AutoFunctionInvocationLoopスパンの下にグループ化する必要があります。

TracesAdvancedScenarioPython

これら 5 つのスパンは、自動関数呼び出しが有効になっているカーネルの内部操作を表します。 最初にモデルを呼び出し、関数呼び出しを要求します。 その後、カーネルは関数 find_available_rooms を自動的に実行し、結果をモデルに返します。 その後、モデルは予約を行うために別の関数呼び出しを要求し、カーネルは関数 book_room を自動的に実行し、結果をモデルに返します。 最後に、モデルはユーザーに自然言語の応答を返します。

最後のスパンをクリックし、 gen_ai.content.prompt イベントでプロンプトを探すと、次のような内容が表示されます。

[
  { "role": "user", "content": "Reserve a conference room for me today." },
  {
    "role": "assistant",
    "tool_calls": [
      {
        "id": "call_ypqO5v6uTRlYH9sPTjvkGec8",
        "type": "function",
        "function": {
          "name": "BookingPlugin-find_available_rooms",
          "arguments": "{}"
        }
      }
    ]
  },
  {
    "role": "tool",
    "content": "['Room 101', 'Room 201', 'Room 301']",
    "tool_call_id": "call_ypqO5v6uTRlYH9sPTjvkGec8"
  },
  {
    "role": "assistant",
    "tool_calls": [
      {
        "id": "call_XDZGeTfNiWRpYKoHoH9TZRoX",
        "type": "function",
        "function": {
          "name": "BookingPlugin-book_room",
          "arguments": "{\"room\":\"Room 101\"}"
        }
      }
    ]
  },
  {
    "role": "tool",
    "content": "Room Room 101 booked.",
    "tool_call_id": "call_XDZGeTfNiWRpYKoHoH9TZRoX"
  }
]

これは、モデルとして構築され、カーネルが相互に対話するチャット履歴です。 これは、自然言語の応答を取得するために、最後のイテレーションでモデルに送信されます。

Note

セマンティック カーネルの可観測性は、Java ではまだ使用できません。

エラー処理

関数の実行中にエラーが発生した場合、カーネルは自動的にエラーをキャッチし、エラー メッセージをモデルに返します。 モデルでは、このエラー メッセージを使用して、ユーザーに自然言語の応答を提供できます。

C# コードの BookRoomAsync 関数を変更して、エラーをシミュレートします。

[KernelFunction("BookRoom")]
[Description("Books a conference room.")]
public async Task<string> BookRoomAsync(string room)
{
    // Simulate a remote call to a booking system.
    await Task.Delay(1000);

    throw new Exception("Room is not available.");
}

アプリケーションをもう一度実行し、ダッシュボードでトレースを観察します。 カーネル関数呼び出しを表すスパンがエラーで表示されます。

TracesAdvancedScenarioErrorDotNet

Note

エラーに対するモデルの応答は、モデルが確率的であるため、アプリケーションを実行するたびに異なる可能性が非常に高くなります。 モデルが 3 つのすべての部屋を同時に予約している場合や、1 つの部屋を最初に予約した後、もう 2 つを 2 回目に予約する場合などが表示される場合があります。

Python コードの book_room 関数を変更して、エラーをシミュレートします。

@kernel_function(
    name="book_room",
    description="Book a conference room.",
)
async def book_room(self, room: str) -> Annotated[str, "A confirmation message."]:
    # Simulate a remote call to a booking system
    await asyncio.sleep(1)

    raise Exception("Room is not available.")

アプリケーションをもう一度実行し、ダッシュボードでトレースを観察します。 エラーとスタック トレースを含むカーネル関数呼び出しを表すスパンが表示されます。

TracesAdvancedScenarioErrorPython

Note

エラーに対するモデルの応答は、モデルが確率的であるため、アプリケーションを実行するたびに異なる可能性が非常に高くなります。 モデルが 3 つのすべての部屋を同時に予約している場合や、1 つの部屋を最初に予約した後、もう 2 つを 2 回目に予約する場合などが表示される場合があります。

Note

セマンティック カーネルの可観測性は、Java ではまだ使用できません。

次の手順と詳細を読む

運用環境では、サービスが多数の要求を受け取る場合があります。 セマンティック カーネルは大量のテレメトリ データを生成します。 その一部はユース ケースには役に立たない可能性があり、データを格納するための不要なコストが発生します。 サンプリング機能を使用して、収集されるテレメトリ データの量を減らすことができます。

セマンティック カーネルの可観測性は常に向上しています。 最新の更新プログラムと新機能は、 GitHub リポジトリで確認できます。