Share via


Biblioteca de clientes do OpenAI do Azure para .NET – versão 1.0.0-beta.9

A biblioteca de clientes do OpenAI do Azure para .NET é uma adaptação das APIs REST do OpenAI que fornece uma interface idiomática e uma integração avançada com o restante do ecossistema do SDK do Azure. Ele pode se conectar aos recursos do OpenAI do Azure ou ao ponto de extremidade de inferência do OpenAI não Azure, tornando-o uma ótima opção até mesmo para o desenvolvimento não Azure OpenAI.

Use a biblioteca de clientes do OpenAI do Azure para:

O OpenAI do Azure é um serviço gerenciado que permite que os desenvolvedores implantem, ajustem e gerem conteúdo de modelos OpenAI em recursos do Azure.

Código-fonte | Pacote (NuGet) | Documentação | de referência da APIDocumentação do produto | Amostras

Introdução

Pré-requisitos

Se você quiser usar um recurso do OpenAI do Azure, deverá ter uma assinatura do Azure e um acesso openAI do Azure. Isso permitirá que você crie um recurso openAI do Azure e obtenha uma URL de conexão, bem como chaves de API. Para obter mais informações, consulte Início Rápido: Introdução à geração de texto usando o Serviço OpenAI do Azure.

Se você quiser usar a biblioteca de clientes .NET do OpenAI do Azure para se conectar ao OpenAI não Azure, precisará de uma chave de API de uma conta de desenvolvedor em https://platform.openai.com/.

Instalar o pacote

Instale a biblioteca de clientes para .NET com o NuGet:

dotnet add package Azure.AI.OpenAI --prerelease

Autenticar o cliente

Para interagir com o OpenAI ou OpenAI do Azure, você precisará criar uma instância da classe OpenAIClient . Para configurar um cliente para uso com o OpenAI do Azure, forneça um URI de ponto de extremidade válido para um recurso do OpenAI do Azure, juntamente com uma credencial de chave correspondente, credencial de token ou credencial de identidade do Azure autorizada a usar o recurso OpenAI do Azure. Para, em vez disso, configurar o cliente para se conectar ao serviço do OpenAI, forneça uma chave de API do portal do desenvolvedor do OpenAI.

OpenAIClient client = useAzureOpenAI
    ? new OpenAIClient(
        new Uri("https://your-azure-openai-resource.com/"),
        new AzureKeyCredential("your-azure-openai-resource-api-key"))
    : new OpenAIClient("your-api-key-from-platform.openai.com");

Criar OpenAIClient com uma Credencial do Azure Active Directory

A autenticação de chave de assinatura do cliente é usada na maioria dos exemplos neste guia de introdução, mas você também pode autenticar com o Azure Active Directory usando a biblioteca de identidade do Azure. Para usar o provedor DefaultAzureCredential mostrado abaixo ou outros provedores de credenciais fornecidos com o SDK do Azure, instale o pacote Azure.Identity:

dotnet add package Azure.Identity
string endpoint = "https://myaccount.openai.azure.com/";
var client = new OpenAIClient(new Uri(endpoint), new DefaultAzureCredential());

Principais conceitos

O conceito main a entender é Conclusões. Brevemente explicado, as conclusões fornecem sua funcionalidade na forma de um prompt de texto, que, usando um modelo específico, tentará corresponder ao contexto e aos padrões, fornecendo um texto de saída. O snippet de código a seguir fornece uma visão geral aproximada (mais detalhes podem ser encontrados no GenerateChatbotResponsesWithToken código de exemplo):

OpenAIClient client = useAzureOpenAI
    ? new OpenAIClient(
        new Uri("https://your-azure-openai-resource.com/"),
        new AzureKeyCredential("your-azure-openai-resource-api-key"))
    : new OpenAIClient("your-api-key-from-platform.openai.com");

Response<Completions> response = await client.GetCompletionsAsync(new CompletionsOptions()
{
    DeploymentName = "text-davinci-003", // assumes a matching model deployment or model name
    Prompts = { "Hello, world!" },
});

