前言

在鸿蒙智能体应用中,除了纯文本回复外,经常需要展示结构化交互内容,如商品展示、订单信息、壁纸生成结果等。消息卡片(DisplayFaCard)提供了一套标准化的富媒体消息机制,让智能体的回复更加丰富和直观。

本文将从卡片创建 → 数据结构 → 开发编辑 → 发送集成 → 发布管理的完整生命周期出发,详细介绍如何在鸿蒙智能体中开发自定义消息卡片。文章涵盖平台操作流程与底层协议实现,帮助开发者全面掌握卡片开发技能。

一、卡片概述

1.1 什么是消息卡片

消息卡片是一种结构化消息展示形式,以卡片容器承载图片、文本、按钮、列表等多种元素,通过 A2A 协议的 artifact-update 事件传递给客户端渲染。

消息卡片示意图

上图展示了鸿蒙智能体应用中的卡片消息效果,图片和文本内容以卡片形式混合呈现。

1.2 卡片的核心优势

与纯文本回复相比,消息卡片具有以下优势:

  1. 丰富的展示形式:支持图片、按钮、列表、地图、音视频等多种组件
  2. 结构化数据:便于前端解析和渲染,支持变量绑定动态数据
  3. 统一的交互模式:标准化的事件配置(点击跳转、发送消息、调用插件等)
  4. 更好的用户体验:图文混排比纯文本更直观、更美观
  5. 可复用性强:卡片模板支持多插件/工作流绑定,一次创建多处使用

卡片功能的适用场景非常广泛:

  • 内容展示型:壁纸生成结果、新闻摘要、商品详情
  • 操作确认型:支付确认、授权确认、风险提示
  • 信息导航型:菜单列表、分类导航、快捷入口
  • 媒体播放型:音乐播放控制、视频预览

二、创建和管理卡片

2.1 新建卡片

在鸿蒙智能体开放平台中,进入【工作空间】→【卡片】→【新建卡片】,填写卡片信息后即可创建。卡片创建提供两种方式

创建方式 适用场景 特点
标准创建 从零开始设计卡片 完全自定义,灵活性最高
AI生成创建 基于示例图片快速生成 上传图片+描述,1~3分钟自动生成

卡片创建注意事项:

  • 标准创建后可通过自定义卡片编辑进一步调整组件布局
  • AI生成创建的图片需满足:10×10~1024×1024像素,大小不超过5MB,支持JPG/PNG格式
  • AI生成的卡片可能与原图在结构和样式上有差异,需确认后手动调整

新建卡片界面

AI生成创建特别适合快速原型验证,开发者上传设计稿图片即可获得可编排的卡片组件结构。

2.2 卡片管理

卡片创建完成后,在卡片列表中可以执行以下操作:

  1. 查看:查看已创建的卡片详情
  2. 编辑:修改卡片组件布局和配置(已发布版本只能查看,不能编辑)
  3. 删除:删除未关联的卡片(已被任何插件或工作流关联的卡片不能删除)
  4. 发布:将卡片发布到生产环境

卡片管理列表

三、卡片数据结构

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"
            }]
        }
    }

按钮配置说明:

  1. text:按钮上显示的文本,如"确认"、“取消”、“查看详情”
  2. 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 编辑组件

开发者可以从左侧功能区拖动组件到中间画布,在画布上选中组件后,右侧会展示该组件的属性配置,修改属性配置可以实时在画布上生效

组件编辑的核心操作:

  1. 拖拽组件:从功能区拖拽到画布指定位置
  2. 选中组件:点击画布上的组件,右侧显示属性
  3. 调整布局:在结构树上拖拽改变组件父子关系
  4. 复制/删除:选中组件后,右上角操作图标支持复制和删除

编辑器支持以下高级组件类型:

  • 基础组件:图片、文本、图标、按钮
  • 布局组件:行布局、列布局、堆叠组件
  • 高级组件:动态列表、多页签、选项组、地图、音视频、H5卡片

组件编辑操作示意

上图展示了在画布上选中组件后的编辑状态:右侧属性配置区显示该组件的详细属性,可实时修改。

5.3 编辑变量

卡片可能需要根据场景动态调整参数,因此需要定义变量用于在卡片中绑定动态内容。

变量类型 说明 适用场景
String 字符串 文本内容绑定
Number 数字 数值显示
Array 数组 列表数据绑定
Object 对象 复杂数据结构
Object(聚合链接) 特殊链接对象 跳转配置
Boolean 布尔值 条件判断

定义变量后,可以在组件的属性配置区中绑定对应的变量。绑定变量时,会对不支持的变量类型进行过滤,例如文本组件无法绑定 Array 和 Object 类型的变量。

5.4 事件配置

事件功能使卡片具备交互能力。卡片支持两种触发方式:

  • 点击时:用户点击组件时触发
  • 加载时:卡片加载完成时自动触发

可配置的执行动作包括:

  1. 跳转:跳转到指定应用或页面
  2. 给小艺对话或智能体发消息:触发展开新对话
  3. 设置变量值:动态更新卡片变量
  4. 修改组件属性:改变组件的样式和内容
  5. 切换页面:卡片多页面切换
  6. 查询应用安装状态:检测应用是否安装
  7. 调用插件:触发插件执行
  8. 复制到剪切板:复制文本内容
  9. 预览图片:放大查看图片
  10. 删除列表数据:移除列表项
  11. 操作小艺:调用系统操作

