DevExpress Blazor中文教程 - 如何用AI聊天组件构建大型语言模型聊天APP?

发布于:2025-05-21 ⋅ 阅读:(20) ⋅ 点赞:(0)

DevExpress Blazor UI组件使用了C#为Blazor Server和Blazor WebAssembly创建高影响力的用户体验,这个UI自建库提供了一套全面的原生Blazor UI组件(包括Pivot Grid、调度程序、图表、数据编辑器和报表等)。

AI服务提供各种量身定制的模型来满足用户需求、偏好和资源限制,模型有优点和缺点——有些是为编码任务而优化的,而另一些则更适合于创造性写作或实时信息检索。

为了选择合适的模型,您通常需要平衡性能和成本。例如,像GPT-4.1这样的高级模型可以提供出色的结果,但需要更多的计算资源。相比之下,GPT-4.1 Mini或Nano等较轻的模型提供更快的响应速度和更低的成本,使其成为追求效率和响应能力的理想选择。

对于企业应用程序来说,连接到基于云的AI服务能力——可以选择回退到在受限环境中运行的离线模型,这通常是必须的。

点击获取DevExpress最新版下载

在这篇文章中,我们将向您展示如何使用DevExpress Blazor AI Chat和ComboBox组件构建一个多llm(大型语言模型)聊天应用程序,您将学习如何实现IChatClient接口来管理多个聊天客户端及其各自的会话历史(以及如何使用DevExpress Blazor ComboBox在聊天会话期间切换llm)。

开始

要开始您必须首先将DxAIChat组件集成到应用程序中(有关这方面的其他信息,请参阅官方指南):Add AI Chat to a Project

在本指南中,我们将使用两个LLMs: 来自Azure OpenAI的GPT-4o和来自Ollama的本地运行的Phi4。

以下是从Program.cs代码单元中为两个聊天客户端注册的示例,请注意,我们将LLM凭据和设置存储在应用程序配置文件(appSettings.Development.json)中,您可以修改以下代码来满足具体要求:

using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;
...
var openAiServiceSettings = builder.Configuration.GetSection("OpenAISettings").Get<OpenAIServiceSettings>();
var ollamaSettings = builder.Configuration.GetSection("OllamaSettings").Get<OllamaSettings>();

IChatClient azureChatClient = new AzureOpenAIClient(
new Uri(openAiServiceSettings.Endpoint),
new AzureKeyCredential(openAiServiceSettings.Key))
.AsChatClient(openAiServiceSettings.DeploymentName);

IChatClient ollamaChatClient = new OllamaChatClient(
new Uri(ollamaSettings.Uri),
ollamaSettings.ModelName);

注意:要安装、运行和下载Ollama模型,请参考DevExpress AI Extensions — Prerequisities帮助主题。

接下来,我们将实现用于跟踪每个客户端及其历史记录的基础设施。

CompositeChatClient实现

首先,声明代表单个LLM会话的ChatClientSession类(包括用户友好的模型/服务名称,客户端实例和消息历史):

using Microsoft.Extensions.AI;
//...
public class ChatClientSession {
public string Name { get; set; }
public IChatClient Client { get; }
public List<BlazorChatMessage> Messages { get; set; }

public ChatClientSession(IChatClient client, string name) {
Name = name;
Client = client ?? throw new ArgumentNullException(nameof(client));
Messages = new List<BlazorChatMessage>();
}
}

接下来,定义CompositeChatClient类,该类实现了IChatClient接口,并充当多个聊天客户端的容器。通过在单个接口实现中封装多个客户端,可以将此功能与DevExpress Blazor AI Chat组件(依赖于IChatClient接口)集成。

该类旨在支持LLM切换,同时保留个人对话历史。主要考虑因素包括:

  • 聊天客户端管理:存储ChatClientSession对象的列表,其中每个对象代表一个LLM及其关联的聊天历史记录。
  • 消息处理:将消息路由到当前选定的聊天客户端并检索响应。

下面是实现:

using Microsoft.Extensions.AI;
//...
public class CompositeChatClient : IChatClient {
public List<ChatClientSession> AvailableChatClients { get; }
public ChatClientSession? SelectedSession { get; set; }
public CompositeChatClient(params ChatClientSession[] chatClients) {
AvailableChatClients = chatClients.ToList();
SelectedSession = AvailableChatClients[0];
}

public Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null,
CancellationToken cancellationToken = new CancellationToken()) {
return SelectedSession?.Client.GetResponseAsync(messages, options, cancellationToken);
}

public IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null,
CancellationToken cancellationToken = new CancellationToken()) {
return SelectedSession?.Client.GetStreamingResponseAsync(messages, options, cancellationToken);
}

public void Dispose() {
for (int i = 0; i < AvailableChatClients.Count; i++) {
AvailableChatClients[i].Client.Dispose();
AvailableChatClients[i].Messages.Clear();
}
}
public object? GetService(Type serviceType, object? serviceKey = null) {
throw new NotImplementedException();
}
}

