Biblioteca cliente de Azure OpenAI para .NET: versión 1.0.0-beta.9

La biblioteca cliente de Azure OpenAI para .NET es una adaptación de las API REST de OpenAI que proporciona una interfaz idiomática e integración enriquecida con el resto del ecosistema del SDK de Azure. Puede conectarse a recursos de Azure OpenAI o al punto de conexión de inferencia de OpenAI que no es de Azure, lo que lo convierte en una excelente opción para el desarrollo de OpenAI que no sea de Azure.

Use la biblioteca cliente de Azure OpenAI para:

Azure OpenAI es un servicio administrado que permite a los desarrolladores implementar, optimizar y generar contenido a partir de modelos de OpenAI en recursos de Azure.

Código | fuentePaquete (NuGet) | Documentación | de referencia de API | Documentación del productoMuestras

Introducción

Requisitos previos

Si desea usar un recurso de Azure OpenAI, debe tener una suscripción de Azure y acceso a Azure OpenAI. Esto le permitirá crear un recurso de Azure OpenAI y obtener una dirección URL de conexión, así como claves de API. Para más información, consulte Inicio rápido: Introducción a la generación de texto mediante el servicio Azure OpenAI.

Si quiere usar la biblioteca cliente de .NET de Azure OpenAI para conectarse a openAI que no es de Azure, necesitará una clave de API de una cuenta de desarrollador en https://platform.openai.com/.

Instalar el paquete

Instale la biblioteca cliente para .NET con NuGet:

dotnet add package Azure.AI.OpenAI --prerelease

Autenticar el cliente

Para interactuar con Azure OpenAI o OpenAI, deberá crear una instancia de la clase OpenAIClient . Para configurar un cliente para su uso con Azure OpenAI, proporcione un URI de punto de conexión válido a un recurso de Azure OpenAI junto con una credencial de clave, credenciales de token o credenciales de identidad de Azure autorizadas para usar el recurso de Azure OpenAI. Para configurar el cliente para conectarse al servicio de OpenAI, proporcione una clave de API desde el portal para desarrolladores de 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");

Creación de OpenAIClient con una credencial de Azure Active Directory

La autenticación de clave de suscripción de cliente se usa en la mayoría de los ejemplos de esta guía de introducción, pero también puede autenticarse con Azure Active Directory mediante la biblioteca de identidades de Azure. Para usar el proveedor DefaultAzureCredential que se muestra a continuación u otros proveedores de credenciales proporcionados con el SDK de Azure, instale el paquete Azure.Identity:

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

Conceptos clave

El concepto principal que se debe comprender es Finalizaciones. Brevemente explicado, las finalizaciones proporcionan su funcionalidad en forma de mensaje de texto, que mediante un modelo específico, intentarán coincidir con el contexto y los patrones, proporcionando un texto de salida. El siguiente fragmento de código proporciona información general aproximada (puede encontrar más detalles en el código de GenerateChatbotResponsesWithToken ejemplo):

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);
}

Seguridad para subprocesos

Garantizamos que todos los métodos de instancia de cliente son seguros para subprocesos e independientes entre sí (instrucciones). Esto garantiza que la recomendación de reutilizar instancias de cliente siempre es segura, incluso entre subprocesos.

Conceptos adicionales

Opciones | de clienteAcceso a la respuesta | Operaciones | de larga duraciónControl de errores | Diagnóstico | Burla | Duración del cliente

Ejemplos

Puede familiarizarse con diferentes API mediante ejemplos.

Generar respuesta de bot de chat

El GenerateChatbotResponse método se autentica mediante defaultAzureCredential y, a continuación, genera respuestas de texto a las solicitudes 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}");

Generación de varias respuestas de bot de chat con clave de suscripción

El GenerateMultipleChatbotResponsesWithSubscriptionKey método proporciona un ejemplo de generación de respuestas de texto a mensajes de entrada mediante una clave de suscripción de 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 con finalización

El SummarizeText método genera un resumen del mensaje 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}");

Transmisión de mensajes de chat con OpenAI que no es de 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);
    }
}

Al solicitar explícitamente más de uno Choice durante el streaming, use la ChoiceIndex propiedad en StreamingChatCompletionsUpdate para determinar a qué Choice se corresponde cada actualización.

// 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;
        }
    }
}

Uso de funciones de chat

Las funciones de chat permiten que un autor de la llamada de finalizaciones de chat defina las funcionalidades que el modelo puede usar para ampliar su funcionalidad a herramientas externas y orígenes de datos.

Puede leer más sobre funciones de chat en el blog de OpenAI: https://openai.com/blog/function-calling-and-other-api-updates

NOTA: Las funciones de chat requieren versiones del modelo que comienzan con las etiquetas gpt-4 y gpt-3.5-turbo -0613 . No están disponibles con versiones anteriores de los modelos.

NOTA: Todavía no se admite el uso simultáneo de funciones de chat y extensiones de chat de Azure en una sola solicitud. Proporcionar ambos dará como resultado que se omita la información de Las funciones de chat y la operación se comporte como si solo se proporcionaran las extensiones de Chat de Azure. Para solucionar esta limitación, considere la posibilidad de separar la evaluación de Funciones de chat y extensiones de Chat de Azure en varias solicitudes en el diseño de la solución.

Para usar funciones de chat, primero debe definir la función que desea que el modelo pueda usar cuando corresponda. Con el ejemplo de la entrada de blog vinculada, anteriormente:

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 }),
};

