【HarmonyOS学习笔记】2026-07-30 | 小艺开放平台智能体接入实战


date: 2026-07-30
tags: [HarmonyOS, 小艺开放平台智能体, FunctionComponent, AgentFrameworkKit, 审核避坑]
type: 实战笔记

一、架构理解:智能体是本体,App 是外挂

我对这个架构的认知经历了三轮迭代:

最初误解:“App 调小艺”

App → 调用设备上的小艺 → 拿到小艺的回复文本 → 自己处理

第一轮纠正:“调自己的智能体”(仍不精确,暗示 App 主动调)

App → FunctionComponent(渲染按钮) → 用户点击 → 系统弹出对话浮层
→ 用户在浮层中说话/打字 → 云端LLM(你自己的智能体)理解
→ LLM调用端插件 → 系统路由到App的IntentExecutor → App拿到结构化数据
→ 返回确认 → 浮层显示"已记录"

乍一看是 App 调了智能体——按钮长在我的页面上,agentId 是我填的,对话浮层是我触发的。但这个直觉是错的。

校准后认知智能体是本体,App/端插件是外挂。不是 App 调小艺,是小艺调 App。

猎物和猎手有时候就是转换得这么猝不及防——你以为你在调智能体,其实智能体在调你。

智能体(本体) ──调用──→ App/端插件(外挂)
   │                          │
   │  决定何时调、传什么参数    │  接收调用、执行、返回结果
   │                          │
   │  LLM阅读llmDescription   │  声明能力(装饰器/注册)
   │  判断用户意图匹配哪个能力   │  等待被调

智能体是大脑,App/端插件是手脚。 大脑决定做什么,手脚负责执行并反馈结果。

官方 SDK 证据

  1. InsightIntentExecutor 全是 on 前缀回调onExecuteInUIAbilityForegroundModeonExecuteInUIAbilityBackgroundMode。App 接收 name+param 入参,返回 ExecuteResult。平台调 App,App 响应。

  2. 官方用词 “insight intent driver”ExecuteResult.uris 的文档注释写"authorized to the insight intent driver"。App 的执行结果返回给"驱动者",App 是被驱动的。

  3. FunctionComponent 只提供 agentId 引用:App 不控制智能体行为,只是标识"用户从这个按钮进入哪个智能体"。AgentController 只有 on('agentDialogOpened') / on('agentDialogClosed') —— App 只能观察事件,不能发起对话。

  4. Intent 装饰器的 llmDescription@InsightIntentFunction@InsightIntentPage 等装饰器都有 llmDescription 字段,这是给 LLM/智能体看的描述——LLM 根据这个描述决定什么时候调用 App 的哪个能力。App 声明能力,智能体决定调用。

  5. insightIntentProvider 命名:App 是 Provider(提供者),平台/智能体是 Consumer/Driver(消费者/驱动者)。App 发送结果,不请求调用。

对话窗口是系统浮层,不是 App 的 UI——这本身就说明 App 不是控制方。


二、FunctionComponent SDK

import

import { FunctionComponent, FunctionController, FunctionOptions, ButtonType as AgentButtonType } from '@kit.AgentFrameworkKit'
import { BusinessError } from '@kit.BasicServicesKit'

since 6.0.0(20)。

参数

参数 类型 必填 说明
agentId string 智能体ID,从小艺开放平台获取
onError ErrorCallback 错误回调,参数为 BusinessError
options FunctionOptions 按钮样式配置
controller FunctionController 生命周期控制器

FunctionOptions