六、卡片显示类型

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 绑定卡片到插件

创建并发布卡片后,需要将卡片与智能体的插件或工作流进行绑定,这样智能体在回复时才能使用该卡片:

  1. 进入智能体配置页面的"能力扩展"面板
  2. 在插件区域点击插件参数设置按钮
  3. 在弹出的窗口中可以看到插件的输出参数
  4. 点击卡片模板配置按钮,选择已发布的卡片
  5. 将插件输出参数映射到卡片的变量绑定

7.2 绑定卡片到工作流

工作流的绑定方式与插件类似:

  1. 在工作流区域找到需要绑卡的工作流
  2. 点击卡片模板配置按钮
  3. 选择已发布的卡片模板
  4. 配置工作流输出参数与卡片变量的映射关系

注意:执行类型为"前台执行"和"卡片执行"的端插件不支持绑卡。

八、在服务层生成卡片

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

服务层封装的好处:

  1. 职责单一:业务逻辑与路由分离,便于测试和维护
  2. 可复用性:同一服务方法可被多个路由调用
  3. 流程清晰:状态更新、卡片发送、完成通知的流程一目了然

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 流式发送卡片的完整流程包含以下步骤:

  1. 状态更新:发送 status-update 通知客户端开始处理
  2. 业务处理:调用模型、查询数据、生成卡片内容
  3. 卡片发送:通过 artifact-update 事件携带卡片数据
  4. 完成通知:发送 status-update 标识任务完成

各步骤的状态变化:

{"state": "submitted", "message": {"text": "正在处理..."}}  # 已提交
{"state": "working", "message": {"text": "正在生成..."}}      # 处理中
{"state": "completed", "message": {"text": "完成!"}}          # 已完成
{"state": "failed", "message": {"text": "错误信息"}}           # 已失败

十、卡片预览、保存与发布

10.1 卡片预览

自定义卡片创建完成后,在编辑器中点击预览按钮,可在弹出的窗口中预览卡片效果。Web 预览展示了卡片在设备上的预期显示效果

预览功能至关重要,建议在发布前仔细检查各组件的显示效果,特别是不同屏幕尺寸下的适配情况。

卡片预览与发布

上图展示了卡片的发布管理界面,可预览效果后保存并发布到生产环境。

10.2 保存与发布

预览效果符合预期后,点击保存并发布按钮发布卡片:

  1. 卡片发布后,在智能体插件和工作流绑卡时,可以关联到已发布的卡片
  2. 已发布的版本只能查看,不能编辑
  3. 如需修改已发布卡片,需创建新版本

10.3 卡片版本管理

版本状态 操作限制
编辑中(未发布) 可编辑、可删除
已发布 只能查看,不能编辑
已关联(被插件/工作流引用) 不能删除

最佳实践:建议在开发环境中先创建卡片并反复预览,确认无误后再发布到生产环境。

十一、卡片升级场景

11.1 版本更新流程

在实际开发中,卡片内容可能需要迭代更新。如果当前插件绑定的是 A 卡片的版本 1,开发者新建了 A 卡片的版本 2,则在回复模板配置中已绑卡界面上会显示"待更新"提示。

点击之后会弹出升级提示,升级之后绑定的参数会被清空,需要开发者重新配置保存。

11.2 升级注意事项

卡片升级操作需要注意以下事项:

  1. 升级后绑定的参数映射会被清空,需要重新配置
  2. 建议升级前记录当前参数映射关系,便于快速重新配置
  3. 生产环境的卡片升级建议在低峰期进行,避免影响用户体验

十二、完整示例:壁纸生成卡片

下面是一个完整的壁纸生成卡片示例,展示了从状态更新到卡片发送的完整流程:

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"

总结

本文从卡片开发的完整生命周期出发,全面介绍了鸿蒙智能体自定义消息卡片的开发流程:

  1. 卡片概述:了解卡片的概念、优势和适用场景
  2. 创建卡片:通过标准创建或 AI 生成两种方式在平台创建卡片
  3. 数据结构:掌握 A2A 协议中的 CardData、CardInfo 数据模型
  4. 卡片类型:图片、图文、按钮、列表四种基础卡片的使用方法
  5. 自定义编辑:可视化编辑器的组件、变量和事件配置
  6. 回复模板配置:将卡片绑定到插件和工作流
  7. 显示类型:DisplayFaCard 等显示类型的选择
  8. 服务层集成:在 AssistantService 中生成卡片数据
  9. SSE 流式发送:在 artifact-update 事件中嵌入卡片数据
  10. 发布与管理:卡片的预览、保存、发布和版本升级

消息卡片是提升智能体用户体验的重要手段,合理的卡片设计能让信息展示更加直观、交互更加流畅。建议开发者结合具体业务场景,灵活组合不同的卡片类型和交互方式。

在后续文章中,我们将深入介绍自定义插件开发技能开发,进一步扩展智能体的能力边界。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

Logo

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

更多推荐