No desenvolvimento de software moderno assistido por IA, muitas vezes enfrentamos o desafio de discernir qual solução proposta por um modelo de linguagem (LLM) é realmente viável. Um único modelo pode alucinar ou oferecer uma abordagem subótima. Uma estratégia eficaz para mitigar isso é consultar diversos modelos simultaneamente e identificar os padrões ou soluções recorrentes entre eles. Se três ou quatro modelos sugerem a mesma biblioteca ou algoritmo, a probabilidade de essa ser a escolha correta é significativamente maior.
Abaixo, exploramos como construir uma aplicação utilizando C# e o ecossistema .NET para disparar prompts em paralelo, exibir as respostas em tempo real via streaming e consoldiar um resumo final.
Interface de Usuário com Avalonia UI
Para a visualização, utilizaremos o Avalonia UI. A interface consiste em um campo de entrada para a pergunta e uma grade para exibir as respostas de seis modelos diferentes de forma simultânea.
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:MultiAI.ViewModels"
x:DataType="vm:ConsensusViewModel"
x:Class="MultiAI.Views.ConsensusView">
<StackPanel Margin="10">
<TextBox Text="{Binding UserPrompt}" Watermark="Digite sua dúvida técnica aqui..." />
<Button Content="Consultar Especialistas"
Command="{Binding ExecuteParallelQueryCommand}"
HorizontalAlignment="Stretch" Margin="0,5"/>
<ScrollViewer Height="600">
<UniformGrid Columns="3" Rows="2">
<!-- Repetir para cada modelo -->
<StackPanel Margin="5">
<TextBlock Text="Modelo A" FontWeight="Bold"/>
<TextBox Text="{Binding Output1}" Height="250" TextWrapping="Wrap" AcceptsReturn="True" IsReadOnly="True"/>
</StackPanel>
<StackPanel Margin="5">
<TextBlock Text="Modelo B" FontWeight="Bold"/>
<TextBox Text="{Binding Output2}" Height="250" TextWrapping="Wrap" AcceptsReturn="True" IsReadOnly="True"/>
</StackPanel>
<!-- ... Outros modelos ... -->
</UniformGrid>
</ScrollViewer>
</StackPanel>
</UserControl>
Implementação do Back end com Microsoft.Extensions.AI
Utilizaremos a abstração IChatClient fornecida pelas bibliotecas experimentais da Microsoft, que padronizam o acesso a diferentes provedores de IA (OpenAI, DeepSeek, modelos locais via Ollama, etc.).
A abordagem inicial sequencial é ineficiente. Para uma experiência de usuário fluida, implementamos chamadas assíncronas paralelas com processamento de fluxo (streaming).
public partial class ConsensusViewModel : ViewModelBase
{
[ObservableProperty] private string _userPrompt = string.Empty;
[ObservableProperty] private string _output1 = string.Empty;
[ObservableProperty] private string _output2 = string.Empty;
// ... propriedades para outros outputs
private readonly IServiceProvider _services;
[RelayCommand]
private async Task ExecuteParallelQuery()
{
// Configuração base do cliente (Exemplo via SiliconFlow ou OpenAI)
var auth = new ApiKeyCredential("SUA_CHAVE_API");
var config = new OpenAIClientOptions { Endpoint = new Uri("https://api.provider.com/v1") };
// Limpa os resultados anteriores
ResetOutputs();
// Lista de tarefas para execução simultânea
var operations = new List<Task>
{
StreamToProperty("gpt-4o", auth, config, text => Output1 += text),
StreamToProperty("deepseek-v3", auth, config, text => Output2 += text),
StreamToProperty("claude-3-5-sonnet", auth, config, text => Output3 += text),
StreamToProperty("llama-3.1-70b", auth, config, text => Output4 += text),
StreamToProperty("qwen-2.5-72b", auth, config, text => Output5 += text),
StreamToProperty("mistral-large", auth, config, text => Output6 += text)
};
await Task.WhenAll(operations);
// Após coletar todas, gera a sumarização
await GenerateSummary(auth, config);
}
private async Task StreamToProperty(string modelId, ApiKeyCredential auth, OpenAIClientOptions opt, Action<string> updater)
{
IChatClient client = new OpenAI.Chat.ChatClient(modelId, auth, opt).AsChatClient();
await foreach (var chunk in client.GetStreamingResponseAsync(UserPrompt))
{
updater(chunk.ToString());
}
}
}
Lógica de Sumarização e Consenso
Assim que todos os modelos terminam suas respostas, enviamos o conteúdo acumulado para um "modelo juiz". O objetivo deste prompt é analisar as divergências e convergências entre as respostas.
private async Task GenerateSummary(ApiKeyCredential auth, OpenAIClientOptions opt)
{
IChatClient summarizer = new OpenAI.Chat.ChatClient("gpt-4o", auth, opt).AsChatClient();
string context = $"""
Analise as propostas abaixo e resuma as 3 abordagens mais citadas pelos assistentes:
1: {Output1}
2: {Output2}
3: {Output3}
4: {Output4}
5: {Output5}
6: {Output6}
""";
var finalResult = await summarizer.GetResponseAsync(context);
// Exibe o resultado em uma janela de diálogo ou componente de destaque
ShowSummaryWindow(finalResult.ToString());
}
Considerações Técnicas
Ao implementar essa arquitetura, alguns pontos merecem atenção:
- Custos de API: Consultar seis modelos simultaneamente consome tokens proporcionalmente à quantidade de modelos. É ideal usar provedores que ofereçam modelos rápidos e baratos para as consultas de base e um modelo mais robusto para a sumarização.
- Modelos de Raciocínio (Reasoning): Modelos como o DeepSeek-R1 ou o o1 da OpenAI retornam blocos de "pensamento" (thought process). Atualmente, as abstrações padrão podem precisar de tratamento manual para separar o raciocínio da resposta final se você desejar limpar a interface.
- Agregadores: Utilizar serviços que unificam várias APIs de IA simplifica a gestão de endpoints e chaves de autenticação no código C#.
Esta ferramenta transforma o processo de tentativa e erro com IA em um fluxo de trabalho baseado em consenso, reduzindo drasticamente o tempo gasto testando soluções incorretas.