鸿蒙小艺智能体开放平台实战-接入系统级AI-Agent能力
本文从零开始,手把手带你完成智能体创建、LLM模式配置、自定义云插件开发、端插件Intents Kit桥接、调试测试到上架发布的全流程。附带完整可运行的ArkTS源码和4个真实踩坑记录。
鸿蒙小艺智能体开放平台实战:接入系统级AI Agent能力
摘要:小艺智能体开放平台是HarmonyOS 6.0 AI生态的核心入口,提供50+系统插件、3种编排模式、端/云/MCP三类插件开发体系。本文从零开始,手把手带你完成智能体创建、LLM模式配置、自定义云插件开发、端插件Intents Kit桥接、调试测试到上架发布的全流程。附带完整可运行的ArkTS源码和4个真实踩坑记录。
一、为什么小艺平台是鸿蒙AI开发者的必争之地?
截至2026年4月,小艺智能体开放平台已有50+官方系统插件覆盖天气、日历、邮件、地图、文件管理等高频场景。 这意味着你不需要从零开发这些能力——绑定即用。
但真正让小艺平台与众不同的,不是"插件多",而是三层打通:
第1层:打通鸿蒙系统意图框架(Intents Kit)
→ 你的智能体可以调用打电话、发短信、设置闹钟等系统级能力
→ 这是iOS Shortcuts和Android App Actions做不到的深度
第2层:打通鸿蒙分布式软总线
→ 智能体可以跨设备协同(手机→平板→手表→车机)
→ 用户说"把这份文件投到电视上",一句话搞定
第3层:打通鸿蒙应用生态
→ 其他鸿蒙App可以通过Agent Framework Kit拉起你的智能体
→ 反过来,你的智能体也能通过端插件操控其他App
对比友商生态:
| 维度 | 小艺智能体平台 | Apple Intelligence | Google Gemini |
|---|---|---|---|
| 系统级插件 | 50+ | ~10 | ~20 |
| 端侧RAG | Data Augmentation Kit | 无独立方案 | 无独立方案 |
| 跨应用Agent通信 | A2A协议(API 20+) | App Intents(受限) | 无原生支持 |
| 插件开发门槛 | 云/端/MCP三通道 | 仅App Intents | 仅Google Actions |
| 自定义工具链 | 完全开放 | 半封闭 | 半封闭 |
本文的目标:带你完整走一遍小艺平台的开发全流程,从创建第一个智能体到开发自定义插件,全实战、全源码。
二、平台全景:从注册到上架的完整链路
2.1 开发流程总览
┌─────────────────────────────────────────────────────┐
│ 小艺智能体开发全流程 │
│ │
│ 1. 平台注册 → 2. 创建智能体 → 3. 配置编排模式 │
│ ↓ ↓ ↓ │
│ 6. 上架发布 ← 5. 调试测试 ← 4. 开发插件 │
│ │
│ 7. 应用接入(Agent Framework Kit) │
│ ↓ │
│ 8. 用户使用 → 数据反馈 → 迭代优化 │
└─────────────────────────────────────────────────────┘
2.2 前置条件(必须满足)
| 条件 | 说明 | 获取方式 |
|---|---|---|
| 华为开发者账号 | 企业或个人均可 | developer.huawei.com |
| HarmonyOS 6.0真机 | 不支持模拟器 | Mate 60/Pura 70系列及以上 |
| DevEco Studio 6.0+ | 最新版IDE | 华为开发者官网下载 |
| 设备登录华为账号 | 且同意AI隐私协议 | 设置→华为账号→隐私 |
| 网络环境 | 中国境内 | - |
三、实战Step 1:创建第一个LLM模式智能体
3.1 平台入口与创建
登录小艺智能体开放平台,进入"创建智能体"页面。
关键配置项:
| 配置项 | 填写建议 | 注意事项 |
|---|---|---|
| 智能体名称 | 简洁+功能描述(如"运维助手") | 用户搜索时的关键词 |
| 智能体描述 | 100-200字,说明能力边界 | 影响意图识别准确率 |
| 头像 | 建议使用品牌IP形象 | 120×120px,PNG格式 |
| 编排模式 | 初学者选LLM模式 | 后续可切换 |
| 关联应用 | 绑定你的鸿蒙App包名 | 必须与上架应用一致 |
3.2 角色指令(Prompt工程)
角色指令是智能体的"灵魂",决定了它的行为边界和能力范围。以下是一个经过优化的运维助手Prompt模板:
## 角色
你是一个专业的服务器运维助手,帮助用户监控和管理Linux服务器。
## 能力边界
1. 服务器状态查询(CPU、内存、磁盘、网络)
2. 日志分析与异常告警
3. 常见故障诊断与修复建议
4. 定时任务管理(crontab)
## 回答规则
1. 始终用中文回答
2. 给出具体命令时标注风险等级(低/中/高)
3. 涉及数据删除操作时,必须要求用户二次确认
4. 不确定的操作,建议用户查阅官方文档
5. 回答控制在200字以内,命令用代码块包裹
## 上下文
当前管理服务器列表:{serverList}
当前用户权限级别:{userRole}
踩坑经验:角色指令中的变量(如{serverList})需要在云插件或端插件中动态填充,不能在Prompt中硬编码。
3.3 绑定系统插件
进入"插件管理"页面,选择"系统插件"标签,搜索并绑定你需要的插件:
推荐的插件组合(运维助手场景):
| 插件名称 | 能力 | 绑定理由 |
|---|---|---|
| 天气查询 | 获取服务器所在地区天气 | 天气异常告警 |
| 日历管理 | 创建运维工单 | 定时巡检提醒 |
| 邮件服务 | 发送告警邮件 | 故障通知 |
| 文件管理 | 读写服务器配置文件 | 配置变更 |
| 消息通知 | 推送告警到手机 | 实时监控 |
踩坑1:系统插件不需要开发,但需要正确配置权限
系统插件的权限分三级:
公开插件:所有智能体可直接绑定
受限插件:需要申请审核(如通讯录、相册)
付费插件:企业级能力,需要商业授权
部分受限插件在绑定时会提示"需要关联应用",这时你必须在"关联应用"中填入已上架的鸿蒙App包名,否则无法通过审核。
四、实战Step 2:开发自定义云插件
云插件是最灵活的扩展方式,通过HTTP协议对接你的后端服务。
4.1 云插件创建流程
1. 在"插件管理"→"自定义插件"→"创建云插件"
↓
2. 填写基本信息
├── 插件名称:服务器监控
├── 描述:查询服务器实时状态和性能指标
├── 认证方式:AK/SK签名(推荐)或OAuth 2.0
└── 协议类型:RESTful
↓
3. 配置API接口
├── 接口地址:https://your-server.com/api/server/status
├── 请求方法:POST
├── 超时时间:2000ms(⚠️ 必须小于2200ms)
└── 请求/响应Schema定义
↓
4. 配置工具描述(帮助LLM理解插件能力)
↓
5. 测试验证 → 发布
4.2 后端服务实现
# server_monitor_api.py
# 云插件后端服务实现(Flask示例)
from flask import Flask, request, jsonify
from functools import wraps
import hmac
import hashlib
import base64
import time
import json
import psutil
import os
app = Flask(__name__)
# AK/SK签名验证配置
ACCESS_KEY = os.environ.get('ACCESS_KEY', 'your_access_key')
SECRET_KEY = os.environ.get('SECRET_KEY', 'your_secret_key')
TIMESTAMP_TOLERANCE = 15 * 60 # 15分钟时间窗口
# 简易缓存(生产环境请用Redis)
_cache = {}
def verify_signature(f):
"""AK/SK签名验证装饰器"""
@wraps(f)
def decorated(*args, **kwargs):
access_key = request.headers.get('accessKey')
sign = request.headers.get('sign')
ts = request.headers.get('ts')
if not all([access_key, sign, ts]):
return jsonify({'error': '缺少认证参数'}), 401
# 1. 验证AK
if access_key != ACCESS_KEY:
return jsonify({'error': '无效的accessKey'}), 401
# 2. 验证时间戳(防重放攻击)
try:
request_ts = int(ts)
except ValueError:
return jsonify({'error': '无效的时间戳'}), 401
if abs(time.time() * 1000 - request_ts) > TIMESTAMP_TOLERANCE * 1000:
return jsonify({'error': '请求已过期'}), 401
# 3. 验证签名
expected_sign = base64.b64encode(
hmac.new(
SECRET_KEY.encode('utf-8'),
ts.encode('utf-8'),
hashlib.sha256
).digest()
).decode('utf-8')
if sign != expected_sign:
return jsonify({'error': '签名验证失败'}), 401
return f(*args, **kwargs)
return decorated
@app.route('/api/server/status', methods=['POST'])
@verify_signature
def get_server_status():
"""获取服务器状态"""
data = request.get_json()
# 支持查询指定服务器
server_id = data.get('serverId', 'default')
# 缓存检查(5秒TTL)
cache_key = f"status_{server_id}"
if cache_key in _cache and time.time() - _cache[cache_key]['ts'] < 5:
return jsonify(_cache[cache_key]['data'])
# 采集服务器指标
result = {
'serverId': server_id,
'cpu': {
'usage': psutil.cpu_percent(interval=0.1),
'cores': psutil.cpu_count(),
'loadAvg': os.getloadavg()[0] if hasattr(os, 'getloadavg') else 0
},
'memory': {
'total': psutil.virtual_memory().total,
'used': psutil.virtual_memory().used,
'percent': psutil.virtual_memory().percent
},
'disk': {
'total': psutil.disk_usage('/').total,
'used': psutil.disk_usage('/').used,
'percent': psutil.disk_usage('/').percent
},
'timestamp': int(time.time() * 1000)
}
# 格式化(让LLM更容易理解)
result['summary'] = (
f"服务器{server_id}状态:"
f"CPU使用率{result['cpu']['usage']}%,"
f"内存使用率{result['memory']['percent']}%,"
f"磁盘使用率{result['disk']['percent']}%。"
f"{'⚠️ 警告:' if result['cpu']['usage'] > 80 or result['memory']['percent'] > 85 else '✅ 状态正常。'}"
)
# 写入缓存
_cache[cache_key] = {'data': result, 'ts': time.time()}
return jsonify(result)
@app.route('/api/server/logs', methods=['POST'])
@verify_signature
def get_server_logs():
"""获取最近日志"""
data = request.get_json()
log_type = data.get('type', 'system') # system / application / error
lines = data.get('lines', 50)
keyword = data.get('keyword', '')
log_file_map = {
'system': '/var/log/syslog',
'application': '/var/log/app.log',
'error': '/var/log/error.log'
}
log_file = log_file_map.get(log_type)
if not log_file or not os.path.exists(log_file):
return jsonify({
'logs': [],
'message': f'日志文件不存在或无权限访问: {log_file}'
})
# 读取日志(带关键词过滤)
logs = []
try:
with open(log_file, 'r', errors='ignore') as f:
for line in f:
if keyword and keyword not in line:
continue
logs.append(line.strip())
if len(logs) >= lines:
break
except PermissionError:
return jsonify({
'logs': [],
'message': '权限不足,无法读取日志文件'
}), 403
# 只返回最后N条
logs = logs[-lines:]
return jsonify({
'logs': logs,
'total': len(logs),
'type': log_type,
'keyword': keyword
})
if __name__ == '__main__':
# 生产环境请使用gunicorn + HTTPS
app.run(host='0.0.0.0', port=8080, debug=False)
4.3 工具描述配置(关键步骤)
工具描述是小艺平台让LLM理解你插件能力的核心配置。描述质量直接决定智能体能否正确调用你的插件。
{
"name": "queryServerStatus",
"description": "查询服务器的实时运行状态,包括CPU使用率、内存使用率、磁盘使用率。当用户询问服务器状态、性能、负载、健康情况时使用此工具。",
"parameters": {
"type": "object",
"properties": {
"serverId": {
"type": "string",
"description": "服务器ID,如prod-web-01、dev-db-02。不传则查询默认服务器。"
}
},
"required": []
},
"examples": [
{
"userQuery": "服务器状态怎么样",
"params": {}
},
{
"userQuery": "查看prod-web-01的CPU使用率",
"params": { "serverId": "prod-web-01" }
},
{
"userQuery": "数据库服务器负载高不高",
"params": { "serverId": "prod-db-01" }
}
]
}
踩坑2:工具描述写不好,LLM会"幻觉"调用
| 错误做法 | 正确做法 |
|---|---|
| 描述太模糊:“查询服务器” | 明确能力边界:“查询服务器实时状态,包括CPU、内存、磁盘” |
| 没有示例 | 至少3个示例覆盖不同提问方式 |
| 参数描述缺失 | 每个参数都有详细说明和默认值 |
| description与name重复 | description补充使用场景 |
五、实战Step 3:开发端插件(Intents Kit桥接)
端插件是小艺平台最具鸿蒙特色的能力——让智能体直接操控设备侧的鸿蒙应用,数据不离开设备。
5.1 端插件 vs 云插件的本质区别
云插件调用链路(5跳,数据出设备):
用户 → 小艺智能体 → 华为云端 → 你的服务器 → API处理 → 原路返回
端插件调用链路(2跳,数据不出设备):
用户 → 小艺智能体 → Intents Kit → 你的App本地处理 → 返回
端插件的优势:
- 零网络延迟:本地执行,响应<100ms
- 数据隐私:敏感数据不离开设备
- 离线可用:无网络也能调用
- 权限可控:用户明确授权后才允许调用
5.2 端插件开发全流程
Step 1:在鸿蒙App中定义意图能力
// entry/src/main/module.json5
{
"module": {
"name": "entry",
"abilities": [
{
"name": "MonitorAbility",
"srcEntry": "./ets/MonitorAbility.ts",
"description": "$string:monitor_ability_desc",
"icon": "$media:monitor_icon",
"label": "$string:monitor_label"
}
],
// 定义意图能力
"intents": [
{
"action": "action.monitor.queryStatus",
"entities": ["entity.server"],
"uris": [
{
"scheme": "monitor",
"host": "server",
"path": "status"
}
]
}
]
}
}
Step 2:实现意图处理逻辑
// entry/src/main/ets/MonitorAbility.ts
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { monitorService } from '../service/MonitorService';
const TAG = 'MonitorAbility';
export default class MonitorAbility extends UIAbility {
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
hilog.info(0x0001, TAG, 'MonitorAbility onCreate');
// 初始化监控服务
monitorService.init();
}
onNewWant(want: Want): void {
hilog.info(0x0001, TAG, `收到新意图: action=${want.action}`);
if (!want.action) {
hilog.error(0x0001, TAG, '未知的意图action');
return;
}
switch (want.action) {
case 'action.monitor.queryStatus':
this.handleQueryStatus(want);
break;
case 'action.monitor.queryLogs':
this.handleQueryLogs(want);
break;
case 'action.monitor.setAlert':
this.handleSetAlert(want);
break;
default:
hilog.warn(0x0001, TAG, `不支持的action: ${want.action}`);
}
}
// 处理状态查询
private handleQueryStatus(want: Want): void {
const serverId = want.parameters?.serverId as string || 'default';
// 本地采集数据(不经过网络)
const status = monitorService.getLocalStatus(serverId);
// 通过WantParams返回结果
const resultWant: Want = {
action: 'action.monitor.result',
parameters: {
code: 0,
data: JSON.stringify(status),
timestamp: Date.now()
}
};
// 通知调用方
this.context.terminateSelfWithResult(resultWant)
.catch((err: Error) => {
hilog.error(0x0001, TAG, `返回结果失败: ${err.message}`);
});
}
// 处理日志查询
private handleQueryLogs(want: Want): void {
const logType = want.parameters?.type as string || 'system';
const lines = want.parameters?.lines as number || 50;
const logs = monitorService.getLocalLogs(logType, lines);
const resultWant: Want = {
action: 'action.monitor.result',
parameters: {
code: 0,
data: JSON.stringify({ logs, total: logs.length }),
timestamp: Date.now()
}
};
this.context.terminateSelfWithResult(resultWant)
.catch((err: Error) => {
hilog.error(0x0001, TAG, `返回结果失败: ${err.message}`);
});
}
// 处理告警设置
private handleSetAlert(want: Want): void {
const alertType = want.parameters?.alertType as string;
const threshold = want.parameters?.threshold as number;
const contact = want.parameters?.contact as string;
// 本地设置告警规则
const success = monitorService.setAlertRule({
type: alertType,
threshold: threshold,
contact: contact
});
const resultWant: Want = {
action: 'action.monitor.result',
parameters: {
code: success ? 0 : -1,
message: success ? '告警规则设置成功' : '设置失败,请检查参数',
timestamp: Date.now()
}
};
this.context.terminateSelfWithResult(resultWant)
.catch((err: Error) => {
hilog.error(0x0001, TAG, `返回结果失败: ${err.message}`);
});
}
}
Step 3:监控服务实现
// entry/src/main/ets/service/MonitorService.ts
import { hilog } from '@kit.PerformanceAnalysisKit';
import { deviceInfo } from '@kit.BasicServicesKit';
interface ServerStatus {
serverId: string;
cpu: { usage: number; cores: number };
memory: { total: number; used: number; percent: number };
disk: { total: number; used: number; percent: number };
uptime: number;
timestamp: number;
}
interface AlertRule {
type: string;
threshold: number;
contact: string;
}
class MonitorServiceImpl {
private initialized: boolean = false;
private alertRules: AlertRule[] = [];
private statusCache: Map<string, { data: ServerStatus; ts: number }> = new Map();
private CACHE_TTL = 5000; // 5秒缓存
init(): void {
if (this.initialized) return;
this.initialized = true;
hilog.info(0x0001, 'MonitorService', '监控服务初始化完成');
this.startPeriodicCollection();
}
/**
* 获取本地设备状态(端侧数据,不出设备)
*/
getLocalStatus(serverId: string): ServerStatus {
// 检查缓存
const cached = this.statusCache.get(serverId);
if (cached && Date.now() - cached.ts < this.CACHE_TTL) {
return cached.data;
}
// 采集设备信息
// 注意:以下为鸿蒙设备侧API调用
const totalMemory = deviceInfo.totalMemory; // 总内存(字节)
const usedMemory = Math.floor(totalMemory * (0.4 + Math.random() * 0.3)); // 模拟已用内存
const status: ServerStatus = {
serverId: serverId,
cpu: {
usage: Math.floor(30 + Math.random() * 50), // 模拟CPU使用率
cores: deviceInfo.deviceType === 'phone' ? 8 : 4
},
memory: {
total: totalMemory,
used: usedMemory,
percent: Math.floor((usedMemory / totalMemory) * 100)
},
disk: {
total: 128 * 1024 * 1024 * 1024, // 128GB
used: Math.floor(40 * 1024 * 1024 * 1024 * (1 + Math.random())),
percent: Math.floor(40 + Math.random() * 30)
},
uptime: Date.now(),
timestamp: Date.now()
};
// 写入缓存
this.statusCache.set(serverId, { data: status, ts: Date.now() });
return status;
}
/**
* 获取本地日志
*/
getLocalLogs(logType: string, lines: number): string[] {
// 实际项目中从日志系统读取
// 这里返回模拟数据
const logs: string[] = [];
const types = ['INFO', 'WARN', 'ERROR'];
for (let i = 0; i < Math.min(lines, 100); i++) {
const type = types[Math.floor(Math.random() * types.length)];
const time = new Date(Date.now() - i * 60000).toISOString();
logs.push(`[${time}] [${type}] [MonitorService] 服务器监控日志条目 #${i + 1}`);
}
return logs.reverse();
}
/**
* 设置告警规则
*/
setAlertRule(rule: AlertRule): boolean {
if (!rule.type || !rule.threshold || rule.threshold <= 0) {
hilog.error(0x0001, 'MonitorService', '告警规则参数无效');
return false;
}
this.alertRules.push(rule);
hilog.info(0x0001, 'MonitorService', `新增告警规则: ${rule.type} 阈值${rule.threshold}%`);
return true;
}
/**
* 启动周期性数据采集
*/
private startPeriodicCollection(): void {
// 每30秒采集一次,用于告警检查
setInterval(() => {
const status = this.getLocalStatus('default');
this.checkAlerts(status);
}, 30000);
}
/**
* 检查告警
*/
private checkAlerts(status: ServerStatus): void {
for (const rule of this.alertRules) {
let triggered = false;
let message = '';
switch (rule.type) {
case 'cpu':
triggered = status.cpu.usage > rule.threshold;
message = `CPU使用率${status.cpu.usage}%超过阈值${rule.threshold}%`;
break;
case 'memory':
triggered = status.memory.percent > rule.threshold;
message = `内存使用率${status.memory.percent}%超过阈值${rule.threshold}%`;
break;
case 'disk':
triggered = status.disk.percent > rule.threshold;
message = `磁盘使用率${status.disk.percent}%超过阈值${rule.threshold}%`;
break;
}
if (triggered) {
hilog.warn(0x0001, 'MonitorService', `告警触发: ${message}`);
// 实际项目中通过Notification Kit发送通知
}
}
}
}
export const monitorService = new MonitorServiceImpl();
Step 4:在小艺平台注册端插件
完成App端的意图能力开发后,回到小艺开放平台:
- 进入"插件管理"→"自定义插件"→"创建端插件"
- 填写插件名称和描述
- 关联你在module.json5中定义的意图(
action.monitor.queryStatus等) - 创建工具模拟集(帮助LLM理解端插件能力)
- 配置权限管控(设置哪些智能体可以调用)
六、实战Step 4:应用端接入(Agent Framework Kit)
智能体和插件都配置完成后,在鸿蒙App中通过Agent Framework Kit接入:
// entry/src/main/ets/pages/MonitorPage.ets
import { FunctionComponent, FunctionController } from '@kit.AgentFrameworkKit';
import { BusinessError } from "@kit.BasicServicesKit";
import { hilog } from "@kit.PerformanceAnalysisKit";
import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
@Entry
@Component
struct MonitorPage {
private controller: FunctionController = new FunctionController();
// 小艺开放平台创建的运维助手Agent ID
private agentId: string = 'agentproxy_ops_monitor_2026';
@State isAgentReady: boolean = false;
@State cpuUsage: number = 0;
@State memUsage: number = 0;
@State diskUsage: number = 0;
@State alertMessage: string = '';
async aboutToAppear() {
await this.checkAgentAvailability();
}
// 检查智能体可用性
async checkAgentAvailability() {
try {
let context = this.getUIContext()?.getHostContext() as common.UIAbilityContext;
this.isAgentReady = await this.controller.isAgentSupport(context, this.agentId);
if (!this.isAgentReady) {
hilog.warn(0x0001, 'MonitorPage', '智能体不可用,使用本地模式');
}
} catch (err) {
this.handleAgentError(err as BusinessError);
this.isAgentReady = false;
}
}
// 智能体错误处理
private handleAgentError(err: BusinessError): void {
switch (err.code) {
case 1022400011:
promptAction.showToast({ message: '请先在设置中同意AI隐私协议' });
break;
case 1022400012:
promptAction.showToast({ message: '请先登录华为账号' });
break;
case 1022400013:
promptAction.showToast({ message: '网络异常,请检查网络连接' });
break;
default:
hilog.error(0x0001, 'MonitorPage', `未知错误: ${err.code} ${err.message}`);
}
}
build() {
Column({ space: 16 }) {
// 顶部标题栏
Text('服务器运维监控')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.width('100%')
.padding({ top: 20, bottom: 10 })
// 服务器状态卡片
Row({ space: 12 }) {
this.StatusCard('CPU', this.cpuUsage, '#FF6B6B')
this.StatusCard('内存', this.memUsage, '#4ECDC4')
this.StatusCard('磁盘', this.diskUsage, '#45B7D1')
}
.width('100%')
.padding({ left: 16, right: 16 })
// 告警消息
if (this.alertMessage) {
Text(this.alertMessage)
.fontSize(14)
.fontColor('#FF4444')
.width('100%')
.padding(12)
.margin({ left: 16, right: 16 })
.backgroundColor('#FFF0F0')
.borderRadius(8)
}
Blank()
// 核心:Agent Framework Kit接入
if (this.isAgentReady) {
// 形态一:场景化快捷按钮(queryText预填指令)
Column({ space: 12 }) {
Text('智能运维助手')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.width('100%')
.padding({ left: 16 })
Row({ space: 10 }) {
// 快捷按钮1:查询状态
FunctionComponent({
agentId: this.agentId,
controller: this.controller,
options: {
title: '状态总览',
queryText: '帮我查看所有服务器的当前运行状态,如果有异常请高亮显示',
buttonType: 1, // CAPSULE
isShowShadow: true
},
onError: (err: BusinessError) => {
this.handleAgentError(err);
}
})
// 快捷按钮2:日志分析
FunctionComponent({
agentId: this.agentId,
controller: this.controller,
options: {
title: '日志分析',
queryText: '帮我分析最近1小时的错误日志,找出异常趋势',
buttonType: 1,
isShowShadow: true
},
onError: (err: BusinessError) => {
this.handleAgentError(err);
}
})
}
.width('100%')
.padding({ left: 16, right: 16 })
// 快捷按钮3:故障诊断
FunctionComponent({
agentId: this.agentId,
controller: this.controller,
options: {
title: '故障诊断',
queryText: 'CPU使用率持续超过80%,帮我排查可能的原因并给出处理建议',
buttonType: 1,
isShowShadow: true
},
onError: (err: BusinessError) => {
this.handleAgentError(err);
}
})
.width('calc(100% - 32vp)')
}
.width('100%')
.padding({ bottom: 20 })
} else {
// 降级UI:智能体不可用时
Column({ space: 12 }) {
Text('AI助手暂不可用')
.fontSize(14)
.fontColor('#999999')
Text('请检查:1.网络连接 2.华为账号登录 3.系统版本≥6.0')
.fontSize(12)
.fontColor('#BBBBBB')
}
.width('100%')
.padding({ bottom: 20 })
}
}
.width('100%')
.height('100%')
.backgroundColor('#F8F9FA')
}
@Builder
StatusCard(label: string, value: number, color: string) {
Column({ space: 4 }) {
Text(label)
.fontSize(12)
.fontColor('#666666')
Text(`${value}%`)
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor(value > 80 ? '#FF4444' : color)
Text(value > 80 ? '异常' : '正常')
.fontSize(11)
.fontColor(value > 80 ? '#FF4444' : '#999999')
}
.layoutWeight(1)
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({ radius: 4, color: '#00000010', offsetY: 2 })
}
}
6.1 queryText场景化设计思路
在运维监控页面中,我通过3个不同的queryText实现了3个场景化的AI入口:
| 按钮 | queryText | 触发场景 |
|---|---|---|
| 状态总览 | “查看所有服务器运行状态” | 日常巡检,一键获取全局状态 |
| 日志分析 | “分析最近1小时错误日志” | 故障排查,快速定位问题 |
| 故障诊断 | “CPU持续超80%,排查原因” | 告警触发,自动带入上下文 |
踩坑3:queryText太长会被截断
queryText有长度限制(实测约200字符)。如果你的预填指令包含大量上下文信息,建议通过云插件/端插件传递,而不是塞进queryText:
// ❌ 错误做法:把所有上下文塞进queryText
queryText: '帮我分析服务器prod-web-01在2026-05-07 14:00-15:00之间的CPU使用率异常,阈值80%,当前峰值95%,进程列表:java(45%) nginx(12%) ...'
// ✅ 正确做法:queryText简洁,上下文通过插件获取
queryText: '帮我分析当前服务器CPU异常原因',
// 然后在智能体的工具链中,LLM会自动调用端插件获取实时数据
七、调试与测试:智能体调试全攻略
7.1 小艺平台在线调试
小艺开放平台提供了在线调试工具,无需真机即可测试智能体的对话能力:
调试流程:
1. 进入"我的智能体"→ 选择目标智能体
2. 点击"调试"按钮
3. 在对话框中输入测试用例
4. 观察智能体的意图识别、工具调用、回复生成全流程
调试面板关键信息:
| 面板 | 显示内容 | 作用 |
|---|---|---|
| 意图识别 | 用户意图分类结果 | 判断Prompt是否清晰 |
| 工具调用 | 调用了哪个插件、传了什么参数 | 判断工具描述是否准确 |
| 思考过程 | LLM的推理链 | 判断模型选择是否合适 |
| 响应内容 | 最终回复 | 判断整体效果 |
7.2 真机端到端测试
在线调试通过后,需要在真机上进行端到端测试。以下是测试用例矩阵:
基础功能测试:
├── TC-001: 点击FunctionComponent,确认对话框正常弹出
├── TC-002: 输入简单问题,确认回复正常
├── TC-003: 点击带queryText的按钮,确认预填指令发送成功
├── TC-004: 关闭对话框,确认生命周期回调正常
插件调用测试:
├── TC-005: 触发云插件调用,确认数据返回正常
├── TC-006: 触发端插件调用,确认本地能力响应
├── TC-007: 同时触发多个插件(链式调用)
├── TC-008: 插件超时场景(模拟网络异常)
异常场景测试:
├── TC-009: 断网状态下的表现
├── TC-010: 未登录华为账号时的降级处理
├── TC-011: 隐私协议未同意时的错误提示
├── TC-012: Agent ID错误时的容错
踩坑4:端插件在调试模式下不生效
小艺平台的在线调试器只能测试云插件,端插件必须通过真机测试。如果你发现端插件在调试器中始终不触发,不要浪费时间去排查——这是因为端插件依赖设备侧的Intents Kit,调试器没有这个环境。
解决步骤:
- 确保App已安装到真机
- 确保App中已正确注册意图能力(module.json5)
- 在App中通过Agent Framework Kit拉起智能体
- 在真机上进行测试
八、三种编排模式的实战对比
在实际开发中,不同编排模式的选择会显著影响开发复杂度和功能上限:
8.1 实战对比表
| 维度 | LLM模式 | 工作流模式 | A2A模式 |
|---|---|---|---|
| 开发复杂度 | ⭐ 低 | ⭐⭐⭐ 中高 | ⭐⭐⭐⭐ 高 |
| 灵活性 | ⭐⭐⭐⭐ 高 | ⭐⭐ 中 | ⭐⭐⭐⭐ 高 |
| 可控性 | ⭐⭐ 低(LLM自主决策) | ⭐⭐⭐⭐⭐ 高(完全确定) | ⭐⭐⭐ 中 |
| 适合场景 | 对话问答、知识检索 | 流程固定的业务 | 接入已有云侧Agent |
| 插件支持 | 50+系统+自定义 | 系统插件 | 插件绑定 |
| 知识库 | ✅ 端侧RAG | ❌ | ❌ |
8.2 如何选择?
问自己三个问题:
1. 任务的步骤是确定的还是动态的?
├── 确定(如:订单处理、审批流程)→ 工作流模式
└── 动态(如:问答、推荐)→ LLM模式
2. 是否需要接入已有的云侧智能体?
├── 是(如企业已有知识库Agent)→ A2A模式
└── 否 → LLM模式或工作流模式
3. 是否需要端侧RAG(数据不出设备)?
├── 是 → LLM模式(唯一支持知识库的模式)
└── 否 → 任意模式
九、4个真实踩坑记录总结
踩坑1:系统插件权限申请被拒
现象:绑定了通讯录、短信等受限插件后,智能体审核不通过。
原因:受限插件要求关联应用已上架,且应用权限说明必须包含该插件的用途。
解决:先确保App上架,在应用描述中明确说明AI能力需要的权限和用途,再重新提交审核。
踩坑2:云插件响应超时(2.2秒限制)
现象:后端接口本地测试正常,接入小艺平台后频繁超时。
原因:平台对RESTful协议的硬性时延限制为2200ms,经过平台网关中转后实际可用时间更短。
解决:对关键接口增加缓存层(Redis,TTL=5s),并将复杂计算异步化。如确实无法满足时延要求,改用SSE或WebSocket协议。
踩坑3:端插件onNewWant不触发
现象:通过智能体调用端插件,但App的MonitorAbility.onNewWant始终不执行。
原因:module.json5中的intents配置与平台注册的意图不匹配——action名称不一致。
解决:确保App端的action字符串与平台注册时填写的完全一致,区分大小写。
踩坑4:FunctionComponent在多页面复用崩溃
现象:在Tab页的多个子页面中分别放置FunctionComponent,切换Tab时应用崩溃。
原因:FunctionController在多个页面间共享时,生命周期回调冲突。
解决:每个页面使用独立的FunctionController实例,并在aboutToDisappear中正确注销监听器。
// ✅ 正确做法:每个页面独立Controller
@Entry
@Component
struct PageA {
private controller: FunctionController = new FunctionController();
// ...
}
@Entry
@Component
struct PageB {
private controller: FunctionController = new FunctionController();
// ...
}
十、从开发到上架:发布清单
10.1 上架前检查清单
□ 智能体基本信息完整(名称、描述、头像、分类)
□ 编排模式已配置
□ 角色指令已优化(覆盖边界情况)
□ 插件绑定完成(系统插件+自定义插件)
□ 工具描述已优化(至少3个示例)
□ 在线调试通过(20+测试用例)
□ 真机端到端测试通过(12项测试用例)
□ 关联应用已上架(如需端插件)
□ 隐私协议和权限说明完整
□ 无敏感内容(政治、色情、暴力等)
10.2 上架审核周期
| 审核类型 | 周期 | 备注 |
|---|---|---|
| 个人开发者 | 3-5个工作日 | 含插件审核 |
| 企业开发者 | 1-3个工作日 | 优先审核通道 |
| 紧急更新 | 24小时内 | 需提交紧急说明 |
总结与互动
小艺智能体开放平台的核心价值可以概括为三个字:“系统级”。
与第三方AI SDK(LangChain、OpenAI Assistants)相比,小艺平台提供了其他平台无法企及的能力:
- 50+系统插件,开箱即用——天气、日历、邮件、文件管理,绑定即调用
- 端插件Intents Kit桥接——数据不出设备,零网络延迟,这是iOS和Android做不到的
- A2A跨应用Agent通信——多智能体协同不再需要自己造轮子
对开发者来说,现在是入局的最佳时机:平台刚起步,竞争少,先发优势明显。
下一篇预告:《OpenHarmony A2A协议实战:多智能体跨应用协同架构与实现》——我们将深入Agent-to-Agent通信协议,实现文件管理Agent与邮件Agent的跨应用协作。
💬 讨论话题:
- 你觉得小艺平台的"端插件"(Intents Kit桥接)在实际项目中有多大价值?哪些场景最适合?
- LLM模式和工作流模式,你更倾向于哪种?为什么?
- 你在小艺平台开发过程中遇到过什么坑?评论区分享~
👍 觉得有用请点赞收藏,关注我获取更多AI+鸿蒙实战内容!
❓ 思考题:为什么端插件的响应延迟可以做到<100ms,而云插件即使优化到极致也通常在200-500ms?这个差异的本质原因是什么?评论区聊聊~
更多推荐



所有评论(0)