首页
/ OpenHands Canvas Extensions 扩展机制解析:契约、信任模型与前后端运行时实现

OpenHands Canvas Extensions 扩展机制解析:契约、信任模型与前后端运行时实现

2026-09-04 23:06:55作者:苗圣禹Peter

本文基于 OpenHands 仓库中的规范文档 specs/canvas-extensions.md 展开,系统讲解 Canvas Extensions 的产品定位、v1 信任决策、canvas-extension.json 清单契约、Agent Server API、前端运行时激活流程与交付切分。读完本文,你将理解一个 Canvas 扩展包从安装、启用、热激活到卸载的完整生命周期,并能对照仓库中已落地的类型定义、服务层、模块加载器、路由与 fixture 实现验证每一项契约。

一、产品定位:扩展改变的是"应用",不是"Agent"

规范开篇给出了一个清晰的产品定义:Canvas Extensions 是安装后会改变 Agent Canvas(前端应用)本身的安装包,它们贡献 UI 与本地产品行为——路由页面(routed pages)、会话面板(conversation panels)、渲染器(renderers)、插槽(slots)与主题(themes)。规范用一个对比句划定了边界:

Skills 和 plugins 改变的是 agent;Canvas Extensions 改变的是 app。

同时规范约定了产品入口:Customize 区域是 Skills、Plugins、MCP 与 Canvas Extensions 的唯一清单(inventory),清单条目统一命名为 Extensions("addon" 只是非正式别名)。从仓库源码看,这一入口对应 extensions-hub 路由route("customize", "routes/extensions-hub.tsx"),而扩展管理页挂在 route("extensions", "routes/canvas-extensions.tsx"),扩展页面路由为 route("extensions/:extensionName/*", "routes/canvas-extension-page.tsx")

规范开头还声明了状态属性:这是"实现计划与 v1 契约"(Implementation plan and v1 contract)。前端纵向切片(vertical slice)可以在 Agent Server 端点尚未就绪时,依托后端的**能力探测(capability detection)**先行发布。

二、六项关键决策:信任模型是理解一切的前提

规范用六个编号决策框定了 v1 的行为边界,每一条都有明确的实现对应。

1. 活跃的 Agent Server 拥有扩展

扩展安装在运行 Agent Server 的计算机或容器上。它的源码、解析后的 revision、文件、清单与启用状态都存放在后端;Canvas(前端)只从当前活跃后端发现并加载扩展。切换后端即整体替换活跃扩展集合。这一决策在源码中体现为查询键设计:use-canvas-extensions.ts 中 query key 由 backend.idorgIdconnectionRevision 三元组构成,且 retry: falsestaleTime: 0,确保不同后端的扩展清单互不串扰。

2. 扩展代码是受信的同域代码(trusted, same-realm)

v1 没有 iframe、worker 沙箱或细粒度权限系统。启用后,扩展拥有与 Canvas 自身代码相同的浏览器环境权限。Shadow DOM 未来可能作为可选样式隔离提供,但绝不作为安全边界。规范第 7 节的 non-goals 再次强调这一点(见下文"v1 明确不做的事")。

3. 安装与启用是分离的

安装永远产生一个禁用态扩展。Agent 可以安装或更新扩展,但必须由用户回到 Customize → Extensions 显式启用。v1 将"安装与启用分离"作为产品同意不变量(product consent invariant),而不是针对 agent 的人类在场证明——因为 agent 可以调用同样的已认证 API。未来后端策略可能允许 agent 驱动启用。

4. 启用是热的(Enablement is hot)

启用无需重启应用或 Agent Server 即可加载并激活扩展;禁用会卸载已注册的界面并调用生命周期清理(disposer)。由于代码是受信的同域 JavaScript,清理是**尽力而为(best-effort)**的——扩展可以创建宿主无法撤销的全局副作用。这一流程在 canvas-extensions-runtime.tsx 中完整实现:激活效果函数在 cleanup 阶段反向遍历 disposers 逐一调用,异常仅记录日志而不阻断其余清理。

