小艺端侧 A2A 接入:训练助手的轻量落地

Feng 10 阅读 移动端

小艺端侧 A2A 接入:训练助手的轻量落地

本文以运动训练助手为例,说明如何复用现有业务接口来接入端侧 A2A。把协议适配置于应用层,让已有的登录态和业务服务直接承接小艺的请求,可以减少后端改造的工作量。应用内的 Agent 通过独立的初始化恢复必要状态,保证在没有页面的场景下仍然能正确响应冷启动任务。会话管理需要区分不同标识的生命周期,避免账号切换或并发请求引发数据交叉污染与状态异常。最终,答案、状态与建议项分步收尾,并通过严格的连接代次检查阻断失效结果干扰当前会话。

前言:让一次对话真正完成,复用现有业务接口,完成改造。

一个训练助手的接入思路:把协议适配放在应用里,让已有业务链路继续工作

从训练助手的实际接入说起:冷启动、任务收尾、协议回包与应用内交接

背景:APP 内已经有成熟的智能体相关业务接入,需要再接入小艺 A2A,并且详细评估各项方案后,决定接入端侧 A2A。

最初看到”端侧 A2A”,我第一反应是敏感数据:有些内容适合留在设备里处理,应用在端内完成必要判断,再提供允许输出的结果。华为的能力介绍也强调了响应体验与端侧隐私安全。沿着这个方向理解它,很自然。

但把训练助手接入小艺时,我发现还有一个很实际的价值。应用里本来就有登录态恢复、训练业务请求、会话管理和结果页面。用户换成从小艺发起问题后,这些能力仍然用得上。接入的重点可以放在应用内:接住小艺请求,调用已经存在的业务服务,再把结果整理为 A2A 的产物和状态。

这让端侧 A2A 成为一种值得考虑的轻量接入方式:对于已经具备成熟 App 和业务接口的团队,可以由应用承接协议适配,减少后端为了新增入口而做的改造。模型或其他业务计算仍然可以留在原有后端,端侧负责把这个入口接到已有能力上。

本文想展开的正是这个思路,以及落地后暴露出来的问题。轻量的前提是什么,哪些东西确实能复用,哪些工作仍然要补;当答案已经生成时,又怎样让小艺收到正确的结果、结束任务,并让用户回到应用继续处理。

案例来自一个运动训练助手的实际接入实现。产品名称、包名、Agent 标识、链接、账号和业务数据均已替换或省略;自然语言请求为构造示例,机制图为重绘。有关客户端呈现的经验来自实现中保留的联调备注,需要结合目标版本复核。

端侧入口、计算位置和数据边界,可以分开考虑

读完框架说明后,我对“最初是为敏感数据设计”的理解做了一点修正。官方把端侧 A2A 放在标准化通信、跨应用协作和端云互调的背景下介绍。隐私是它的重要使用价值,但现有资料不足以把它的设计初衷限定为“只处理不能出端的数据”。端侧 A2A 框架概述

对接入设计来说,更有帮助的是把三个决定拆开看:小艺请求先由哪里承接,业务计算在哪里完成,哪些数据允许经过哪一段链路。

本文选择由应用内 Agent 承接小艺请求。训练建议仍可以通过应用已有的业务 API 生成,计算位置没有因此被强制改到设备上。同时,应用可以只读取完成任务必需的本地状态,按原业务接口要求发起请求,并从业务结果中选择适合返回给小艺的内容。

因此,同一条端侧 A2A 路径里,可能同时存在留在本地的草稿、允许发送到业务后端的提问,以及允许呈现给小艺的结果。是否“不出端”,要逐项看真实数据流,不能用接入模式的名称代替判断。返回给小艺也已经跨过应用自身的边界,小艺后续怎样处理这些内容,需要按平台约定确认。

这里的思路转变,是把关注点从“哪些数据需要留在端内”,扩展到“哪些能力已经在 App 里,能直接服务于新的系统入口”。隐私约束和接入成本可以一起考虑,不必为一种价值放弃另一种。

轻量来自复用,适配放在哪里要选清楚

在训练助手原来的使用路径中,页面负责收集问题和展示答案,业务 Repository 负责请求与结果转换,认证基础设施负责账号状态和访问凭据。接入后,A2A 会话服务构造业务请求,仍然调用现有 Repository 的 chat 方法;Repository 再经过应用的 API 客户端访问业务服务。这是当前代码里能直接核对的一条复用关系。

