Partilhar via


Estenda a IA do Azure usando Ferramentas e execute uma Função local com o .NET

Comece a usar o Kernel Semântico criando um aplicativo de bate-papo simples do console .NET 8. O aplicativo será executado localmente e usará o modelo OpenAI gpt-35-turbo implantado em uma conta OpenAI do Azure, no entanto, usando a Ferramenta para estender os recursos do modelo, ele chamará de função local. Siga estas etapas para provisionar o Azure OpenAI e saiba como usar o Kernel Semântico.

Comece a usar o SDK do OpenAI do Azure .NET criando um aplicativo de chat de console simples do .NET 8. O aplicativo será executado localmente e usará o modelo OpenAI gpt-35-turbo implantado em uma conta OpenAI do Azure, no entanto, usando a Ferramenta para estender os recursos do modelo, ele chamará de função local. Siga estas etapas para provisionar o Azure OpenAI e saiba como usar o SDK do OpenAI do Azure .NET.

Pré-requisitos

Implantar os recursos do Azure

Certifique-se de seguir os Pré-requisitos para ter acesso ao Serviço OpenAI do Azure, bem como à CLI do Desenvolvedor do Azure e, em seguida, siga o guia a seguir para começar com o aplicativo de exemplo.

  1. Clone o repositório: dotnet/ai-samples

  2. A partir de um terminal ou prompt de comando, navegue até o diretório de inícios rápidos .

  3. Isso provisiona os recursos do Azure OpenAI. Pode levar vários minutos para criar o serviço Azure OpenAI e implantar o modelo.

    azd up
    

Nota

Se você já tiver um serviço OpenAI do Azure disponível, poderá ignorar a implantação e usar esse valor no Program.cs, de preferência a partir de um IConfigurationarquivo .

Resolver problemas

No Windows, você pode receber as seguintes mensagens de erro após a execução azd up:

postprovision.ps1 não está assinado digitalmente. O script não será executado no sistema

O script postprovision.ps1 é executado para definir os segredos de usuário do .NET usados no aplicativo. Para evitar esse erro, execute o seguinte comando do PowerShell:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

Em seguida, execute novamente o azd up comando.

Outro erro possível:

'pwsh' não é reconhecido como um comando interno ou externo, programa operável ou ficheiro batch. AVISO: O gancho 'postprovision' falhou com o código de saída: '1', Caminho: '.\infra\post-script\postprovision.ps1'. : código de saída: 1 A execução continuará desde que ContinueOnError tenha sido definido como true.

O script postprovision.ps1 é executado para definir os segredos de usuário do .NET usados no aplicativo. Para evitar esse erro, execute manualmente o script usando o seguinte comando do PowerShell:

.\infra\post-script\postprovision.ps1

Os aplicativos de IA .NET agora têm os segredos do usuário configurados e podem ser testados.

Experimente o exemplo do HikerAI Pro

  1. Em um terminal ou prompt de comando, navegue até o semantic-kernel\04-HikerAIPro diretório.
  1. Em um terminal ou prompt de comando, navegue até o azure-openai-sdk\04-HikerAIPro diretório.
  1. Agora é hora de experimentar o aplicativo de console. Digite o seguinte para executar o aplicativo:

    dotnet run
    

    Se você receber uma mensagem de erro, os recursos do Azure OpenAI podem não ter concluído a implantação. Aguarde alguns minutos e tente novamente.

Compreender o código

Nosso aplicativo usa o Microsoft.SemanticKernel pacote, que está disponível no NuGet, para enviar e receber solicitações para um serviço OpenAI do Azure implantado no Azure.

Todo o aplicativo está contido no arquivo Program.cs . As primeiras linhas de código carregam segredos e valores de configuração que foram definidos no dotnet user-secrets para você durante o provisionamento do aplicativo.

var config = new ConfigurationBuilder().AddUserSecrets<Program>().Build();
string endpoint = config["AZURE_OPENAI_ENDPOINT"];
string deployment = config["AZURE_OPENAI_GPT_NAME"];
string key = config["AZURE_OPENAI_KEY"];

A Kernel turma facilita as solicitações e respostas com a ajuda do AddAzureOpenAIChatCompletion serviço.

// Create a Kernel containing the Azure OpenAI Chat Completion Service
IKernelBuilder b = Kernel.CreateBuilder();

Kernel kernel = b
    .AddAzureOpenAIChatCompletion(deployment, endpoint, key)
    .Build();