5. 分发跟随 plugins 的模式,而非其运行时

安装坐标为 source、可选 ref、可选 repo_path,由 Agent Server 负责解析与 pin(锁定版本)。一个仓库可以在子路径下包含多个扩展及其他工件类型。后端本地路径在后端机器上解释,绝不在前端进程解释

6. 更新保留启用状态

Refresh 以原子方式解析并安装新 revision,保留先前的启用状态,UI 展示解析后的 revision。v1 是受信代码模型,因此不存在误导性的权限 diff 审批门。规范同时说明:staged check/apply 流程目前只存在于 Agent Server 服务层,在通过 HTTP 暴露之前,Customize UI 不提供 Refresh 操作

三、信任披露:如实告知,不虚构权限

规范对信任披露(trust disclosure)有明确规定:

  • 启用前,Canvas 明确告知用户:该扩展可以访问并修改 Canvas 页面,并可以发起当前浏览器会话可用的已认证请求
  • 审查界面展示:来源(source)、请求的 ref、解析后的 revision、清单元数据、贡献的界面(surfaces);
  • 界面不展示虚构的细粒度权限

规范特别强调:安装和更新仍是有效的信任动作,因为它们选择了后端存储的代码 revision;而"启用"是 Canvas 实际执行该代码的显式时点。在前端实现中,管理页 canvas-extensions.tsx 顶部常驻一条琥珀色信任提示条(I18nKey.SETTINGS$CANVAS_EXTENSIONS_TRUST_NOTICE),且启用操作会先弹出 ConfirmationModal 确认框,只有禁用和解安装是直接执行或二次确认后的操作——与规范"install/enable 分离、enable 需显式同意"的不变量一致。

四、包契约:canvas-extension.json 与 activate 导出

4.1 清单文件格式

清单文件名固定为 canvas-extension.json。规范给出的 v1 示例:

{
  "schema_version": 1,
  "name": "example-dashboard",
  "display_name": "Example dashboard",
  "version": "0.1.0",
  "description": "A backend-specific project dashboard.",
  "entrypoint": "dist/extension.js",
  "contributes": {
    "pages": [
      {
        "id": "dashboard",
        "title": "Dashboard",
        "path": "/dashboard",
        "nav_label": "Dashboard"
      }
    ]
  }
}

v1 清单规则:

  • name 与贡献 ID 只允许小写字母、数字和连字符;
  • Page 的 path 是绝对 kebab-case 路由(以 / 开头);Canvas 将其挂载在 /extensions/{name} 之下;
  • entrypoint 是安装包内部的路径;
  • entrypoint 必须是单个自包含的浏览器 ESM bundle——不允许未解析的裸导入(bare imports)或外部 chunk;依赖、CSS 与小资源由作者模板负责打包或内嵌;
  • 后端会校验 manifest、entrypoint 以及未来任何 asset 路径不得逃逸出已安装扩展根目录
  • 面向市场分发的宿主兼容字段将在市场分发前补充;初始 schema 刻意保持精简,页面 ABI(应用二进制接口)由一个独立构建的示例扩展先行验证。

仓库中恰好存在规范所指的"示例扩展"——fixture 扩展 demo-page。其清单 canvas-extension.json 与规范示例同构:schema_version: 1name: "demo-page"entrypoint: "extension.js",并在 contributes.pages 中声明了 id: "hello"path: "/hello" 的页面。README 说明该 fixture 是"清单 schema 1 与 host API 1 的独立、无依赖实现",其 extension.js 本身就是自包含浏览器 ES 模块,Agent Server 测试实现可直接安装该目录并从已认证的 bundle 端点提供 extension.js

共享的 TypeScript 类型定义落在 src/types/canvas-extension.tsCANVAS_EXTENSION_MANIFEST_SCHEMA_VERSION = 1CANVAS_EXTENSION_HOST_API_VERSION = "1",以及 CanvasExtensionManifestInstalledCanvasExtensionInfoInstallCanvasExtensionRequestCanvasExtensionHost 等接口。其中 InstalledCanvasExtensionInfo.manifest 字段标注为"在 OSS-10048 之前后端不返回页面贡献时缺席"——即规范中"manifest 字段尚未由当前路由返回,Canvas 将其视为可选"这一过渡态的类型化表达。