新增的是调用入口及其适配。原来的“用户打开页面、填写问题”,增加了“小艺发起请求、应用内 Agent 处理”的路径;两条路径可以使用已有业务模块,不需要为新入口复制一套业务 API。A2A 会话服务仍有自己的会话映射、任务控制与结果呈现逻辑,所以这里的复用也不意味着两个入口的整个流程完全相同。

图1

图 1 两个入口复用应用业务模块,由应用侧承担 A2A 适配

我用“轻量”描述的是新增改造的范围。平台连接、协议消息、Task 生命周期和展示转换主要集中在应用侧;原有后端可以继续处理熟悉的业务请求,无须先理解这条入口的整套 A2A 任务状态。新入口如需补充来源字段、审计、权限或幂等能力,仍然要按实际缺口修改后端。

处理位置

可复用的已有能力

A2A 接入需要补的部分

账号与权限

认证基础设施、服务端校验

冷启动恢复、入口授权和账号切换检查

业务请求

Repository、请求模型与 API 客户端

小艺输入到业务请求的映射

会话与任务

业务 session、已有结果模型

contextId 关联、取消和任务终态

结果与页面

结构化答案、既有详情与编辑页面

结果裁剪、Part 转换和跳转交接

服务端

既有业务 API、规则与计算服务

按需补权威额度、幂等、审计等缺口

这种复用还有一个长期收益:业务规则不必因为入口增加而各维护一份。比如训练结果的字段映射和业务错误处理集中在 Repository,调整时可以同时服务页面与 A2A。两个入口独有的交互规则则各自维护,避免把小艺的任务状态带进原页面的展示逻辑。

账号复用也有边界。应用自己使用现有认证能力访问自己的后端,不代表要把访问令牌交给小艺;用户已经登录应用,也不自动代表允许对话入口读取所有个人资料。这里复用的是经过校验的业务访问路径,仍要限制本次任务使用和返回的数据。

仅从应用侧调用关系,可以证明这条路线具备复用现有后端的条件,却不能据此证明整个项目的后端“零改动”。本文没有做前后端完整提交差异审计,也没有测量节省了多少人天,因此把结论收在可核对的范围内:减少为新入口重复建设业务链路的需要。

把改造放在端侧,也就接过了端侧的成本

这条路线尤其适合这样的应用:业务接口已经稳定,认证和请求处理能够脱离页面运行,结果能整理成明确的数据结构,用户需要在手机上发起请求、查看结果或继续编辑。能力主要沉淀在 App 中时,让应用来承接系统入口,往往比先把这些状态和业务编排搬到另一层更直接。

如果团队已经有成熟的云端 Agent 和对外服务能力,云 A2A 同样可以复用已有后端;它并不天然要求重写业务。需要跨多个客户端统一提供服务、依赖持续运行的任务,或能力主要集中在云端时,应该评估云端承接的方案和对应的平台支持,不能只因为“端侧听起来轻”就把处理强行塞进应用。

如果业务逻辑还散落在页面回调里,第一步通常是提取可独立调用的用例与服务。这个整理成本真实存在。接入后还要面对组件冷启动、进程回收、设备网络、系统与小艺版本差异,以及应用发版节奏。后端少承担一部分协议适配,应用就要把这部分运行条件管起来。

更容易低估的是任务语义。用户在小艺里取消了生成,不代表一个已经发到后端的请求也被撤销了。客户端可以拒绝迟到的答案,涉及扣费、额度、保存或其他写操作时,仍需要服务端的权威约束;把入口放在端侧不会取消这些要求。现有代码也明确保留了跨入口额度需要后端兜底的边界。

下面这些看似琐碎的联调问题,正是这条轻量路线的实际成本。我希望把它们讲清楚,让“可以复用”落成一条能工作的调用链,而不是停在架构图上。

先跑通一条够小的链路

我把最小接入场景收在一句话里:“帮我安排一次二十分钟的基础步法练习。”先让小艺收到一段完整文字,再增加追问建议与应用跳转。这句话足以走过请求、初始化、业务执行、产物返回和任务结束,也不会一开始就把自定义卡片、长任务和复杂权限混在一起。

