Agent 不只是聊天框:用 ArkUI 做一個鴻蒙任務工作台

把智慧體能力接進鴻蒙應用時,最容易走偏的一步,是先做一個聊天框。輸入框、送出按鈕、氣泡列表,看起來很快能跑起來,但聯調時真正需要看的並不是一句回覆,而是任務現在處在哪個階段:服務是否在線、模型辨識出了什麼意圖、準備呼叫哪個工具、是否需要確認、失敗時錯誤落在哪裡。

這次做的是一個更偏工程側的 Agent 工作台。頁面不追求複雜互動,只保留幾個必要物件:本機 Agent Gateway、ArkUI 訊息列表、Network Kit 請求、意圖路由結果、錯誤狀態。它的目標不是讓使用者和模型閒聊,而是讓一次端側任務從輸入到路由都有清楚的狀態。

先把端側後面的服務跑起來

工作台頁面不能靠靜態資料撐起來。這裡先在 M1 Mac 本機啟動一個 Agent Gateway,監聽 127.0.0.1:18081。大模型 Key 只放在 Gateway 環境變數裡,鴻蒙端只存取本機服務,不把 Key 寫進 ArkTS 程式碼。

本機 Agent Gateway 啟動

這個 Gateway 提供幾個基礎介面:/health 用來判斷服務是否可達,/llm/route 用來把自然語言任務轉換成結構化意圖。頁面後面顯示的服務狀態和意圖結果,都來自這兩個介面。

/health 回傳裡能看到 status: ok 和服務名,說明端側要連接的物件已經存在。這裡先用 curl 驗證,是為了把問題拆開:如果終端都存取不了,DevEco 裡的頁面失敗就不是 ArkUI 的問題。

健康檢查介面回傳

接著直接請求 /llm/route,輸入是「現在這台 Gateway 機器狀態怎麼樣?」。回傳結果不再是一段自然語言,而是包含 intenttoolneedConfirmrouteSource 的結構化物件。

意圖路由介面回傳

這裡已經能看出工作台和聊天框的區別。聊天框關心「模型怎麼回答」,Agent 工作台關心「模型判斷出了什麼任務,準備把任務交給哪個能力執行」。後面的 ArkUI 頁面只是把這條鏈路放到端側。

從 DevEco Studio 的空專案開始

專案是 DevEco Studio 裡新建的空白鴻蒙應用,目錄裡能看到 entry/src/main/ets/pages/Index.ets。剛開始沒有複雜工程結構,只需要一個頁面檔和網路權限。

建立 HarmonyAgentLab 工程

頁面裡定義了一個很小的訊息模型:role 表示來源,status 表示狀態,content 放介面回傳後的展示文字,time 記錄本地時間。這個模型不複雜,但它比單純拼接字串更適合承載 Agent 過程。

核心狀態只有四個:

ts 代碼解讀複製代碼@State inputText: string = '現在這台 Gateway 機器狀態怎麼樣?'
@State messages: MessageItem[] = []
@State sending: boolean = false
@State serviceText: string = '未檢查'

messages 負責承載使用者輸入、助理分析和錯誤;sending 控制重複點擊;serviceText 用來放 /health 的回傳結果。頁面一開始看起來很輕,但它已經把「任務狀態」從普通文字裡拆出來了。

DevEco Studio 中執行頁面

網路權限放在 entry/src/main/module.json5 裡。沒有這一步,頁面程式碼寫得再完整,也可能在執行時被網路權限擋住。

json 代碼解讀複製代碼{
  "name": "ohos.permission.INTERNET"
}

這裡保持一個原則:端側只負責請求 Gateway 和展示狀態,不在端側保存模型 Key,也不把工具執行邏輯塞進 ArkTS 頁面。

健康檢查先進入頁面

頁面右上角放了一個「健康檢查」按鈕。點擊後請求 /health,服務狀態直接顯示在頁面頂部。這個按鈕看起來不起眼,但聯調時很有用:它能快速區分「頁面程式碼錯了」和「Gateway 沒啟動」。

鴻蒙端完成健康檢查

健康檢查通過後,頁面就不再是一個孤立 UI。它已經能從鴻蒙端存取本機 Agent Gateway,Network Kit、端側權限、服務端埠三件事都跑通了。

