遙測的更進階案例
注意
本文將使用 Aspire 儀錶板 進行說明。 如果您想要使用其他工具,請參閱您在安裝指示中使用的工具檔。
自動函數呼叫
自動函式呼叫是語意核心功能,可讓核心在模型回應函式呼叫時自動執行函式,並將結果傳回模型。 這項功能適用於查詢需要多次反覆執行函式呼叫才能取得最終自然語言回應的案例。 如需詳細資訊,請參閱這些 GitHub 範例。
注意
所有模型都不支援函數呼叫。
提示
您將會聽到「工具」和「工具呼叫」一詞,有時會與「函式」和「函式呼叫」交替使用。
必要條件
- 支援函式呼叫的 Azure OpenAI 聊天完成部署。
- Docker
- 您作業系統的最新 .Net SDK 。
- 支援函式呼叫的 Azure OpenAI 聊天完成部署。
- Docker
- 計算機上安裝 Python 3.10、3.11 或 3.12 。
注意
Java 尚未提供語意核心可觀察性。
設定
建立新的主控台應用程式
在終端機中,執行下列命令以在 C# 中建立新的控制台應用程式:
dotnet new console -n TelemetryAutoFunctionCallingQuickstart
在命令完成之後,流覽至新建立的項目目錄。
安裝必要的套件
語意核心
dotnet add package Microsoft.SemanticKernel
OpenTelemetry 控制台導出工具
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
使用語意核心建立簡單的應用程式
從項目目錄,使用您慣用的編輯器開啟 Program.cs
檔案。 我們將建立使用 Semantic Kernel 將提示傳送至聊天完成模型的簡單應用程式。 以下列程式代碼取代現有的內容,並填入、 endpoint
和 apiKey
的必要值deploymentName
:
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);
}
}
}
在上述程式代碼中,我們會先定義具有兩個功能的模擬會議室預約外掛程式: FindAvailableRoomsAsync
和 BookRoomAsync
。 然後,我們會建立簡單的控制台應用程式,以向核心註冊外掛程式,並要求核心在需要時自動呼叫函式。
建立新的 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 腳本。 以下列程式代碼取代現有的內容,並填入、 endpoint
和 api_key
的必要值deployment_name
:
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_rooms
和 book_room
。 然後,我們會建立簡單的 Python 腳本,向核心註冊外掛程式,並要求核心在需要時自動呼叫函式。
環境變數
注意
Java 尚未提供語意核心可觀察性。
啟動 Aspire 儀錶板
請遵循這裡的指示來啟動儀錶板。 儀錶板執行之後,請開啟瀏覽器並流覽至 http://localhost:18888
以存取儀錶板。
Run
使用下列命令執行主控台應用程式:
dotnet run
使用下列命令執行 Python 文稿:
python telemetry_auto_function_calling_quickstart.py
注意
Java 尚未提供語意核心可觀察性。
您應該會看到如下所示的輸出:
Room 101 has been successfully booked for you today.
檢查遙測數據
執行應用程式之後,請前往儀錶板來檢查遙測數據。
在 [追蹤] 索引標籤中 尋找應用程式的追蹤 。您應該在追蹤中跨越五個範圍:
這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." }
]
這是建立為模型和核心彼此互動的聊天記錄。 這會在上次反覆運算中傳送至模型,以取得自然語言回應。
在 [追蹤] 索引標籤中 尋找應用程式的追蹤 。您應該在範圍下 AutoFunctionInvocationLoop
分組的追蹤中,有五個範圍:
這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"
}
]
這是建立為模型和核心彼此互動的聊天記錄。 這會在上次反覆運算中傳送至模型,以取得自然語言回應。
注意
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.");
}
再次執行應用程式,並觀察儀錶板中的追蹤。 您應該看到代表核心函式呼叫的範圍,並出現錯誤:
注意
每次執行應用程式時,模型回應錯誤可能會有所不同,因為模型是隨機的。 您可能會看到模型同時保留所有三個房間,或第一次保留一個,然後保留另外兩個第二次等。
修改 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.")
再次執行應用程式,並觀察儀錶板中的追蹤。 您應該看到以錯誤和堆疊追蹤表示核心函式呼叫的時間範圍:
注意
每次執行應用程式時,模型回應錯誤可能會有所不同,因為模型是隨機的。 您可能會看到模型同時保留所有三個房間,或第一次保留一個,然後保留另外兩個第二次等。
注意
Java 尚未提供語意核心可觀察性。
後續步驟和進一步閱讀
在生產環境中,您的服務可能會收到大量要求。 語意核心會產生大量的遙測數據。 其中一些可能不適用於您的使用案例,並會導致不必要的成本來儲存數據。 您可以使用 取樣 功能來減少所收集的遙測數據量。
語意核心中的可檢視性不斷改善。 您可以在 GitHub 存放庫中找到最新的更新和新功能。