平台侧先创建端 A2A 模式的 Agent、关联应用并导入 AgentCard;应用侧注册 AgentExtensionAbility,让 metadata 指向 AgentCard 配置。AgentCard 中的标识、平台导入的标识和实际连接使用的标识要能对应起来。能力描述先写清可处理的任务和输入边界,不把尚未实现的技能写进去。端 A2A 模式配置

应用内这几个文件有不同职责:module.json5 注册组件,agent_config.json 声明能力,AgentExtensionAbility 接收请求并回传数据。自定义卡片需要的 AgentUIExtensionAbility 是按需实现项;本文先使用文字、建议项和跳转,不依赖它完成首轮接入。应用内 Agent 开发接入

版本也要先对齐。按本次核对的本机官方文档,端侧 A2A 框架从 API 24 开始;Agent Framework Kit 的 createA2AServer 服务端封装标注起始版本为 26.0.0。本文展示的是后一套封装的使用关系,不能把最低版本写成 API 23 就认为它可用。平台能力开放、设备系统、小艺客户端和本地 SDK 都需要匹配;文章配套的逻辑回归也不等于已经在小艺真机环境跑通。端侧 A2A 框架概述、A2A 服务端开发指导

第一处改造:让 Agent 在没有页面时也能工作

原来从应用页面进入训练助手时,很多前提已经成立:偏好设置读过了,登录态恢复过了,会员权益也可能查过了。AgentExtensionAbility 被系统拉起时,这些事情不一定发生过,甚至不能假设它和 UIAbility 共享同一份内存。

复用既有 App 能力,首先要把它们从“页面已经启动”的前提中解放出来。如果把整套页面初始化照搬过来,又会带上与当前请求无关的媒体、消息和界面工作。现有实现采用独立的 headless 初始化,只准备认证、偏好设置、训练业务所需的状态,以及交接存储。没有页面不应该影响回答一条训练问题。

初始化本身也有一个容易忽略的竞态:首轮对话和感知推荐可能紧挨着到达。两个请求不应该各自启动一次恢复流程。处理方式是保存一份正在执行的 Promise,让并发请求等待它;只有成功才设置 initialized,失败后清空这份 Promise,允许下一次重试。

// 关系示意;完整可执行逻辑见配套 HeadlessGate.ets。
if (initialized) return;
if (inFlight !== null) return inFlight;
inFlight = initializeRequiredServices();
try {
  await inFlight;
  initialized = true;
} finally {
  inFlight = null;
}

这份代码还有一处具体取舍:已登录用户的权益状态,需要在相关业务判断前恢复。否则一次冷启动可能把状态尚未同步的用户当成没有权益。这里不需要让每个请求都重新查一遍全部服务,而是明确哪些前置状态影响本次决策,并把它们放进同一条等待链。

初始化成功也不意味着请求一定还有效。等待结束后,业务开始前还要检查任务是否被取消;业务调用返回后再检查一次。每个 await 都可能跨过一次用户操作。

把会话、任务和消息分开管理

接入后最容易写乱的是各种 ID。JSON-RPC 的 id、contextId、taskId、messageId,以及业务服务自己的 sessionId,看上去都能用来“关联一次对话”,实际生命周期并不相同。

标识

在本文实现里的用途

不应代替什么

JSON-RPC id

将回包归属到原请求

业务会话或全局 trace

contextId

关联多轮会话上下文

已登录用户身份

taskId

管理一次任务、取消及终态

每条展示消息的 ID

statusMessageId

更新同一任务的状态展示

所有任务共用的固定消息 ID

业务 sessionId

继续业务侧会话

平台 taskId

训练助手需要多轮交流,不能每次收到 Execute 都开一个与上一轮无关的业务会话。现有实现使用 contextId 的摘要做本地索引,并同时检查当前用户、运动类型和业务 sessionId。换账号之后,旧请求不能把上一位用户的会话或训练草稿写回当前状态。

为此,存储逻辑在异步边界前后检查账号和会话代次。这里的代次是应用内部的变化计数:一次请求拿到的是旧代次,就不能覆盖切换账号后的新状态。仅检查“请求发起时用户已登录”是不够的。

摘要只是避免直接保存原始 contextId,不是加密,也不是权限校验。现有实现的会话缓存还有数量与有效期限制,这些都属于应用的存储策略。它们不应包装成 A2A 协议要求,更不能用 contextId 的相同来证明两个请求属于同一个业务用户。

答案、状态和建议项,要分别收尾

这次整理中最值得单独讲的一处,是代码里保留的“答案已返回,思考状态仍残留”的处理。

