鸿蒙小艺智能体开放平台实战:接入系统级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端的意图能力开发后,回到小艺开放平台:

  1. 进入"插件管理"→"自定义插件"→"创建端插件"
  2. 填写插件名称和描述
  3. 关联你在module.json5中定义的意图(action.monitor.queryStatus等)
  4. 创建工具模拟集(帮助LLM理解端插件能力)
  5. 配置权限管控(设置哪些智能体可以调用)

六、实战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,调试器没有这个环境。

解决步骤:

  1. 确保App已安装到真机
  2. 确保App中已正确注册意图能力(module.json5)
  3. 在App中通过Agent Framework Kit拉起智能体
  4. 在真机上进行测试

八、三种编排模式的实战对比

在实际开发中,不同编排模式的选择会显著影响开发复杂度和功能上限:

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)相比,小艺平台提供了其他平台无法企及的能力:

  1. 50+系统插件,开箱即用——天气、日历、邮件、文件管理,绑定即调用
  2. 端插件Intents Kit桥接——数据不出设备,零网络延迟,这是iOS和Android做不到的
  3. A2A跨应用Agent通信——多智能体协同不再需要自己造轮子

对开发者来说,现在是入局的最佳时机:平台刚起步,竞争少,先发优势明显。

下一篇预告:《OpenHarmony A2A协议实战:多智能体跨应用协同架构与实现》——我们将深入Agent-to-Agent通信协议,实现文件管理Agent与邮件Agent的跨应用协作。


💬 讨论话题:

  1. 你觉得小艺平台的"端插件"(Intents Kit桥接)在实际项目中有多大价值?哪些场景最适合?
  2. LLM模式和工作流模式,你更倾向于哪种?为什么?
  3. 你在小艺平台开发过程中遇到过什么坑?评论区分享~

👍 觉得有用请点赞收藏,关注我获取更多AI+鸿蒙实战内容!

❓ 思考题:为什么端插件的响应延迟可以做到<100ms,而云插件即使优化到极致也通常在200-500ms?这个差异的本质原因是什么?评论区聊聊~

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