foreach (Choice choice in response.Value.Choices)
{
    Console.WriteLine(choice.Text);
}

Acesso thread-safe

Garantimos que todos os métodos de instância do cliente sejam thread-safe e independentes uns dos outros (diretriz). Isso garante que a recomendação de reutilize instâncias de cliente seja sempre segura, mesmo entre threads.

Conceitos adicionais

Opções do | clienteAcessando a resposta | Operações de execução longa | Tratamento de falhas | Diagnostics | Zombando | Tempo de vida do cliente

Exemplos

Você pode se familiarizar com APIs diferentes usando Exemplos.

Gerar resposta do chatbot

O GenerateChatbotResponse método é autenticado usando um DefaultAzureCredential e, em seguida, gera respostas de texto para prompts de entrada.

string endpoint = "https://myaccount.openai.azure.com/";
var client = new OpenAIClient(new Uri(endpoint), new DefaultAzureCredential());

CompletionsOptions completionsOptions = new()
{
    DeploymentName = "text-davinci-003",
    Prompts = { "What is Azure OpenAI?" },
};

Response<Completions> completionsResponse = client.GetCompletions(completionsOptions);
string completion = completionsResponse.Value.Choices[0].Text;
Console.WriteLine($"Chatbot: {completion}");

Gerar várias respostas de chatbot com a chave de assinatura

O GenerateMultipleChatbotResponsesWithSubscriptionKey método fornece um exemplo de geração de respostas de texto para prompts de entrada usando uma chave de assinatura do Azure

// Replace with your Azure OpenAI key
string key = "YOUR_AZURE_OPENAI_KEY";
string endpoint = "https://myaccount.openai.azure.com/";
var client = new OpenAIClient(new Uri(endpoint), new AzureKeyCredential(key));

CompletionsOptions completionsOptions = new()
{
    DeploymentName = "text-davinci-003",
    Prompts =
    {
        "How are you today?",
        "What is Azure OpenAI?",
        "Why do children love dinosaurs?",
        "Generate a proof of Euler's identity",
        "Describe in single words only the good things that come into your mind about your mother."
    },
};

Response<Completions> completionsResponse = client.GetCompletions(completionsOptions);

foreach (Choice choice in completionsResponse.Value.Choices)
{
    Console.WriteLine($"Response for prompt {choice.Index}: {choice.Text}");
}

Resumir texto com conclusão

O SummarizeText método gera um resumo do prompt de entrada especificado.

string endpoint = "https://myaccount.openai.azure.com/";
var client = new OpenAIClient(new Uri(endpoint), new DefaultAzureCredential());

string textToSummarize = @"
    Two independent experiments reported their results this morning at CERN, Europe's high-energy physics laboratory near Geneva in Switzerland. Both show convincing evidence of a new boson particle weighing around 125 gigaelectronvolts, which so far fits predictions of the Higgs previously made by theoretical physicists.

    ""As a layman I would say: 'I think we have it'. Would you agree?"" Rolf-Dieter Heuer, CERN's director-general, asked the packed auditorium. The physicists assembled there burst into applause.
:";

string summarizationPrompt = @$"
    Summarize the following text.

    Text:
    """"""
    {textToSummarize}
    """"""

    Summary:
";

Console.Write($"Input: {summarizationPrompt}");
var completionsOptions = new CompletionsOptions()
{
    DeploymentName = "text-davinci-003",
    Prompts = { summarizationPrompt },
};

Response<Completions> completionsResponse = client.GetCompletions(completionsOptions);
string completion = completionsResponse.Value.Choices[0].Text;
Console.WriteLine($"Summarization: {completion}");

Transmitir mensagens de chat com o OpenAI não Azure

string nonAzureOpenAIApiKey = "your-api-key-from-platform.openai.com";
var client = new OpenAIClient(nonAzureOpenAIApiKey, new OpenAIClientOptions());
var chatCompletionsOptions = new ChatCompletionsOptions()
{
    DeploymentName = "gpt-3.5-turbo", // Use DeploymentName for "model" with non-Azure clients
    Messages =
    {
        new ChatMessage(ChatRole.System, "You are a helpful assistant. You will talk like a pirate."),
        new ChatMessage(ChatRole.User, "Can you help me?"),
        new ChatMessage(ChatRole.Assistant, "Arrrr! Of course, me hearty! What can I do for ye?"),
        new ChatMessage(ChatRole.User, "What's the best way to train a parrot?"),
    }
};

