查看: 155|回复: 3

鸿蒙端侧Agent接入:ArkUI实现LLM意图路由与错误状态展示

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
将智能体能力接入鸿蒙应用时,最容易走偏的一步是先做一个聊天框——输入框、发送按钮、气泡列表,看起来很快能跑通,但联调时真正需要关注的并非一句回复,而是任务当前处于哪个阶段:服务是否在线、模型识别出了什么意图、准备调用哪个工具、是否需要确认、失败时错误落在哪里。本文基于 ArkUI 和 DevEco Studio,分享一个更偏工程侧的 Agent 工作台实现方案,它将端侧任务从“能聊天”推进到“能看见任务过程”。

整个工作台围绕几个必要对象构建:本机 Agent Gateway、ArkUI 消息列表、Network Kit 请求、意图路由结果和错误状态。它的目标不是让用户与模型闲聊,而是让一次端侧任务从输入到路由都有清楚的状态。

先把端侧后面的服务跑起来

工作台页面不能依赖静态数据撑起来。这里先在 M1 Mac 本机启动一个 Agent Gateway,监听
  1. 127.0.0.1:18081
复制代码
。大模型 Key 只放在 Gateway 的环境变量中,鸿蒙端只访问本机服务,不把 Key 写进 ArkTS 代码。这个 Gateway 提供两个基础接口:
  1. /health
复制代码
用于判断服务是否可达,
  1. /llm/route
复制代码
用于将自然语言任务转换成结构化意图。页面显示的服务状态和意图结果都来自这两个接口。

先用 curl 验证是一个好习惯:如果终端都访问不了
  1. /health
复制代码
(返回
  1. status: ok
复制代码
和服务名),那么 DevEco 里的页面失败就不是 ArkUI 的问题。接着直接请求
  1. /llm/route
复制代码
,输入“现在这台 Gateway 机器状态怎么样?”,返回结果不再是自然语言,而是包含
  1. intent
复制代码
  1. tool
复制代码
  1. needConfirm
复制代码
  1. routeSource
复制代码
的结构化对象。这一步已经能看出工作台与聊天框的区别:聊天框关心“模型怎么回答”,Agent 工作台关心“模型判断出了什么任务,准备把任务交给哪个能力执行”。

从 DevEco Studio 的空项目开始

项目在 DevEco Studio 中新建空白鸿蒙应用,目录里能看到
  1. entry/src/main/ets/pages/Index.ets
复制代码
。不需要复杂工程结构,只需要一个页面文件和网络权限。页面中定义一个很小的消息模型,包含
  1. role
复制代码
(来源)、
  1. status
复制代码
(状态)、
  1. content
复制代码
(接口返回后的展示文本)、
  1. time
复制代码
(本地时间)。这个模型比单纯拼接字符串更适合承载 Agent 过程。

核心状态只有四个:
  1. @State inputText: string = '现在这台 Gateway 机器状态怎么样?'
  2. @State messages: MessageItem[] = []
  3. @State sending: boolean = false
  4. @State serviceText: string = '未检查'
复制代码
  1. messages
复制代码
负责承载用户输入、助手分析和错误;
  1. sending
复制代码
控制重复点击;
  1. serviceText
复制代码
用来放
  1. /health
复制代码
的返回结果。页面看起来很轻,但已经把“任务状态”从普通文本里拆出来了。

网络权限放在
  1. entry/src/main/module.json5
复制代码
中:
  1. {
  2.   "name": "ohos.permission.INTERNET"
  3. }
复制代码
没有这一步,页面代码写得再完整也可能在运行时被网络权限拦住。端侧只负责请求 Gateway 和展示状态,不在端侧保存模型 Key,也不把工具执行逻辑塞进 ArkTS 页面。

健康检查先进入页面

页面右上角放置一个“健康检查”按钮,点击后请求
  1. /health
复制代码
,服务状态直接显示在页面顶部。这个按钮看起来不起眼,但联调时很有用:它能快速区分“页面代码错了”还是“Gateway 没启动”。健康检查通过后,页面就不再是孤立 UI——Network Kit、端侧权限、服务端端口三件事都跑通了。

输入一句话,页面展示意图而不是闲聊回答

输入框默认放一个真实联调问题:
  1. 现在这台 Gateway 机器状态怎么样?
复制代码
点击发送后,页面先插入一条用户消息,再插入一条
  1. assistant / pending
复制代码
消息。接口返回后,pending 消息被替换成结构化路由结果:
  1. intent=查看Gateway机器状态
  2. tool=local_status
  3. needConfirm=false
  4. source=llm
  5. reason=用户想查看当前Gateway所在机器的系统、磁盘和负载摘要
复制代码
这一步是工作台的核心。端侧没有直接展示一段“机器状态看起来正常”的回答,而是先展示模型选择出来的工具。用户能看到这次任务会交给
  1. local_status
复制代码
,也能看到它不需要二次确认。对一个 Agent 入口来说,这些字段比一段顺滑回答更重要:它们决定用户能否判断这次任务是否可信、是否可执行。

页面里的
  1. postRoute
