【文章內容使用 Gemini 2.5 Flash 自動翻譯產生】
生成式使用者介面 (Generative UI,簡稱 GenUI) 是一種使用者體驗模式,其中代理程式不僅會生成內容,還會決定如何向使用者顯示內容並使其具有互動性。對於 Flutter 開發人員來說,實作 GenUI 意味著使用 A2UI,這是一個開放協議,定義了代理程式和客戶端 (或「渲染器」) 之間如何協同合作以組成和維護使用者介面。為了利用這一點,Flutter 團隊開發了 genui,這是一個使用 A2UI 與代理程式連接並為其提供 Widget 目錄以供使用,然後將這些 Widget 呈現給使用者的套件。
genui 套件和 A2UI 協議最近都獲得了更新!
genui 的最新版本引入了架構上的多項變革。受 A2UI 協議 v0.9 採用的推動,此次更新將 genui 從「結構化輸出優先」的理念 (其中 A2UI 訊息透過結構化輸出 API 串流傳輸) 轉變為「提示優先」的方法 (其中代理程式將 JSON 區塊作為文字包含在其回應中)。它還解耦了架構,提供了對應用程式如何與大型語言模型 (LLM) 互動的更直接控制。
如果您要將應用程式從 genui 套件的 v0.7.0 遷移到 v0.9.0,本指南涵蓋了必要的步驟,從依賴項清理到連接新的聊天循環。
架構解耦
在之前的版本中,GenUI 依賴於一系列基於 ContentGenerator 的類別。這些類別隱藏了提示建構、LLM 網路呼叫和回應解析的細節。
package:genui 的最新版本移除了 ContentGenerator。相反,框架現在分為不同的層次:
- 引擎 (SurfaceController):管理 UI 的狀態和渲染。
- 傳輸 (A2uiTransportAdapter):在代理程式和渲染器之間串流傳輸訊息。
- 外觀 (Conversation):提供用於管理聊天狀態的高階 API。
這種解耦意味著您可以控制聊天歷史記錄、重試邏輯和錯誤處理。這也意味著您可以隨意設定與 LLM 的連接。框架不再使用 ContentGenerator「包裝」您的代理程式,因此您可以自由使用您喜歡的模型和提供者,調整生成設定,添加自己的函數等等,而無需透過框架的 API 來完成。
由於 ContentGenerator 已不復存在,因此不再需要特定於提供者的包裝套件。如果您提取套件的最新版本,您會發現諸如 genui_dartantic、genui_google_generative_ai 和 genui_firebase_ai 等名稱不再出現在樹中。
這是遷移中最重要的程式碼變更。您的應用程式負責建立與代理程式的連接,並透過 TransportAdapter 來回傳遞訊息,而不是將 ContentGenerator 傳遞給 SurfaceController。
舊方法:
1 | // 建立一個 ContentGenerator,封裝與代理程式的互動。 |
新方法:
1 | final catalog = BasicCatalogItems.asCatalog(); |
您可能會看著這兩個範例並想:「等等,API 改進不應該意味著我必須撰寫 更少 的程式碼而不是更多嗎?」 的確,以前連接代理程式的這部分「連線」程式碼包含在 genui 套件中,隱藏在 ContentGenerator 類別中,但新方法有一些具體的優點:
- 無需 ContentGenerator,您可以按照自己的喜好設定代理程式,將其保留在您喜歡的記憶體中,並管理其生命週期。您還可以幾乎隨意使用任何您喜歡的 AI 來源,而無需等待帶有新 ContentGenerator 的套件更新。
- 您不再需要將與代理程式的連接「注入」到 genui API 中。它們是鬆散耦合的,只有 token 來回移動。
- 測試更簡單。genui 直接接受 token,它們可以來自代理程式、模擬代理程式或硬編碼測試。
- 如果您想將連接封裝到類別中,仍然可以這樣做。事實上,genui 的幾個範例 採用了這種方法。
提示優先
在以前的版本中,genui 套件嚴重依賴 LLM 提供者嚴格的 API 級別限制 (例如「JSON 模式」或複雜的函數呼叫定義) 來強制模型輸出有效的 UI 結構。綱要透過特定的 API 參數帶外傳遞給 LLM,LLM 實際上被鎖定在一個嚴格的結構中。
雖然這引導模型建立可預測、格式良好的 JSON,但深度巢狀的綱要有時可能會混淆模型或與其自然文字生成傾向相衝突。此外,對結構化輸出的依賴限制了目錄的整體大小和複雜性。這也使得偵錯更加困難,因為約束完全存在於網路層中,而不是您可以在純文字中輕鬆讀取和調整的內容。
「提示優先」方法將真相的來源轉移回 LLM 擅長的地方:系統指令。UI 綱要和與 A2UI 相關的指令不是完全依賴嚴格的 API 開關,而是直接以純文字形式注入到 LLM 的系統提示中。LLM 讀取指令,詳細說明如何為客戶端建構訊息。
這種方法有幾個優點。首先,現代 LLM 經過高度優化,可以遵循詳細的系統指令和範例。在提示中提供綱要與它們的「思維」方式一致。此外,由於 UI 綱要現在只是您提示中的純文字,您可以根據應用程式的需求進行更改。
這項變更表示將正確的提示放入代理程式的上下文視窗的責任現在落在您的應用程式上。幸運的是,genui 套件提供了一個新工具來幫助您為應用程式建立正確的系統提示:PromptBuilder。給定一個目錄和您想提供的任何其他指令,PromptBuilder 將建立一個系統提示,其中包含您的 LLM 需要正確格式化 A2UI 訊息的綱要定義和規則。
1 | final promptBuilder = PromptBuilder.chat( |
設定完成後,您的應用程式可以使用 promptBuilder.systemPrompt 取得提示的 String 版本,然後將該值傳遞給 LLM。
協定與綱要調整
如果您的程式碼手動建構 A2UI JSON 或依賴特定的有效載荷結構,請注意 A2UI v0.9 更新中的這些重大變更:
- 表面建立:
beginRendering現已更名為createSurface。 - 平面組件定義: 不再使用巢狀鍵 (例如
{“Text”: {“text”: “Hello”}}),組件現在使用平面識別符號:{“component”: “Text”, “text”: “Hello”}。 - 資料綁定: 綁定已簡化。使用
{ "path": "/path/to/var" }進行路徑解析。
屬性重新命名:
- distribution => justify
- alignment => align
- usageHint => variant
- text (in TextField) => value
- userAction => action
其他項目也重新命名了!
除了其他一些小調整之外,幾乎所有核心類別的 GenUi 前綴都已移除:
- GenUiConversation => Conversation
- GenUiController => SurfaceController
- GenUiSurface => Surface
- GenUiHost => SurfaceHost
- GenUiContext => SurfaceContext
- GenUiTransport => Transport
- GenUiFallback => FallbackWidget
此外,CoreCatalogItems 更名為 BasicCatalogItems,以闡明它作為基準實作而非嚴格要求。
最後,為了與標準 LLM 函式呼叫術語保持一致,GenUiFunctionDeclaration 和對「工具」的引用已更名為 ClientFunction。
新的 genai_primitives 套件
genui 套件不再附帶自己的訊息類型。相反,GenUI 團隊建立了一個新的 genai_primitives 套件,其中包含 GenAI 應用程式所需常見功能的原始類型。此套件包含 ChatMessage、MessagePart 和 ToolDefinition 等類型。
這些新的類型在 genui 套件 API 中使用,它們足夠靈活,可以融入您可能正在開發的其他 GenAI 應用程式或套件中。
還有更多!
除了上述內容之外,最新版本的 A2UI 和 genui 套件還帶來了幾個新功能,例如:
- 自訂函數,用於驗證客戶端上的資料。
- 新的模組化綱要。
- 改進的錯誤處理。
有關這些的更多資訊,請查看 v0.9 發佈部落格文章 並前往 a2ui.org。
總結
genui 的最新更新引入了一種更加慣用、靈活和穩健的架構。如果您尚未開始使用 GenUI,現在是最好的時機!前往我們的 入門程式設計教學 以獲取對該技術的實作理解,並在大約 90 分鐘內建立一個可運行的應用程式。
A2UI 和 Flutter 的 GenUI 套件的新更新 最初發佈於 Flutter 上的 Medium,人們在那裡透過突出顯示和回應此故事來繼續討論。