Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
U kunt uw eigen SQL-gegevens gebruiken om de context van uw intelligente app te gronden. In dit artikel wordt uitgelegd hoe u een retrieval augmented generation (RAG) .NET Blazor-toepassing maakt door hybride vectorzoekopdrachten in te stellen vanuit een Azure SQL-database met gevectoriseerde embeddings. Azure SQL Vector-ondersteuning biedt nieuwe vectorfuncties waarmee de vectorgegevens kunnen worden beheerd.
Vereiste voorwaarden
- Een Azure SQL-database met gegevens die u kunt vectoriseren
- Een Azure OpenAI-resource
- Een .NET 8- of 9 Blazor-web-app die is geïmplementeerd in Azure App Service
In deze zelfstudie wordt gebruikgemaakt van een aanvullende voorbeeld van Clone en implementeert u een .NET 9 Blazor-chat-app die is verbonden met Azure OpenAI.
1. De Blazor-web-app instellen
Maak een eenvoudig chatvak om mee te communiceren.
Zorg ervoor dat de
Microsoft.SemanticKernelenMicrosoft.Data.SqlClientzijn geïnstalleerd in de ontwikkelomgeving.Vouw in de projectstructuur van de web-app de> uit en maak een nieuw bestand in Pagina's met de naam OpenAI.razor.
Voeg de volgende chatvakcode toe aan het OpenAI.razor-bestand en sla het bestand op.
@page "/openai" @rendermode InteractiveServer @inject Microsoft.Extensions.Configuration.IConfiguration _config <PageTitle>OpenAI</PageTitle> <h3>OpenAI input query: </h3> <input class="col-sm-4" @bind="userMessage" /> <button class="btn btn-primary" @onclick="SemanticKernelClient">Send Request</button> <br /> <br /> <h4>Server response:</h4> <p>@serverResponse</p> @code { @using Microsoft.SemanticKernel; @using Microsoft.SemanticKernel.ChatCompletion; }
2. De Azure OpenAI-client instellen
Nadat u de chatinterface hebt toegevoegd, stelt u de Azure OpenAI-client in met behulp van Semantic Kernel. Met de volgende code wordt de client gemaakt die verbinding maakt met de Azure OpenAI-resource.
De codeverwijzingen DEPLOYMENT_NAME, ENDPOINTen API_KEYMODEL_ID. Zorg ervoor dat u deze waarden opslaat in appsettings.json of als omgevingsvariabelen. Zie Key Vault-verwijzingen gebruiken als app-instellingen in Azure App Service en Azure Functions voor instructies over het ophalen en beheren van de Azure OpenAI-sleutel en eindpuntinformatie.
Opmerking
Indien mogelijk moet u beheerde identiteit gebruiken om uw client te beveiligen zonder API-sleutels te hoeven beheren. Zie zelfstudie: Een chatbot bouwen met Azure App Service en Azure OpenAI (.NET) voor instructies over het instellen van een Azure OpenAI-client voor het gebruik van beheerde identiteiten.
Voeg de volgende code toe aan het OpenAI.razor-bestand .
@inject Microsoft.Extensions.Configuration.IConfiguration _config
@code {
@using Microsoft.SemanticKernel;
@using Microsoft.SemanticKernel.ChatCompletion;
private string? userMessage;
private string? serverResponse;
private async Task SemanticKernelClient()
{
// App settings
string deploymentName = _config["DEPLOYMENT_NAME"];
string endpoint = _config["ENDPOINT"];
string apiKey = _config["API_KEY"];
string modelId = _config["MODEL_ID"];
var builder = Kernel.CreateBuilder();
// Chat completion service
builder.Services.AddAzureOpenAIChatCompletion(
deploymentName: deploymentName,
endpoint: endpoint,
apiKey: apiKey,
modelId: modelId
);
var kernel = builder.Build();
// Create prompt template
var chat = kernel.CreateFunctionFromPrompt(
@"{{$history}}
User: {{$request}}
Assistant: ");
ChatHistory chatHistory = new("""You are a helpful assistant that answers questions""");
var chatResult = kernel.InvokeStreamingAsync<StreamingChatMessageContent>(
chat,
new()
{
{ "request", userMessage },
{ "history", string.Join("\n", chatHistory.Select(x => x.Role + ": " + x.Content)) }
}
);
string message = "";
await foreach (var chunk in chatResult)
{
message += chunk;
}
// Add messages to chat history
chatHistory.AddUserMessage(userMessage!);
chatHistory.AddAssistantMessage(message);
serverResponse = message;
U hebt nu een werkende chattoepassing die is verbonden met OpenAI. Stel vervolgens uw Azure SQL-database in voor gebruik met uw chattoepassing.
3. Implementeer Azure OpenAI modellen
Als u een Azure SQL-database wilt voorbereiden voor hybride vectorzoekopdrachten, gebruikt u een insluitingsmodel om insluitingen te genereren die u wilt gebruiken om te zoeken. Nadat de juiste insluitingen zijn opgenomen in de database, kunt u deze samen met uw eerste taalmodel gebruiken om een hybride vectorzoekopdracht uit te voeren op de database.
In dit voorbeeld wordt het model gebruikt voor het text-embedding-ada-002 genereren van insluitingen en gpt-4o-mini voor het taalmodel. Voordat u verdergaat, gebruikt u de Microsoft Foundry-portal om de twee modellen in uw OpenAI-resource te implementeren. Zie Microsoft Foundry Models implementeren in de Foundry-portal voor meer informatie.
4. Uw Azure SQL-database vectoriseren
Als u een hybride vectorzoekopdracht wilt uitvoeren op een Azure SQL-database, moeten de juiste insluitingen zich in de database bevinden. Vectoriseer uw database voordat u doorgaat. Er zijn veel manieren om een database te vectoriseren. Een optie is om de Azure SQL DB Vectorizer te gebruiken.
Maak een opgeslagen procedure die embeddings genereert
U kunt Azure SQL Vector-ondersteuning gebruiken om een opgeslagen procedure te maken die gebruikmaakt van een VECTOR gegevenstype voor het opslaan van gegenereerde insluitingen voor zoekquery's. De opgeslagen procedure roept een extern REST API-eindpunt aan om de inbeddingen op te halen.
Met de volgende SQL-query wordt de opgeslagen procedure gemaakt. Vervang de <resourcename> tijdelijke aanduiding in de @url parameter door de naam van uw Azure OpenAI-resource en vervang de <openAIkey> tijdelijke aanduiding door de API-sleutel van uw tekst embedding model. De modelnaam maakt deel uit van de @url, die wordt gevuld met de zoekquery.
U kunt Query-editor in Azure Portal of Visual Studio Code gebruiken om verbinding te maken met uw database en de query uit te voeren. Zie de msSQL-extensiedocumentatie voor meer informatie over het gebruik van Visual Studio Code.
CREATE PROCEDURE [dbo].[GET_EMBEDDINGS]
(
@model VARCHAR(MAX),
@text NVARCHAR(MAX),
@embedding VECTOR(1536) OUTPUT
)
AS
BEGIN
DECLARE @retval INT, @response NVARCHAR(MAX);
DECLARE @url VARCHAR(MAX);
DECLARE @payload NVARCHAR(MAX) = JSON_OBJECT('input': @text);
-- Set the @url variable with proper concatenation before the EXEC statement
SET @url = 'https://<resourcename>.openai.azure.com/openai/deployments/' + @model + '/embeddings?api-version=2023-03-15-preview';
EXEC dbo.sp_invoke_external_rest_endpoint
@url = @url,
@method = 'POST',
@payload = @payload,
@headers = '{"Content-Type":"application/json", "api-key":"<openAIkey>"}',
@response = @response OUTPUT;
-- Use JSON_QUERY to extract the embedding array directly
DECLARE @jsonArray NVARCHAR(MAX) = JSON_QUERY(@response, '$.result.data[0].embedding');
SET @embedding = CAST(@jsonArray as VECTOR(1536));
END
GO
Na het maken wordt deze opgeslagen procedure weergegeven onder Opgeslagen procedures in de map Programmeerbaarheid van de Azure SQL-database. U kunt een zoekopdracht voor overeenkomsten testen uitvoeren in de SQL-queryeditor met behulp van de naam van het insluitmodel voor tekst. Deze test maakt gebruik van de opgeslagen procedure voor het genereren van insluitingen, het berekenen van vectorafstand met behulp van een vectorafstandsfunctie en het retourneren van resultaten op basis van de tekstquery.
6. Verbind en doorzoek je database
Nu uw database is ingesteld voor het maken van insluitingen, kunt u verbinding maken met de database in uw toepassing en de hybride vectorzoekquery instellen. Voeg de volgende code toe aan het OpenAI.razor-bestand en zorg ervoor dat de verbindingsreeks gebruikmaakt van de geïmplementeerde Azure SQL-databaseverbindingsreeks.
De code maakt gebruik van een SQL-parameter die de gebruikersinvoer van de chat-app veilig doorgeeft aan de query. In het voorbeeld wordt de gegevensset Amazon Fine Food Reviews gebruikt.
// Database connection string
var connectionString = _config["AZURE_SQL_CONNSTRING"];
try
{
await using var connection = new SqlConnection(connectionString);
Console.WriteLine("\nQuery results:");
await connection.OpenAsync();
// Hybrid search query
var sql =
@"DECLARE @e VECTOR(1536);
EXEC dbo.GET_EMBEDDINGS @model = 'text-embedding-ada-002', @text = '@userMessage', @embedding = @e OUTPUT;
-- Comprehensive query with multiple filters.
SELECT TOP(5)
f.Score,
f.Summary,
f.Text,
VECTOR_DISTANCE('cosine', @e, VectorBinary) AS Distance,
CASE
WHEN LEN(f.Text) > 100 THEN 'Detailed Review'
ELSE 'Short Review'
END AS ReviewLength,
CASE
WHEN f.Score >= 4 THEN 'High Score'
WHEN f.Score BETWEEN 2 AND 3 THEN 'Medium Score'
ELSE 'Low Score'
END AS ScoreCategory
FROM finefoodembeddings10k$ f
WHERE
f.UserId NOT LIKE 'Anonymous%' -- User-based filter to exclude anonymous users
AND f.Score >= 4 -- Score threshold filter
AND LEN(f.Text) > 50 -- Text length filter for detailed reviews
AND (f.Text LIKE '%juice%') -- Inclusion of specific words
ORDER BY
Distance, -- Order by distance
f.Score DESC, -- Secondary order by review score
ReviewLength DESC; -- Tertiary order by review length
";
// Set SQL Parameter to pass in user message
SqlParameter param = new SqlParameter();
param.ParameterName = "@userMessage";
param.Value = userMessage;
await using var command = new SqlCommand(sql, connection);
// add parameter to SqlCommand
command.Parameters.Add(param);
await using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
// write results to console logs
Console.WriteLine("{0} {1} {2} {3}", "Score: " + reader.GetDouble(0), "Text: " + reader.GetString(1), "Summary: " + reader.GetString(2), "Distance: " + reader.GetDouble(3));
Console.WriteLine();
// add results to chat history
chatHistory.AddSystemMessage(reader.GetString(1) + ", " + reader.GetString(2));
}
}
catch (SqlException e)
{
Console.WriteLine($"SQL Error: {e.Message}");
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("Done");
De SQL-query zelf maakt gebruik van een hybride zoekopdracht om de opgeslagen procedure uit te voeren waarmee insluitingen worden gemaakt en de resultaten worden gefilterd met behulp van SQL. In dit voorbeeld worden scores toegewezen aan de resultaten, wordt de uitvoer geordeerd om de beste resultaten te krijgen en worden ze gebruikt als geaarde context om een antwoord te genereren.
Uw gegevens beveiligen met een beheerde identiteit
Azure SQL kan een beheerde identiteit gebruiken met Microsoft Entra om de SQL-resource te beveiligen door verificatie zonder wachtwoord te configureren. Gebruik de volgende stappen om een verbindingsreeks zonder wachtwoord te configureren voor gebruik in uw toepassing.
- Selecteer in de Azure SQL-serverresource in de Azure-portal Instellingen>Microsoft Entra ID in het linkernavigatie-menu.
- Selecteer Beheerder instellen, zoek en selecteer uzelf en selecteer vervolgens Opslaan. Entra-id is nu ingesteld op uw SQL-server en accepteert Entra ID-verificatie.
- Ga naar uw databaseresource, selecteer Verbindingsreeks instellingen> in het linkernavigatiemenu en kopieer de verbindingsreeks ADO.NET (Microsoft Entra-verificatie zonder wachtwoord).
- Werk de verbindingsreeks in de appsettings.jsonvan uw app bij.
U kunt uw toepassing nu lokaal testen met uw verbindingsreeks zonder wachtwoord.
Toegang tot de database verlenen aan App Service
Voordat u uw web-app kunt gebruiken om de Azure SQL-database aan te roepen met behulp van een beheerde identiteit, moet u de app de benodigde toegang verlenen tot de database.
Selecteer Instellingen>Identiteit in het linkernavigatiemenu van uw web-app in Azure Portal.
Stel op het tabblad Door systeem toegewezenstatus in op Aan als dit nog niet is ingesteld en selecteer vervolgens Opslaan.
Ga naar uw Azure SQL-databaseresource en selecteer Query-editor in het linkernavigatiemenu. Meld u indien nodig aan bij uw database.
Voer in de Query Editor de volgende SQL-opdrachten uit waarmee de web app als gebruiker wordt aangemaakt en ken deze de benodigde rollidmaatschappen toe, waarbij
<your-app-name>vervangen wordt door de naam van uw web app.-- Create member, alter roles to your database CREATE USER "<your-app-name>" FROM EXTERNAL PROVIDER; ALTER ROLE db_datareader ADD MEMBER "<your-app-name>"; ALTER ROLE db_datawriter ADD MEMBER "<your-app-name>"; ALTER ROLE db_ddladmin ADD MEMBER "<your-app-name>"; GOGeef vervolgens de web-app toegang om de opgeslagen procedure en het Azure OpenAI-eindpunt te gebruiken.
-- Grant access to use stored procedure GRANT EXECUTE ON OBJECT::[dbo].[GET_EMBEDDINGS] TO "<your-app-name>" GO -- Grant access to use Azure OpenAI endpoint in stored procedure GRANT EXECUTE ANY EXTERNAL ENDPOINT TO "<your-app-name>"; GO
Uw Azure SQL-database is nu beveiligd.
7. De app implementeren
U kunt uw toepassing nu implementeren in Azure App Service. Zie azd als u de app wilt implementeren met behulp van een sjabloon.
Volledig voorbeeld
De volgende code toont de volledige toegevoegde OpenAI.razor-pagina .
@page "/openai"
@rendermode InteractiveServer
@inject Microsoft.Extensions.Configuration.IConfiguration _config
<PageTitle>OpenAI</PageTitle>
<h3>OpenAI input query: </h3>
<input class="col-sm-4" @bind="userMessage" />
<button class="btn btn-primary" @onclick="SemanticKernelClient">Send Request</button>
<br />
<br />
<h4>Server response:</h4> <p>@serverResponse</p>
@code {
@using Microsoft.SemanticKernel;
@using Microsoft.SemanticKernel.ChatCompletion;
@using Microsoft.Data.SqlClient;
private string? userMessage;
private string? serverResponse;
private async Task SemanticKernelClient()
{
// App settings
string deploymentName = _config["DEPLOYMENT_NAME"];
string endpoint = _config["ENDPOINT"];
string apiKey = _config["API_KEY"];
string modelId = _config["MODEL_ID"];
// Semantic Kernel builder
var builder = Kernel.CreateBuilder();
// Chat completion service
builder.Services.AddAzureOpenAIChatCompletion(
deploymentName: deploymentName,
endpoint: endpoint,
apiKey: apiKey,
modelId: modelId
);
var kernel = builder.Build();
// Create prompt template
var chat = kernel.CreateFunctionFromPrompt(
@"{{$history}}
User: {{$request}}
Assistant: ");
ChatHistory chatHistory = new("""You are a helpful assistant that answers questions about my data""");
#region Azure SQL
// Database connection string
var connectionString = _config["AZURE_SQL_CONNECTIONSTRING"];
try
{
await using var connection = new SqlConnection(connectionString);
Console.WriteLine("\nQuery results:");
await connection.OpenAsync();
// Hybrid search query
var sql =
@"DECLARE @e VECTOR(1536);
EXEC dbo.GET_EMBEDDINGS @model = 'text-embedding-ada-002', @text = '@userMessage', @embedding = @e OUTPUT;
-- Comprehensive query with multiple filters.
SELECT TOP(5)
f.Score,
f.Summary,
f.Text,
VECTOR_DISTANCE('cosine', @e, VectorBinary) AS Distance,
CASE
WHEN LEN(f.Text) > 100 THEN 'Detailed Review'
ELSE 'Short Review'
END AS ReviewLength,
CASE
WHEN f.Score >= 4 THEN 'High Score'
WHEN f.Score BETWEEN 2 AND 3 THEN 'Medium Score'
ELSE 'Low Score'
END AS ScoreCategory
FROM finefoodembeddings10k$ f
WHERE
f.UserId NOT LIKE 'Anonymous%' -- User-based filter to exclude anonymous users
AND f.Score >= 4 -- Score threshold filter
AND LEN(f.Text) > 50 -- Text length filter for detailed reviews
AND (f.Text LIKE '%juice%') -- Inclusion of specific words
ORDER BY
Distance, -- Order by distance
f.Score DESC, -- Secondary order by review score
ReviewLength DESC; -- Tertiary order by review length
";
// Set SQL Parameter to pass in user message
SqlParameter param = new SqlParameter();
param.ParameterName = "@userMessage";
param.Value = userMessage;
await using var command = new SqlCommand(sql, connection);
// add parameter to SqlCommand
command.Parameters.Add(param);
await using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
// write results to console logs
Console.WriteLine("{0} {1} {2} {3}", "Score: " + reader.GetDouble(0), "Text: " + reader.GetString(1), "Summary: " + reader.GetString(2), "Distance: " + reader.GetDouble(3));
Console.WriteLine();
// add results to chat history
chatHistory.AddSystemMessage(reader.GetString(1) + ", " + reader.GetString(2));
}
}
catch (SqlException e)
{
Console.WriteLine($"SQL Error: {e.Message}");
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("Done");
#endregion
var chatResult = kernel.InvokeStreamingAsync<StreamingChatMessageContent>(
chat,
new()
{
{ "request", userMessage },
{ "history", string.Join("\n", chatHistory.Select(x => x.Role + ": " + x.Content)) }
}
);
string message = "";
await foreach (var chunk in chatResult)
{
message += chunk;
}
// Append messages to chat history
chatHistory.AddUserMessage(userMessage!);
chatHistory.AddAssistantMessage(message);
serverResponse = message;
}
}