鸿蒙智能体开发实战:10.自定义消息卡片
前言
在鸿蒙智能体应用中,除了纯文本回复外,经常需要展示结构化交互内容,如商品展示、订单信息、壁纸生成结果等。消息卡片(DisplayFaCard)提供了一套标准化的富媒体消息机制,让智能体的回复更加丰富和直观。
本文将从卡片创建 → 数据结构 → 开发编辑 → 发送集成 → 发布管理的完整生命周期出发,详细介绍如何在鸿蒙智能体中开发自定义消息卡片。文章涵盖平台操作流程与底层协议实现,帮助开发者全面掌握卡片开发技能。
一、卡片概述
1.1 什么是消息卡片
消息卡片是一种结构化消息展示形式,以卡片容器承载图片、文本、按钮、列表等多种元素,通过 A2A 协议的 artifact-update 事件传递给客户端渲染。

上图展示了鸿蒙智能体应用中的卡片消息效果,图片和文本内容以卡片形式混合呈现。
1.2 卡片的核心优势
与纯文本回复相比,消息卡片具有以下优势:
- 丰富的展示形式:支持图片、按钮、列表、地图、音视频等多种组件
- 结构化数据:便于前端解析和渲染,支持变量绑定动态数据
- 统一的交互模式:标准化的事件配置(点击跳转、发送消息、调用插件等)
- 更好的用户体验:图文混排比纯文本更直观、更美观
- 可复用性强:卡片模板支持多插件/工作流绑定,一次创建多处使用
卡片功能的适用场景非常广泛:
- 内容展示型:壁纸生成结果、新闻摘要、商品详情
- 操作确认型:支付确认、授权确认、风险提示
- 信息导航型:菜单列表、分类导航、快捷入口
- 媒体播放型:音乐播放控制、视频预览
二、创建和管理卡片
2.1 新建卡片
在鸿蒙智能体开放平台中,进入【工作空间】→【卡片】→【新建卡片】,填写卡片信息后即可创建。卡片创建提供两种方式:
| 创建方式 | 适用场景 | 特点 |
|---|---|---|
| 标准创建 | 从零开始设计卡片 | 完全自定义,灵活性最高 |
| AI生成创建 | 基于示例图片快速生成 | 上传图片+描述,1~3分钟自动生成 |
卡片创建注意事项:
- 标准创建后可通过自定义卡片编辑进一步调整组件布局
- AI生成创建的图片需满足:10×10~1024×1024像素,大小不超过5MB,支持JPG/PNG格式
- AI生成的卡片可能与原图在结构和样式上有差异,需确认后手动调整

AI生成创建特别适合快速原型验证,开发者上传设计稿图片即可获得可编排的卡片组件结构。
2.2 卡片管理
卡片创建完成后,在卡片列表中可以执行以下操作:
- 查看:查看已创建的卡片详情
- 编辑:修改卡片组件布局和配置(已发布版本只能查看,不能编辑)
- 删除:删除未关联的卡片(已被任何插件或工作流关联的卡片不能删除)
- 发布:将卡片发布到生产环境