4.2 模块导出 activate 与宿主 API

v1 模块导出 activate,规范给出的最小示例:

export function activate(host: CanvasExtensionHost): void | (() => void) {
  return host.registerPage("dashboard", ({ container, path, navigate }) => {
    container.textContent = `Extension route: ${path}`;
    return () => container.replaceChildren();
  });
}

fixture 扩展 extension.js 是该 ABI 的无依赖落地版本:activate(host) 调用 host.registerPage("hello", mount),mount 向 container 追加 DOM 并返回 disposer(() => wrapper.remove());渲染内容使用 var(--oh-text-primary) 等宿主 CSS token 与 host.apiVersionhost.backend.id 元数据,直观演示了宿主 API 的使用方式。

CanvasExtensionHost 独立于 manifest 版本演进。首个宿主 API 包含(类型定义见 src/types/canvas-extension.tsCanvasExtensionHost 接口):

  • apiVersion: "1"
  • 不可变的扩展/后端元数据(extension: { name, version, resolvedRef }backend: { id, kind, orgId },运行时用 Object.freeze 冻结后传入);
  • registerPage(id, mount),用于注册清单中声明的页面工厂;
  • navigate(path),走 Canvas 感知 base path 的路由(实现中直接委托 React Router 的 navigate);
  • agentServer.request(...),指向扩展所属后端的已认证请求辅助。

4.3 为什么用 Blob URL 加载 bundle

规范给出了一条关键实现约束:运行时以已认证文本方式获取 bundle,再通过临时 Blob URL 导入。原因是直接 <script src>import(backendUrl) 无法携带 X-Session-API-Key 请求头,因此不是 v1 的加载路径。同时,导入 bundle 不等于激活——Canvas 只对已启用的安装调用 activate

源码 canvas-extension-module-loader.ts 精确实现了该契约:

export async function loadCanvasExtensionModule(
  source: string,
): Promise<CanvasExtensionModule> {
  const blob = new Blob([source], { type: "text/javascript" });
  const moduleUrl = URL.createObjectURL(blob);
  try {
    const imported: unknown = await import(/* @vite-ignore */ moduleUrl);
    assertCanvasExtensionModule(imported);
    return imported;
  } finally {
    URL.revokeObjectURL(moduleUrl);
  }
}

配套的 assertCanvasExtensionModule 断言模块必须导出名为 activate 的函数,否则抛出 InvalidCanvasExtensionModuleError。函数注释直接点明动机:"不把 Agent Server 会话密钥放进 URL,来源已经通过 Canvas 的已认证 HTTP 客户端获取"。

五、Agent Server API:与插件分发管理同构、运行时独立

规范声明该 API "刻意镜像插件的分发管理,但保持独立的运行时",并给出端点表:

Method Path Purpose
GET /api/canvas-extensions/installed 列出已安装扩展与解析后的清单
POST /api/canvas-extensions/install 从 git 或后端本地路径安装;始终禁用态
GET /api/canvas-extensions/installed/{name} 读取单个安装项
PATCH /api/canvas-extensions/installed/{name} 设置启用状态
DELETE /api/canvas-extensions/installed/{name} 卸载
GET /api/canvas-extensions/installed/{name}/bundle 以 JavaScript 文本返回 entrypoint

Refresh 端点(POST /api/canvas-extensions/installed/{name}/refresh)已规划但不在当前路由中;其背后的服务层 staged check/apply 流程单独跟踪。列表端点把结果包在 canvas_extensions 数组里。

安装请求体:

{
  "source": "github:owner/repository",
  "ref": "main",
  "repo_path": "extensions/example-dashboard",
  "force": false
}

安装响应体:

{
  "name": "example-dashboard",
  "version": "0.1.0",
  "description": "A backend-specific project dashboard.",
  "enabled": false,
  "source": "github:owner/repository",
  "resolved_ref": "6f5f9a...",
  "repo_path": "extensions/example-dashboard",
  "installed_at": "2026-08-01T12:00:00Z",
  "install_path": "/home/user/.openhands/canvas-extensions/installed/example-dashboard",
  "manifest": {}
}

manifest 字段(解析后的 canvas-extension.json,含 contributes.pages)在当前路由实现中尚未返回;在其上线之前,Canvas 将其视为可选,对没有页面贡献或 display name 的安装项照常渲染。

服务层实现 canvas-extensions-service.ts 与规范逐条对应:

  • listInstalled() 请求 GET /api/canvas-extensions/installed 并解包 canvas_extensions 数组(response.canvas_extensions ?? []),与"列表端点包裹在 canvas_extensions 数组"一致;
  • install(request) 对应 POST install;setEnabled(name, enabled) 走 PATCH;uninstall(name) 走 DELETE;
  • fetchBundle(name, backend?)responseType: "text" 拉取 installed/{name}/bundle,即"以 JavaScript 文本返回 entrypoint"的已认证文本加载路径;
  • 一个值得注意的细节:requestAgentServer 强制 path 必须以 / 开头且不能以 // 开头(根相对路径校验),这正是规范"后端本地路径在后端机器上解释、绝不进入前端"以及请求必须锚定在后端路由空间的具体约束。

404 能力探测:不把所有失败混为一谈

规范对过渡期有一处硬性要求:在 API 存在之前,Canvas 把 HTTP 404 视为"该后端不支持 Canvas Extensions"。Customize 页面显示升级提示,而全局运行时保持静默。并且必须不能把"不支持"、"不可达"与"空清单"折叠成同一种状态

服务层的实现方式是 mapUnsupported 包装:捕获到 404 状态错误时转换为 CanvasExtensionsUnsupportedError("missing-api")。该类还定义了另外两种原因——no-backend(未添加 Agent Server 后端)与 cloud-backend(Cloud 后端暂不可用),分别对应不同的用户提示文案。管理页 canvas-extensions.tsx 据此渲染"不可用"空态(含具体原因文案与重试按钮),而查询层 use-canvas-extensions.ts 设置 retry: false 且 query meta 中 disableToast: true,注释写明:"旧后端返回 404 是预期的能力探测结果,管理页负责渲染它,全局运行时保持安静"。三种状态(不支持 / 错误 / 空清单)在 UI 上是彼此独立分支,满足规范的要求。

六、前端运行时:键控激活、清单准入与错误边界

规范对前端运行时给出了七步流程。运行时以后端 ID、组织作用域与连接 revision 为键,防止某个后端的已启用 bundle 或缓存清单在切换/重连后出现在另一个后端上。

对每个已启用的安装项:

  1. 从所属后端获取已认证的 entrypoint 文本;
  2. 从 Blob URL 导入自包含 ESM bundle;
  3. 校验 activate 及其宿主 API 版本;
  4. 创建扩展作用域的注册表并调用 activate
  5. 只有 ID 已在清单中声明的注册才被准入("Admit registrations only when their IDs were declared in the manifest");
  6. 每个界面渲染在 Canvas 拥有的错误边界之内;
  7. 在禁用、卸载、更新或切换后端时,卸载所有界面、调用扩展 disposer、移除其注册表条目。

初始路由形状为 /extensions/{extension-name}/{declared-page-path}/extensions 本身是 Customize 清单页;所有路由经过 React Router,保证 VITE_BASE_PATH 继续生效。

