Cenários mais avançados para telemetria
Observação
Este artigo usará o Aspire Dashboard para ilustração. Se preferir usar outras ferramentas, consulte a documentação da ferramenta que você está usando nas instruções de configuração.
Chamada automática de função
A Chamada Automática de Função é um recurso do Kernel Semântico que permite que o kernel execute funções automaticamente quando o modelo responde com chamadas de função e forneça os resultados de volta ao modelo. Esse recurso é útil para cenários em que uma consulta requer várias iterações de chamadas de função para obter uma resposta final de linguagem natural. Para obter mais detalhes, consulte estes exemplos do GitHub.
Observação
A chamada de função não é suportada por todos os modelos.
Dica
Você ouvirá o termo "ferramentas" e "chamada de ferramenta" às vezes usados de forma intercambiável com "funções" e "chamada de função".
Pré-requisitos
- Uma implantação de conclusão de chat do OpenAI do Azure que dá suporte à chamada de função.
- Docker
- O SDK do .Net mais recente para o seu sistema operacional.
- Uma implantação de conclusão de chat do OpenAI do Azure que dá suporte à chamada de função.
- Docker
- Python 3.10, 3.11 ou 3.12 instalado em sua máquina.
Observação
A observabilidade semântica do kernel ainda não está disponível para Java.
Instalação
Crie um novo aplicativo de console
Em um terminal, execute o seguinte comando para criar um novo aplicativo de console em C#:
dotnet new console -n TelemetryAutoFunctionCallingQuickstart
Navegue até o diretório do projeto recém-criado após a conclusão do comando.
Instalar os pacotes necessários
Semantic Kernel
dotnet add package Microsoft.SemanticKernel
Exportador de console do OpenTelemetry
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
Criar um aplicativo simples com o Semantic Kernel
No diretório do projeto, abra o Program.cs
arquivo com seu editor favorito. Vamos criar um aplicativo simples que usa o Kernel Semântico para enviar um prompt para um modelo de conclusão de bate-papo. Substitua o conteúdo existente pelo código a seguir e preencha os valores necessários para deploymentName
, endpoint
e 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);
}
}
}
No código acima, primeiro definimos um plug-in de reserva de sala de conferência simulada com duas funções: FindAvailableRoomsAsync
e BookRoomAsync
. Em seguida, criamos um aplicativo de console simples que registra o plug-in no kernel e solicitamos que o kernel chame automaticamente as funções quando necessário.
Criar um novo ambiente virtual Python
python -m venv telemetry-auto-function-calling-quickstart
Ative o ambiente virtual.
telemetry-auto-function-calling-quickstart\Scripts\activate
Instalar os pacotes necessários
pip install semantic-kernel opentelemetry-exporter-otlp-proto-grpc
Criar um script Python simples com o Semantic Kernel
Crie um novo script Python e abra-o com seu editor favorito.
New-Item -Path telemetry_auto_function_calling_quickstart.py -ItemType file
Vamos criar um script Python simples que usa o Kernel Semântico para enviar um prompt para um modelo de conclusão de bate-papo. Substitua o conteúdo existente pelo código a seguir e preencha os valores necessários para deployment_name
, endpoint
e api_key
:
import asyncio
import logging
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,) -> list[str]:
return ["Room 101", "Room 201", "Room 301"]
@kernel_function(
name="book_room",
description="Book a conference room.",
)
def book_room(self, room: str) -> str:
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())
No código acima, primeiro definimos um plug-in de reserva de sala de conferência simulada com duas funções: find_available_rooms
e book_room
. Em seguida, criamos um script Python simples que registra o plug-in no kernel e pedimos ao kernel que chame automaticamente as funções quando necessário.
Variáveis de ambiente
Consulte este artigo para obter mais informações sobre como configurar as variáveis de ambiente necessárias para permitir que o kernel emita intervalos para conectores de IA.
Observação
A observabilidade semântica do kernel ainda não está disponível para Java.
Inicie o painel do Aspire
Siga as instruções aqui para iniciar o painel. Quando o painel estiver em execução, abra um navegador e navegue até http://localhost:18888
para acessar o painel.
Executar
Execute o aplicativo de console com o seguinte comando:
dotnet run
Execute o script Python com o seguinte comando:
python telemetry_auto_function_calling_quickstart.py
Observação
A observabilidade semântica do kernel ainda não está disponível para Java.
Você deverá ver um resultado semelhante ao seguinte:
Room 101 has been successfully booked for you today.
Inspecionar dados de telemetria
Depois de executar o aplicativo, vá até o painel para inspecionar os dados de telemetria.
Localize o rastreamento do aplicativo na guia Rastreamentos . Você deve cinco intervalos no rastreamento:
Esses 5 intervalos representam as operações internas do kernel com a chamada automática de função habilitada. Primeiro, ele invoca o modelo, que solicita uma chamada de função. Em seguida, o kernel executa automaticamente a função FindAvailableRoomsAsync
e retorna o resultado para o modelo. Em seguida, o modelo solicita outra chamada de função para fazer uma reserva, e o kernel executa automaticamente a função BookRoomAsync
e retorna o resultado para o modelo. Por fim, o modelo retorna uma resposta de linguagem natural para o usuário.
E se você clicar no último intervalo e procurar o prompt no gen_ai.content.prompt
evento, deverá ver algo semelhante ao seguinte:
[
{ "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." }
]
Este é o histórico de bate-papo que é construído à medida que o modelo e o kernel interagem entre si. Isso é enviado ao modelo na última iteração para obter uma resposta de linguagem natural.
Localize o rastreamento do aplicativo na guia Rastreamentos . Você deve ter cinco intervalos no rastreamento agrupados sob o AutoFunctionInvocationLoop
intervalo:
Esses 5 intervalos representam as operações internas do kernel com a chamada automática de função habilitada. Primeiro, ele invoca o modelo, que solicita uma chamada de função. Em seguida, o kernel executa automaticamente a função find_available_rooms
e retorna o resultado para o modelo. Em seguida, o modelo solicita outra chamada de função para fazer uma reserva, e o kernel executa automaticamente a função book_room
e retorna o resultado para o modelo. Por fim, o modelo retorna uma resposta de linguagem natural para o usuário.
E se você clicar no último intervalo e procurar o prompt no gen_ai.content.prompt
evento, deverá ver algo semelhante ao seguinte:
[
{ "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"
}
]
Este é o histórico de bate-papo que é construído à medida que o modelo e o kernel interagem entre si. Isso é enviado ao modelo na última iteração para obter uma resposta de linguagem natural.
Observação
A observabilidade semântica do kernel ainda não está disponível para Java.
Tratamento de erros
Se ocorrer um erro durante a execução de uma função, o kernel detectará automaticamente o erro e retornará uma mensagem de erro ao modelo. O modelo pode usar essa mensagem de erro para fornecer uma resposta de linguagem natural ao usuário.
Modifique a BookRoomAsync
função no código C# para simular um erro:
[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.");
}
Execute o aplicativo novamente e observe o rastreamento no painel. Você deve ver o span que representa a chamada de função do kernel com um erro:
Observação
É muito provável que as respostas do modelo ao erro possam variar cada vez que você executa o aplicativo, porque o modelo é estocástico. Você pode ver o modelo reservando todos os três quartos ao mesmo tempo, ou reservando um na primeira vez e reservando os outros dois na segunda vez, etc.
Modifique a book_room
função no código Python para simular um erro:
@kernel_function(
name="book_room",
description="Book a conference room.",
)
async def book_room(self, room: str) -> str:
# Simulate a remote call to a booking system
await asyncio.sleep(1)
raise Exception("Room is not available.")
Execute o aplicativo novamente e observe o rastreamento no painel. Você deve ver o span que representa a chamada de função do kernel com um erro e o rastreamento de pilha:
Observação
É muito provável que as respostas do modelo ao erro possam variar cada vez que você executa o aplicativo, porque o modelo é estocástico. Você pode ver o modelo reservando todos os três quartos ao mesmo tempo, ou reservando um na primeira vez e reservando os outros dois na segunda vez, etc.
Observação
A observabilidade semântica do kernel ainda não está disponível para Java.
Próximos passos e leitura adicional
Na produção, seus serviços podem receber um grande número de solicitações. O Kernel Semântico gerará uma grande quantidade de dados de telemetria. alguns dos quais podem não ser úteis para o seu caso de uso e introduzirão custos desnecessários para armazenar os dados. Você pode usar o recurso de amostragem para reduzir a quantidade de dados de telemetria coletados.
A observabilidade no Semantic Kernel está melhorando constantemente. Você pode encontrar as atualizações mais recentes e os novos recursos no repositório GitHub.