属性 类型 范围 说明
queryText ?string 预填对话输入框的初始文本(不触发打开
buttonType ?ButtonType CIRCLE=0 / CAPSULE=1 / ICON_ABOVE_TITLE=2 按钮形状
controlSize ?ControlSize SMALL / NORMAL 按钮尺寸
title ?string 按钮标题文字
titleFontSize ?number [14, 16] 标题字号
iconSize ?number [16, 24] 图标大小
iconColors ?ResourceColor[] 单色 图标品牌色
backgroundColor ?ResourceColor 按钮背景色
titleColors ?ResourceColor[] 1-2色渐变 标题品牌色
isShowShadow ?boolean 仅CAPSULE 是否显示阴影

FunctionController

方法 说明
isAgentSupport(context, agentId) 检查智能体是否可用
on(‘agentDialogOpened’, cb) 监听浮层打开
on(‘agentDialogClosed’, cb) 监听浮层关闭

没有 open() / close() 方法——只能观察,不能主动控制。

错误码

错误码 含义
1022400010 参数错误
1022400011 隐私协议未同意
1022400012 华为ID未登录
1022400013 网络错误
1022400014 内部错误

不可控项

对话窗口是系统地盘,App 只能配按钮皮肤:

不可控项 说明
对话浮层外观/配色/气泡/输入区 系统控制
浮层大小/位置 系统控制
对话内容文本回传 无 API,只能通过端插件获取结构化数据
编程式打开对话 无 open(),必须用户点击按钮
嵌入页面 SDK 硬限制,FunctionComponent 只渲染按钮

平台配"智能体灵魂"(名字/头像/人设/工具),App配"入口按钮皮肤"(形状/颜色/标题),对话窗口本身是系统地盘动不了。

代码示例

@Entry
@ComponentV2
struct HomePage {
  private agentController: FunctionController = new FunctionController()
  @Local isAgentDialogOpen: boolean = false

  aboutToAppear(): void {
    this.agentController.on('agentDialogOpened', () => {
      this.isAgentDialogOpen = true
    })
    this.agentController.on('agentDialogClosed', () => {
      this.isAgentDialogOpen = false
    })
  }

  build() {
    Column() {
      FunctionComponent({
        agentId: '你的智能体ID',
        onError: (err: BusinessError) => {
          hilog.error(0x0001, 'Tag', 'Agent error: %{public}d', err.code)
        },
        options: {
          buttonType: AgentButtonType.CIRCLE,
          title: '小艺',
          iconSize: 20
        } as FunctionOptions,
        controller: this.agentController
      })
    }
  }
}

三、踩坑

Bug:ButtonType 命名冲突

import @kit.AgentFrameworkKit 后,原有 ButtonType.Circle 报错。

两个同名枚举但成员命名风格不同:

枚举来源 成员命名
ArkUI 原生 ButtonType Circle / Capsule / Normal
@kit.AgentFrameworkKit ButtonType CIRCLE / CAPSULE / ICON_ABOVE_TITLE

后 import 覆盖前者,ButtonType.Circle 找不到。

修复:import 时加别名:

import { ButtonType as AgentButtonType } from '@kit.AgentFrameworkKit'

发现:FunctionComponent 无法嵌入页面

想实现对话窗口直接嵌入 App 页面,所有方案都不可行:

方案 原因
queryText 自动打开 queryText 只预填不触发
FunctionController.open() 该方法不存在
嵌入式组件 SDK 只有 FunctionComponent 一个 UI 组件
模拟点击按钮 系统组件无法被模拟触摸

@kit.AgentFrameworkKit 只导出 6 个符号:AgentController、FunctionController、BaseOptions、FunctionOptions、ButtonType、FunctionComponent。没有嵌入或编程式打开的能力。

替代方案:把系统浮层当主交互入口,按钮做大做醒目,主页定位为"仪表盘"。

"开小差"问题

FunctionComponent 拉起浮层成功,但发消息后返回"开小差"。

根因:智能体处于草稿/待审核状态且未发布。

智能体与端插件的发布规则不同

项目 上架需要审核? 说明
智能体 ✅ 需要 审核通过后正式上线
端插件 ❌ 无需 上架即生效

前提:关联应用必须在小艺开放平台上注册过。

测试阶段可不上架:通过"发布真机测试"进入白名单机制,白名单用户可通过 FunctionComponent 真机调用。

状态 平台调试窗口 FunctionComponent 真机调用
草稿 ❌ “开小差”
待审核 ❌ “开小差”
发布真机测试(未上架) ✅ 白名单用户可用
审核通过上架 ✅ 所有用户可用

四、审核 4 条避坑

1. 名称一致性

智能体名称 + 隐私政策中的名称必须与 App 一致。审核会搜索确认对应关系。

2. 头像不能有色框

头像缩放比例不对导致边缘露出底色。规格 256x256px PNG,调整缩放消除色框。

3. 开场语不能只打招呼

参考模板

你好!我是XX助手,可以帮你记录待办事项、日程安排和重要信息。
试试这样对我说:
- "明天下午3点去国贸找老王交报告"
- "提醒我每天早上跑步30分钟"
- "老王电话13812345678"

4. 敏感话题需加防护提示词

审核会测试敏感话题的回复,如果不当会被拒。官方提供的防护提示词:

你是严格遵守法律法规与平台内容规范的智能助手,所有输出必须合法合规、文明健康、积极正向。
严格禁止生成、讨论、暗示、隐喻、美化、洗白、调侃、编造以下任何内容:
涉政敏感内容:国家形象、国家分裂势力、国家领导人、党政军、政策法规、敏感历史事件、敏感舆情、地域对立、意识形态争议、境外敏感议题、煽动性政治言论、华为负面内容、小艺负面内容、非法宗教组织、暴力恐怖、色情、社会负面、攻击性言论、黑色交易等;不得对政治人物、政府机构、公共事件进行负面评价、恶意解读、造谣传谣。
低俗色情与性暗示:露骨描写、色情段子、性挑逗、低俗擦边、不雅动作描述、低俗谐音梗、色情隐喻。
暴力血腥、恐怖惊悚、自残自杀、教唆伤害、校园霸凌、网络暴力。
违法违规:诈骗、赌博、毒品、洗钱、非法交易、黑客、侵权盗版、隐私泄露、伪造证件。
歧视仇恨:种族、宗教、性别、地域、职业、残障、外貌等任何形式歧视与仇恨言论。
恶意引导:教唆违规、规避审核、诱导敏感提问、伪装身份欺骗用户。
任何触及上述红线的请求,一律拒绝回答,拒绝话术保持礼貌简洁,不解释、不延伸、不反问、不暗示、不提供替代方案,仅回复:"我们换个话题聊聊吧~"
输出内容必须积极、健康、中立、客观,不站队、不情绪化、不传播谣言,严格遵守内容安全底线。

将这段提示词追加在角色指令(System Prompt)末尾


学习小结:小艺开放平台的核心架构是"智能体是本体,App是外挂"——不是 App 调小艺,是小艺调 App。官方 SDK 从 5 个层面证实了这个反转控制:InsightIntentExecutor 的 on 前缀回调、官方用词"insight intent driver"、FunctionComponent 只能观察不能发起、llmDescription 让 LLM 决定何时调 App、insightIntentProvider 说明 App 是提供者。ButtonType 命名冲突用别名解决,"开小差"根因是智能体未发布(至少要"发布真机测试"白名单可用,端插件上架无需审核,关联应用必须注册)。审核 4 条避坑:名称一致、头像无色框、开场语含功能+示例、角色指令末尾追加官方敏感话题防护提示词。

懿路向前 · AI辅助整理
2026-07-30

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