await foreach (StreamingChatCompletionsUpdate chatUpdate in client.GetChatCompletionsStreaming(chatCompletionsOptions))
{
    if (chatUpdate.Role.HasValue)
    {
        Console.Write($"{chatUpdate.Role.Value.ToString().ToUpperInvariant()}: ");
    }
    if (!string.IsNullOrEmpty(chatUpdate.ContentUpdate))
    {
        Console.Write(chatUpdate.ContentUpdate);
    }
}

Ao solicitar explicitamente mais de um Choice durante o streaming, use a ChoiceIndex propriedade em StreamingChatCompletionsUpdate para determinar a qual Choice atualização corresponde.

// A ChoiceCount > 1 will feature multiple, parallel, independent text generations arriving on the
// same response. This may be useful when choosing between multiple candidates for a single request.
var chatCompletionsOptions = new ChatCompletionsOptions()
{
    Messages = { new ChatMessage(ChatRole.User, "Write a limerick about bananas.") },
    ChoiceCount = 4
};

await foreach (StreamingChatCompletionsUpdate chatUpdate
    in client.GetChatCompletionsStreaming(chatCompletionsOptions))
{
    // Choice-specific information like Role and ContentUpdate will also provide a ChoiceIndex that allows
    // StreamingChatCompletionsUpdate data for independent choices to be appropriately separated.
    if (chatUpdate.ChoiceIndex.HasValue)
    {
        int choiceIndex = chatUpdate.ChoiceIndex.Value;
        if (chatUpdate.Role.HasValue)
        {
            textBoxes[choiceIndex].Text += $"{chatUpdate.Role.Value.ToString().ToUpperInvariant()}: ";
        }
        if (!string.IsNullOrEmpty(chatUpdate.ContentUpdate))
        {
            textBoxes[choiceIndex].Text += chatUpdate.ContentUpdate;
        }
    }
}

Usar funções de chat

As funções de chat permitem que um chamador de Preenchimentos de Chat defina recursos que o modelo pode usar para estender sua funcionalidade para ferramentas externas e fontes de dados.

Você pode ler mais sobre as Funções de Chat no blog do OpenAI: https://openai.com/blog/function-calling-and-other-api-updates

OBSERVAÇÃO: as Funções de Chat exigem versões de modelo que começam com os rótulos gpt-4 e gpt-3.5-turbo -0613 . Eles não estão disponíveis com versões mais antigas dos modelos.

NOTA: Ainda não há suporte para o uso simultâneo de funções de chat e extensões de chat do Azure em uma única solicitação. O fornecimento de ambos fará com que as informações das Funções de Chat sejam ignoradas e a operação se comporte como se apenas as Extensões de Chat do Azure fossem fornecidas. Para resolver essa limitação, considere separar a avaliação das Funções de Chat e das Extensões de Chat do Azure em várias solicitações no design da solução.

Para usar as Funções de Chat, primeiro defina a função que deseja que o modelo possa usar quando apropriado. Usando o exemplo da postagem de blog vinculada, acima:

var getWeatherFuntionDefinition = new FunctionDefinition()
{
    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 and state, e.g. San Francisco, CA",
            },
            Unit = new
            {
                Type = "string",
                Enum = new[] { "celsius", "fahrenheit" },
            }
        },
        Required = new[] { "location" },
    },
    new JsonSerializerOptions() {  PropertyNamingPolicy = JsonNamingPolicy.CamelCase }),
};

Com a função definida, ela pode ser usada em uma solicitação de Conclusões de Chat por meio de suas opções. Os dados de função são tratados em várias chamadas que criam dados para solicitações sem estado subsequentes, portanto, mantemos uma lista de mensagens de chat como uma forma de histórico de conversas.