Con la función definida, se puede usar en una solicitud de finalizaciones de chat a través de sus opciones. Los datos de función se controlan en varias llamadas que crean datos para las solicitudes sin estado posteriores, por lo que mantenemos una lista de mensajes de chat como una forma de historial de conversaciones.

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);

Si el modelo determina que debe llamar a una función de chat, se rellenará un motivo de finalización de "FunctionCall" en la elección y los detalles estarán presentes en la propiedad del FunctionCall mensaje de respuesta. Normalmente, el nombre de la llamada de función será aquel que se proporcionó y los argumentos serán un documento JSON rellenado que coincida con el esquema incluido en el FunctionDefinition usado; no se garantiza que estos datos sean válidos o incluso con el formato correcto, sin embargo, por lo que la validación y la comprobación de errores siempre deben acompañar el procesamiento de llamadas de función.

Para resolver la llamada de función y continuar con la interacción orientada al usuario, procese la carga del argumento según sea necesario y, a continuación, serialice los datos de respuesta adecuados en un nuevo mensaje con ChatRole.Function. A continuación, realice una nueva solicitud con todos los mensajes hasta ahora (el mensaje inicial User , el mensaje de FunctionCall la primera respuesta y el mensaje de resolución Function generado en respuesta a la llamada de función) para que el modelo pueda usar los datos para formular mejor una respuesta de finalizaciones de chat.

Tenga en cuenta que la respuesta de llamada de función proporcionada no necesita seguir ningún esquema proporcionado en la llamada inicial. El modelo deducirá el uso de los datos de respuesta en función del contexto inferido de nombres y 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
    }
}

Al usar streaming, capture los componentes de respuesta de streaming a medida que llegan y acumulan argumentos de función de streaming de la misma manera que se usan para el contenido de streaming. A continuación, en lugar de usar desde ChatMessage la respuesta que no es de streaming, agregue en su lugar una nueva ChatMessage instancia para el historial, creada a partir de la información transmitida.

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
}

Tenga en cuenta que, mientras que la información de la función transmitida (nombre, argumentos) se puede evaluar a medida que llega, no debe considerarse completa ni confirmada hasta que se reciba .FinishReasonFunctionCall Puede ser adecuado realizar intentos de mejor esfuerzo en "preparación" u otra preparación especulativa basada en un nombre de función o clave/valor concreto que aparezca en los argumentos JSON acumulados y parciales, pero no se debe evaluar ninguna suposición sólida sobre la validez, el orden u otros detalles hasta que los argumentos estén totalmente disponibles y confirmados a través FinishReasonde .

Uso de sus propios datos con Azure OpenAI

El uso de su propia característica de datos es único para Azure OpenAI y no funcionará con un cliente configurado para usar el servicio que no es de Azure. Consulte la guía de inicio rápido de Azure OpenAI con sus propios datos para obtener instrucciones de configuración detalladas y en segundo plano conceptual.

NOTA: Todavía no se admite el uso simultáneo de funciones de chat y extensiones de chat de Azure en una sola solicitud. Proporcionar ambos dará como resultado que se omita la información de Las funciones de chat y la operación se comporte como si solo se proporcionaran las extensiones de Chat de Azure. Para solucionar esta limitación, considere la posibilidad de separar la evaluación de Funciones de chat y extensiones de Chat de Azure en varias solicitudes en el diseño de la solución.

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}");
}

Generación de inserciones

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;

Generación de imágenes con modelos de generación de imágenes 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;

Transcripción de datos de audio con modelos de voz de susurro

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);

Traducción de datos de audio a inglés con modelos de voz de susurro

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);

Solución de problemas

Al interactuar con Azure OpenAI mediante el SDK de .NET, los errores devueltos por el servicio corresponden a los mismos códigos de estado HTTP devueltos para las solicitudes de API REST .

Por ejemplo, si intenta crear un cliente mediante un punto de conexión que no coincide con el punto de conexión de recursos de Azure OpenAI, se devuelve un 404 error, que indica Resource Not Found.

Pasos siguientes

  • Proporcione un vínculo a ejemplos de código adicionales, idealmente para aquellos que se encuentran junto con el archivo Léame en el directorio del /samples paquete.
  • Si procede, apunte a los usuarios a otros paquetes que podrían ser útiles.
  • Si cree que hay una buena posibilidad de que los desarrolladores se produzcan errores en el paquete (ya que buscan una funcionalidad específica y piensan erróneamente que el paquete proporciona esa funcionalidad), apunte a los paquetes que podrían estar buscando.

Contribuciones

Consulte la CONTRIBUTING.md de OpenAI para obtener más información sobre la compilación, las pruebas y la contribución a esta biblioteca.

Este proyecto agradece las contribuciones y sugerencias. La mayoría de las contribuciones requieren que acepte un Contrato de licencia para el colaborador (CLA) que declara que tiene el derecho a concedernos y nos concede los derechos para usar su contribución. Para obtener más información, visite cla.microsoft.com.

Cuando se envía una solicitud de incorporación de cambios, un bot de CLA determinará de forma automática si tiene que aportar un CLA y completar la PR adecuadamente (por ejemplo, la etiqueta, el comentario). Solo siga las instrucciones que le dará el bot. Solo será necesario que lo haga una vez en todos los repositorios con nuestro CLA.

Este proyecto ha adoptado el Código de conducta de Microsoft Open Source. Para más información, consulte las preguntas más frecuentes del código de conducta o póngase en contacto con opencode@microsoft.com si tiene cualquier otra pregunta o comentario.

Impresiones