輸入一句話,頁面展示意圖而不是閒聊回答

輸入框預設放的是一個真實聯調問題:

text 代碼解讀複製代碼現在這台 Gateway 機器狀態怎麼樣?

點擊送出後,頁面先插入一條使用者訊息,再插入一條 assistant / pending 訊息。介面回傳後,pending 訊息被替換成結構化路由結果:

text 代碼解讀複製代碼intent=查看Gateway機器狀態
tool=local_status
needConfirm=false
source=llm
reason=使用者想查看目前Gateway所在機器的系統、磁碟和負載摘要

鴻蒙端展示使用者意圖路由

這一步是工作台的核心。端側沒有直接展示一段「機器狀態看起來正常」的回答,而是先展示模型選擇出來的工具。使用者能看到這次任務會交給 local_status,也能看到它不需要二次確認。對一個 Agent 入口來說,這些欄位比一段順暢回答更重要:它們決定使用者能不能判斷這次任務是否可信、是否可執行。

頁面裡的 postRoute 只做三件事:發請求、解析回傳、更新訊息狀態。

ts 代碼解讀複製代碼const route = JSON.parse(String(data.result)) as RouteResult
const content = 'intent=' + String(route.intent) + '\n' +
  'tool=' + String(route.tool) + '\n' +
  'needConfirm=' + String(route.needConfirm) + '\n' +
  'source=' + String(route.routeSource) + '\n' +
  'reason=' + String(route.reason)

為了讓狀態可見,訊息並不是直接追加最終結果,而是先顯示 pending,再替換為 success。這個細節會影響實際使用體驗:使用者能知道請求已經送出,不會因為網路等待誤以為按鈕沒按上。

錯誤狀態也要進入工作台

Agent 頁面不能只做成功路徑。這裡把 Gateway 位址暫時改錯,讓頁面請求失敗。失敗後訊息列表裡出現 error / failed,內容保留 Network Kit 回傳的錯誤資訊。

錯誤位址下的不可達狀態

這個失敗狀態比彈一個籠統提示更有用。它讓問題停留在目前任務上下文裡:前面使用者發了什麼,後面哪一步失敗,失敗資訊是什麼,都在同一個頁面裡。聯調時不用切回終端猜測是不是服務沒啟動、埠寫錯或者權限沒配。

這裡也暴露出一個工程邊界:Previewer 或模擬器存取 127.0.0.1 時,要確認它指向的是 Mac 本機服務。如果換成區網存取,Gateway 需要用 0.0.0.0 啟動,端側位址也要換成 Mac 的區網 IP。能在本機解決的問題,不需要繞到 Linux 伺服器上處理。

工作台的邊界要落在任務狀態上

這個工作台只接了 /health/llm/route 兩個介面,範圍很小,但它已經把端側 Agent 入口裡最容易混在一起的幾件事拆開了:服務可達性、使用者輸入、模型路由、執行狀態和錯誤回饋。

聊天 UI 往往只關心訊息是否返回,任務工作台還要關心返回的內容是否能指導下一步操作。intent 說明任務被理解成什麼,tool 說明準備使用哪個能力,needConfirm 說明是否需要更謹慎的處理,reason 解釋模型為什麼這樣判斷。頁面把這些欄位擺出來以後,使用者不需要從一段自然語言裡猜系統到底做了什麼。

從這次聯調結果看,鴻蒙端接入 Agent 能力時,ArkUI 頁面至少要承擔三個職責:

  1. 讓使用者發起任務。
  2. 讓模型判斷過程可見。
  3. 讓失敗狀態留在任務上下文裡。

普通聊天框只能完成第一件事。Agent 工作台要多做兩步,才適合放進真實應用流程裡。

這次實作驗證了四個點:本機 Gateway 可存取,Network Kit 能從鴻蒙端發起請求,自然語言任務能被路由成結構化結果,錯誤位址能在頁面裡形成明確失敗狀態。頁面功能不大,但它把 Agent 入口從「能聊天」推進到了「能看見任務過程」。這一步做好以後,端側互動才有資格承載真實任務,而不是只停留在一個問答殼子裡。


原文出處:https://juejin.cn/post/7660062528128516146


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝17   💬3   ❤️5
714
🥈
我愛JS
📝2   💬5   ❤️3
143
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登