Condividi tramite


Autenticare e autorizzare il servizio app a un database vettoriale

Questo articolo illustra come gestire la connessione tra l'applicazione .NET del servizio app e una soluzione di database vettoriale. Illustra l'uso delle identità gestite di Microsoft Entra per i servizi supportati e l'archiviazione sicura delle stringhe di connessione per altri utenti.

Aggiungendo un database vettoriale all'applicazione, è possibile abilitare [memorie semantiche o archivi vettoriali](archivi vettoriali) per l'IA. Il Semantic Kernel SDK per .NET consente di implementare facilmente l'archiviazione della memoria e richiamare usando la soluzione di database vettoriale preferita.

Prerequisiti

Usare l'identità gestita di Microsoft Entra per l'autenticazione

Se un servizio di database vettoriale supporta l'autenticazione di Microsoft Entra, è possibile usare un'identità gestita con il servizio app per accedere in modo sicuro al database vettoriale senza dover effettuare manualmente il provisioning o ruotare i segreti. Per un elenco dei servizi di Azure che supportano l'autenticazione di Microsoft Entra, vedere Servizi di Azure che supportano l'autenticazione di Microsoft Entra.

Aggiungere un'identità gestita al Servizio app

All'applicazione possono essere concessi due tipi di identità:

  • Un'identità assegnata dal sistema viene associata all'applicazione e viene eliminata in caso di eliminazione dell'app. Un'app può avere una sola identità assegnata dal sistema.
  • Un'identità assegnata dall'utente è una risorsa di Azure autonoma che può essere assegnata all'app. Un'app può avere più identità assegnate dall'utente.

Aggiungere un'identità assegnata dal sistema

  1. Passare alla pagina dell'app nel portale di Azure e quindi scorrere verso il basso fino al gruppo Impostazioni.
  2. Selezionare Identità.
  3. Nella scheda Assegnata dal sistema, impostare Stato su e quindi selezionare Salva.

Eseguire il comando az webapp identity assign per creare un'identità assegnata dal sistema:

az webapp identity assign --name <appName> --resource-group <groupName>

Aggiungere un'identità assegnata dall'utente

Per aggiungere un'identità assegnata dall'utente all'app, creare l'identità e quindi aggiungerne l'identificatore alla configurazione dell'app.

  1. Creare una risorsa identità gestita assegnata dall'utente seguendo queste istruzioni.

  2. Nel riquadro di spostamento sinistro della pagina dell'app, scorrere verso il basso fino al gruppo Impostazioni.

  3. Selezionare Identità.

  4. Selezionare Assegnata dall'utente>Aggiungi.

  5. Individuare l'identità creata in precedenza, selezionarla e quindi selezionare Aggiungi.

    Importante

    Dopo aver selezionato Aggiungi, l'app viene riavviata.

  1. Creare un'identità assegnata dall'utente:

    az identity create --resource-group <groupName> --name <identityName>
    
  2. Assegnare l'identità all'app:

    az webapp identity assign --resource-group <groupName> --name <appName> --identities <identityId>
    

Aggiungere un ruolo di Azure all'identità gestita

  1. Nel portale di Azure passare all'ambito a cui si vuole concedere l'accesso al database vettoriale. L'ambito può essere un gruppo di gestione, una sottoscrizione, un gruppo di risorse o una risorsa di Azure specifica.
  2. Nel riquadro di spostamento sinistro selezionare Controllo di accesso (IAM).
  3. Selezionare Aggiungi e quindi Aggiungi assegnazione di ruolo.
  4. Nella scheda Ruolo, selezionare il ruolo appropriato che concede l'accesso in lettura al database vettoriale.
  5. Nella scheda Membri selezionare l'identità gestita.
  6. Nella scheda Rivedi e assegna selezionare Rivedi e assegna per assegnare il ruolo.

Ambito risorsa

az role assignment create --assignee "<managedIdentityObjectID>" \
--role "<myVectorDbReaderRole>" \
--scope "/subscriptions/<subscriptionId>/resourcegroups/<resourceGroupName>/providers/<providerName>/<resourceType>/<resourceSubType>/<resourceName>"

Ambito del gruppo di risorse

az role assignment create --assignee "<managedIdentityObjectID>" \
--role "<myVectorDbReaderRole>" \
--scope "/subscriptions/<subscriptionId>/resourcegroups/<resourceGroupName>"

Ambito sottoscrizione

az role assignment create --assignee "<managedIdentityObjectID>" \
--role "<myVectorDbReaderRole>" \
--scope "/subscriptions/<subscriptionId>"