三、卡片数据结构
3.1 基础数据模型
在 A2A 协议中,卡片数据遵循以下数据结构:
# models/schemas.py
from pydantic import BaseModel
from typing import Optional, Any, List
class CardData(BaseModel):
"""卡片数据"""
imageUrl: Optional[str] = None # 图片 URL
title: Optional[str] = None # 卡片标题
description: Optional[str] = None # 卡片描述
subtitle: Optional[str] = None # 副标题
buttons: Optional[List[dict]] = None # 按钮列表
class CardInfo(BaseModel):
"""卡片信息"""
cardName: str # 卡片名称/类型
cardData: CardData # 卡片数据
displayType: str = "DisplayFaCard" # 显示类型
CardData 定义了卡片的通用字段,CardInfo 则封装了完整的卡片信息。displayType 字段决定了客户端如何渲染卡片。
3.2 MessagePart 中的卡片数据
卡片数据作为 MessagePart 的一部分,嵌套在 data.cardsInfo 字段中传递:
class MessagePart(BaseModel):
"""消息部分内容"""
kind: str = "data" # 类型为 data
data: Optional[dict[str, Any]] = None
# 卡片数据嵌套结构
@property
def cards_info(self) -> Optional[List[CardInfo]]:
if self.data and "cardsInfo" in self.data:
return self.data["cardsInfo"]
return None
kind字段的值决定了消息部分的类型,当kind为"data"时,表示该部分为卡片数据。与文本内容(kind: "text")可以混合输出,实现图文混排效果。
3.3 完整的卡片消息结构
{
"kind": "data",
"data": {
"cardsInfo": [
{
"cardName": "wallpaper",
"cardData": {
"imageUrl": "https://example.com/image.png",
"title": "生成的图片",
"description": "根据您的描述生成"
},
"displayType": "DisplayFaCard"
}
]
}
}
3.4 卡片消息与SSE事件类型对照
在 A2A 协议中,卡片消息主要通过以下 SSE 事件类型传递:
| SSE事件类型 | kind值 | 说明 | 携带卡片? |
|---|---|---|---|
| 状态更新 | status-update | 更新对话状态(进行中/已完成/失败) | 否 |
| 内容更新 | artifact-update | 更新对话内容 | 是(通过parts.data) |
| 任务完成 | task-complete | 标识任务完整结束 | 否 |
卡片数据嵌入在
artifact-update事件的artifact.parts[].data.cardsInfo中,与文本内容可以混合输出,实现图文混排效果。
3.5 在 SSE 事件中发送卡片
data: {
"jsonrpc": "2.0",
"id": "task-001",
"result": {
"taskId": "task-001",
"kind": "artifact-update",
"append": false,
"lastChunk": true,
"final": false,
"artifact": {
"artifactId": "task-001-image",
"parts": [{
"kind": "data",
"data": {
"cardsInfo": [{
"cardName": "wallpaper",
"cardData": {
"imageUrl": "https://example.com/image.png"
},
"displayType": "DisplayFaCard"
}]
}
}]
}
},
"error": {
"code": 0,
"message": "success"
}
}
四、常见卡片类型
4.1 卡片类型对比
鸿蒙智能体支持四种基础卡片类型,开发者可以根据业务需求灵活选用:
| 卡片类型 | cardName | 核心数据 | 适用场景 |
|---|---|---|---|
| 图片卡片 | image | imageUrl, title | 图片展示、壁纸生成 |
| 图文卡片 | image_text | imageUrl, title, description | 图文混排、商品展示 |
| 按钮卡片 | button | title, description, buttons | 操作确认、功能入口 |
| 列表卡片 | list | title, items | 信息列表、菜单展示 |
在实际应用中,这些卡片类型可以组合使用。例如在列表中嵌入按钮卡片,或者图文卡片下方跟随按钮卡片,实现丰富的交互体验。
4.2 图片卡片
用于展示单张图片,适合壁纸生成、照片展示等场景:
def create_image_card(image_url: str, title: str = None) -> dict:
"""创建图片卡片"""
return {
"kind": "data",
"data": {
"cardsInfo": [{
"cardName": "image",
"cardData": {
"imageUrl": image_url,
"title": title or "图片"
},
"displayType": "DisplayFaCard"
}]
}
}
4.3 图文卡片
带标题和描述的图片卡片,适合商品展示、新闻摘要等场景:
def create_image_text_card(
image_url: str,
title: str,
description: str,
) -> dict:
"""创建图文卡片"""
return {
"kind": "data",
"data": {
"cardsInfo": [{
"cardName": "image_text",
"cardData": {
"imageUrl": image_url,
"title": title,
"description": description
},
"displayType": "DisplayFaCard"
}]
}
}
4.4 按钮卡片
带操作按钮的卡片,适合确认操作、功能入口等交互场景:
def create_button_card(
title: str,
description: str,
buttons: List[dict],
) -> dict:
"""创建按钮卡片"""
return {
"kind": "data",
"data": {
"cardsInfo": [{
"cardName": "button",
"cardData": {
"title": title,
"description": description,
"buttons": buttons # [{"text": "按钮文本", "action": "action_name"}]
},
"displayType": "DisplayFaCard"
}]
}
}
按钮配置说明:
- text:按钮上显示的文本,如"确认"、“取消”、“查看详情”
- action:按钮触发的事件动作,在卡片事件配置中定义
4.5 列表卡片
展示多个项目,适合菜单导航、分类列表等场景:
def create_list_card(
title: str,
items: List[dict],
) -> dict:
"""创建列表卡片"""
return {
"kind": "data",
"data": {
"cardsInfo": [{
"cardName": "list",
"cardData": {
"title": title,
"items": items # [{"title": "项目标题", "subtitle": "描述"}]
},
"displayType": "DisplayFaCard"
}]
}
}
五、自定义卡片编辑
鸿蒙智能体开放平台提供了可视化拖拽式卡片编辑器,无需编写代码即可创建精美的交互式卡片。
5.1 编辑界面布局
自定义卡片编辑页面分为三个区域:
| 区域 | 位置 | 功能 |
|---|---|---|
| 功能区 | 左侧面板 | 包含可拖拽的组件列表 |
| 预览画布区 | 中间区域 | 实时展示卡片的视觉效果 |
| 属性配置区 | 右侧面板 | 配置选中组件的属性和样式 |

