Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Você pode usar seus próprios dados SQL para fundamentar o contexto de seu aplicativo inteligente. Este artigo explica como criar um aplicativo de geração aumentada de recuperação (RAG) .NET Blazor configurando a busca em vetores híbrida em relação a um banco de dados SQL do Azure que tenha incorporações vetorizadas. O suporte ao vetor SQL do Azure fornece novas funções de vetor que ajudam a gerenciar os dados de vetor.
Pré-requisitos
- Um banco de dados SQL do Azure com dados que você pode vetorizar
- Um recurso do Azure OpenAI
- Um aplicativo Web Blazor do .NET 8 ou 9 implantado no Serviço de Aplicativo do Azure
Este tutorial usa um exemplo complementar de Clonar e implantar um aplicativo de chat Blazor do .NET 9 conectado ao Azure OpenAI.
1. Configurar o aplicativo Web Blazor
Crie uma caixa de chat básica com a qual interagir.
Verifique se os pacotes
Microsoft.SemanticKerneleMicrosoft.Data.SqlClientestão instalados no ambiente de desenvolvimento.Na árvore de projeto do aplicativo web, expanda Componentes>Páginas, e crie um novo arquivo em Páginas nomeado OpenAI.razor.
Adicione o código da caixa de chat a seguir ao arquivo OpenAI.razor e salve o arquivo.
@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. Configurar o cliente do Azure OpenAI
Depois de adicionar a interface de chat, configure o cliente do Azure OpenAI usando Kernel Semântico. O código a seguir cria o cliente que se conecta ao recurso do Azure OpenAI.
O código refere-se a DEPLOYMENT_NAME, ENDPOINT, API_KEY e MODEL_ID. Certifique-se de armazenar esses valores em appsettings.json ou como variáveis de ambiente. Para obter instruções sobre como obter e gerenciar as informações de chave e ponto de extremidade do Azure OpenAI, consulte Usar referências do Key Vault como configurações de aplicativo no Serviço de Aplicativo do Azure e no Azure Functions.
Observação
Se possível, você deve usar a identidade gerenciada para proteger seu cliente sem precisar gerenciar chaves de API. Confira o Tutorial: Crie um chatbot com o Serviço de Aplicativo do Azure e o Azure OpenAI (.NET) para obter instruções sobre como configurar um cliente do Azure OpenAI para usar a identidade gerenciada.
Adicione o código a seguir ao arquivo OpenAI.razor .
@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;
Agora você tem um aplicativo de chat funcional conectado ao OpenAI. Em seguida, configure o banco de dados SQL do Azure para trabalhar com seu aplicativo de chat.
3. Implantar modelos OpenAI do Azure
Para preparar um banco de dados SQL do Azure para pesquisa de vetor híbrido, use um modelo de inserção para gerar inserções a serem usadas para pesquisa. Depois que as inserções apropriadas estiverem no banco de dados, você poderá usá-las junto com seu modelo de idioma inicial para executar uma pesquisa de vetor híbrido no banco de dados.
Este exemplo usa o text-embedding-ada-002 modelo para gerar inserções e gpt-4o-mini para o modelo de linguagem. Antes de continuar, use o portal do Microsoft Foundry para implantar os dois modelos em seu recurso OpenAI. Para obter mais informações, consulte Implantar modelos do Microsoft Foundry no portal do Foundry.
4. Vectorizar seu banco de dados SQL do Azure
Para executar uma pesquisa de vetor híbrido em um banco de dados SQL do Azure, as inserções apropriadas devem estar no banco de dados. Vectorize seu banco de dados antes de continuar. Há muitas maneiras de vetorizar um banco de dados. Uma opção é usar o vetorizador SQL DB do Azure.
5. Criar um procedimento armazenado que gere inserções
Você pode usar o suporte ao vetor SQL do Azure para criar um procedimento armazenado que usa um VECTOR tipo de dados para armazenar inserções geradas para consultas de pesquisa. O procedimento armazenado invoca um ponto de extremidade da API REST externa para obter as incorporações.
A consulta SQL a seguir cria o procedimento armazenado. Substitua o marcador de posição <resourcename> no parâmetro @url pelo nome do recurso do Azure OpenAI e substitua o marcador de posição <openAIkey> pela chave de API do seu modelo de incorporação de texto. O nome do modelo faz parte do @url, que é preenchido com a consulta de pesquisa.
Você pode usar o Editor de Consultas no portal do Azure ou no Visual Studio Code para se conectar ao banco de dados e executar a consulta. Para obter mais informações sobre como usar o Visual Studio Code, consulte a documentação da extensão MSSQL.
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
Após a criação, esse procedimento armazenado aparece em Procedimentos Armazenados na pasta Programação do Banco de Dados SQL do Azure. Você pode executar uma pesquisa de similaridade de teste no editor de consultas SQL usando o nome do modelo de inserção de texto. Esse teste usa o procedimento armazenado para gerar inserções, calcular a distância do vetor usando uma função de distância de vetor e retornar resultados com base na consulta de texto.
6. Conectar e pesquisar seu banco de dados
Agora que seu banco de dados está configurado para criar inserções, você pode se conectar ao banco de dados em seu aplicativo e configurar a consulta de pesquisa de vetor híbrido. Adicione o código a seguir ao arquivo OpenAI.razor , certificando-se de que a cadeia de conexão use a cadeia de conexão de banco de dados SQL do Azure implantada.
O código usa um parâmetro SQL que passa com segurança a entrada do usuário do aplicativo de chat para a consulta. O exemplo usa o conjunto de dados Amazon Fine Food Reviews .
// 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");
A consulta SQL em si usa uma pesquisa híbrida para executar o procedimento armazenado que cria inserções e filtra os resultados usando SQL. Este exemplo atribui pontuações aos resultados, organiza a saída para capturar os melhores resultados e os utiliza como contexto fundamentado para gerar uma resposta.
Proteger seus dados com a identidade gerenciada
O SQL do Azure pode usar a identidade gerenciada com o Microsoft Entra para proteger o recurso SQL configurando a autenticação sem senha. Use as etapas a seguir para configurar uma cadeia de conexão sem senha a ser usada em seu aplicativo.
- No recurso do SQL Server do Azure no portal do Azure, selecione Configurações>Microsoft Entra ID no menu de navegação à esquerda.
- Selecione Definir administrador, pesquise e selecione você mesmo e, em seguida, selecione Salvar. Entra ID agora está configurada em seu SQL Server, e aceita a autenticação Entra ID.
- Vá para o recurso de banco de dados, selecione Configurações>Cadeia de conexão no menu de navegação esquerdo e copie a cadeia de conexão ADO.NET (autenticação sem senha do Microsoft Entra).
- Atualize a cadeia de conexão no appsettings.jsondo aplicativo.
Agora você pode testar seu aplicativo localmente com sua cadeia de conexão sem senha.
Conceder acesso de banco de dados ao Serviço de Aplicativo
Antes de poder usar seu aplicativo Web para chamar o banco de dados SQL do Azure usando a identidade gerenciada, você deve conceder ao aplicativo o acesso necessário ao banco de dados.
Em seu aplicativo web no portal do Azure, selecione Configurações>Identidade no menu de navegação à esquerda.
Na guia Atribuída pelo sistema , defina Status como Ativado se ainda não estiver definido e, em seguida, selecione Salvar.
Vá para o recurso do banco de dados SQL do Azure e selecione o editor de consultas no menu de navegação à esquerda. Entre no banco de dados, se necessário.
No editor de Consultas, execute os seguintes comandos SQL que criam o aplicativo Web como um usuário e atribuam a ele as associações de função necessárias, substituindo
<your-app-name>pelo nome do aplicativo Web.-- 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>"; GOEm seguida, conceda ao aplicativo web acesso aos direitos de uso do procedimento armazenado e do endpoint do Azure OpenAI.
-- 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
Seu banco de dados SQL do Azure agora está seguro.
7. Implantar o aplicativo
Agora você pode implantar seu aplicativo no Serviço de Aplicativo do Azure. Se você quiser implantar o aplicativo usando um azd modelo, consulte Implantar com a CLI do Desenvolvedor do Azure.
Exemplo completo
O código a seguir mostra a página completa do OpenAI.razor adicionada.
@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;
}
}