HarmonyOS 7 新特性(三)封面

本文基于 HarmonyOS 7(API 26)Developer Beta 阶段公开资料,讨论通过 Agent Framework Kit、AgentAbilityExtension 实现端侧 A2A 服务的工程方法。文中的协议对象是便于理解的简化模型,具体字段和接口签名请以当前 SDK 文档为准。

端侧 A2A(Agent to Agent)最吸引人的演示,是一个智能体自动找到另一个智能体并完成任务。但在真实项目里,“调用成功一次”只是起点:能力如何发现、调用方是否可信、长任务如何恢复、重复请求如何处理、用户取消后谁负责收尾,这些才决定它能否上线。

本文用一个旅行场景串起完整链路:行程助手需要调用天气智能体与日历智能体,生成建议后再由用户决定是否写入日程。重点不是某个接口,而是 Agent Card、任务状态机、权限和幂等如何形成闭环。

一、先把协作拆成三段,而不是一条“魔法链路”

一次可靠 A2A 协作至少分为三段:

  1. 发现:读取 Agent Card,判断对方是否真的提供所需技能;
  2. 执行:发送结构化任务,并持续处理运行、完成、失败和取消;
  3. 落地:把结果交给本地领域服务,必要时再次确认后产生副作用。

天气查询是只读动作,写入日历是可逆写入。两者不能因为都发生在一次旅行规划中,就共享同一套授权策略。

子任务数据副作用推荐策略
查询目的地天气城市、日期最小授权,可自动执行
生成行程建议偏好、时间范围数据最小化,结果可解释
写入系统日历标题、地点、时间创建事件展示变更摘要后确认
向同行人发消息联系人、内容对外发送必须明确确认

HarmonyOS 7 新特性核心链路图

二、Agent Card 是版本化契约

官方将 Agent Card 描述为 JSON 元数据文档,用于表达智能体身份、能力、端点、技能与认证要求。它不是产品宣传页,而是客户端决定“能不能调用、应该怎样调用”的依据。

下面是简化示例,字段以当前官方规范为准:

{
  "name": "weather-agent",
  "version": "2.1.0",
  "endpoint": "local://weather-agent",
  "authentication": ["user-consent"],
  "skills": [
    {
      "id": "query-forecast",
      "description": "查询指定城市和日期范围的天气预报",
      "input": ["cityCode", "startDate", "endDate"],
      "output": ["dailyForecast", "sourceTime"]
    }
  ]
}

客户端至少要校验 Agent 身份、Card 版本、目标技能、认证方式和字段兼容性。若服务端删除字段、改变单位或把只读能力改成写操作,应视为契约变化,不能静默升级。

三、不要把 Card 描述直接变成执行权限

“对方声明自己会做什么”与“当前用户允许它做什么”是两件事。发现层只负责能力匹配,策略层必须再次结合用户、设备、数据范围和动作风险做决策。

type CapabilityDecision =
  | { allowed: true; scope: string[] }
  | { allowed: false; reason: 'UNTRUSTED_AGENT' | 'NO_CONSENT' | 'SCOPE_DENIED' }

function authorize(card: AgentCard, task: TaskRequest,
  session: UserSession): CapabilityDecision {
  if (!trustStore.contains(card.identity)) {
    return { allowed: false, reason: 'UNTRUSTED_AGENT' }
  }
  if (!session.scopes.includes(task.requiredScope)) {
    return { allowed: false, reason: 'SCOPE_DENIED' }
  }
  return { allowed: true, scope: [task.requiredScope] }
}

这是领域层伪代码。实际身份与授权应使用官方能力,不要自造加密或把凭据写进 Agent Card。Card 可以说明需要什么认证,但绝不能携带长期令牌。

四、把每次请求建模为任务

官方 A2A 指导包含接收请求、触发业务逻辑、更新任务状态和返回结果。工程上应为每次调用分配 taskId,并维护可穷尽状态,而不是只返回一个 Promise。

RECEIVED
   ↓
VALIDATING ─────→ REJECTED
   ↓
RUNNING ────────→ FAILED
   ├────────────→ CANCELLED
   ↓
COMPLETED

建议约束:终态不可再次迁移;CANCELLED 后业务不得继续提交;COMPLETED 必须有结果摘要;FAILED 必须有稳定错误码和是否可重试标记。

type TaskState =
  | 'RECEIVED' | 'VALIDATING' | 'RUNNING'
  | 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'REJECTED'

interface TaskSnapshot<T> {
  taskId: string
  state: TaskState
  progress?: number
  result?: T
  errorCode?: string
  retryable?: boolean
  updatedAt: number
}

五、AgentAbilityExtension 只做协议入口

在 ArkTS 应用中,A2A 服务端可以通过 AgentAbilityExtension 承接请求。Extension 层不应直接拼页面或写数据库,而应做四件事:解析协议、校验身份、创建任务、交给领域用例。

// 协议骨架:生命周期和回调名称以 API 26 当前文档为准
class TravelAgentEntry {
  async handle(request: A2ARequest): Promise<A2AResponse> {
    const parsed = requestParser.parse(request)
    const decision = policy.authorize(parsed)
    if (!decision.allowed) return responses.rejected(decision.reason)

    const task = await taskStore.createOrGet({
      idempotencyKey: parsed.idempotencyKey,
      skillId: parsed.skillId,
      callerId: parsed.callerId
    })

    taskRunner.start(task.id, parsed.command)
    return responses.accepted(task.snapshot())
  }
}

领域层只认识“查询天气”“创建日历事件”等命令,不认识 A2A 报文。这样协议升级时,只需修改适配层。

