Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Ao criar aplicativos inteligentes, convém fundamentar o contexto do seu aplicativo usando seus próprios dados SQL. Com o recente anúncio do suporte a vetores SQL do Azure (visualização), você pode fundamentar o contexto usando os dados SQL do Azure que você já tem com novas funções vetoriais que ajudam a gerenciar dados vetoriais.
Neste tutorial, você criará um aplicativo de exemplo RAG configurando uma pesquisa vetorial híbrida em seu banco de dados SQL do Azure usando um aplicativo Blazor .NET 8. Este exemplo se baseia na documentação anterior para implantar um aplicativo .NET Blazor com OpenAI. Se quiser implantar o aplicativo usando um modelo azd, visite o repositório de Exemplos do Azure com instruções de implantação.
Pré-requisitos
- Um recurso OpenAI do Azure com modelos implantados
- Um Blazor Web App .NET 8 ou 9 implementado no Serviço de Aplicações
- Um recurso de banco de dados SQL do Azure com incorporações vetoriais.
1. Configurar o aplicativo Web Blazor
Para este exemplo, estamos criando uma caixa de bate-papo simples para interagir. Se você estiver usando o aplicativo .NET Blazor de pré-requisito do artigo anterior, poderá ignorar as alterações no arquivo OpenAI.razor , pois o conteúdo é o mesmo. No entanto, você precisa certificar-se de que os seguintes pacotes estão instalados:
Instale os seguintes pacotes para interagir com o Azure OpenAI e o Azure SQL.
Microsoft.SemanticKernelMicrosoft.Data.SqlClient
- Clique com o botão direito do mouse na pasta Pages encontrada na pasta Componentes e adicione um novo item chamado OpenAI.razor
- Adicione o seguinte código ao arquivo OpenAI.razor e clique em Salvar
@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;
}
Chaves de API e endpoints
Usar o recurso OpenAI do Azure requer o uso de chaves de API e valores de ponto de extremidade. Consulte Usar referências do Cofre de Chaves como configurações de aplicação no Serviço de Aplicações do Azure e no Azure Functions para gerir e tratar dos seus segredos com o Azure OpenAI. Embora não seja obrigatório, recomendamos o uso de identidade gerenciada para proteger seu cliente sem a necessidade de gerenciar chaves de API. Consulte a documentação anterior para configurar seu cliente Azure OpenAI na próxima etapa para usar a identidade gerenciada com o Azure OpenAI.
2. Adicionar cliente OpenAI do Azure
Depois de adicionar a interface de chat, podemos configurar o cliente Azure OpenAI usando o Kernel Semântico. Adicione o código a seguir para criar o cliente que se conecta ao seu recurso do Azure OpenAI. Você precisa usar as suas chaves de API do Azure OpenAI e as informações do ponto de extremidade que foram configuradas e geridas na etapa anterior.
@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;
A partir daqui, você deve ter um aplicativo de bate-papo de trabalho que esteja conectado ao OpenAI. Em seguida, configuraremos nosso banco de dados SQL do Azure para trabalhar com nosso aplicativo de chat.
3. Implantar modelos do Azure OpenAI
Para preparar seu banco de dados SQL do Azure para pesquisa vetorial, você precisa usar um modelo de incorporação para gerar incorporações usadas para pesquisa, além do seu modelo de linguagem implantado inicial. Para este exemplo, estamos usando os seguintes modelos:
-
text-embedding-ada-002é usado para gerar as incorporações -
gpt-3.5-turboé usado para o modelo de linguagem
Esses dois modelos precisam ser implantados antes de continuar a próxima etapa. Consulte a documentação para implementar modelos com Azure OpenAI usando Microsoft Foundry.
4. Vetorize seu banco de dados SQL
Para executar uma pesquisa vetorial híbrida em seu banco de dados SQL do Azure, primeiro você precisa ter as incorporações apropriadas em seu banco de dados. Há muitas maneiras de vetorizar seu banco de dados. Uma opção é usar o seguinte vetorizador de banco de dados SQL do Azure para gerar incorporações para seu banco de dados SQL. Vetorize seu banco de dados SQL do Azure antes de continuar.
5. Criar procedimento para gerar incorporações
Com o suporte a vetores SQL do Azure (visualização), você pode criar um procedimento armazenado que usará um tipo de dados Vetor para armazenar incorporações geradas para consultas de pesquisa. O procedimento armazenado invoca um endpoint externo da API REST para obter as incorporações. Consulte a documentação para usar o Azure Data Studio para se conectar ao seu banco de dados antes de executar a consulta.
Use o seguinte para criar um procedimento armazenado com seu editor de consultas SQL preferido. Você precisa preencher o parâmetro @url com o nome do seu recurso Azure OpenAI e preencher o endpoint restante com a chave da API do seu modelo de incorporação de texto. Você notará o nome do modelo como parte do @url, que será preenchido com a sua consulta de pesquisa.
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
Depois de criar seu procedimento armazenado, você poderá visualizá-lo na pasta Stored Procedures localizada na pasta Programabilidade do seu banco de dados SQL. Uma vez criado, pode executar um teste de pesquisa de similaridade no seu editor de consultas SQL usando o nome do modelo de texto de incorporação. Isso usa seu procedimento armazenado para gerar incorporações e usar uma função de distância vetorial para calcular a distância do vetor e retornar resultados com base na consulta de texto.
6. Conecte-se e pesquise seu banco de dados
Agora que seu banco de dados está configurado para criar incorporações, podemos nos conectar a ele em nosso aplicativo e configurar a consulta de pesquisa vetorial híbrida.
Adicione o código a seguir ao seu OpenAI.razor arquivo e verifique se a cadeia de conexão está atualizada para usar sua cadeia de conexão do banco de dados SQL do Azure implantada. O código está usando um parâmetro SQL que passará com segurança pela entrada do usuário do aplicativo de chat para a consulta.
// 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 está usando uma pesquisa híbrida que executa o procedimento armazenado configurado anteriormente para criar incorporações e usa SQL para filtrar os resultados desejados. Neste exemplo, estamos a atribuir pontuações aos resultados e a ordenar a saída para captar os melhores resultados, antes de os usar como contexto fundamentado para gerar uma resposta.
Proteja seus dados com o Managed Identity
O SQL do Azure pode usar a Identidade Gerenciada com o Microsoft Entra para proteger seu recurso SQL configurando a autenticação sem senha. Siga as etapas abaixo para configurar uma cadeia de conexão sem senha que será usada em seu aplicativo.
- Navegue até o recurso do servidor SQL do Azure e clique em Microsoft Entra ID em Configurações.
- Em seguida, clique em +Definir administrador, pesquise, escolha-se para configurar o Entra ID e clique em Salvar. Agora o Entra ID está configurado no seu servidor SQL e aceita a autenticação do Entra ID.
- Em seguida, vá para o recurso de banco de dados e copie a cadeia de conexão ADO.NET (autenticação sem senha do Microsoft Entra) e adicione-a ao seu código, onde mantém a cadeia de conexão.
Neste ponto, você pode testar seu aplicativo localmente com sua cadeia de conexão sem senha.
Conceder acesso ao Serviço de Aplicativo
Antes de fazer uma chamada para seu banco de dados ao usar a identidade gerenciada com o SQL do Azure, primeiro você precisará conceder acesso ao banco de dados ao Serviço de Aplicativo. Se você não tiver feito isso neste momento, precisará criar um aplicativo Web primeiro antes de concluir as próximas etapas.
Siga estas etapas para conceder acesso ao seu aplicativo Web:
- Navegue até a sua aplicação web e clique no painel Identidade encontrado nas Definições.
- Ative a identidade gerenciada atribuída ao sistema, caso ainda não o tenha feito.
- Navegue até o recurso de banco de dados e abra o editor de consultas encontrado no menu do lado esquerdo. Talvez seja necessário fazer login para usar o editor.
- Execute os seguintes comandos para criar um usuário e alterar as funções: adicionar o aplicativo Web como membro:
-- 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>";
GO
- Em seguida, conceda o acesso para usar o procedimento armazenado e o 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
A partir daqui, seu banco de dados SQL do Azure agora está seguro e você pode implantar seu aplicativo no Serviço de Aplicativo.
Aqui está o exemplo completo da página 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;
}
}