最后,切换回Program.cs并注册CompositeChatClient,如下所示:

// Register both clients within a single instance of the IChatClient
var compositeChatClient = new CompositeChatClient(
new ChatClientSession(azureChatClient, "Azure Open AI — GPT4o"),
new ChatClientSession(ollamaChatClient, "Ollama — Phi 4"));

builder.Services.AddScoped<IChatClient>((provider) => compositeChatClient);
builder.Services.AddDevExpressAI();
使用组合框选择LLM

为了允许LLM选择,使用DxAIChat组件打开razor页面,并添加DevExpress Blazor ComboBox。主要考虑因素包括:

  • CompositeChatClient注入:将CompositeChatClient注入到页面中,它为UI应用程序提供了一个可用聊天客户端列表。
  • LLM选择:DxComboBox组件绑定到客户端列表,并在选择更改时触发回调。
  • 聊天会话管理:OnModelChanged回调处理聊天历史保存和加载操作(当用户在llm之间切换时)。

下面是实现的一个片段:

@page "/"
@using DevExpress.AIIntegration.Blazor.Chat
@using DXBlazorChatSelector.Services
@using Microsoft.Extensions.AI

<div class="main-container">
<div class="top-container">
<DxComboBox Data="@ModelsList"
CssClass="selector-container-combo-editor"
TextFieldName="@nameof(ChatClientSession.Name)"
Value="ChatClientProvider.SelectedSession"
ValueChanged="@((ChatClientSession session) => OnModelChanged(session))"/>
</div>
<DxAIChat @ref="DxAiChat" CssClass="my-chat"></DxAIChat>
</div>

@code{

[Inject]
IChatClient? ChatClient { get; set; }
CompositeChatClient ChatClientProvider => ChatClient as CompositeChatClient;
DxAIChat? DxAiChat { get; set; }
List<ChatClientSession> ModelsList => ChatClientProvider?.AvailableChatClients;

private void OnModelChanged(ChatClientSession value) {
SaveLastAssistantMessage(DxAiChat.SaveMessages());
ChatClientProvider.SelectedSession = value;
DxAiChat.LoadMessages(ChatClientProvider.SelectedSession.Messages);
}

private void SaveLastAssistantMessage(IEnumerable<BlazorChatMessage> saveMessages) {
if(ChatClientProvider.SelectedSession != null) {
ChatClientProvider.SelectedSession.Messages.Clear();
ChatClientProvider.SelectedSession.Messages.AddRange(saveMessages);
}
}
}
开始新的聊天实现

要清除所选LLM的消息历史记录并开始新的聊天会话,我们将添加DxButton并将其放置在Index.razor页面中的DxComboBox附近。主要考虑因素包括:

  • 按钮样式:按钮使用在CSS中声明的SVG图标。
  • 会话重置:单击后,该按钮清除所选LLM的消息历史记录并更新聊天UI。

更新后的Index.razor页面代码:

@page "/"
@using DevExpress.AIIntegration.Blazor.Chat
@using DXBlazorChatSelector.Services
@using Microsoft.Extensions.AI

<div class="main-container">
<div class="top-container">
<DxButton RenderStyle="ButtonRenderStyle.Primary"
RenderStyleMode="ButtonRenderStyleMode.Contained"
IconCssClass="refresh"
IconPosition="ButtonIconPosition.BeforeText"
CssClass="refresh-button"
Text="Start New Chat"
Click="ClearHistory"/>
<DxComboBox Data="@ModelsList"
CssClass="selector-container-combo-editor"
TextFieldName="@nameof(ChatClientSession.Name)"
Value="ChatClientProvider.SelectedSession"
ValueChanged="@((ChatClientSession session) => OnModelChanged(session))"/>
</div>
<DxAIChat @ref="DxAiChat" CssClass="my-chat"></DxAIChat>
</div>

@code{

[Inject]
IChatClient? ChatClient { get; set; }
CompositeChatClient ChatClientProvider => ChatClient as CompositeChatClient;
DxAIChat? DxAiChat { get; set; }
List<ChatClientSession> ModelsList => ChatClientProvider?.AvailableChatClients;

private void ClearHistory() {
ChatClientProvider.SelectedSession.Messages.Clear();
DxAiChat.LoadMessages(ChatClientProvider.SelectedSession.Messages);
}
private void OnModelChanged(ChatClientSession value) {
SaveLastAssistantMessage(DxAiChat.SaveMessages());
ChatClientProvider.SelectedSession = value;
DxAiChat.LoadMessages(ChatClientProvider.SelectedSession.Messages);
}

private void SaveLastAssistantMessage(IEnumerable<BlazorChatMessage> saveMessages) {
if(ChatClientProvider.SelectedSession != null) {
ChatClientProvider.SelectedSession.Messages.Clear();
ChatClientProvider.SelectedSession.Messages.AddRange(saveMessages);
}
}
}
最终结果

下面图片展示了我们的实现:

DevExpress VCL v25.1产品图集


网站公告

今日签到

点亮在社区的每一天
去签到