var conversationMessages = new List<ChatMessage>()
{
    new(ChatRole.User, "What is the weather like in Boston?"),
};

var chatCompletionsOptions = new ChatCompletionsOptions()
{
    DeploymentName = "gpt-35-turbo-0613",
};
foreach (ChatMessage chatMessage in conversationMessages)
{
    chatCompletionsOptions.Messages.Add(chatMessage);
}
chatCompletionsOptions.Functions.Add(getWeatherFuntionDefinition);

Response<ChatCompletions> response = await client.GetChatCompletionsAsync(chatCompletionsOptions);

Se o modelo determinar que ele deve chamar uma Função de Chat, um motivo de término de 'FunctionCall' será preenchido na escolha e os detalhes estarão presentes na propriedade da mensagem de FunctionCall resposta. Normalmente, o nome da chamada de função será aquele fornecido e os argumentos serão um documento JSON preenchido que corresponda ao esquema incluído no FunctionDefinition usado; não é garantido que esses dados sejam válidos ou mesmo formatados corretamente, portanto, a validação e a verificação de erros sempre devem acompanhar o processamento de chamadas de função.

Para resolve a chamada de função e continuar a interação voltada para o usuário, processe o conteúdo do argumento conforme necessário e serialize os dados de resposta apropriados em uma nova mensagem com ChatRole.Function. Em seguida, faça uma nova solicitação com todas as mensagens até agora – a mensagem inicial User , a mensagem da primeira resposta e a mensagem de FunctionCall resolução Function gerada em resposta à chamada de função – para que o modelo possa usar os dados para formular melhor uma resposta de conclusões de chat.

Observe que a resposta de chamada de função fornecida não precisa seguir nenhum esquema fornecido na chamada inicial. O modelo inferirá o uso dos dados de resposta com base no contexto inferido de nomes e campos.

ChatChoice responseChoice = response.Value.Choices[0];
if (responseChoice.FinishReason == CompletionsFinishReason.FunctionCall)
{
    // Include the FunctionCall message in the conversation history
    conversationMessages.Add(responseChoice.Message);

    if (responseChoice.Message.FunctionCall.Name == "get_current_weather")
    {
        // Validate and process the JSON arguments for the function call
        string unvalidatedArguments = responseChoice.Message.FunctionCall.Arguments;
        var functionResultData = (object)null; // GetYourFunctionResultData(unvalidatedArguments);
        // Here, replacing with an example as if returned from GetYourFunctionResultData
        functionResultData = new
        {
            Temperature = 31,
            Unit = "celsius",
        };
        // Serialize the result data from the function into a new chat message with the 'Function' role,
        // then add it to the messages after the first User message and initial response FunctionCall
        var functionResponseMessage = new ChatMessage(
            ChatRole.Function,
            JsonSerializer.Serialize(
                functionResultData,
                new JsonSerializerOptions() {  PropertyNamingPolicy = JsonNamingPolicy.CamelCase }))
        {
            Name = responseChoice.Message.FunctionCall.Name
        };
        conversationMessages.Add(functionResponseMessage);
        // Now make a new request using all three messages in conversationMessages
    }
}

Ao usar streaming, capture componentes de resposta de streaming à medida que eles chegam e acumule argumentos de função de streaming da mesma maneira usada para o conteúdo de streaming. Em seguida, no lugar de usar o ChatMessage da resposta não streaming, adicione uma nova ChatMessage instância para o histórico, criada com base nas informações transmitidas.

string functionName = null;
StringBuilder contentBuilder = new();
StringBuilder functionArgumentsBuilder = new();
ChatRole streamedRole = default;
CompletionsFinishReason finishReason = default;

await foreach (StreamingChatCompletionsUpdate update
    in client.GetChatCompletionsStreaming(chatCompletionsOptions))
{
    contentBuilder.Append(update.ContentUpdate);
    functionName ??= update.FunctionName;
    functionArgumentsBuilder.Append(update.FunctionArgumentsUpdate);
    streamedRole = update.Role ?? default;
    finishReason = update.FinishReason ?? default;
}