canvas-extensions-runtime.tsx 是上述流程的实现主体,几个细节与规范严格对应:

  • 激活签名(activation signature):以 JSON.stringify({ backendId, backendKind, connectionRevision, orgId, extensions: [...] }) 作为 effect 依赖,注释解释了原因——useActiveBackend 可能在每次渲染合成新的 backend 对象、refetch 产生内容相同的新数组,若按引用做依赖会反复拆毁重建扩展。这正是规范"运行时键控于后端 ID、组织作用域、连接 revision"的代码化。
  • 清单准入registerPage 调用 getDeclaredPage,在 manifest.contributes.pages 中查找同 ID 声明;未声明者直接抛错 registered undeclared page;同时校验扩展名、贡献 ID、路径段均匹配 /^[a-z0-9]+(?:-[a-z0-9]+)*$/,与规范"name 与贡献 ID 使用小写字母、数字、连字符"以及"绝对 kebab-case 路由"的规则一致;同一 contributionId 重复注册也会抛错。
  • 路径规范化:后端声明的页面路径是绝对路由(如 /dashboard),前端归一化为相对形式用于 href 构造(buildCanvasExtensionPageHref 生成 /extensions/{name}/{path}),保证与 React Router 的 extensions/:extensionName/* 通配路由匹配。
  • agentServer.request 委托:宿主对象的 agentServer.request 直接绑定到 CanvasExtensionsService.requestAgentServer(request, backend),即扩展发出的请求由前端服务层代理、携带当前后端凭据发出,对应规范"指向扩展所属后端的已认证请求辅助"。
  • 清理即卸载:effect cleanup 中先清空 pages,再反序执行所有 disposer,单个 disposer 抛错仅 console.error,其余继续——即规范"best-effort 清理"的实现。

路由侧,/extensions/:extensionName/*canvas-extension-page.tsx 承接,其中 path 挂载上下文会把声明路径之下的剩余段作为 remainder 传给扩展(CanvasExtensionPageMountContext.path 的注释:"the remainder of the route below the page contribution's declared path"),使扩展页面自身支持嵌套路由。

七、本地验证:fixture、MSW 模拟与 Mock-LLM E2E

规范第 8 节(Verification)要求:每个切片都新增服务与运行时单元测试;路由页面切片另增一个 Mock-LLM 端到端用例,驱动生产构建走完 install → enable → page render → disable → uninstall;在 pinned agent-server 提供端点之前,用例通过 Playwright 路由拦截、从 src/fixtures/canvas-extensions/demo-page 提供 API 契约,端点就位后移除 stub、同步骤改跑真实后端。合并前必须运行 npm run lintnpm testnpm run buildnpm run build:lib

仓库中这些验证均已落地:

VITE_FRONTEND_PORT=3102 \
VITE_BACKEND_BASE_URL=http://127.0.0.1:8000 \
VITE_SESSION_API_KEY=canvas-extension-dev \
npm run dev:mock

文档说明:3102 端口避开常规本地栈使用的 3001 Vite 进程;后端 URL 只为给 Canvas 一个本地后端身份,MSW 会在浏览器内拦截扩展请求;mock 还覆盖了设置与服务信息探测端点,使该后端被标记为健康——因此 Agent Server 既不需要实现扩展端点,甚至无需运行。测试入口为 http://localhost:3102/extensions。该路径只面向前端开发,不测试 Agent Server 的安装、文件系统校验、持久化、认证或 Git 解析。

  • Mock-LLM E2Etests/e2e/mock-llm/canvas-extensions/mock-llm-canvas-extensions.spec.ts 驱动真实构建的 agent-canvas 栈(预构建静态前端、ingress、后端注册表、会话认证)走完完整生命周期:①从 source 路径安装 → 禁用态、无侧边栏项;②在信任确认之后启用 → 侧边栏项出现、贡献页面渲染在 /extensions/{name}/{path}(含嵌套 remainder 路径);③禁用 → 侧边栏项与页面被拆毁;④卸载 → 清单为空。其文件头注释还特别说明:这是唯一在真实浏览器中导入扩展 bundle 的测试(即 canvas-extension-module-loader.ts 的 Blob-URL ESM 导入路径),单元测试则注入 loader stub;由于 pinned agent-server 尚无 /api/canvas-extensions 端点,用例目前在浏览器层用 demo fixture 应答 API,"一旦 pin 包含端点,删除该 helper 并以绝对路径安装 fixture,UI 步骤保持不变"——与规范 Verification 一节的 stub→真实后端的切换计划完全一致。

八、交付计划与 v1 非目标

交付切片

规范的 Delivery plan 分为五个切片,仓库现状可对照判断其推进程度:

  • Slice 1(契约与管理):落地规范与共享 TypeScript 类型(已见于 src/types/canvas-extension.ts);后端键控的服务与查询钩子(已见于 canvas-extensions-service.tsuse-canvas-extensions.tsuse-manage-canvas-extensions.ts);Customize → Extensions 页面,含不支持后端态、源码安装表单、来源/revision/贡献审查与显式启用控件(已见于 canvas-extensions.tsxadd-canvas-extension-modal.tsx);活跃后端返回 404 时全局运行时保持静默(已实现于 query meta 的 disableToast)。
  • Slice 2(路由页面证明):版本化宿主 API、模块加载器、生命周期注册表、错误边界(已见于 canvas-extensions-runtime.tsxcanvas-extension-module-loader.ts);动态页面路由与左侧导航(见 sidebar-rail-body.tsx 对扩展侧边栏项的渲染);独立 fixture 扩展(已见于 demo-page);以及规范列出的验证清单:enable、热激活、导航、禁用清理、更新、后端切换、带会话密钥认证的 bundle 加载、VITE_BASE_PATH
  • Slice 3(主题证明):定义无代码的主题贡献(严格 Canvas token schema)、后端校验主题清单、在 Settings 中展示已安装主题;主题选择在浏览器/用户作用域内,可用性跟随活跃后端,切离提供后端时干净回退。
  • Slice 4(会话界面):用命名空间运行时 ID 替换封闭的会话 tab 联合类型、集中化持久化准入;先加扩展 tab/面板,再加宿主拥有的 header/footer/badge 插槽;在开放给作者前定义移动端行为、排序、冲突、扩展缺失回退与按会话的生命周期上下文。
  • Slice 5(可视化器与作者闭环):增加 augment/replace 可视化器注册(显式事件匹配、优先级、内建回退);发布稳定的扩展 SDK 类型、打包器模板、校验命令与示例仓库;到那时才发布 /build-canvas-extension(含后端本地 vs 远端工作区指导、临时安装、手动启用说明、git 发布/更新流程)。

v1 明确不做的事

规范以非目标清单划界,这些"不做"与前述决策同等重要:

  • 不做 iframe/worker 隔离或可执行的细粒度权限;
  • 不在安装期运行任意生命周期脚本;
  • 不允许 agent 自动启用;
  • 不做市场排序或发布者签名;
  • 首个纵向切片不包含会话 tab、插槽或可视化器替换;
  • 不把 @openhands/extensions 当作可安装运行时格式对待。

九、小结:契约如何贯穿三层

Canvas Extensions 的架构可以用一句话概括:清单声明界面,后端托管代码与信任,前端按版本化宿主 API 热激活。清单(canvas-extension.json,schema v1)声明"能贡献什么";Agent Server API(/api/canvas-extensions/*)负责"装在哪、装的是哪个 revision、谁有资格启用";前端运行时(runtime provider + 模块加载器 + 路由)负责"安全地、可撤销地执行"。三者之间靠两个独立的版本号解耦:schema_version 约束清单形状,apiVersion: "1" 约束宿主 API 形状——这使得宿主 API 可以独立于清单演进,也为 Slice 3–5 逐步开放主题、会话界面与可视化器保留了契约扩展空间。

对希望在本地先行体验该机制的开发者,最短路径是:按 docs/CANVAS_EXTENSIONS_TESTING.md 的命令启动 mock 前端,访问 /extensions,安装指向 src/fixtures/canvas-extensions/demo-page 的演示扩展,走一遍安装 → 启用 → 页面渲染 → 禁用 → 卸载;对实现细节有疑虑时,可直接对照本文引用的服务层、运行时与 loader 源码验证契约的落地情况。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
981
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384