Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo guia-o na criação de uma aplicação WinUI que utiliza um modelo Phi3 e a ONNX Runtime Generative AI biblioteca para implementar uma aplicação simples de chat de IA generativa. Os modelos de linguagem grande (LLMs) permitem que você adicione recursos de geração, transformação, raciocínio e tradução de texto ao seu aplicativo. Para obter mais informações sobre como usar modelos de IA e aprendizado de máquina em seu aplicativo do Windows, consulte Introdução à IA no Windows. Para obter mais informações sobre o tempo de execução do ONNX e a IA generativa, consulte IA generativa com ONNX Runtime.
Ao utilizar recursos de IA, recomendamos que você analise: Desenvolvendo aplicativos e recursos de IA generativa responsável no Windows.
Qual é o ONNX Runtime
ONNX Runtime é um acelerador de modelo de aprendizado de máquina multiplataforma, com uma interface flexível para integrar bibliotecas específicas de hardware. ONNX Runtime pode ser usado com modelos de PyTorch, Tensorflow/Keras, TFLite, scikit-learne outros frameworks. Para mais informações, visite o site ONNX Runtime em https://onnxruntime.ai/docs/.
Pré-requisitos
- Seu dispositivo deve ter o modo de desenvolvedor ativado. Para obter mais informações, consulte Habilitar seu dispositivo para desenvolvimento.
- Visual Studio 2022 ou posterior com o workload de desenvolvimento .NET para desktop.
Criar um novo aplicativo WinUI em C#
No Visual Studio, crie um novo projeto. Na caixa de diálogo Criar um novo projeto , defina o filtro de idioma como "C#" e o filtro de tipo de projeto como "winui" e, em seguida, selecione o modelo Aplicativo em branco, Empacotado (WinUI3 na área de trabalho ). Nomeie o novo projeto como "GenAIExample".
Adicionar referências ao pacote ONNX Runtime Generative AI Nuget
No Explorador de Soluções, clique com o botão direito em Dependências e selecione Gerir pacotes NuGet.... No gestor de pacotes NuGet, selecione o separador Navegar. Procure por "Microsoft.ML.OnnxRuntimeGenAI.WinML", selecione a versão estável mais recente no menu suspenso Versão e depois clique em Instalar.
Este pacote utiliza Windows ML para selecionar automaticamente o melhor fornecedor de hardware disponível (NPU → GPU → CPU). Não é preciso escolher entre DirectML, QNN ou pacotes específicos para CPU — o Windows ML trata disso.
Note
O pacote .WinML requer uma estrutura de destino específica do Windows (por exemplo, net8.0-windows10.0.19041.0 ou posterior). Se precisar de um pacote multiplataforma ou quiser direcionar um fornecedor de execução específico, consulte a secção de pacotes alternativos no final deste artigo.
Adicione um arquivo de modelo e vocabulário ao seu projeto
No Gerenciador de Soluções, clique com o botão direito do mouse em seu projeto e selecione Adicionar> Nova Pasta. Nomeie a nova pasta "Modelos". Para este exemplo, usaremos o modelo de https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/tree/main/directml/directml-int4-awq-block-128.
Existem várias maneiras diferentes de recuperar modelos. Para este passo a passo, usaremos a CLI (Hugging Face Command Line Interface). Se você obter os modelos usando outro método, talvez seja necessário ajustar os caminhos de arquivo para o modelo no código de exemplo. Para obter informações sobre como instalar a CLI do Hugging Face e configurar sua conta para usá-la, consulte Interface de linha de comando (CLI).
Depois de instalar a CLI, abra um terminal, navegue até o diretório Models criado e digite o seguinte comando.
huggingface-cli download microsoft/Phi-3-mini-4k-instruct-onnx --include directml/* --local-dir .
Quando a operação estiver concluída, verifique se existe o seguinte ficheiro: [Project Directory]\Models\directml\directml-int4-awq-block-128\model.onnx.
No Gerenciador de Soluções , expanda a pasta "directml-int4-awq-block-128" e selecione todos os arquivos na pasta. No painel Propriedades do Arquivo
Adicionar uma interface do usuário simples para interagir com o modelo
Para este exemplo, criaremos uma interface do usuário muito simplista que tem um TextBox para especificar um prompt, um Button para enviar o prompt e um TextBlock para exibir mensagens de status e as respostas do modelo. Substitua o elemento padrão StackPanel em MainWindow.xaml pelo seguinte XAML.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column ="0">
<TextBox x:Name="promptTextBox" Text="Compose a haiku about coding."/>
<Button x:Name="myButton" Click="myButton_Click">Submit prompt</Button>
</StackPanel>
<Border Grid.Column="1" Margin="20">
<TextBlock x:Name="responseTextBlock" TextWrapping="WrapWholeWords"/>
</Border>
</Grid>
Inicializar o modelo
No MainWindow.xaml.cs, adicione uma diretiva using para o namespace Microsoft.ML.OnnxRuntimeGenAI .
using Microsoft.ML.OnnxRuntimeGenAI;
Declare variáveis de membro dentro da definição de classe
private Model? model = null;
private Tokenizer? tokenizer = null;
private readonly string ModelDir =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
@"Models\directml\directml-int4-awq-block-128");
Crie um método auxiliar para inicializar o modelo de forma assíncrona. Esse método chama o construtor para a classe Model , passando o caminho para o diretório do modelo. Em seguida, ele cria um novo Tokenizer a partir do modelo.
public Task InitializeModelAsync()
{
DispatcherQueue.TryEnqueue(() =>
{
responseTextBlock.Text = "Loading model...";
});
return Task.Run(() =>
{
var sw = Stopwatch.StartNew();
model = new Model(ModelDir);
tokenizer = new Tokenizer(model);
sw.Stop();
DispatcherQueue.TryEnqueue(() =>
{
responseTextBlock.Text = $"Model loading took {sw.ElapsedMilliseconds} ms";
});
});
}
Para este exemplo, carregaremos o modelo quando a janela principal for ativada. Atualize o construtor da página para registar um manipulador para o evento Activated.
public MainWindow()
{
this.InitializeComponent();
this.Activated += MainWindow_Activated;
}
O evento Activated pode ser gerado várias vezes, portanto, no manipulador de eventos, verifique se o modelo é nulo antes de inicializá-lo.
private async void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
{
if (model == null)
{
await InitializeModelAsync();
}
}
Enviar o prompt para o modelo
Crie um método auxiliar que envia o prompt para o modelo e, em seguida, retorna assíncronamente os resultados para o chamador com um IAsyncEnumerable.
Nesse método, a classe
public async IAsyncEnumerable<string> InferStreaming(string prompt)
{
if (model == null || tokenizer == null)
{
throw new InvalidOperationException("Model is not ready");
}
var generatorParams = new GeneratorParams(model);
var sequences = tokenizer.Encode(prompt);
generatorParams.SetSearchOption("max_length", 2048);
using var tokenizerStream = tokenizer.CreateStream();
using var generator = new Generator(model, generatorParams);
generator.AppendTokenSequences(sequences);
StringBuilder stringBuilder = new();
while (!generator.IsDone())
{
string part;
try
{
await Task.Delay(10).ConfigureAwait(false);
generator.GenerateNextToken();
part = tokenizerStream.Decode(generator.GetSequence(0)[^1]);
stringBuilder.Append(part);
if (stringBuilder.ToString().Contains("<|end|>")
|| stringBuilder.ToString().Contains("<|user|>")
|| stringBuilder.ToString().Contains("<|system|>"))
{
break;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
break;
}
yield return part;
}
}
Adicione o código da interface do usuário para enviar o prompt e exibir os resultados
No manipulador de cliques do botão , verifique primeiro se o modelo não é nulo. Crie uma cadeia de caracteres de prompt com o prompt do sistema e do usuário e chame InferStreaming, atualizando o TextBlock com cada parte da resposta.
O modelo usado neste exemplo foi treinado para aceitar prompts no formato a seguir, onde systemPrompt são as instruções de como o modelo deve se comportar e userPrompt é a pergunta do usuário.
<|system|>{systemPrompt}<|end|><|user|>{userPrompt}<|end|><|assistant|>
Os modelos devem documentar as suas convenções imediatas. Para este modelo, o formato está documentado no cartão modelo Huggingface.
private async void myButton_Click(object sender, RoutedEventArgs e)
{
responseTextBlock.Text = "";
if(model != null)
{
var systemPrompt = "You are a helpful assistant.";
var userPrompt = promptTextBox.Text;
var prompt = $@"<|system|>{systemPrompt}<|end|><|user|>{userPrompt}<|end|><|assistant|>";
await foreach (var part in InferStreaming(prompt))
{
responseTextBlock.Text += part;
}
}
}
Execute o exemplo
No Visual Studio, na lista suspensa Plataformas de Solução , verifique se o processador de destino está definido como x64. A biblioteca ONNXRuntime Generative AI não suporta x86. Crie e execute o projeto. Aguarde até que o TextBlock indique que o modelo foi carregado. Digite um prompt na caixa de texto do prompt e clique no botão enviar. Você deve ver os resultados preencherem gradualmente o bloco de texto.
Pacotes GenAI alternativos
Se precisar de direcionar um fornecedor de execução específico em vez de usar a seleção automática do Windows ML, pode usar um destes pacotes em vez de.WinML:
| Package | Caso de utilização |
|---|---|
Microsoft.ML.OnnxRuntimeGenAI.DirectML |
Apenas GPU (NVIDIA, AMD, Intel) |
Microsoft.ML.OnnxRuntimeGenAI.QNN |
Apenas NPU (Qualcomm) |
Microsoft.ML.OnnxRuntimeGenAI |
Só CPU (multiplataforma) |
Não faça referência a mais do que um destes pacotes no mesmo projeto — eles enviam ficheiros conflitantes onnxruntime.dll . O código da API GenAI (Modelo, Tokenizador, Gerador) é o mesmo independentemente do pacote que uses.