六、幂等键必须覆盖副作用

A2A 调用可能出现经典的“不确定成功”:服务端已经写入日历,但结果回传前调用方超时。调用方重试后,如果服务端再创建一次,就会出现重复事件。

async function createCalendarEvent(cmd: CreateEventCommand): Promise<EventResult> {
  const previous = await resultStore.find(cmd.idempotencyKey)
  if (previous) return previous

  const event = await calendar.create(cmd.payload)
  const result = { code: 'OK', eventId: event.id }
  await resultStore.save(cmd.idempotencyKey, result)
  return result
}

生产实现要考虑“副作用成功、结果保存失败”的事务边界。优先使用业务唯一键或可原子提交的存储策略,不要只依赖进程内缓存。

七、长任务要有进度、取消和超时

旅行计划可能依次查询天气、交通和日历。若只有 RUNNING,用户无法判断卡在哪一步。建议对外返回稳定阶段,而不是泄露内部线程或组件名。

progressstage用户可见含义
10validating正在检查日期与权限
35fetching-context正在获取天气与交通信息
70composing正在生成计划草案
90awaiting-confirmation等待确认是否写入日历
100completed已完成

取消不是“把状态改成 CANCELLED”就结束。任务执行器必须感知取消信号,停止后续请求,清理临时数据,并阻止迟到结果覆盖终态。

async function transition(taskId: string, next: TaskState, version: number) {
  const current = await taskStore.get(taskId)
  if (current.version !== version) throw new ConflictError()
  if (isTerminal(current.state)) return current
  assertAllowedTransition(current.state, next)
  return taskStore.update(taskId, next, version + 1)
}

版本号可以阻止并发更新互相覆盖。系统回收进程后,任务也应能从持久化快照恢复或明确失败,不能永久停在 RUNNING

八、返回结构化结果和证据时间

智能体结果不应只有一段自然语言。天气建议至少返回来源时间、城市、日期、结构化预报和置信边界;行程写入则返回事件 ID 和可撤销入口。

interface TravelPlanResult {
  planId: string
  status: 'DRAFT' | 'SAVED'
  days: Array<{ date: string; items: PlanItem[] }>
  evidenceTime: string
  warnings: string[]
  undoToken?: string
}

自然语言摘要可以由展示层生成,但业务真相必须来自结构化结果。过期天气或缺失交通数据要进入 warnings,不能被“计划已生成”掩盖。

九、错误码要让调用方知道下一步

建议错误至少覆盖:未知 Agent、未知技能、版本不兼容、认证失败、授权不足、参数非法、业务冲突、用户取消、执行超时、暂时不可用和内部错误。

{
  "taskId": "task-20260828-001",
  "state": "FAILED",
  "error": {
    "code": "DEPENDENCY_TIMEOUT",
    "retryable": true,
    "retryAfterMs": 3000,
    "userMessage": "天气服务暂时无响应,可稍后重试"
  }
}

内部堆栈不能直接返回给另一个 Agent;对外保留稳定语义,对内用 traceId 关联详细日志。

十、可观测性要贯穿整个调用链

一次 A2A 协作建议统一关联 traceIdtaskIdcallerAgentIdskillId、Card 版本和幂等键摘要。指标不要只看总耗时,而要拆为发现、鉴权、排队、执行、确认等待和结果封装。

敏感内容只记录类型、长度或哈希摘要,不记录联系人、精确行程和令牌正文。调试便利不能凌驾于用户隐私。

十一、测试矩阵必须包含乱序和恢复

describe('A2A task state', () => {
  it('does not let a late success override cancellation', async () => {
    await store.transition(id, 'RUNNING')
    await store.transition(id, 'CANCELLED')
    await runner.reportCompleted(id, fixtureResult)
    expect((await store.get(id)).state).toBe('CANCELLED')
  })

  it('reuses the result for the same idempotency key', async () => {
    const a = await entry.handle(fixtureRequest)
    const b = await entry.handle(fixtureRequest)
    expect(b.taskId).toBe(a.taskId)
    expect(calendar.createCount).toBeLessThanOrEqual(1)
  })
})

除成功路径外,还要覆盖:伪造 Card、技能不存在、权限过期、参数边界、用户中途撤回授权、依赖超时、应用被回收、结果乱序、重复请求和不支持 API 26 的设备。

十二、上线检查清单

  • Agent Card 只声明真实上线并经过测试的技能;
  • 客户端校验身份、版本、认证方式和字段兼容性;
  • 能力发现不直接等价为用户授权;
  • 每次请求都有 taskId、幂等键和可追踪终态;
  • 取消、超时、恢复和迟到结果都有确定规则;
  • 写操作在提交前展示变更摘要,并提供撤销或补偿;
  • 结果是结构化数据,包含证据时间和警告;
  • 日志可关联全链路,同时完成敏感信息最小化;
  • 真机验证前台、后台、进程回收与多 Agent 并发。

HarmonyOS 7 新特性落地验收清单

结语

端侧 A2A 的价值,是让应用从孤立入口变成可协作的能力节点;它的工程难点,则是把“智能调用”约束为一套可验证的分布式任务。把 Agent Card 当版本化契约、把请求当状态机、把副作用放进幂等领域服务,再补齐取消、恢复和审计,A2A 才能从演示走向生产。

官方参考

  • 通过 AgentAbilityExtension 实现智能体间 A2A 通信:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/hmaf-a2a-dev-guide
  • HarmonyOS 7 新能力一览:https://developer.huawei.com/consumer/cn/features/
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