复制代码
只做三件事:发请求、解析返回、更新消息状态。
  1. const route = JSON.parse(String(data.result)) as RouteResult
  2. const content = 'intent=' + String(route.intent) + '\n' +
  3. 'tool=' + String(route.tool) + '\n' +
  4. 'needConfirm=' + String(route.needConfirm) + '\n' +
  5. 'source=' + String(route.routeSource) + '\n' +
  6. 'reason=' + String(route.reason)
复制代码
为了让状态可见,消息不是直接追加最终结果,而是先显示 pending,再替换为 success。这个细节影响实际使用体验:用户能知道请求已经发出,不会因网络等待误以为按钮没点上。

错误状态也要进入工作台

Agent 页面不能只做成功路径。将 Gateway 地址临时改错,使页面请求失败。失败后消息列表里出现
  1. error / failed
复制代码
,内容保留 Network Kit 返回的错误信息。这个失败状态比弹一个笼统提示更有用——它让问题停留在当前任务上下文里:前面用户发了什么,后面哪一步失败,失败信息是什么,都在同一个页面中。联调时不用切回终端猜测是不是服务没启动、端口写错或者权限没配。

这里也暴露出一个工程边界:Previewer 或模拟器访问
  1. 127.0.0.1
复制代码
时,要确认它指向的是 Mac 本机服务。如果换成局域网访问,Gateway 需要用
  1. 0.0.0.0
复制代码
启动,端侧地址也要换成 Mac 的局域网 IP。能在本机解决的问题,不需要绕到 Linux 服务器上处理。

工作台的边界要落在任务状态上

这个工作台只接了
  1. /health
复制代码
  1. /llm/route
复制代码
两个接口,范围很小,但它已经把端侧 Agent 入口里最容易混在一起的几件事拆开了:服务可达性、用户输入、模型路由、执行状态和错误反馈。

聊天 UI 往往只关心消息是否返回,任务工作台还要关心返回的内容是否能指导下一步操作。
  1. intent
复制代码
说明任务被理解成什么,
  1. tool
复制代码
说明准备使用哪个能力,
  1. needConfirm
复制代码
说明是否需要更谨慎的处理,
  1. reason
复制代码
解释模型为什么这样判断。页面把这些字段摆出来以后,用户不需要从一段自然语言里猜系统到底做了什么。

从联调结果看,鸿蒙端接入 Agent 能力时,ArkUI 页面至少要承担三个职责:让用户发起任务,让模型判断过程可见,让失败状态留在任务上下文里。普通聊天框只能完成第一件。Agent 工作台多做了两步,才适合放进真实应用流程中。

这次实现验证了四个点:本机 Gateway 可访问,Network Kit 能从鸿蒙端发起请求,自然语言任务能被路由成结构化结果,错误地址能在页面里形成明确失败状态。页面功能不大,但它把 Agent 入口从“能聊天”推进到了“能看见任务过程”。这一步做好以后,端侧交互才有资格承载真实任务,而不是只停留在一个问答壳子里。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: 鸿蒙端侧Agent接入:ArkUI实现LLM意图路由与错误状态展示

楼主这个方案很实在,避免了上来就做聊天框的坑。把Agent工作台做成“任务状态可视化”的思路,比单纯展示模型回复要清晰得多。健康检查按钮、意图路由结果的结构化展示、错误状态保留在消息上下文中,这几个点对实际联调排查都很实用。特别是把失败信息留在消息列表而不是弹个提示框,减少了很多来回切终端确认的麻烦。谢谢分享,学到不少。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: 鸿蒙端侧Agent接入:ArkUI实现LLM意图路由与错误状态展示

这个思路很务实,确实很多人在做端侧 Agent 时第一反应是先搭聊天 UI,结果联调时才发现服务状态、意图路由这些才是真正耗时的部分。你把任务过程拆成健康检查、意图路由、错误状态三个可观测的环节,等于在端侧给开发者留了清晰的调试锚点。 有个地方想请教:页面里 `pending` 消息替换为 `success` 时,你是直接替换整个消息对象,还是保留原消息 ID 只更新状态字段?如果是后者,ArkUI 里用 `LazyForEach` 配合数据源 `id` 来做列表更新,应该能避免整个列表重绘。另外,`/llm/route` 返回的 `needConfirm` 字段在实际任务中怎么跟用户交互?是弹出确认对话框还是用内联确认按钮? 错误状态的处理我理解是直接展示 `Network Kit` 的原始错误信息,但正式对外发布时可能需要做一层脱敏(比如隐藏端口号或 IP),这部分你有考虑过吗?
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: 鸿蒙端侧Agent接入:ArkUI实现LLM意图路由与错误状态展示

很详实的分享,刚好卡在工程落地的关键点上——把任务拆解为“意图-工具-状态”三个可观测环节,而不是传统问答里的文本流。从健康检查到结构化路由再到错误上下文保留,每一步都很落地。想问一下,Gateway 端在路由时对意图粒度和工具命中策略是怎么设计的?比如 intent 和 tool 之间有没有置信度阈值兜底,还是完全让 LLM 自由输出?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-7-9 16:55 , Processed in 0.028257 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部