业务上,一段训练建议生成完了;协议上,Agent 还需要提交 Artifact,并将 Task 收到终态;界面上,小艺还要把对应的状态展示更新掉。这三件事发生在不同层。只看到 addArtifact 被调用,不能说明客户端已经完成整轮展示。

现有实现给每个 taskId 分配一个 statusMessageId,并在该任务的状态更新中重复使用它。代码中的联调备注指出,每次更新都换一个 messageId,会让某些客户端把“正在思考”保留成另一条独立消息。这个处理针对的是状态展示身份;答案 Artifact、后续任务及其他独立消息仍有各自的 ID。

对正常完成的训练请求,我把这份实现的收尾顺序整理为:先写最终文字 Artifact,再尝试添加可选建议项;如果需要展示跳转提示,先发一次非终态状态更新;最后仅发送一次 COMPLETED。可选建议项失败会被记录,但不应阻止已经成功交付的答案结束任务。

// SDK 调用关系摘录;省略具体类型构造与业务执行。
writeFinalTextArtifact(task, answer);
try { writeOptionalSuggestions(task, suggestions); }
catch (_) { recordOptionalUiFailure(); }
if (jump !== undefined && task.canWrite()) {
  writeStatus(task, TaskState.WORKING, jump);
}
if (task.canWrite()) {
  writeStatus(task, TaskState.COMPLETED);
  task.markTerminal();
}

为什么跳转提示放在终态之前?这份实现的联调备注指出,目标客户端链路在完成事件中可能省略状态 message。若依赖完成事件携带最后一次可见信息,这段信息就可能丢失。于是先发送需要呈现的状态,再结束任务。它是一条需要随 SDK 与客户端版本复核的兼容性处理,不是“所有完成事件都不能带 message”。

同样,现有代码将最终文字 Artifact 保持为 text/plain,把建议项作为另一个可选产物,避免在目标客户端上因混合内容呈现留下等待状态。这不等于协议禁止混合 Part。遇到新版本时,应重新抓取结构证据和观察界面,再决定是否保留这种拆分。

终态以后不再“补一个 WORKING”。如果登录是继续业务的前提,而当前实现没有恢复同一 A2A Task 的能力,就明确返回登录引导并结束本轮。登录后重新发起或衔接新业务请求,比把一个无法恢复的任务长期留在认证等待状态更诚实。

联调要看最终发出的报文

还有一种情况更隐蔽:构造 Artifact 或 Message 时已经附上 metadata,客户端需要的外层位置却仍没有对应信息。原因在于 SDK 会序列化并包装响应,传入 API 的对象并不等于 proxy.sendData 最终发送的字符串。

排查点应该放在 server.onMessage 的 sender 回调中。这里拿到的是实际准备发给小艺的响应,可以核对 result 的种类、状态、Part 类型,以及 metadata 究竟在哪一层。业务代码里的“写成功”日志只能证明本地走过某一行。

// 省略错误处理;sender 绑定当前请求与连接代次。
const envelope = RequestEnvelope.forRequest(rawRequest);
const connectionId = connections.get(proxy);
const serving = this.server;
serving.onMessage(rawRequest, (rawResponse: string): void => {
  if (this.server !== serving) return;
  if (connections.get(proxy) !== connectionId) return;
  const outbound = envelope.apply(rawResponse);
  recordStructureOnly(outbound);
  proxy.sendData(outbound);
});

这份实现加了一层很薄的请求级 envelope:从请求中只选取通过类型、长度和控制字符检查的 traceId、agentId;仅当响应 id 与原请求严格相等、属于已知 result 形态、且不是 JSON-RPC 错误时,才补齐缺失的外层 metadata。SDK 已有的字段要保留,遇到身份冲突或未知结构就不改写。

两个细节值得保留下来。第一,不能把“最近一次请求的 traceId”放进全局变量。并发请求的回调可以交错到达,这样很容易把 A 的答案归到 B。第二,traceId 不一定是 UUID;不应为了日志方便把一个合法的关联标识重新格式化。传输时保留校验通过的原值,日志里只记录字段是否存在。

补 envelope 不是接入的必做仪式。先确认目标 SDK 最终回包确有缺失,并且平台确实要求该位置,再引入这层适配。如果未来 SDK 已经补齐,测试应证明适配器不会覆盖它;没有证据就改写整个协议对象,反而会扩大问题。