上图展示了自定义卡片编辑器的三区布局:左侧为功能区、中间为预览画布、右侧为属性配置区。
5.2 编辑组件
开发者可以从左侧功能区拖动组件到中间画布,在画布上选中组件后,右侧会展示该组件的属性配置,修改属性配置可以实时在画布上生效。
组件编辑的核心操作:
- 拖拽组件:从功能区拖拽到画布指定位置
- 选中组件:点击画布上的组件,右侧显示属性
- 调整布局:在结构树上拖拽改变组件父子关系
- 复制/删除:选中组件后,右上角操作图标支持复制和删除
编辑器支持以下高级组件类型:
- 基础组件:图片、文本、图标、按钮
- 布局组件:行布局、列布局、堆叠组件
- 高级组件:动态列表、多页签、选项组、地图、音视频、H5卡片

上图展示了在画布上选中组件后的编辑状态:右侧属性配置区显示该组件的详细属性,可实时修改。
5.3 编辑变量
卡片可能需要根据场景动态调整参数,因此需要定义变量用于在卡片中绑定动态内容。
| 变量类型 | 说明 | 适用场景 |
|---|---|---|
| String | 字符串 | 文本内容绑定 |
| Number | 数字 | 数值显示 |
| Array | 数组 | 列表数据绑定 |
| Object | 对象 | 复杂数据结构 |
| Object(聚合链接) | 特殊链接对象 | 跳转配置 |
| Boolean | 布尔值 | 条件判断 |
定义变量后,可以在组件的属性配置区中绑定对应的变量。绑定变量时,会对不支持的变量类型进行过滤,例如文本组件无法绑定 Array 和 Object 类型的变量。
5.4 事件配置
事件功能使卡片具备交互能力。卡片支持两种触发方式:
- 点击时:用户点击组件时触发
- 加载时:卡片加载完成时自动触发
可配置的执行动作包括:
- 跳转:跳转到指定应用或页面
- 给小艺对话或智能体发消息:触发展开新对话
- 设置变量值:动态更新卡片变量
- 修改组件属性:改变组件的样式和内容
- 切换页面:卡片多页面切换
- 查询应用安装状态:检测应用是否安装
- 调用插件:触发插件执行
- 复制到剪切板:复制文本内容
- 预览图片:放大查看图片
- 删除列表数据:移除列表项
- 操作小艺:调用系统操作
六、卡片显示类型
6.1 显示类型定义
鸿蒙智能体支持多种卡片显示类型,通过 displayType 字段指定:
class DisplayType:
FA_CARD = "DisplayFaCard" # 富媒体卡片
SIMPLE_CARD = "DisplaySimple" # 简单卡片
LIST_CARD = "DisplayList" # 列表卡片
BUTTON_CARD = "DisplayButton" # 按钮卡片
6.2 显示类型对比
| 显示类型 | 适用场景 | 特性 |
|---|---|---|
| DisplayFaCard | 富媒体内容(图文、按钮) | 支持图片、标题、描述、按钮等 |
| DisplaySimple | 简单文本展示 | 仅展示标题和描述 |
| DisplayList | 列表型数据 | 支持多个项目展示 |
| DisplayButton | 按钮导航 | 主按钮+辅助按钮 |
displayType字段决定了客户端如何渲染卡片数据。同一个cardData结构可以通过不同的显示类型呈现不同的视觉效果。实际开发中,建议优先使用DisplayFaCard,它兼容性最好。
七、回复模板配置
7.1 绑定卡片到插件
创建并发布卡片后,需要将卡片与智能体的插件或工作流进行绑定,这样智能体在回复时才能使用该卡片:
- 进入智能体配置页面的"能力扩展"面板
- 在插件区域点击插件参数设置按钮
- 在弹出的窗口中可以看到插件的输出参数
- 点击卡片模板配置按钮,选择已发布的卡片
- 将插件输出参数映射到卡片的变量绑定
7.2 绑定卡片到工作流
工作流的绑定方式与插件类似:
- 在工作流区域找到需要绑卡的工作流
- 点击卡片模板配置按钮
- 选择已发布的卡片模板
- 配置工作流输出参数与卡片变量的映射关系
注意:执行类型为"前台执行"和"卡片执行"的端插件不支持绑卡。
八、在服务层生成卡片
8.1 集成到业务服务
在实际项目中,卡片生成逻辑通常封装在业务服务层,与 AI 模型调用、数据查询等逻辑集成:
# services/assistant_service.py
class AssistantService:
async def generate_with_card(
self,
user_prompt: str,
context_messages: List[Dict] = None,
) -> AsyncGenerator[Dict, None]:
"""生成内容并返回卡片"""
# 第一步:优化 prompt
enhanced_prompt = await self.enhance_prompt(user_prompt, context_messages)
yield {
"type": "status",
"status": "optimizing",
"message": "正在优化提示词..."
}
# 第二步:生成内容
result = await self.call_generation_model(enhanced_prompt)
yield {
"type": "status",
"status": "generating",
"message": "正在生成..."
}
# 第三步:构建卡片数据
card = {
"type": "card",
"card_data": {
"cardsInfo": [{
"cardName": "result",
"cardData": {
"imageUrl": result.get("url"),
"title": "生成结果",
"description": enhanced_prompt[:100] + "..."
},
"displayType": "DisplayFaCard"
}]
}
}
yield card
服务层封装的好处:
- 职责单一:业务逻辑与路由分离,便于测试和维护
- 可复用性:同一服务方法可被多个路由调用
- 流程清晰:状态更新、卡片发送、完成通知的流程一目了然
8.2 卡片数据与知悉同意协议集成
在智能体应用中,卡片不仅可以展示信息,还可以与 A2A协议 中的知悉同意协议集成,实现用户确认交互流程:
{
"kind": "data",
"data": {
"cardsInfo": [{
"cardName": "confirmation",
"cardData": {
"title": "请确认操作",
"description": "您确认要执行以下操作吗?",
"buttons": [
{"text": "确认", "action": "confirm"},
{"text": "取消", "action": "cancel"}
]
},
"displayType": "DisplayFaCard"
}]
}
}
这种交互方式适用于高风险操作确认(如支付、授权等),确保用户明确知晓并同意操作内容。
九、在路由中发送卡片
9.1 SSE 流式发送卡片
卡片数据通过 SSE(Server-Sent Events)协议推送到客户端,可以在路由层通过流式方式发送:
# routes/agent_routes.py
async def handle_message_stream(...):
async def generate_sse():
try:
# 处理业务逻辑
async for result in assistant_service.generate_with_card(
user_text, conversation_contexts[session_id]
):
if result["type"] == "status":
# 发送状态更新
yield create_sse_event(
task_id,
"status-update",
{
"state": "working",
"message": {"text": result["message"]}
}
)
elif result["type"] == "card":
# 发送卡片
yield create_sse_event(
task_id,
"artifact-update",
{
"artifactId": f"{task_id}-card",
"parts": [{
"kind": "data",
"data": result["card_data"]
}]
}
)
# 完成状态
yield create_sse_event(
task_id,
"status-update",
{"state": "completed", "message": {"text": "完成!"}}
)
except Exception as e:
yield create_sse_event(
task_id,
"status-update",
{"state": "failed", "message": {"text": str(e)}}
)
9.2 SSE 发送流程
SSE 流式发送卡片的完整流程包含以下步骤:
- 状态更新:发送
status-update通知客户端开始处理 - 业务处理:调用模型、查询数据、生成卡片内容
- 卡片发送:通过
artifact-update事件携带卡片数据 - 完成通知:发送
status-update标识任务完成
各步骤的状态变化:
{"state": "submitted", "message": {"text": "正在处理..."}} # 已提交
{"state": "working", "message": {"text": "正在生成..."}} # 处理中
{"state": "completed", "message": {"text": "完成!"}} # 已完成
{"state": "failed", "message": {"text": "错误信息"}} # 已失败
十、卡片预览、保存与发布
10.1 卡片预览
自定义卡片创建完成后,在编辑器中点击预览按钮,可在弹出的窗口中预览卡片效果。Web 预览展示了卡片在设备上的预期显示效果。
预览功能至关重要,建议在发布前仔细检查各组件的显示效果,特别是不同屏幕尺寸下的适配情况。