As funções ImportPluginFromFunctions são e CreateFromMethod são usadas para definir a função local que será chamada pelo modelo.

// Add a new plugin with a local .NET function that should be available to the AI model
// For convenience and clarity of into the code, this standalone local method handles tool call responses. It will fake a call to a weather API and return the current weather for the specified location.
kernel.ImportPluginFromFunctions("WeatherPlugin",
[
    KernelFunctionFactory.CreateFromMethod(([Description("The city, e.g. Montreal, Sidney")] string location, string unit = null) =>
    {
        // Here you would call a weather API to get the weather for the location
        return "Periods of rain or drizzle, 15 C";
    }, "get_current_weather", "Get the current weather in a given location")
]);

Depois que o kernel cliente é criado, fornecemos mais contexto ao modelo adicionando um prompt do sistema. Isso instrui o modelo como você gostaria que ele agisse durante a conversa. Observe como o tempo é enfatizado no prompt do sistema.

ChatHistory chatHistory = new("""
    You are a hiking enthusiast who helps people discover fun hikes in their area. You are upbeat and friendly.
    A good weather is important for a good hike. Only make recommendations if the weather is good or if people insist.
    You introduce yourself when first saying hello. When helping people out, you always ask them 
    for this information to inform the hiking recommendation you provide:

    1. Where they are located
    2. What hiking intensity they are looking for

    You will then provide three suggestions for nearby hikes that vary in length after you get that information. 
    You will also share an interesting fact about the local nature on the hikes when making a recommendation.
    """);

Em seguida, você pode adicionar uma mensagem de usuário ao modelo usando o AddUserMessage functon.

Para que o modelo gere uma resposta com base no prompt do sistema e na solicitação do usuário, use a GetChatMessageContentAsync função.

chatHistory.AddUserMessage("""
    Is the weather is good today for a hike?
    If yes, I live in the greater Montreal area and would like an easy hike. I don't mind driving a bit to get there.
    I don't want the hike to be over 10 miles round trip. I'd consider a point-to-point hike.
    I want the hike to be as isolated as possible. I don't want to see many people.
    I would like it to be as bug free as possible.
    """);

Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last().Content}");

chatHistory.Add(await service.GetChatMessageContentAsync(chatHistory, new OpenAIPromptExecutionSettings() { MaxTokens = 400 }));
Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last().Content}");

Personalize o prompt do sistema e a mensagem do usuário para ver como o modelo responde para ajudá-lo a encontrar uma caminhada que você vai gostar.

Compreender o código

Nosso aplicativo usa o SDK do Azure.AI.OpenAI cliente, que está disponível no NuGet, para enviar e receber solicitações para um serviço OpenAI do Azure implantado no Azure.

Todo o aplicativo está contido no arquivo Program.cs . As primeiras linhas de código carregam segredos e valores de configuração que foram definidos no dotnet user-secrets para você durante o provisionamento do aplicativo.

// == Retrieve the local secrets saved during the Azure deployment ==========
var config = new ConfigurationBuilder().AddUserSecrets<Program>().Build();
string openAIEndpoint = config["AZURE_OPENAI_ENDPOINT"];
string openAIDeploymentName = config["AZURE_OPENAI_GPT_NAME"];
string openAiKey = config["AZURE_OPENAI_KEY"];

// == Creating the AIClient ==========
var endpoint = new Uri(openAIEndpoint);
var credentials = new AzureKeyCredential(openAiKey);

A OpenAIClient classe facilita as solicitações e respostas. ChatCompletionOptions Especifica parâmetros de como o modelo responderá. Observe como a propriedade Tools é usada para adicionar a definição.

var openAIClient = new OpenAIClient(endpoint, credentials);

var completionOptions = new ChatCompletionsOptions
{
    MaxTokens = 400,
    Temperature = 1f,
    FrequencyPenalty = 0.0f,
    PresencePenalty = 0.0f,
    NucleusSamplingFactor = 0.95f, // Top P
    DeploymentName = openAIDeploymentName,
    Tools = { getWeather }
};

A classe ChatCompletionsFunctionToolDefinition é usada para definir a função local que será chamada pelo modelo.