图2

图 2 同一次业务结果,要通过任务与连接两道检查

取消任务以后,晚回来的答案也要停住

取消有两个层面:让业务尽量停止,以及禁止已经失效的结果继续影响当前会话。如果底层请求不支持立即中止,至少要做到第二件事。

当前实现为每个任务保存 canceled 与 terminal 状态。Cancel 到达后标记取消,并在允许的情况下提交 CANCELED;初始化、业务响应、可选 UI 写入等后续路径都检查这个标记。终态后的再次取消没有必要重开一个状态流程,重复完成也要被挡住。

连接的生命周期是另一条线。小艺断开后,旧 sender 闭包可能还活着;重连甚至可能出现复用的代理对象。只有比较代理对象本身还不够,所以现有实现给连接登记递增代次,发送时同时检查服务实例、代理和代次。旧连接返回的结果不会因为“刚好又连上了”就混进新会话。

这里也不能夸大现有保护:任务 Map 内的去重以及完成后一段时间的保留,只覆盖进程内、有限时段。它不是跨重启、永久的业务幂等保证。对“保存训练计划”之类写操作,仍需使用业务自己的幂等键和持久化约束。

异常路径要与这些状态规则一起检查。业务失败应进入失败终态;组件销毁要使任务和连接失效;可选 UI 失败不应把已成功的答案留在 WORKING。至于 sendData 调用返回,只能说明本地完成发送调用,不能单凭它断言小艺已经渲染。

进入应用时,链接只带交接凭证

应用复用还有一个直接价值:小艺里完成提问,细致编辑可以继续交给已有的应用页面。训练建议显示完以后,用户可能还想在应用里继续编辑。直接把问题、整份训练草稿和用户标识塞进 DeepLink 很省事,却会让真实业务数据出现在路由、诊断信息和可能流转的链接里。

这份实现采用另一种交接:Agent 把草稿留在应用沙箱的跨进程存储中,链接只携带目标类型和随机 nonce。UIAbility 启动后,用 nonce 找到记录,再检查目标、有效期、归属用户和消费状态。本文示例使用 trainingdemo://agent/open?target=plan&nonce=<随机值>,其中 scheme、包名和随机值都是示例,不对应线上应用。

AgentExtensionAbility 与 UIAbility 不共享一份普通单例,因此这段数据不能只放在内存里。现有实现选择 Preferences 的 GSKV 存储类型,并先检查支持情况;不可用时禁用相关交接能力,不伪装成一次成功跳转。

有了跨进程可见性,也还没有自动得到跨进程事务。代码中的 Promise 队列只串行化当前进程的修改,不能充当所有进程共享的锁。单消费者的 UI 路径可以利用它约束本地重复操作;如果业务要求多个进程竞争下的严格“一次消费”,需要有原子条件更新或事务能力的存储设计。不能把一组 get/put 加上内存锁就宣布解决了这个问题。

消费的时机也要对齐用户看到的结果:读出草稿、页面准备好并确认接管,是一条完整链路。若路由失败,要按业务规则释放本次占用或提供重试入口,避免用户点一次链接就永久丢掉草稿。跨越登录或账号变化时,需要重新校验归属,不能沿用此前读到的对象。

在呈现层,业务返回的自定义 actions 不会自动成为小艺支持的操作。当前实现将建议项与跳转映射到目标客户端识别的 Part.data 结构:对话后的建议项使用 suggestions,感知推荐使用 suggestionCandidates,跳转使用独立 jump 数据。它们是不同场景的载体,字段名和层级应结合当时平台规范与实际 sender 输出核对。

应用拉起还受系统组件启动规则约束。不要把业务中一次 WantAgent 调用的成功,写成任意后台场景都可以直接弹出界面。优先把可用操作交给小艺呈现,并验证用户触发后的冷启动、热启动和页面就绪路径。

保留排障证据,也给日志做减法

为了排查“不显示”和“不结束”,最容易临时加上一行 JSON.stringify(request)。但 A2A 请求里可能有用户问题、上下文、鉴权内容或链接。整包记录下来,再等文章发布时打码,处理范围会越来越大。

现有诊断器从采集阶段就只输出结构:已知字段是否存在、Part 的类型、建议项数量、状态枚举、metadata 的存在性。连字段名也使用允许列表,防止自定义键本身携带业务信息。数据是字符串、对象还是数组,可以记录;字符串里面具体是什么,不记录。

