快速建立可傳圖的 AI 聊天網站 - 從 AIChatWeb 專案出發
| | | 0 | |
前幾天介紹過用 .NET 的 AI 應用程式範本花兩分鐘生出 RAG 文件檢索網站,不少人好奇為什麼文章縮圖是紅色警戒 2 的基地車?

有玩過 RA2 的人 (換言之要年紀夠大的老人,年輕同學想體驗可以試試神人耗時五年復刻的網頁版) 都知道,遊戲開局只有一台基地車,先開到合適地點部署,轉成建造廠後開始採礦、建造發電場、兵營與兵工廠,生產部隊與製造各式武器 參考。基地車是整場遊戲的起點,攻守陣地與所有部隊的源頭。
.NET 的 AI 應用程式範本 AIChatWeb 提供一個開箱可用的 AI 互動網站框架,經過修改擴充,可以打造各式各樣的 AI 應用系統。因此,我覺得 AIChatWeb 專案可扮演類似 RA2 基地車的角色,做為開發複雜 AI 應用程式互動網站的起點。
為了驗證此想法,我對上次的專案進行了一番修改,看可否為只能做 RAG 文件查詢的網站,加入可上傳圖片及詢問 LLM 任意問題的功能,驗證以 AIChatWeb 專案為基礎,改裝為通用型 AI 互動聊天網站的可行性。
修改過程發現一個問題:AIChatWeb 專案使用 Blazor 技術,我對它的認識還停在當年的預覽版本,歷經七年的發展,Blazor 技術與當年已有不少出入。除了將 C# 編譯成 WASM 的 Blazor WebAssembly 模式,還有以 SignalR 為基礎的 Blazor Server 模式 參考,二者的做法大異其趣,共通之處是都能實現不必學 JavaScript,用 C# 統一前後端的夢想。而 AIChatWeb 使用的是我不熟悉的 Blazor Server 模式。
AIChatWeb 的 Blazor 相關程式在 /Pages 目錄,採用類似前端框架將網頁拆成多個元件,各元件的 HTML/CSS/JavaScript 自成一體彼此獨立,可個別開發設計,最後再組裝出完整功能的元件化架構:
- App.razor 整個網頁的進入點,HTML 結構類似 ASP.NET MVC 的 _Layout.cshtml
- Routes.razor 定義路由 (.NET 8+ 將路由自 App.razor 拆出)
- 頁面被拆解成多個元件(Component),這些元件被放在 Chat 目錄下,每個元件有 .razor 與對應的 .css,必要時還會有獨立 .js
- .razor 包含 HTML 及 C# 程式,其互動串接方式有點特別,需要學習及適應
- Chat.razor 是聊天功能主頁面(路由 /),負責核心邏輯:管理對話歷史、呼叫 IChatClient 串流取得 AI 回應、整合語意搜尋工具(SemanticSearch)並組合其他子元件
- ChatHeader.razor 為頁面頂部標題列,顯示應用程式名稱,以及「New chat」按鈕(觸發時通知父元件重置對話)
- ChatInput.razor 使用者輸入區域,包含可自動調整高度的文字框、傳送按鈕
- ChatMessageList.razor 訊息列表容器,依序渲染對話訊息,顯示進行中的回應與載入動畫,並在沒有訊息時顯示自訂提示內容
- ChatMessageItem.razor 單則訊息渲染元件。根據角色 (User 或 Assistant) 顯示不同樣式
- ChatCitation.razor 引用來源連結,顯示在 AI 回應下方,可點擊開啟對應的原始文件
- ChatSuggestions.razor 後續問題建議列。在每次 AI 回應後,自動呼叫 AI 產生最多 3 個建議問題,讓使用者可一鍵點選繼續對話
.razor 結構與用法需要一些時間熟悉與適應,若完全沒有基礎,MS Learn 有個幼班教學還不錯:使用 Blazor 組建您的第一個具有 ASP.NET Core 的 Web 應用程式
原始 AIChatWeb 限定 RAG 查詢,輸入文字聊天功能很 OK,但沒有上傳圖檔功能,故本次的修改重點為:
- 修改 Chat.razor,更改 System Prompt,允許非 RAG 問答,呼叫 IChatClient 時增加可上傳檔案 (同時傳文字與圖檔到 LLM 模型的做法,研究 MS Agent Framework 時玩過,現在可派上用場)
- 修改 ChatMessageItem.razor 使用者訊息支援顯示附加的圖片
- 修改 ChatInput.razor,加入拖拉上傳檔案功能(透過 JS 分塊處理大型檔案)
過程踩到兩個小坑:
- Blazor 有所謂 CSS Isolation Bundling,.css 寫的樣式,實際上會搭配 Scope Identifier。例如 Blah.razor.css 寫
h1 { color: brown; },最後套用時會變成類似<h1 b-3xxtam6d07>及h1[b-3xxtam6d07] { color: brown; },以確保各 Blazor 元件的 CSS 不打架。因此若用 JavaScript 動態生成的元素,不會知道要加 b-3xxtam6d07,故無法套用 .razor.css 樣式。 - Blazor Server 模式透過 JavaScript 執行
await dotnetRef.invokeMethodAsync('AddDroppedFile', f.name, btoa(binary));呼叫 C# 端方法,背後透過 SingalR 傳遞,而每次傳輸 SingalR 訊息資料長度上限預設為 32KB,導致檔案稍大就發生無聲錯誤。我的解法是將檔案內容切成小塊分次傳送,C# 端收齊後再組合還原。
對程式修改細節有興趣的同學可看 Commit,至於執行效果,如下所示:
拖拉圖檔案到文字輸入區可上傳圖片:

圖片會與問題一起傳送,並顯示在訊息區:

這個修改版的完整程式我放在 Github 專案的 with-image 分支,老樣子,需要的同學請自取。
Explores extending the .NET AIChatWeb Blazor Server template beyond RAG into a general AI chat app. Describes adding image upload, handling Blazor quirks, and positioning AIChatWeb as a flexible “base unit” for complex AI web apps.

Comments
Be the first to post a comment