鸿蒙Agent高级技能自定义开发:自定义意图服务/能力注册/Agent技能编排/服务插件开发实战
·



一、前言思考
1.1 Agent 的"能力"从哪里来
Agent 再聪明,也不能凭空执行任务——它需要"技能"(Skill):打车要调用打车服务、查快递要调用快递接口、控制家电要对接 IoT 协议。这些技能来自 开发者注册到 Agent 的能力插件。
让 Agent 具备新能力 = 开发一个"技能插件"并注册:
Agent 核心(规划/记忆/对话)
│
├── 内置技能: 闹钟/计算器/系统设置
└── 第三方技能插件(开发者开发):
├── 打车插件 (开发者A)
├── 快递查询插件 (开发者B)
└── 智能家居插件 (开发者C)
1.2 技能插件的价值
| 角色 | 价值 |
|---|---|
| 开发者 | 复用 Agent 的对话/上下文/分发能力,专注业务 |
| 用户 | 一个入口搞定所有服务,不用装一堆 App |
| 平台 | 生态繁荣,Agent 能力无限扩展 |
二、底层原理
2.1 技能插件架构
┌────────────────────────────────────────────────┐
│ Agent 技能运行时 │
│ 意图分发 → 技能匹配 → 参数校验 → 执行 → 结果回传 │
└──────────┬─────────────────────────┬───────────┘
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ 技能注册表 │ │ 技能执行容器 │
│ skillId/能力描述 │ │ 沙箱/生命周期 │
│ 输入输出schema │ │ 超时/重试/回滚 │
└──────────────────┘ └──────────────────┘
技能三要素:
- 能力描述(skill manifest):这个技能能做什么、需要什么输入;
- 输入输出 Schema:参数校验的契约;
- 执行实现:真正干活的代码。
2.2 技能匹配与分发
Agent 收到用户意图后,需要从技能注册表找到最合适的技能:
意图: "帮我打车去机场"
│
▼
技能匹配(基于能力描述语义匹配):
├─ 打车技能: 匹配度 0.95 ← 选中
├─ 快递技能: 匹配度 0.10
└─ 家居技能: 匹配度 0.05
匹配基于能力描述的自然语言语义(开发者写清楚"我能做什么"),不是写死的 ID。
2.3 技能生命周期
注册 → 启用 → 匹配命中 → 实例化 → 参数校验 → 执行 → 结果回传 → 释放
│ │
└──── 未命中: 保持休眠(低开销) ◀────────────┘
- 技能容器是沙箱:插件不能访问 Agent 内核数据;
- 执行有超时(默认 5s)和重试策略;
- 执行失败必须回滚副作用(如下了单要能取消)。
三、实战落地
3.1 技能清单声明(Skill Manifest)
{
"skillId": "demo.express.query",
"name": "快递查询",
"description": "查询快递物流信息,支持输入快递单号或手机号尾号,返回最新物流状态和预计到达时间",
"version": "1.0.0",
"inputSchema": {
"type": "object",
"required": ["trackingNo"],
"properties": {
"trackingNo": { "type": "string", "description": "快递单号" },
"carrier": { "type": "string", "enum": ["sf", "yt", "zt"], "description": "快递公司(可选)" }
}
},
"outputSchema": {
"type": "object",
"properties": {
"status": { "type": "string" },
"latest": { "type": "string" },
"eta": { "type": "string" }
}
}
}
能力描述写得越清楚,Agent 匹配越准——这是技能开发最重要的"文案工程"。
3.2 技能实现(执行器)
import { agentSkill } from '@kit.AiKit';
@agentSkill.Skill({
skillId: 'demo.express.query',
version: '1.0.0'
})
export class ExpressQuerySkill implements agentSkill.SkillHandler {
// 参数校验(框架调用前执行)
validate(params: Record<string, string>): agentSkill.ValidationResult {
if (!params.trackingNo || params.trackingNo.length < 8) {
return { ok: false, message: '请输入有效的快递单号' };
}
return { ok: true };
}
// 执行(核心逻辑)
async execute(params: Record<string, string>, ctx: agentSkill.SkillContext): Promise<agentSkill.SkillResult> {
ctx.log('开始查询快递: ' + params.trackingNo);
// 调用快递 API(可离线规则优先, 在线 API 兜底)
let result = tryOfflineParse(params.trackingNo);
if (!result) {
result = await queryExpressApi(params.trackingNo, params.carrier);
}
ctx.remember({ lastExpress: params.trackingNo }); // 记忆, 供多轮使用
return {
status: result.status,
latest: result.latest,
eta: result.eta,
speak: '您的快递正在' + result.status + ',预计 ' + result.eta + ' 送达'
};
}
// 失败兜底
async onError(err: Error, ctx: agentSkill.SkillContext): Promise<agentSkill.SkillResult> {
ctx.log('查询失败: ' + err.message);
return { status: 'error', latest: '网络异常', eta: '请稍后重试', speak: '暂时查不到物流信息,请稍后再试' };
}
}
3.3 技能编排(多技能组合)
一个复杂任务可能涉及多个技能,Agent 负责编排:
意图: "查快递到了没, 到了告诉我取件码"
├─ 技能1(快递查询) → 物流状态
└─ 若"已签收" → 技能2(取件码查询) → 通知用户
// Agent 侧编排逻辑(由 Agent 框架执行, 技能作者无需关心)
// 但技能作者可以通过"建议编排"声明组合能力
@agentSkill.Skill({
skillId: 'demo.express.full',
description: '快递全流程: 查询 + 签收后自动提醒取件码',
composeOf: ['demo.express.query', 'demo.pickup.code']
})
export class ExpressFullSkill { /* 组合技能 */ }
3.4 技能调试与热更新
// 开发期: 本地模拟 Agent 调用
const sim = await agentSkill.mockAgent({
skills: ['demo.express.query']
});
const res = await sim.chat('我的顺丰快递 8888888888 到哪了?');
console.log(res.reply);
// 发布后: 技能版本热更新
await agentSkill.updateSkill({
skillId: 'demo.express.query',
version: '1.1.0', // 新增中通支持
rollout: { percent: 20 } // 20% 流量灰度
});
四、性能排查与优化
| 问题 | 表现 | 优化手段 |
|---|---|---|
| 匹配错 | 意图分到错误技能 | 能力描述更具体、加示例、负样本 |
| 执行超时 | 技能 5s 无响应 | 并行请求、缓存、降级规则 |
| 参数误填 | 校验不过 | schema 加描述、Agent 先澄清 |
| 记忆污染 | 多轮上下文混乱 | 命名空间隔离、过期清理 |
| 插件崩溃 | 技能异常影响 Agent | 沙箱隔离、异常兜底 onError |
4.1 技能匹配优化
// 能力描述 + 示例查询 双重匹配
const skillDoc = skill.manifest.description + '\n示例: ' + skill.manifest.examples.join('; ');
const score = semanticMatch(userIntent, skillDoc);
4.2 执行降级链
API 查询失败 → 本地规则解析(部分快递支持) → 返回"稍后重试" → 记录失败供改进
4.3 记忆命名空间
// 每个技能的记忆独立命名空间, 避免互相覆盖
ctx.remember(`express.${params.trackingNo}`, result);
// 上下文查询
ctx.recall('express.8888888888');
五、总结
- 技能插件是 Agent 能力的来源:能力描述 + Schema + 执行器三件套,让 Agent 学会新任务。
- 能力描述是"文案工程":写得越清楚,语义匹配越准——这决定了技能是否被正确调用。
- 编排让技能 1+1>2:复杂任务由 Agent 组合多个技能,技能作者只需聚焦单一能力。
- 工程化四要素:沙箱隔离、超时重试、失败兜底、灰度热更新。
一句话记住:描述清楚好匹配,Schema 严谨防误填,兜底到位不崩会话,沙箱隔离保安全。
🚀 演示功能优化(随项目同步更新)
本文对应的 ArkTS 演示页面已随项目整体优化,主要改进:
- 独立主题风格:靛蓝深色 · 斜切角卡片荧光强调,与其余章节演示页明显区分,不再千篇一律。
- 步骤回放动画:点击演示按钮后,结果行按 260~320ms/步 逐步展示,模拟真实推理过程。
- 运行态保护:演示过程中按钮置灰防重复触发,页面退出自动清理定时器。
- 结果摘要:演示结束后自动给出「一句话结论」,并 Toast 提示完成。
- AI 对话演示:新增 AiChatDemo(根目录 main.py 的 ArkTS 移植),真实 SSE 流式大模型请求,首页「★ AI Chat 流式对话演示」可进入。
对应页面:entry/src/main/ets/pages/AgentSkillDemo.ets
🧪 演示优化:真实 AI 推理接入(v3)
本演示页顶部新增 AI 技能编排师主卡,点击即真实调用云端大模型(SSE 流式),不再是纯模拟回放:
- 请求链路:
utils/AiClient.ets(ArkTS 封装 OpenAI 兼容接口)→ POSThttps://api-ai.gitcode.com/v1/chat/completions,模型deepseek-ai/DeepSeek-V4-Flash,流式stream: true - 演示场景:技能定义 / 多技能编排 / 冲突仲裁 —— 每个场景绑定不同 Agent 编排专家 Prompt,返回内容各有差异
- 交互体验:进入页面自动触发一次真实推理;点场景标签切换并重新请求;按钮手动触发;输出区打字机流式展示
- 真实标识:卡片右上角
LIVE徽标 + 端点/模型名水印,保证"所见即所调"
更多推荐

所有评论(0)