var getWeather = new ChatCompletionsFunctionToolDefinition()
{
    Name = "get_current_weather",
    Description = "Get the current weather in a given location",
    Parameters = BinaryData.FromObjectAsJson(
    new
    {
        Type = "object",
        Properties = new
        {
            Location = new
            {
                Type = "string",
                Description = "The city, e.g. Montreal, Sidney",
            },
            Unit = new
            {
                Type = "string",
                Enum = new[] { "celsius", "fahrenheit" },
            }
        },
        Required = new[] { "location" },
    },
    new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }),
};

Depois que o OpenAIClient cliente é criado, fornecemos mais contexto ao modelo adicionando um prompt do sistema. Isso instrui o modelo como você gostaria que ele agisse durante a conversa. Observe como o tempo é enfatizado no prompt do sistema.

var systemPrompt = 
"""
You are a hiking enthusiast who helps people discover fun hikes in their area. You are upbeat and friendly.
A good weather is important for a good hike. Only make recommendations if the weather is good or if people insist.
You introduce yourself when first saying hello. When helping people out, you always ask them 
for this information to inform the hiking recommendation you provide:

1. Where they are located
2. What hiking intensity they are looking for

You will then provide three suggestions for nearby hikes that vary in length after you get that information. 
You will also share an interesting fact about the local nature on the hikes when making a recommendation.
""";

completionOptions.Messages.Add(new ChatRequestSystemMessage(systemPrompt));

Em seguida, você pode adicionar uma mensagem de usuário ao modelo usando a ChatRequestUserMessage classe.

Para conveniência e clareza do código, esse método local autônomo lida com respostas de chamada de ferramenta. Ele falsificará uma chamada para uma API meteorológica e retornará o tempo atual para o local especificado.

ChatRequestToolMessage GetToolCallResponseMessage(ChatCompletionsToolCall toolCall)
{
    var functionToolCall = toolCall as ChatCompletionsFunctionToolCall;
    if (functionToolCall?.Name == getWeather.Name)
    {
        string unvalidatedArguments = functionToolCall.Arguments;
        var functionResultData = (object)null;

        // == Here call a weather API to get the weather for specified the location ==========
        functionResultData = "Periods of rain or drizzle, 15 C";

        return new ChatRequestToolMessage(functionResultData.ToString(), toolCall.Id);
    }
    else
    {
        throw new NotImplementedException();
    }
}

Para que o modelo gere uma resposta com base no prompt do sistema e na solicitação do usuário, use a GetChatCompletionsAsync função.

string hikeRequest = """
Is the weather is good today for a hike?
If yes, I live in the greater Montreal area and would like an easy hike. I don't mind driving a bit to get there.
I don't want the hike to be over 10 miles round trip. I'd consider a point-to-point hike.
I want the hike to be as isolated as possible. I don't want to see many people.
I would like it to be as bug free as possible.
""";

Console.WriteLine($"\n\nUser >>> {hikeRequest}");
completionOptions.Messages.Add(new ChatRequestUserMessage(hikeRequest));

response = await openAIClient.GetChatCompletionsAsync(completionOptions);

Agora, a resposta tem de ser analisada. Se a resposta incluir ToolCalls, o método declarar previamente manipulá-lo e continuar a conversa. É importante observar que cada mensagem ChatRequestAssistantMessage é adicionada ao histórico de conversas. Isso é importante para manter o contexto da conversa.

ChatChoice responseChoice = response.Choices[0];
if (responseChoice.FinishReason == CompletionsFinishReason.ToolCalls)
{
    // == Include the FunctionCall message in the conversation history ==========
    completionOptions.Messages.Add(new ChatRequestAssistantMessage(responseChoice.Message));

    // == Add a new tool message for each tool call that is resolved ==========
    foreach (ChatCompletionsToolCall toolCall in responseChoice.Message.ToolCalls)
    {
        var ToolCallMsg = GetToolCallResponseMessage(toolCall);
        completionOptions.Messages.Add(ToolCallMsg);
    }

    // == Retrieve the answer from HikeAI Pro ==========
    response = await openAIClient.GetChatCompletionsAsync(completionOptions);
}

assistantResponse = response.Choices[0].Message;
Console.WriteLine($"\n\nAssistant >>> {assistantResponse.Content}");

Personalize o prompt do sistema e a mensagem do usuário para ver como o modelo responde para ajudá-lo a encontrar uma caminhada que você vai gostar.

Clean up resources (Limpar recursos)

Quando você não precisar mais do aplicativo ou recursos de exemplo, remova a implantação correspondente e todos os recursos.

azd down

Próximos passos