跳至內容

Blazor 前端

本導覽說明位於零售分析 API 前方的 Blazor WebAssembly 用戶端。您將瞭解它如何連線至 API、將聊天回應串流至訊息清單、儲存本機設定,並讓模型選擇器與線上模型清單保持一致。

應用程式結構與連接埠

前端是位於下列路徑的 Blazor WebAssembly 應用程式: AgentHQDemo.Web

在此存放庫中,這兩個服務一律以明確的 --urls,而這會覆寫啟動設定檔:

dotnet run --project src/AgentOrchestrator/AgentHQDemo.Web --urls "http://localhost:5051"

因此,文件、圖表與實驗都會參照 5051 供 UI 使用,以及 5050 供 API 使用。⚠️ 簽入的啟動設定檔預設使用 不同 連接埠—Web 專案使用 5240,API 使用 5167—因此若不使用 --urls (或在 IDE 中按 F5)則會改由這些連接埠提供服務,而 UI 的預設 API 基底位址 http://localhost:5050 將不再相符。請傳入 --urls (如文件所述),或設定 ApiBaseUrl 以保持一致。

AgentHQDemo.Web/Program.cs 會設定 API 基底位址:

var apiBase = builder.Configuration["ApiBaseUrl"] ?? "http://localhost:5050";
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(apiBase) });

API 會在 AgentHQDemo.Api/Program.cs 啟用 CORS,並採用允許任何來源、方法與標頭的預設原則。這可讓從本機開發 URL 提供的 WebAssembly 應用程式,在示範期間呼叫已設定的 API 基底位址。

Home.razor

Home.razor 是主要聊天頁面,並擁有下列頁面狀態:

  • Messages:依序排列的聊天記錄。
  • SelectedModel:目前選取的 Copilot 模型。
  • Models:模型 ID 與顯示名稱的字典。
  • IsDarkTheme:目前的佈景主題旗標。
  • IsStreaming:助理回應是否正在進行。

沒有訊息時,頁面會顯示歡迎面板以及 SuggestionChips。只要已有訊息,它便會使用 Message 元件。

串流狀態與捲動

Home.razor.SendMessage 會加入使用者訊息並加以儲存,接著附加一則空白的助理預留訊息。當 IsStreaming 為 true 時,輸入會停用,且助理訊息會隨著來自下列來源的區塊抵達而更新: ChatService.StreamChatAsync

頁面會使用 Timer 以每秒約 20 個畫面的頻率批次更新 UI,而不是每收到一個權杖就重新呈現。呈現後,它會呼叫 JavaScript 協助程式,將訊息容器捲動到底部並醒目提示程式碼區塊。

本機儲存持久化

StorageService 會封裝 Blazored.LocalStorage。它會在本機儲存三個值:

索引鍵 用途
chat_messages 儲存的 ChatMessage 聊天記錄。
selected_model 下次載入頁面時還原的模型。
theme 儲存的 darklight 佈景主題。

Home.razor.OnInitializedAsync 會在擷取模型清單前載入全部三個值。

模型選擇器

Header.razor 會呈現模型選擇器。它會接收線上 Models 字典,並將選取的選項繫結至 SelectedModel

<select id="model-select" @bind="SelectedModel" @bind:after="OnModelChanged">

當使用者變更選項時, Header.OnModelChanged 會叫用 SelectedModelChanged 回呼。 Home.razor.OnModelChanged 會更新本機狀態,並透過下列方式儲存新模型: StorageService.SetSelectedModelAsync

擷取線上模型

ChatService.GetModelsAsync 會從 API 載入模型中繼資料:

var models = await _http.GetFromJsonAsync<List<ApiModel>>("/api/chat/models");

如果 API 至少回傳一個模型,服務會將其轉換為 Dictionary<string, string>,供 Header.razor 使用。如果 API 無法連線或回傳無法使用的資料,則會退回使用 ChatService.AvailableModels,也就是供離線備援使用的靜態目錄。

防範已儲存的過期模型

儲存在本機的模型可能已不再提供給目前登入的帳戶。 Home.razor.OnInitializedAsync 會在擷取線上模型後處理此情況:

if (!Models.ContainsKey(SelectedModel))
{
    SelectedModel = Models.ContainsKey("claude-haiku-4.5")
        ? "claude-haiku-4.5"
        : Models.Keys.First();
}

接著,頁面會儲存替代模型。這可避免使用舊的 localStorage 值,否則聊天要求可能會使用 API 已不再提供的模型。

支援元件

ChatInput 提供文字區域與傳送按鈕。按一下按鈕或按下未搭配 Shift 的 Enter 鍵時會傳送訊息;載入時會停用輸入、修剪空白訊息,並在第一次呈現後將焦點移至輸入欄位。

Message 會呈現使用者與助理訊息。空白助理內容會顯示輸入中指示器;非空白內容會使用 Markdig 從 Markdown 呈現,程式碼區塊則會標記以供 JavaScript 執行醒目提示。

SuggestionChips 會顯示預先定義的零售分析提示詞。選取提示詞按鈕後,會透過相同的 Home.razor.SendMessage 路徑傳送,行為與手動輸入相同。