下面是按这种规则构造的诊断样例(不是真实日志):

stage=execute_received taskPresent=true tracePresent=true
stage=artifact_written textParts=1
stage=status_written state=completed terminal=true
stage=wire_checked resultKind=statusUpdate metadataPresent=true
stage=send_called currentConnection=true

它已经足够回答几类问题:请求是否到达,答案是否写入,任务是否收尾,最终报文带了什么结构,发送时连接是否仍有效。真要定位展示问题,再结合当前客户端的屏幕录制和时间顺序;公开材料只保留重绘示意与合成样例。

日志中的长度单位同样要准确。JavaScript 的 string.length 是 UTF-16 码元数量,不能直接标成网络字节数。若协议限制的是 UTF-8 字节,应按编码后的字节数判断。本文配套 envelope 的上限是应用级字符串长度保护,不冒充平台协议的字节配额。

用失败场景决定接入是否完成

首轮文字返回成功以后,我会优先验证下面这些分支。它们比再换十种正常提问,更容易暴露链路里的问题。

场景

观察位置

通过条件

应用未启动时发起对话

初始化与业务入口

必要状态就绪后执行,没有 UI 前置依赖

初始化失败后重试

初始化 Promise

失败不永久缓存,后续请求能再次初始化

生成中取消

任务与最终 sender

不再产生迟到答案,不重复进入终态

断开后重连

连接代次

旧连接闭包不能向新会话发送结果

两个请求交错返回

回包 id 与 metadata

各自关联到原请求,不使用全局最近值

答案与建议项分别失败

Artifact 与 Task 状态

主结果失败可见,可选 UI 失败不悬挂任务

登录或切换账号后继续

会话存储与交接消费

旧请求不能覆盖新账号状态

连续点击、过期或错误目标链接

交接校验与页面确认

拒绝无效交接,失败有明确反馈

本次文章配套提供四个 ArkTS 纯逻辑文件和 Node 标准测试:任务状态门闩、请求级 envelope、连接代次、可重试初始化。测试直接加载这些文件的去类型结果,验证状态与关联规则;它不运行 Agent Framework Kit,不模拟一次通过审核的平台接入,也不替代 GSKV 跨进程与小艺界面验证。接入层调用关系另附说明片段,便于迁移到自己的鸿蒙工程。

截至本次整理,完成的是源码核对、官方文档版本核对与配套逻辑回归。没有重新连接小艺设备做全流程验收,也没有据此宣称新增性能收益、上架状态或客户端兼容范围。复现时应记录 DevEco/SDK、设备系统及小艺客户端版本,并保存脱敏后的回包结构和用户操作路径。

让已有应用成为一个可复用的能力入口

回到最初的思考:端侧 A2A 的使用价值,可以从端内数据保护延伸到既有应用能力的复用。对这个训练助手而言,应用已经知道怎样恢复账号、组织训练请求、解释业务结果,以及让用户继续编辑。把这些能力接到小艺入口,是一条有明确工程依据的路线。

这篇希望留下的判断方法是:先找到能力已经成熟的那一层,再决定把 A2A 适配放在哪里。App 中已经形成完整业务链路时,应用侧适配有机会减少后端新增工作;云端已经具备成熟 Agent 服务时,也应尊重那里的已有能力。选择的依据是现有架构和运行需求。

轻量接入要靠具体实现成立:业务有结果,Task 有终态,回包属于正确请求,发送对应当前连接,应用页面接到正确草稿。把这些位置管住,增加一个系统入口时,才能把改造范围主要收在协议与交互适配中,让原有业务继续发挥作用。

实现来源与参考资料

本文基于 HarmonyOS 应用接入代码整理,重点核对了 AgentExtensionAbility、A2A 会话服务到既有业务 Repository 的调用关系、headless 初始化、请求 metadata 与回包适配、结构诊断器,以及应用启动交接存储。文中的兼容性经验来自该实现的联调备注;机制图为重新绘制,日志和自然语言请求为合成示例,未使用真实用户截图或原始协议包。

配套逻辑由上述工程经验提炼并重新编写,按 MIT 许可提供;原业务应用及 Huawei SDK 不包含在配套代码的授权范围内。本文不是华为官方教程,接口、平台结构和版本要求请以对应环境的官方文档为准。

Feng
这位作者很神秘,还没有填写简介。