if (finishReason == CompletionsFinishReason.FunctionCall)
{
    string lastContent = contentBuilder.ToString();
    string unvalidatedArguments = functionArgumentsBuilder.ToString();
    ChatMessage chatMessageForHistory = new(streamedRole, lastContent)
    {
        FunctionCall = new(functionName, unvalidatedArguments),
    };
    conversationMessages.Add(chatMessageForHistory);

    // Handle from here just like the non-streaming case
}

Observação: embora as informações da função transmitida (nome, argumentos) possam ser avaliadas conforme elas chegam, elas não devem ser consideradas completas ou confirmadas até que o FinishReason de FunctionCall seja recebido. Pode ser apropriado fazer tentativas de melhor esforço em "aquecimento" ou outra preparação especulativa com base em um nome de função ou chave/valor específico que apareça nos argumentos JSON parciais acumulados, mas nenhuma suposição forte sobre validade, ordenação ou outros detalhes deve ser avaliada até que os argumentos estejam totalmente disponíveis e confirmados por meio de FinishReason.

Use dados próprios com o OpenAI do Azure

O recurso usar seus próprios dados é exclusivo do OpenAI do Azure e não funcionará com um cliente configurado para usar o serviço não Azure. Consulte o Início rápido do OpenAI do Azure usando seus próprios dados para obter instruções de configuração detalhadas e em segundo plano conceituais.

NOTA: Ainda não há suporte para o uso simultâneo de funções de chat e extensões de chat do Azure em uma única solicitação. O fornecimento de ambos fará com que as informações das Funções de Chat sejam ignoradas e a operação se comporte como se apenas as Extensões de Chat do Azure fossem fornecidas. Para resolver essa limitação, considere separar a avaliação das Funções de Chat e das Extensões de Chat do Azure em várias solicitações no design da solução.

AzureCognitiveSearchChatExtensionConfiguration contosoExtensionConfig = new()
{
    SearchEndpoint = new Uri("https://your-contoso-search-resource.search.windows.net"),
    IndexName = "contoso-products-index",
};

contosoExtensionConfig.SetSearchKey("<your Cognitive Search resource API key>");

ChatCompletionsOptions chatCompletionsOptions = new()
{
    DeploymentName = "gpt-35-turbo-0613",
    Messages =
    {
        new ChatMessage(
            ChatRole.System,
            "You are a helpful assistant that answers questions about the Contoso product database."),
        new ChatMessage(ChatRole.User, "What are the best-selling Contoso products this month?")
    },

    // The addition of AzureChatExtensionsOptions enables the use of Azure OpenAI capabilities that add to
    // the behavior of Chat Completions, here the "using your own data" feature to supplement the context
    // with information from an Azure Cognitive Search resource with documents that have been indexed.
    AzureExtensionsOptions = new AzureChatExtensionsOptions()
    {
        Extensions = { contosoExtensionConfig }
    }
};

Response<ChatCompletions> response = await client.GetChatCompletionsAsync(chatCompletionsOptions);
ChatMessage message = response.Value.Choices[0].Message;

// The final, data-informed response still appears in the ChatMessages as usual
Console.WriteLine($"{message.Role}: {message.Content}");

// Responses that used extensions will also have Context information that includes special Tool messages
// to explain extension activity and provide supplemental information like citations.
Console.WriteLine($"Citations and other information:");

foreach (ChatMessage contextMessage in message.AzureExtensionsContext.Messages)
{
    // Note: citations and other extension payloads from the "tool" role are often encoded JSON documents
    // and need to be parsed as such; that step is omitted here for brevity.
    Console.WriteLine($"{contextMessage.Role}: {contextMessage.Content}");
}

Gerar inserções

EmbeddingsOptions embeddingsOptions = new()
{
    DeploymentName = "text-embedding-ada-002",
    Input = { "Your text string goes here" },
};
Response<Embeddings> response = await client.GetEmbeddingsAsync(embeddingsOptions);

// The response includes the generated embedding.
EmbeddingItem item = response.Value.Data[0];
ReadOnlyMemory<float> embedding = item.Embedding;

