diff --git a/.openpublishing.redirection.ai.json b/.openpublishing.redirection.ai.json index b163e96c7859a..553533a2de2ef 100644 --- a/.openpublishing.redirection.ai.json +++ b/.openpublishing.redirection.ai.json @@ -11,6 +11,10 @@ { "source_path_from_root": "/docs/ai/how-to/work-with-local-models.md", "redirect_url": "/dotnet/ai" + }, + { + "source_path_from_root": "/docs/ai/how-to/app-service-db-auth.md", + "redirect_url": "/dotnet/ai" } ] } \ No newline at end of file diff --git a/docs/ai/how-to/app-service-aoai-auth.md b/docs/ai/how-to/app-service-aoai-auth.md index 27cb0e91bd3d4..685d18cf9fdba 100644 --- a/docs/ai/how-to/app-service-aoai-auth.md +++ b/docs/ai/how-to/app-service-aoai-auth.md @@ -185,6 +185,5 @@ az role assignment create --assignee "" \ ## Related content -* [Authenticate and authorize App Service to a vector database](app-service-db-auth.md) * [How to use managed identities for App Service and Azure Functions](/azure/app-service/overview-managed-identity) * [Role-based access control for Azure OpenAI Service](/azure/ai-services/openai/how-to/role-based-access-control) diff --git a/docs/ai/how-to/app-service-db-auth.md b/docs/ai/how-to/app-service-db-auth.md deleted file mode 100644 index f92a4441a9be5..0000000000000 --- a/docs/ai/how-to/app-service-db-auth.md +++ /dev/null @@ -1,317 +0,0 @@ ---- -title: "Authenticate and Authorize App Service to a Vector Database" -description: "Learn how to authenticate and authorize your App Service .NET application to a vector database solution using Microsoft Entra managed identities, Key Vault, or app settings" -author: haywoodsloan -ms.topic: how-to -ms.custom: devx-track-azurecli -ms.date: 11/24/2024 -zone_pivot_groups: azure-interface -#customer intent: As a .NET developer, I want authenticate and authorize my App Service to a vector database so that I can securely add memories to the AI in my .NET application. ---- - -# Authenticate and authorize App Service to a vector database - -This article demonstrates how to manage the connection between your App Service .NET application and a [vector database solution](../conceptual/vector-databases.md). It covers using Microsoft Entra managed identities for supported services and securely storing connection strings for others. - -By adding a vector database to your application, you can enable [semantic memories or *vector stores*](/semantic-kernel/concepts/vector-store-connectors/) for your AI. The [Semantic Kernel SDK](/semantic-kernel/overview) for .NET enables you to easily implement memory storage and recall using your preferred vector database solution. - -## Prerequisites - -* An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). -* [.NET SDK](https://dotnet.microsoft.com/download/visual-studio-sdks) -* [`Microsoft.SemanticKernel` NuGet package](https://www.nuget.org/packages/Microsoft.SemanticKernel) -* [`Microsoft.SemanticKernel.Plugins.Memory` NuGet package](https://www.nuget.org/packages/Microsoft.SemanticKernel.Plugins.Memory) -* [Create and deploy a .NET application to App Service](/azure/app-service/quickstart-dotnetcore) -* [Create and deploy a vector database solution](/semantic-kernel/concepts/ai-services/integrations#vector-database-solutions) - -## Use Microsoft Entra managed identity for authentication - -If a vector database service supports Microsoft Entra authentication, you can use a managed identity with your App Service to securely access your vector database without having to manually provision or rotate any secrets. For a list of Azure services that support Microsoft Entra authentication, see [Azure services that support Microsoft Entra authentication](/entra/identity/managed-identities-azure-resources/services-id-authentication-support). - -### Add a managed identity to App Service - -Your application can be granted two types of identities: - -* A **system-assigned identity** is tied to your application and is deleted if your app is deleted. An app can have only one system-assigned identity. -* A **user-assigned identity** is a standalone Azure resource that can be assigned to your app. An app can have multiple user-assigned identities. - -:::zone target="docs" pivot="azure-portal" - -# [System-assigned](#tab/system-assigned) - -1. Navigate to your app's page in the [Azure portal](https://aka.ms/azureportal), and then scroll down to the **Settings** group. -1. Select **Identity**. -1. On the **System assigned** tab, toggle *Status* to **On**, and then select **Save**. - -## [User-assigned](#tab/user-assigned) - -To add a user-assigned identity to your app, create the identity, and then add its resource identifier to your app config. - -1. Create a user-assigned managed identity resource by following [these instructions](/azure/active-directory/managed-identities-azure-resources/how-to-manage-ua-identity-portal#create-a-user-assigned-managed-identity). -1. In the left navigation pane of your app's page, scroll down to the **Settings** group. -1. Select **Identity**. -1. Select **User assigned** > **Add**. -1. Locate the identity that you created earlier, select it, and then select **Add**. - - > [!IMPORTANT] - > After you select **Add**, the app restarts. - ---- - -:::zone-end - -:::zone target="docs" pivot="azure-cli" - -## [System-assigned](#tab/system-assigned) - -Run the `az webapp identity assign` command to create a system-assigned identity: - -```azurecli -az webapp identity assign --name --resource-group -``` - -## [User-assigned](#tab/user-assigned) - -1. Create a user-assigned identity: - - ```azurecli - az identity create --resource-group --name - ``` - -1. Assign the identity to your app: - - ```azurecli - az webapp identity assign --resource-group --name --identities - ``` - ---- - -:::zone-end - -### Add an Azure role to your managed identity - -:::zone target="docs" pivot="azure-portal" - -1. In the [Azure Portal](https://aka.ms/azureportal), navigate to the scope that you want to grant vector database access to. The scope can be a **Management group**, **Subscription**, **Resource group**, or a specific Azure resource. -1. In the left navigation pane, select **Access control (IAM)**. -1. Select **Add**, then select **Add role assignment**. -1. On the **Role** tab, select the appropriate role that grants read access to your vector database. -1. On the **Members** tab, select the managed identity. -1. On the **Review + assign** tab, select **Review + assign** to assign the role. - -:::zone-end - -:::zone target="docs" pivot="azure-cli" - -**Resource scope** - -```azurecli -az role assignment create --assignee "" \ ---role "" \ ---scope "/subscriptions//resourcegroups//providers////" -``` - -**Resource group scope** - -```azurecli -az role assignment create --assignee "" \ ---role "" \ ---scope "/subscriptions//resourcegroups/" -``` - -**Subscription scope** - -```azurecli -az role assignment create --assignee "" \ ---role "" \ ---scope "/subscriptions/" -``` - -**Management group scope** - -```azurecli -az role assignment create --assignee "" \ ---role "" \ ---scope "/providers/Microsoft.Management/managementGroups/" -``` - -:::zone-end - -### Implement token-based authentication with the vector database - -The following code samples require these additional libraries: - -* [`Azure.Identity` NuGet package](https://www.nuget.org/packages/Azure.Identity) -* [`Microsoft.SemanticKernel.Connectors.AzureAISearch` NuGet package](https://www.nuget.org/packages/Microsoft.SemanticKernel.Connectors.AzureAISearch) - -1. Initialize a `DefaultAzureCredential` object to pick up your app's managed identity: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="tokenCredential"::: - -1. Initialize an `IMemoryStore` object for your vector database, then use it to build an `ISemanticTextMemory`: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="aiStore"::: - -1. Build a `Kernel` object, then import the `ISemanticTextMemory` object using the `TextMemoryPlugin`: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="addMemory"::: - -1. Use the `Kernel` object to invoke a prompt that includes memory recall: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="useMemory"::: - -## Use Key Vault to store connection secrets - -If a vector database doesn't support Microsoft Entra authentication, you can use a Key Vault to store your connection secrets and retrieve them with your App Service application. By using a Key Vault to store your connection secrets you can share them with multiple applications, and control access to individual secrets per application. - -Before following these steps, retrieve a connection string for your vector database. For example, see [Use Azure Cache for Redis with an ASP.NET Core web app](/azure/azure-cache-for-redis/cache-web-app-aspnet-core-howto#retrieve-host-name-ports-and-access-keys-from-the-azure-portal). - -### Add a connection string to Key Vault - -:::zone target="docs" pivot="azure-portal" - -> [!IMPORTANT] -> Before following these steps, ensure you have [created a Key Vault using the Azure Portal](/azure/key-vault/general/quick-create-portal). - -1. Navigate to your key vault in the [Azure Portal](https://aka.ms/azureportal). -1. In the Key Vault left navigation, select **Objects** then select **Secrets**. -1. Select **+ Generate/Import**. -1. On the **Create a secret** screen choose the following values: - * **Upload options**: `Manual`. - * **Name**: Type a name for the secret. The secret name must be unique within a Key Vault. - * **Value**: The connection string for your vector database. - * Leave the other values to their defaults. Select **Create**. -1. When you receive the message that the secret has been successfully created, it's ready to use in your application. - -:::zone-end - -:::zone target="docs" pivot="azure-cli" - -> [!IMPORTANT] -> Before following these steps, ensure you have [created a Key Vault using the Azure CLI](/azure/key-vault/general/quick-create-cli). - -1. Grant your user account permissions to your key vault through Role-Based Access Control (RBAC), assign a role using the Azure CLI command [`az role assignment create`](/cli/azure/role/assignment#az-role-assignment-create): - - ```azurecli - az role assignment create \ - --role "Key Vault Secrets User" \ - --assignee "" \ - --scope "/subscriptions//resourceGroups//providers/Microsoft.KeyVault/vaults/" - ``` - -1. Add the connection string to Key Vault using the Azure CLI command [`az keyvault secret set`](/cli/azure/keyvault/secret#az-keyvault-secret-set): - - ```azurecli - az keyvault secret set \ - --vault-name "" \ - --name "" \ - --value "" - ``` - -:::zone-end - -### Grant your App Service access to Key Vault - -1. [Assign a managed identity to your App Service](#add-a-managed-identity-to-app-service). -1. [Add the `Key Vault Secrets User` and `Key Vault Reader` roles to your managed identity](#add-an-azure-role-to-your-managed-identity). - -### Implement connection string retrieval from Key Vault - -To use the following code samples, you need these additional libraries: - -* [`Azure.Identity` NuGet package](https://www.nuget.org/packages/Azure.Identity) -* [`Azure.Extensions.AspNetCore.Configuration.Secrets` NuGet package](https://www.nuget.org/packages/Azure.Extensions.AspNetCore.Configuration.Secrets) -* [`Microsoft.Extensions.Configuration` NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.Configuration) - -These code samples use a Redis database, but you can apply them to any vector database that supports connection strings. - -1. Initialize a `DefaultAzureCredential` object to pick up your app's managed identity: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="tokenCredential"::: - -1. Add Key Vault when building your configuration, this will map your Key Vault secrets to the `IConfigurationRoot` object: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="vaultConfig"::: - -1. Use your vector database connection string from Key Vault to initialize an `IMemoryStore` object, and then use it to build an `ISemanticTextMemory`: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="redisStore"::: - -1. Build a `Kernel` object, then import the `ISemanticTextMemory` object using the `TextMemoryPlugin`: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="addMemory"::: - -1. Use the `Kernel` object to invoke a prompt that includes memory recall: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="useMemory"::: - -## Use application settings to store connection secrets - -If a vector database doesn't support Microsoft Entra authentication, you can use the App Service application settings to store your connection secrets. By using application settings you can store your connection secrets without provisioning any additional Azure resources. - -Before following these steps, retrieve a connection string for your vector database. For example, see [Use Azure Cache for Redis in .NET Framework](/azure/azure-cache-for-redis/cache-dotnet-how-to-use-azure-redis-cache#retrieve-host-name-ports-and-access-keys-from-the-azure-portal). - -### Add a connection string to application settings - -:::zone target="docs" pivot="azure-portal" - -1. Navigate to your app's page on the [Azure Portal](https://aka.ms/azureportal). -1. In the app's left menu, select **Configuration** > **Application settings**. - * By default, values for application settings are hidden in the portal for security. - * To see a hidden value of an application setting, select its Value field. -1. Select **New connection setting**. -1. On the **Add/Edit connection string** screen choose the following values: - * **Name**: Type a name for the setting. The setting name must be unique. - * **Value**: The connection string for your vector database. - * **Type**: The type of connection, `Custom` if no others apply. - * Leave the other values to their defaults. Select **OK**. -1. Select **Save** back in the Configuration page. - -:::zone-end - -:::zone target="docs" pivot="azure-cli" - -Add or edit an app setting with the Azure CLI command [`az webapp config connection-string set`](/cli/azure/webapp/config/connection-string#az-webapp-config-connection-string-set): - -```azurecli -az webapp config connection-string set \ ---name "" \ ---resource-group "" \ ---connection-string-type "" \ ---settings ='' -``` - -:::zone-end - -### Implement connection string retrieval from app settings - -To use the following code samples, you need these additional libraries: - -* [`Microsoft.Extensions.Configuration` NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.Configuration) -* [`Microsoft.Extensions.Configuration.EnvironmentVariables` NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.EnvironmentVariables) - -These code samples use a Redis database, but you can apply them to any vector database that supports connection strings. - -1. Add environment variables when building your configuration, this will map your connection strings to the `IConfigurationRoot` object: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="appSettingsConfig"::: - -1. Use your vector database connection string from app settings to initialize an `IMemoryStore` object, and then use it to build an `ISemanticTextMemory`: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="redisStore"::: - -1. Build a `Kernel` object, then import the `ISemanticTextMemory` object using the `TextMemoryPlugin`: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="addMemory"::: - -1. Use the `Kernel` object to invoke a prompt that includes memory recall: - - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="useMemory"::: - -## Related content - - - -* [Use Redis for memory storage with the Semantic Kernel SDK] -* [How to use managed identities for App Service and Azure Functions](/azure/app-service/overview-managed-identity) -* [Steps to assign an Azure role](/azure/role-based-access-control/role-assignments-steps) diff --git a/docs/ai/how-to/snippets/semantic-kernel/IdentityExamples.cs b/docs/ai/how-to/snippets/semantic-kernel/IdentityExamples.cs deleted file mode 100644 index a6b547d415c03..0000000000000 --- a/docs/ai/how-to/snippets/semantic-kernel/IdentityExamples.cs +++ /dev/null @@ -1,189 +0,0 @@ -using Azure.Core; -using Azure.Identity; -using Microsoft.Extensions.Configuration; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Connectors.AzureAISearch; -using Microsoft.SemanticKernel.Connectors.OpenAI; -using Microsoft.SemanticKernel.Connectors.Redis; -using Microsoft.SemanticKernel.Memory; -using Microsoft.SemanticKernel.Plugins.Memory; -using StackExchange.Redis; - -// Suppress warning about AzureAISearchMemoryStore still being in evaluation -#pragma warning disable SKEXP0020 - -// Suppress warning about MemoryStore still being in evaluation -#pragma warning disable SKEXP0001 - -// Suppress warning about MemoryBuilder extensions still being in evaluation -#pragma warning disable SKEXP0010 - -// Supress warning about TextMemoryPlugin still being in evaluation -#pragma warning disable SKEXP0050 - -class IdentityExamples -{ - public static async Task Examples() - { - await ManagedIdentityExample(); - await AiSearchExample(); - await RedisKeyVaultExample(); - RedisAppSettingsExample(); - } - - static async Task ManagedIdentityExample() - { - // - // Initialize a DefaultAzureCredential. - // This credential type will try several authentication flows in order until one is available. - // Will pickup Visual Studio or Azure CLI credentials in local environments. - // Will pickup managed identity credentials in production deployments. - TokenCredential credentials = new DefaultAzureCredential( - new DefaultAzureCredentialOptions - { - // If using a user-assigned identity specify either: - // ManagedIdentityClientId or ManagedIdentityResourceId. - // e.g.: ManagedIdentityClientId = "myIdentityClientId". - } - ); - // - - // - // Retrieve the endpoint and deployment obtained from the Azure OpenAI deployment. - // Must use the deployment name not the underlying model name. - IConfigurationRoot config = new ConfigurationBuilder().AddUserSecrets().Build(); - string endpoint = config["AZURE_OPENAI_ENDPOINT"]!; - string deployment = config["AZURE_OPENAI_GPT_NAME"]!; - - // Build a Kernel that includes the Azure OpenAI Chat Completion Service. - // Include the previously created token credential. - Kernel kernel = Kernel - .CreateBuilder() - .AddAzureOpenAIChatCompletion(deployment, endpoint, credentials) - .Build(); - // - - // - // Use the Kernel to invoke prompt completion through Azure OpenAI. - // The Kernel response will be null if the model can't be reached. - string? result = await kernel.InvokePromptAsync("Please list three Azure services"); - Console.WriteLine($"Output: {result}"); - - // Continue sending and receiving messages between the user and AI. - // ... - // - } - - static async Task AiSearchExample() - { - TokenCredential credentials = new DefaultAzureCredential(); - - // - // Retrieve the endpoint obtained from the Azure AI Search deployment. - // Retrieve the endpoint and deployments obtained from the Azure OpenAI deployment. - // Must use the deployment name not the underlying model name. - IConfigurationRoot config = new ConfigurationBuilder().AddUserSecrets().Build(); - string searchEndpoint = config["AZURE_AISEARCH_ENDPOINT"]!; - string openAiEndpoint = config["AZURE_OPENAI_ENDPOINT"]!; - string embeddingModel = config["AZURE_OPENAI_EMBEDDING_NAME"]!; - - // The Semantic Kernel SDK provides a connector extension for Azure AI Search. - // Initialize an AzureAISearchMemoryStore using your managed identity credentials. - IMemoryStore memoryStore = new AzureAISearchMemoryStore(searchEndpoint, credentials); - - // Build a SemanticMemoryStore with Azure AI Search as the store. - // Must also include a text embedding generation service. - ISemanticTextMemory memory = new MemoryBuilder() - .WithOpenAITextEmbeddingGeneration(embeddingModel, openAiEndpoint) - .WithMemoryStore(memoryStore) - .Build(); - // - - // - // Build a Kernel, include a chat completion service. - string chatModel = config["AZURE_OPENAI_GPT_NAME"]!; - Kernel kernel = Kernel - .CreateBuilder() - .AddAzureOpenAIChatCompletion(chatModel, openAiEndpoint, credentials) - .Build(); - - // Import the semantic memory store as a TextMemoryPlugin. - // The TextMemoryPlugin enable recall via prompt expressions. - kernel.ImportPluginFromObject(new TextMemoryPlugin(memory)); - // - - // - // Must configure the memory collection, number of memories to recall, and relevance score. - // The {{...}} syntax represents an expression to Semantic Kernel. - // For more information on this syntax see: - // https://learn.microsoft.com/semantic-kernel/prompts/prompt-template-syntax - string memoryCollection = config["AZURE_OPENAI_MEMORY_NAME"]!; - string? result = await kernel.InvokePromptAsync( - "{{recall 'where did I grow up?'}}", - new() - { - [TextMemoryPlugin.CollectionParam] = memoryCollection, - [TextMemoryPlugin.LimitParam] = "2", - [TextMemoryPlugin.RelevanceParam] = "0.79", - } - ); - Console.WriteLine($"Output: {result}"); - // - } - - static async Task RedisKeyVaultExample() - { - TokenCredential credentials = new DefaultAzureCredential(); - - // - // User secrets let you provide connection strings when testing locally - // For more info see: https://learn.microsoft.com/aspnet/core/security/app-secrets - IConfigurationRoot config = new ConfigurationBuilder() - .AddUserSecrets() - .AddAzureKeyVault(new Uri("{vaultURI}"), credentials) - .Build(); - - // Retrieve the Redis connection string obtained from the Key Vault. - string redisConnectionString = config["AZURE_REDIS_CONNECT_STRING"]!; - // - - // - // Use the connection string to connect to the database - IDatabase database = ( - await ConnectionMultiplexer.ConnectAsync(redisConnectionString) - ).GetDatabase(); - - // The Semantic Kernel SDK provides a connector extension for Redis. - // Initialize an RedisMemoryStore using your managed identity credentials. - IMemoryStore memoryStore = new RedisMemoryStore(database); - - // Retrieve the endpoint and deployments obtained from the Azure OpenAI deployment. - // Must use the deployment name not the underlying model name. - string openAiEndpoint = config["AZURE_OPENAI_ENDPOINT"]!; - string embeddingModel = config["AZURE_OPENAI_EMBEDDING_NAME"]!; - - // Build a SemanticMemoryStore with Azure AI Search as the store. - // Must also include a text embedding generation service. - ISemanticTextMemory memory = new MemoryBuilder() - .WithOpenAITextEmbeddingGeneration(embeddingModel, openAiEndpoint) - .WithMemoryStore(memoryStore) - .Build(); - // - } - - static void RedisAppSettingsExample() - { - // - // User secrets let you provide connection strings when testing locally - // For more info see: https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets - IConfigurationRoot config = new ConfigurationBuilder() - .AddUserSecrets() - .AddEnvironmentVariables() - .Build(); - - // Retrieve the Redis connection string obtained from the app settings. - // The connection string name should match the entry in application settings - string redisConnectionString = config.GetConnectionString("AZURE_REDIS")!; - // - } -} diff --git a/docs/ai/how-to/snippets/semantic-kernel/LocalModelExamples.cs b/docs/ai/how-to/snippets/semantic-kernel/LocalModelExamples.cs deleted file mode 100644 index 2c70e3343aa07..0000000000000 --- a/docs/ai/how-to/snippets/semantic-kernel/LocalModelExamples.cs +++ /dev/null @@ -1,174 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.ChatCompletion; -using Microsoft.SemanticKernel.TextGeneration; - -class LocalModelExamples -{ - public static async Task Examples() - { - AddTextGenerationServiceExample(); - AddChatCompletionServiceExample(); - await UseTextGenerationServiceExample(); - await UseChatCompletionServiceExample(); - } - - static Kernel AddTextGenerationServiceExample() - { - // - IKernelBuilder builder = Kernel.CreateBuilder(); - - // Add your text generation service as a singleton instance - builder.Services.AddKeyedSingleton( - "myTextService1", - new MyTextGenerationService - { - // Specify any properties specific to your service, such as the url or API key - ModelUrl = "https://localhost:38748", - ModelApiKey = "myApiKey" - } - ); - - // Alternatively, add your text generation service as a factory method - builder.Services.AddKeyedSingleton( - "myTextService2", - (_, _) => - new MyTextGenerationService - { - // Specify any properties specific to your service, such as the url or API key - ModelUrl = "https://localhost:38748", - ModelApiKey = "myApiKey" - } - ); - - // Add any other Kernel services or configurations - // ... - Kernel kernel = builder.Build(); - // - - return kernel; - } - - static Kernel AddChatCompletionServiceExample() - { - // - IKernelBuilder builder = Kernel.CreateBuilder(); - - // Add your chat completion service as a singleton instance - builder.Services.AddKeyedSingleton( - "myChatService1", - new MyChatCompletionService - { - // Specify any properties specific to your service, such as the url or API key - ModelUrl = "https://localhost:38748", - ModelApiKey = "myApiKey" - } - ); - - // Alternatively, add your chat completion service as a factory method - builder.Services.AddKeyedSingleton( - "myChatService2", - (_, _) => - new MyChatCompletionService - { - // Specify any properties specific to your service, such as the url or API key - ModelUrl = "https://localhost:38748", - ModelApiKey = "myApiKey" - } - ); - - // Add any other Kernel services or configurations - // ... - Kernel kernel = builder.Build(); - // - - return kernel; - } - - static async Task UseTextGenerationServiceExample() - { - IKernelBuilder builder = Kernel.CreateBuilder(); - builder.Services.AddKeyedSingleton( - "myTextService", - new MyTextGenerationService { ModelApiKey = "myApiKey" } - ); - Kernel kernel = builder.Build(); - - // - var executionSettings = new PromptExecutionSettings - { - // Add execution settings, such as the ModelID and ExtensionData - ModelId = "MyModelId", - ExtensionData = new Dictionary { { "MaxTokens", 500 } } - }; - - // Send a prompt to your model directly through the Kernel - // The Kernel response will be null if the model can't be reached - string prompt = "Please list three services offered by Azure"; - string? response = await kernel.InvokePromptAsync(prompt); - Console.WriteLine($"Output: {response}"); - - // Alteratively, send a prompt to your model through the text generation service - ITextGenerationService textService = kernel.GetRequiredService(); - TextContent responseContents = await textService.GetTextContentAsync( - prompt, - executionSettings - ); - Console.WriteLine($"Output: {responseContents.Text}"); - // - } - - static async Task UseChatCompletionServiceExample() - { - IKernelBuilder builder = Kernel.CreateBuilder(); - builder.Services.AddKeyedSingleton( - "myChatService", - new MyChatCompletionService { ModelApiKey = "myApiKey" } - ); - Kernel kernel = builder.Build(); - - // - var executionSettings = new PromptExecutionSettings - { - // Add execution settings, such as the ModelID and ExtensionData - ModelId = "MyModelId", - ExtensionData = new Dictionary { { "MaxTokens", 500 } } - }; - - // Send a string representation of the chat history to your model directly through the Kernel - // This uses a special syntax to denote the role for each message - // For more information on this syntax see: - // https://learn.microsoft.com/en-us/semantic-kernel/prompts/your-first-prompt?tabs=Csharp - string prompt = """ - the initial system message for your chat history - the user's initial message - """; - - string? response = await kernel.InvokePromptAsync(prompt); - Console.WriteLine($"Output: {response}"); - - // Alteratively, send a prompt to your model through the chat completion service - // First, initialize a chat history with your initial system message - string systemMessage = ""; - Console.WriteLine($"System Prompt: {systemMessage}"); - var chatHistory = new ChatHistory(systemMessage); - - // Add the user's input to your chat history - string userRequest = ""; - Console.WriteLine($"User: {userRequest}"); - chatHistory.AddUserMessage(userRequest); - - // Get the models response and add it to the chat history - IChatCompletionService service = kernel.GetRequiredService(); - ChatMessageContent responseMessage = await service.GetChatMessageContentAsync( - chatHistory, - executionSettings - ); - Console.WriteLine($"Assistant: {responseMessage.Content}"); - chatHistory.Add(responseMessage); - - // Continue sending and receiving messages between the user and model - // ... - // - } -} diff --git a/docs/ai/how-to/snippets/semantic-kernel/MemoryExamples.cs b/docs/ai/how-to/snippets/semantic-kernel/MemoryExamples.cs deleted file mode 100644 index 660935e2604e9..0000000000000 --- a/docs/ai/how-to/snippets/semantic-kernel/MemoryExamples.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System.Text.Json; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Connectors.OpenAI; -using Microsoft.SemanticKernel.Connectors.Redis; -using Microsoft.SemanticKernel.Embeddings; -using Microsoft.SemanticKernel.Memory; -using Microsoft.SemanticKernel.Plugins.Memory; -using StackExchange.Redis; - -// Suppress warning about embedding generation still being in evaluation -#pragma warning disable SKEXP0010 - -// Suppress warning about Redis connector still being in evaluation -#pragma warning disable SKEXP0020 - -// Suppress warning about MemoryStore still being in evaluation -#pragma warning disable SKEXP0001 - -// Supress warning about TextMemoryPlugin still being in evaluation -#pragma warning disable SKEXP0050 - -class MemoryExamples -{ - public static async Task Examples() - { - await RediSearchExample(); - } - - static async Task RediSearchExample() - { - // - // Retrieve the Redis connection config. - IConfigurationRoot config = new ConfigurationBuilder().AddUserSecrets().Build(); - string redisConfig = config["REDIS_CONFIG"]!; - - // Initialize a connection to the Redis database. - ConnectionMultiplexer connectionMultiplexer = await ConnectionMultiplexer.ConnectAsync( - redisConfig - ); - IDatabase database = connectionMultiplexer.GetDatabase(); - // - - // - // Retrieve the Azure OpenAI config and secrets saved during deployment. - string endpoint = config["AZURE_OPENAI_ENDPOINT"]!; - string embeddingModel = config["AZURE_OPENAI_EMBEDDING_NAME"]!; - string completionModel = config["AZURE_OPENAI_GPT_NAME"]!; - string key = config["AZURE_OPENAI_KEY"]!; - - // Build the Kernel; must add an embedding generation service. - Kernel kernel = Kernel - .CreateBuilder() - .AddAzureOpenAITextEmbeddingGeneration(embeddingModel, endpoint, key) - .AddAzureOpenAIChatCompletion(completionModel, endpoint, key) - .Build(); - // - - // - // Retrieve the desired vector size for the memory store. - // If unspecified, the default vector size is 1536. - int vectorSize = int.Parse(config["REDIS_MEMORY_VECTOR_SIZE"]!); - - // Initialize a memory store using the redis database - IMemoryStore memoryStore = new RedisMemoryStore(database, vectorSize); - - // Retrieve the embedding service from the Kernel. - ITextEmbeddingGenerationService embeddingService = - kernel.Services.GetRequiredService(); - - // Initialize a SemanticTextMemory using the memory store and embedding generation service. - SemanticTextMemory textMemory = new(memoryStore, embeddingService); - // - - // - // Initialize a TextMemoryPlugin using the text memory. - TextMemoryPlugin memoryPlugin = new(textMemory); - - // Import the text memory plugin into the Kernel. - KernelPlugin memory = kernel.ImportPluginFromObject(memoryPlugin); - // - - // - // Retrieve the desired memory collection name. - string memoryCollectionName = config["REDIS_MEMORY_COLLECTION_NAME"]!; - - // Save a memory with the Kernel. - await kernel.InvokeAsync( - memory["Save"], - new() - { - [TextMemoryPlugin.InputParam] = "My family is from New York", - [TextMemoryPlugin.CollectionParam] = memoryCollectionName, - [TextMemoryPlugin.KeyParam] = "info1", - } - ); - - // Retrieve a memory with the Kernel. - FunctionResult result = await kernel.InvokeAsync( - memory["Retrieve"], - new() - { - [TextMemoryPlugin.CollectionParam] = memoryCollectionName, - [TextMemoryPlugin.KeyParam] = "info1", - } - ); - - // Get the memory string from the function result; returns a null value if no memory is found. - Console.WriteLine( - $"Retrieved memory: {result.GetValue() ?? "ERROR: memory not found"}" - ); - - // Alternatively, recall similar memories with the Kernel. - // Can configure the memory collection, number of memories to recall, and relevance score. - result = await kernel.InvokeAsync( - memory["Recall"], - new() - { - [TextMemoryPlugin.InputParam] = "Ask: where do I live?", - [TextMemoryPlugin.CollectionParam] = memoryCollectionName, - [TextMemoryPlugin.LimitParam] = "2", - [TextMemoryPlugin.RelevanceParam] = "0.79", - } - ); - - // If memories are recalled, the function result can be deserialized as a string[]. - string? resultStr = result.GetValue(); - string[]? parsedResult = string.IsNullOrEmpty(resultStr) - ? null - : JsonSerializer.Deserialize(resultStr); - Console.WriteLine( - $"Recalled memories: {(parsedResult?.Length > 0 ? resultStr : "ERROR: memory not found")}" - ); - // - - // - // Create a prompt that includes memory recall. - // The {{...}} syntax represents an expression to Semantic Kernel. - // For more information on this syntax see: - // https://learn.microsoft.com/semantic-kernel/prompts/prompt-template-syntax - string memoryRecallPrompt = """ - Consider only the facts below when answering questions: - - BEGIN FACTS - About me: {{recall 'where did I grow up?'}} - END FACTS - - Question: What are some fun facts about my home state? - """; - - // Invoke the prompt with the Kernel. - // Must configure the memory collection, number of memories to recall, and relevance score. - resultStr = await kernel.InvokePromptAsync( - memoryRecallPrompt, - new() - { - [TextMemoryPlugin.CollectionParam] = memoryCollectionName, - [TextMemoryPlugin.LimitParam] = "2", - [TextMemoryPlugin.RelevanceParam] = "0.79", - } - ); - - // If the memory recall fails, the model will indicate it has missing information in its output. - // Otherwise the output will incorporate your memory as context. - Console.WriteLine($"Output: {resultStr}"); - // - } -} diff --git a/docs/ai/how-to/snippets/semantic-kernel/Program.cs b/docs/ai/how-to/snippets/semantic-kernel/Program.cs deleted file mode 100644 index 5073b4d569487..0000000000000 --- a/docs/ai/how-to/snippets/semantic-kernel/Program.cs +++ /dev/null @@ -1,3 +0,0 @@ -await IdentityExamples.Examples(); -await MemoryExamples.Examples(); -await LocalModelExamples.Examples(); diff --git a/docs/ai/how-to/snippets/semantic-kernel/model/MyModelRequest.cs b/docs/ai/how-to/snippets/semantic-kernel/model/MyModelRequest.cs deleted file mode 100644 index 13b3fe2937125..0000000000000 --- a/docs/ai/how-to/snippets/semantic-kernel/model/MyModelRequest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.ChatCompletion; - -// This is a simple mock request to provide the needed function signatures for the snippet code -class MyModelRequest -{ - public required string Request { get; set; } - public PromptExecutionSettings? Settings { get; set; } - public bool Stream { get; set; } = true; - - public static MyModelRequest FromChatHistory( - ChatHistory history, - PromptExecutionSettings? settings - ) - { - return new MyModelRequest() { Request = history.Last().Content!, Settings = settings }; - } - - public static MyModelRequest FromPrompt(string prompt, PromptExecutionSettings? settings) - { - return new MyModelRequest() { Request = prompt, Settings = settings }; - } -} diff --git a/docs/ai/how-to/snippets/semantic-kernel/model/MyModelResponse.cs b/docs/ai/how-to/snippets/semantic-kernel/model/MyModelResponse.cs deleted file mode 100644 index fb0b722a63c16..0000000000000 --- a/docs/ai/how-to/snippets/semantic-kernel/model/MyModelResponse.cs +++ /dev/null @@ -1,5 +0,0 @@ -// This is a simple mock response to provide the needed function signatures for the snippet code -class MyModelResponse -{ - public IReadOnlyList Completions { get; init; } = []; -} diff --git a/docs/ai/how-to/snippets/semantic-kernel/semantic-kernel.csproj b/docs/ai/how-to/snippets/semantic-kernel/semantic-kernel.csproj deleted file mode 100644 index a88ee582982ae..0000000000000 --- a/docs/ai/how-to/snippets/semantic-kernel/semantic-kernel.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - Exe - net8.0 - semantic_kernel - enable - enable - ed6763a1-4d17-4e03-aa62-819cfc78ef3b - - - - - - - - - - - - - - - - diff --git a/docs/ai/how-to/snippets/semantic-kernel/services/MyChatCompletionService.cs b/docs/ai/how-to/snippets/semantic-kernel/services/MyChatCompletionService.cs deleted file mode 100644 index 5d547e26fffb0..0000000000000 --- a/docs/ai/how-to/snippets/semantic-kernel/services/MyChatCompletionService.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.Collections.Immutable; -using System.Net.Http.Json; -using System.Runtime.CompilerServices; -using System.Text.Json; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.ChatCompletion; - -// -class MyChatCompletionService : IChatCompletionService -{ - private IReadOnlyDictionary? _attributes; - public IReadOnlyDictionary Attributes => - _attributes ??= new Dictionary(); - - public string ModelUrl { get; init; } = ""; - public required string ModelApiKey { get; init; } - - public async Task> GetChatMessageContentsAsync( - ChatHistory chatHistory, - PromptExecutionSettings? executionSettings = null, - Kernel? kernel = null, - CancellationToken cancellationToken = default - ) - { - // Build your model's request object - MyModelRequest request = MyModelRequest.FromChatHistory(chatHistory, executionSettings); - - // Send the completion request via HTTP - using var httpClient = new HttpClient(); - - // Send a POST to your model with the serialized request in the body - using HttpResponseMessage httpResponse = await httpClient.PostAsJsonAsync( - ModelUrl, - request, - cancellationToken - ); - - // Verify the request was completed successfully - httpResponse.EnsureSuccessStatusCode(); - - // Deserialize the response body to your model's response object - // Handle when the deserialization fails and returns null - MyModelResponse response = - await httpResponse.Content.ReadFromJsonAsync(cancellationToken) - ?? throw new Exception("Failed to deserialize response from model"); - - // Convert your model's response into a list of ChatMessageContent - return response - .Completions.Select(completion => - new(AuthorRole.Assistant, completion) - ) - .ToImmutableList(); - } - - public async IAsyncEnumerable GetStreamingChatMessageContentsAsync( - ChatHistory chatHistory, - PromptExecutionSettings? executionSettings = null, - Kernel? kernel = null, - [EnumeratorCancellation] CancellationToken cancellationToken = default - ) - { - // Build your model's request object, specify that streaming is requested - MyModelRequest request = MyModelRequest.FromChatHistory(chatHistory, executionSettings); - request.Stream = true; - - // Send the completion request via HTTP - using var httpClient = new HttpClient(); - - // Send a POST to your model with the serialized request in the body - using HttpResponseMessage httpResponse = await httpClient.PostAsJsonAsync( - ModelUrl, - request, - cancellationToken - ); - - // Verify the request was completed successfully - httpResponse.EnsureSuccessStatusCode(); - - // Read your models response as a stream - using StreamReader reader = - new(await httpResponse.Content.ReadAsStreamAsync(cancellationToken)); - - // Iteratively read a chunk of the response until the end of the stream - // It is more efficient to use a buffer that is the same size as the internal buffer of the stream - // If the size of the internal buffer was unspecified when the stream was constructed, its default size is 4 kilobytes (2048 UTF-16 characters) - char[] buffer = new char[2048]; - while (!reader.EndOfStream) - { - // Check the cancellation token with each iteration - cancellationToken.ThrowIfCancellationRequested(); - - // Fill the buffer with the next set of characters, track how many characters were read - int readCount = reader.Read(buffer, 0, buffer.Length); - - // Convert the character buffer to a string, only include as many characters as were just read - string chunk = new(buffer, 0, readCount); - - yield return new StreamingChatMessageContent(AuthorRole.Assistant, chunk); - } - } -} -// diff --git a/docs/ai/how-to/snippets/semantic-kernel/services/MyTextGenerationService.cs b/docs/ai/how-to/snippets/semantic-kernel/services/MyTextGenerationService.cs deleted file mode 100644 index c594f9f2aee2f..0000000000000 --- a/docs/ai/how-to/snippets/semantic-kernel/services/MyTextGenerationService.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Immutable; -using System.Net.Http.Json; -using System.Runtime.CompilerServices; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.TextGeneration; - -// -class MyTextGenerationService : ITextGenerationService -{ - private IReadOnlyDictionary? _attributes; - public IReadOnlyDictionary Attributes => - _attributes ??= new Dictionary(); - - public string ModelUrl { get; init; } = ""; - public required string ModelApiKey { get; init; } - - public async IAsyncEnumerable GetStreamingTextContentsAsync( - string prompt, - PromptExecutionSettings? executionSettings = null, - Kernel? kernel = null, - [EnumeratorCancellation] CancellationToken cancellationToken = default - ) - { - // Build your model's request object, specify that streaming is requested - MyModelRequest request = MyModelRequest.FromPrompt(prompt, executionSettings); - request.Stream = true; - - // Send the completion request via HTTP - using var httpClient = new HttpClient(); - - // Send a POST to your model with the serialized request in the body - using HttpResponseMessage httpResponse = await httpClient.PostAsJsonAsync( - ModelUrl, - request, - cancellationToken - ); - - // Verify the request was completed successfully - httpResponse.EnsureSuccessStatusCode(); - - // Read your models response as a stream - using StreamReader reader = - new(await httpResponse.Content.ReadAsStreamAsync(cancellationToken)); - - // Iteratively read a chunk of the response until the end of the stream - // It is more efficient to use a buffer that is the same size as the internal buffer of the stream - // If the size of the internal buffer was unspecified when the stream was constructed, its default size is 4 kilobytes (2048 UTF-16 characters) - char[] buffer = new char[2048]; - while (!reader.EndOfStream) - { - // Check the cancellation token with each iteration - cancellationToken.ThrowIfCancellationRequested(); - - // Fill the buffer with the next set of characters, track how many characters were read - int readCount = reader.Read(buffer, 0, buffer.Length); - - // Convert the character buffer to a string, only include as many characters as were just read - string chunk = new(buffer, 0, readCount); - - yield return new StreamingTextContent(chunk); - } - } - - public async Task> GetTextContentsAsync( - string prompt, - PromptExecutionSettings? executionSettings = null, - Kernel? kernel = null, - CancellationToken cancellationToken = default - ) - { - // Build your model's request object - MyModelRequest request = MyModelRequest.FromPrompt(prompt, executionSettings); - - // Send the completion request via HTTP - using var httpClient = new HttpClient(); - - // Send a POST to your model with the serialized request in the body - using HttpResponseMessage httpResponse = await httpClient.PostAsJsonAsync( - ModelUrl, - request, - cancellationToken - ); - - // Verify the request was completed successfully - httpResponse.EnsureSuccessStatusCode(); - - // Deserialize the response body to your model's response object - // Handle when the deserialization fails and returns null - MyModelResponse response = - await httpResponse.Content.ReadFromJsonAsync(cancellationToken) - ?? throw new Exception("Failed to deserialize response from model"); - - // Convert your model's response into a list of ChatMessageContent - return response - .Completions.Select(completion => new(completion)) - .ToImmutableList(); - } -} -// diff --git a/docs/ai/toc.yml b/docs/ai/toc.yml index 0220fb5b64337..b5022b1d1b58c 100644 --- a/docs/ai/toc.yml +++ b/docs/ai/toc.yml @@ -3,20 +3,20 @@ items: href: index.yml - name: Overview href: get-started/dotnet-ai-overview.md -- name: Ecosystem tools and SDKs - href: dotnet-ai-ecosystem.md -- name: Microsoft.Extensions.AI building blocks - href: ai-extensions.md +- name: Quickstart - Connect to and prompt an AI model + href: quickstarts/quickstart-openai-summarize-text.md +- name: AI frameworks and SDKs + items: + - name: Overview + href: dotnet-ai-ecosystem.md + - name: Microsoft.Extensions.AI + href: ai-extensions.md + - name: Semantic Kernel + href: semantic-kernel-dotnet-overview.md - name: Learning resources and samples href: azure-ai-for-dotnet-developers.md -- name: What is Semantic Kernel? - href: semantic-kernel-dotnet-overview.md -- name: Authenticate to Azure AI services with .NET - href: azure-ai-services-authentication.md - name: Quickstarts items: - - name: Connect to and prompt an AI model - href: quickstarts/quickstart-openai-summarize-text.md - name: Build a chat app href: quickstarts/get-started-openai.md - name: Build a .NET AI vector search app @@ -35,37 +35,47 @@ items: href: conceptual/how-genai-and-llms-work.md - name: How agents and copilots work with LLMs href: conceptual/agents.md - - name: Understand tokens + - name: Tokens href: conceptual/understanding-tokens.md - - name: Preserve semantic meaning with embeddings + - name: Embeddings href: conceptual/embeddings.md - - name: Vector databases support semantic memory + - name: Vector databases href: conceptual/vector-databases.md - name: Prompt engineering href: conceptual/prompt-engineering-dotnet.md - - name: Chain of thought prompting + - name: Chain-of-thought prompting href: conceptual/chain-of-thought-prompting.md - name: Zero-shot and few-shot learning href: conceptual/zero-shot-learning.md - name: Retrieval-augmented generation href: conceptual/rag.md - - name: Understand OpenAI function calling + - name: OpenAI function calling href: conceptual/understanding-openai-functions.md -- name: How-to articles +- name: Chat with your data (RAG) + items: + - name: Get started with the chat sample + href: get-started-app-chat-template.md + - name: Implement RAG using vector search + href: tutorials/tutorial-ai-vector-search.md + - name: Scale Azure OpenAI with Azure Container Apps + href: get-started-app-chat-scaling-with-azure-container-apps.md +- name: Security and content safety items: - - name: Authenticate to Azure OpenAI from an Azure hosted app - href: how-to/app-service-aoai-auth.md - - name: Authenticate to Azure Databases from an Azure hosted app - href: how-to/app-service-db-auth.md + - name: Authentication for Azure-hosted apps and services + items: + - name: Overview + href: azure-ai-services-authentication.md + - name: Auth to Azure OpenAI from an Azure-hosted app + href: how-to/app-service-aoai-auth.md + - name: Auth to Azure services locally + href: /dotnet/azure/sdk/authentication/local-development-dev-accounts?toc=/dotnet/ai/toc.json&bc=/dotnet/ai/toc.json - name: Work with content filtering href: how-to/content-filtering.md -- name: Tutorials + - name: Use a blocklist with Azure OpenAI + href: /azure/ai-services/openai/how-to/use-blocklists?toc=/dotnet/ai/toc.json&bc=/dotnet/ai/toc.json + - name: Use Risks & Safety monitoring + href: /azure/ai-services/openai/how-to/risks-safety-monitor?toc=/dotnet/ai/toc.json&bc=/dotnet/ai/toc.json +- name: Evaluation items: - - name: Get started with the chat using your data sample - href: get-started-app-chat-template.md - name: Evaluate LLM prompt completions href: tutorials/llm-eval.md - - name: Implement RAG using vector search - href: tutorials/tutorial-ai-vector-search.md - - name: Scale Azure OpenAI with Azure Container Apps - href: get-started-app-chat-scaling-with-azure-container-apps.md