上图展示了卡片的发布管理界面,可预览效果后保存并发布到生产环境。
10.2 保存与发布
预览效果符合预期后,点击保存并发布按钮发布卡片:
- 卡片发布后,在智能体插件和工作流绑卡时,可以关联到已发布的卡片
- 已发布的版本只能查看,不能编辑
- 如需修改已发布卡片,需创建新版本
10.3 卡片版本管理
| 版本状态 | 操作限制 |
|---|---|
| 编辑中(未发布) | 可编辑、可删除 |
| 已发布 | 只能查看,不能编辑 |
| 已关联(被插件/工作流引用) | 不能删除 |
最佳实践:建议在开发环境中先创建卡片并反复预览,确认无误后再发布到生产环境。
十一、卡片升级场景
11.1 版本更新流程
在实际开发中,卡片内容可能需要迭代更新。如果当前插件绑定的是 A 卡片的版本 1,开发者新建了 A 卡片的版本 2,则在回复模板配置中已绑卡界面上会显示"待更新"提示。
点击之后会弹出升级提示,升级之后绑定的参数会被清空,需要开发者重新配置保存。
11.2 升级注意事项
卡片升级操作需要注意以下事项:
- 升级后绑定的参数映射会被清空,需要重新配置
- 建议升级前记录当前参数映射关系,便于快速重新配置
- 生产环境的卡片升级建议在低峰期进行,避免影响用户体验
十二、完整示例:壁纸生成卡片
下面是一个完整的壁纸生成卡片示例,展示了从状态更新到卡片发送的完整流程:
async def generate_wallpaper_card(
user_prompt: str,
session_id: str,
task_id: str,
) -> AsyncGenerator[str, None]:
"""生成壁纸并发送卡片"""
# 1. 提交状态
yield f"data: {json.dumps({
'jsonrpc': '2.0',
'id': task_id,
'result': {
'taskId': task_id,
'kind': 'status-update',
'final': False,
'status': {
'message': {'role': 'agent', 'parts': [{'kind': 'text', 'text': '正在绘制...'}]},
'state': 'submitted'
}
},
'error': {'code': 0, 'message': 'success'}
})}\n\n"
# 2. 生成图片(调用 API)
image_url = await call_image_generation_api(user_prompt)
# 3. 发送卡片
yield f"data: {json.dumps({
'jsonrpc': '2.0',
'id': task_id,
'result': {
'taskId': task_id,
'kind': 'artifact-update',
'append': False,
'lastChunk': True,
'final': False,
'artifact': {
'artifactId': f'{task_id}-image',
'parts': [{
'kind': 'data',
'data': {
'cardsInfo': [{
'cardName': 'wallpaper',
'cardData': {
'imageUrl': image_url,
'title': '壁纸已生成'
},
'displayType': 'DisplayFaCard'
}]
}
}]
}
},
'error': {'code': 0, 'message': 'success'}
})}\n\n"
# 4. 完成状态
yield f"data: {json.dumps({
'jsonrpc': '2.0',
'id': task_id,
'result': {
'taskId': task_id,
'kind': 'artifact-update',
'append': False,
'lastChunk': True,
'final': True,
'artifact': {
'artifactId': f'{task_id}-complete',
'parts': [{'kind': 'text', 'text': '壁纸生成完成!'}]
}
},
'error': {'code': 0, 'message': 'success'}
})}\n\n"
总结
本文从卡片开发的完整生命周期出发,全面介绍了鸿蒙智能体自定义消息卡片的开发流程:
- 卡片概述:了解卡片的概念、优势和适用场景
- 创建卡片:通过标准创建或 AI 生成两种方式在平台创建卡片
- 数据结构:掌握 A2A 协议中的 CardData、CardInfo 数据模型
- 卡片类型:图片、图文、按钮、列表四种基础卡片的使用方法
- 自定义编辑:可视化编辑器的组件、变量和事件配置
- 回复模板配置:将卡片绑定到插件和工作流
- 显示类型:DisplayFaCard 等显示类型的选择
- 服务层集成:在 AssistantService 中生成卡片数据
- SSE 流式发送:在 artifact-update 事件中嵌入卡片数据
- 发布与管理:卡片的预览、保存、发布和版本升级
消息卡片是提升智能体用户体验的重要手段,合理的卡片设计能让信息展示更加直观、交互更加流畅。建议开发者结合具体业务场景,灵活组合不同的卡片类型和交互方式。
在后续文章中,我们将深入介绍自定义插件开发和技能开发,进一步扩展智能体的能力边界。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
更多推荐





所有评论(0)