Ambito del gruppo di gestione

az role assignment create --assignee "<managedIdentityObjectID>" \
--role "<myVectorDbReaderRole>" \
--scope "/providers/Microsoft.Management/managementGroups/<managementGroupName>"

Implementare l'autenticazione basata su token con il database vettoriale

Gli esempi di codice seguenti richiedono queste librerie aggiuntive:

  1. Inizializzare un oggetto DefaultAzureCredential per recuperare l'identità gestita dell'app:

    // 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".
        }
    );
    
  2. Inizializzare un oggetto IMemoryStore per il database vettoriale e quindi usarlo per compilare un ISemanticTextMemory:

    // 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<Program>().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();
    
  3. Compilare un oggetto Kernel, quindi importare l'oggetto ISemanticTextMemory usando il TextMemoryPlugin:

    // 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));
    
  4. Usare l'oggetto Kernel per richiamare un prompt che include il richiamo della memoria:

    // 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<string>(
        "{{recall 'where did I grow up?'}}",
        new()
        {
            [TextMemoryPlugin.CollectionParam] = memoryCollection,
            [TextMemoryPlugin.LimitParam] = "2",
            [TextMemoryPlugin.RelevanceParam] = "0.79",
        }
    );
    Console.WriteLine($"Output: {result}");
    

Usare Key Vault per archiviare i segreti di connessione

Se un database vettoriale non supporta l'autenticazione di Microsoft Entra, è possibile usare un insieme di credenziali delle chiavi per archiviare i segreti di connessione e recuperarli con l'applicazione del servizio app. Usando un insieme di credenziali delle chiavi per archiviare i segreti di connessione, è possibile condividerli con più applicazioni e controllare l'accesso ai singoli segreti per ogni applicazione.

Prima di seguire questa procedura, recuperare una stringa di connessione per il database vettoriale. Ad esempio, vedere Usare Cache Redis di Azure con un'app Web ASP.NET Core.

Aggiungere una stringa di connessione a Key Vault

Importante

Prima di seguire questa procedura, assicurarsi di avere creato un insieme di credenziali delle chiavi usando il portale di Azure.

  1. Passare all'insieme di credenziali delle chiavi nel portale di Azure.
  2. Nel riquadro di spostamento a sinistra dell'insieme di credenziali delle chiavi, selezionare Oggetti e quindi Segreti.
  3. Selezionare + Genera/Importa.
  4. Nella schermata Crea un segreto selezionare i seguenti valori:
    • Opzioni di caricamento: Manual.
    • Nome: digitare un nome per il segreto. Il nome del segreto deve essere univoco all'interno di un'istanza di Key Vault.
    • Valore: stringa di connessione per il database vettoriale.
    • Lasciare invariati gli altri valori predefiniti. Seleziona Crea.
  5. Quando viene visualizzato il messaggio che indica che il segreto è stato creato correttamente, è pronto per l'uso nell'applicazione.

Importante

Prima di seguire questa procedura, assicurarsi di aver creato un insieme di credenziali delle chiavi usando l'interfaccia della riga di comando di Azure.

  1. Concedere all'account utente le autorizzazioni per l'insieme di credenziali delle chiavi tramite il controllo degli accessi in base al ruolo, assegnare un ruolo usando il comando dell'interfaccia della riga di comando di Azure az role assignment create:

    az role assignment create \
    --role "Key Vault Secrets User" \
    --assignee "<yourEmailAddress>" \
    --scope "/subscriptions/<subscriptionId>/resourceGroups/<resourceGroupName>/providers/Microsoft.KeyVault/vaults/<keyVaultName>"
    
  2. Aggiungere la stringa di connessione a Key Vault usando il comando dell'interfaccia della riga di comando di Azure az keyvault secret set:

    az keyvault secret set \
    --vault-name "<keyVaultName>" \
    --name "<secretName>" \
    --value "<connectionString>"
    

Concedere al servizio app l'accesso a Key Vault

  1. Assegnare un'identità gestita al servizio app.
  2. Aggiungere i ruoli Key Vault Secrets User e Key Vault Reader all'identità gestita.

Implementare il recupero della stringa di connessione da Key Vault

Per usare gli esempi di codice seguenti, sono necessarie queste librerie aggiuntive:

Questi esempi di codice usano un database Redis, ma è possibile applicarli a qualsiasi database vettoriale che supporta le stringhe di connessione.

  1. Inizializzare un oggetto DefaultAzureCredential per recuperare l'identità gestita dell'app:

    // 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".
        }
    );
    
  2. Aggiungere Key Vault durante la compilazione della configurazione, verrà eseguito il mapping dei segreti dell'insieme di credenziali delle chiavi all'oggetto IConfigurationRoot:

    // 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<Program>()
        .AddAzureKeyVault(new Uri("{vaultURI}"), credentials)
        .Build();
    
    // Retrieve the Redis connection string obtained from the Key Vault.
    string redisConnectionString = config["AZURE_REDIS_CONNECT_STRING"]!;
    
  3. Usare la stringa di connessione del database vettoriale da Key Vault per inizializzare un oggetto IMemoryStore e quindi usarla per compilare un ISemanticTextMemory:

    // 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();
    
  4. Compilare un oggetto Kernel, quindi importare l'oggetto ISemanticTextMemory usando il TextMemoryPlugin:

    // 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));
    
  5. Usare l'oggetto Kernel per richiamare un prompt che include il richiamo della memoria:

    // 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<string>(
        "{{recall 'where did I grow up?'}}",
        new()
        {
            [TextMemoryPlugin.CollectionParam] = memoryCollection,
            [TextMemoryPlugin.LimitParam] = "2",
            [TextMemoryPlugin.RelevanceParam] = "0.79",
        }
    );
    Console.WriteLine($"Output: {result}");
    

Usare le impostazioni dell'applicazione per archiviare i segreti di connessione

Se un database vettoriale non supporta l'autenticazione di Microsoft Entra, è possibile usare le impostazioni dell'applicazione del servizio app per archiviare i segreti di connessione. Usando le impostazioni dell'applicazione è possibile archiviare i segreti di connessione senza effettuare il provisioning di risorse di Azure aggiuntive.

Prima di seguire questa procedura, recuperare una stringa di connessione per il database vettoriale. Ad esempio, vedere Usare Cache Redis di Azure in .NET Framework.

Aggiungere una stringa di connessione alle impostazioni dell'applicazione

  1. Passare alla pagina dell'app nel portale di Azure.
  2. Nel menu a sinistra dell'app selezionare Configurazione>Impostazioni applicazione.
    • Per impostazione predefinita, i valori per le impostazioni dell'applicazione sono nascosti nel portale per la sicurezza.
    • Per visualizzare un valore nascosto di un'impostazione dell'applicazione, selezionare il relativo campo Valore.
  3. Selezionare Nuova impostazione di connessione.
  4. Nella schermata Aggiungi/modifica stringa di connessione scegliere i valori seguenti:
    • Nome: digitare un nome per l'impostazione. Il nome dell'impostazione deve essere univoco.
    • Valore: stringa di connessione per il database vettoriale.
    • Tipo: il tipo di connessione, Custom se non ne esistono altri.
    • Lasciare invariati gli altri valori predefiniti. Seleziona OK.
  5. Selezionare Salva di nuovo nella pagina Configurazione.

Aggiungere o modificare un'impostazione dell'app con il comando dell'interfaccia della riga di comando di Azure az webapp config connection-string set:

az webapp config connection-string set \
--name "<appName>" \
--resource-group "<groupName>" \
--connection-string-type "<connectionType>" \
--settings <connectionName>='<connectionString>'

Implementare il recupero della stringa di connessione dalle impostazioni dell'app

Per usare gli esempi di codice seguenti, sono necessarie queste librerie aggiuntive:

Questi esempi di codice usano un database Redis, ma è possibile applicarli a qualsiasi database vettoriale che supporta le stringhe di connessione.

  1. Aggiungere variabili di ambiente durante la compilazione della configurazione, verrà eseguito il mapping delle stringhe di connessione all'oggetto IConfigurationRoot:

    // 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<Program>()
        .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")!;
    
  2. Usare la stringa di connessione del database vettoriale dalle impostazioni dell'app per inizializzare un oggetto IMemoryStore e quindi usarla per compilare un ISemanticTextMemory:

    // 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();
    
  3. Compilare un oggetto Kernel, quindi importare l'oggetto ISemanticTextMemory usando il TextMemoryPlugin:

    // 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));
    
  4. Usare l'oggetto Kernel per richiamare un prompt che include il richiamo della memoria:

    // 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<string>(
        "{{recall 'where did I grow up?'}}",
        new()
        {
            [TextMemoryPlugin.CollectionParam] = memoryCollection,
            [TextMemoryPlugin.LimitParam] = "2",
            [TextMemoryPlugin.RelevanceParam] = "0.79",
        }
    );
    Console.WriteLine($"Output: {result}");