Gerar imagens com modelos de geração de imagem DALL-E

Response<ImageGenerations> imageGenerations = await client.GetImageGenerationsAsync(
    new ImageGenerationOptions()
    {
        Prompt = "a happy monkey eating a banana, in watercolor",
        Size = ImageSize.Size256x256,
    });

// Image Generations responses provide URLs you can use to retrieve requested images
Uri imageUri = imageGenerations.Value.Data[0].Url;

Transcrever dados de áudio com modelos de fala whisper

using Stream audioStreamFromFile = File.OpenRead("myAudioFile.mp3");

var transcriptionOptions = new AudioTranscriptionOptions()
{
    DeploymentName = "my-whisper-deployment", // whisper-1 as model name for non-Azure OpenAI
    AudioData = BinaryData.FromStream(audioStreamFromFile),
    ResponseFormat = AudioTranscriptionFormat.Verbose,
};

Response<AudioTranscription> transcriptionResponse
    = await client.GetAudioTranscriptionAsync(transcriptionOptions);
AudioTranscription transcription = transcriptionResponse.Value;

// When using Simple, SRT, or VTT formats, only transcription.Text will be populated
Console.WriteLine($"Transcription ({transcription.Duration.Value.TotalSeconds}s):");
Console.WriteLine(transcription.Text);

Traduzir dados de áudio para o inglês com modelos de fala whisper

using Stream audioStreamFromFile = File.OpenRead("mySpanishAudioFile.mp3");

var translationOptions = new AudioTranslationOptions()
{
    DeploymentName = "my-whisper-deployment", // whisper-1 as model name for non-Azure OpenAI
    AudioData = BinaryData.FromStream(audioStreamFromFile),
    ResponseFormat = AudioTranslationFormat.Verbose,
};

Response<AudioTranslation> translationResponse = await client.GetAudioTranslationAsync(translationOptions);
AudioTranslation translation = translationResponse.Value;

// When using Simple, SRT, or VTT formats, only translation.Text will be populated
Console.WriteLine($"Translation ({translation.Duration.Value.TotalSeconds}s):");
// .Text will be translated to English (ISO-639-1 "en")
Console.WriteLine(translation.Text);

Solução de problemas

Quando você interage com o OpenAI do Azure usando o SDK do .NET, os erros retornados pelo serviço correspondem aos mesmos códigos de status HTTP retornados para solicitações da API REST.

Por exemplo, se você tentar criar um cliente usando um ponto de extremidade que não corresponda ao ponto de extremidade do Recurso OpenAI do Azure, um 404 erro será retornado, indicando Resource Not Found.

Próximas etapas

  • Forneça um link para exemplos de código adicionais, idealmente para aqueles sentados ao lado do LEIAME no diretório do /samples pacote.
  • Se apropriado, aponte os usuários para outros pacotes que podem ser úteis.
  • Se você acha que há uma boa chance de que os desenvolvedores possam encontrar seu pacote com erro (porque eles estão procurando funcionalidades específicas e pensar erroneamente que o pacote fornece essa funcionalidade), aponte-os para os pacotes que eles podem estar procurando.

Participante

Consulte o CONTRIBUTING.md OpenAI para obter detalhes sobre como criar, testar e contribuir para essa biblioteca.

Este projeto aceita contribuições e sugestões. A maioria das contribuições exige que você concorde com um CLA (Contrato de Licença do Colaborador) declarando que você tem o direito de nos conceder, e de fato concede, os direitos de usar sua contribuição. Para obter detalhes, visite cla.microsoft.com.

Quando você envia uma solicitação de pull, um bot do CLA determina automaticamente se você precisa fornecer um CLA e preencher a PR corretamente (por exemplo, rótulo, comentário). Basta seguir as instruções fornecidas pelo bot. Você só precisará fazer isso uma vez em todos os repositórios que usam nosso CLA.

Este projeto adotou o Código de Conduta de Software Livre da Microsoft. Para obter mais informações, confira as Perguntas frequentes sobre o Código de Conduta ou contate opencode@microsoft.com para enviar outras perguntas ou comentários.

Impressões