mcpField
M
metabase-full-app-to-modular-embedding-upgrade

metabase-full-app-to-modular-embedding-upgrade

github.com/metabase开发54360 次安装1 天前更新
代码仓库

关于 metabase-full-app-to-modular-embedding-upgrade

面向使用 Metabase 全应用嵌入的开发者,此 Agent 技能将指向 Metabase 页面的 iframe 迁移为模块化嵌入 Web 组件。它按版本查阅文档,并检查 SSO、页面配置、URL 参数和组件属性的对应关系。

主要功能

  1. 01按步骤执行迁移清单
  2. 02扫描 iframe、SSO 和项目配置
  3. 03将页面类型映射为 Web 组件
  4. 04转换 URL 参数与组件属性
  5. 05验证修改并报告证据

适用场景

  1. 01将仪表板 iframe 迁移为模块化嵌入
  2. 02将问题或集合页面映射为 Web 组件
  3. 03按实例版本规划并验证迁移

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/metabase/agent-skills --skill metabase-full-app-to-modular-embedding-upgrade

安装后在 Claude Code 中输入/metabase-full-app-to-modular-embedding-upgrade确认已加载。

详细介绍

执行约定

请按顺序执行工作流步骤,不要跳过任何步骤。先创建检查清单,然后执行每个步骤,并提供证据明确标记为已完成。每个步骤的输出都会用于后续步骤,因此跳过步骤会导致迁移错误。

如果因缺少信息或工具故障而无法完成某个步骤,你必须:

  1. 将该步骤记录为 ❌ blocked,
  2. 准确说明缺少什么信息/发生了什么故障,
  3. 停止(不要继续后续步骤)。
必需的输出结构

你的回复应按以下顺序包含这些部分:

  1. 步骤 0:Metabase 版本检测
  2. 步骤 0.1:迁移计划检查清单
  3. 步骤 1:项目扫描
  4. 步骤 2:iframe 分析与 Web 组件映射
  5. 步骤 3:迁移计划
  6. 步骤 4:应用代码更改
  7. 步骤 5:验证
  8. 步骤 6:最终总结

每个步骤部分都应以状态行结尾:

  • Status: ✅ complete 或 Status: ❌ blocked

步骤必须按顺序执行——只有前一步状态为 ✅ complete 后,才能开始下一步。

证据要求
  • 步骤 0:检测到的 Metabase 版本(来源:Docker 标签、环境变量或用户答复)。
  • 步骤 1:每个匹配到的文件路径、每个 iframe 位置、SSO 端点、布局/head 文件、Metabase 配置变量。
  • 步骤 2:逐个 iframe 提供已解析的 URL、内容类型、ID、hash 参数,以及映射后的 Web 组件及其属性。
  • 步骤 3:完整的逐文件更改计划,并列出精确的旧/新代码。
  • 步骤 4:逐个文件说明更改内容并提供实际应用的精确差异。
  • 步骤 5:提供每项验证检查的通过/失败结果及证据。
架构一致性

遵循应用现有的架构、模板引擎、布局/局部模板系统、代码风格和路由模式。不要切换范式(例如,不要从模板改为内联 HTML,反之亦然)。如果应用有用于共享模板变量的中间件,请优先使用它,而不是在各个路由处理程序中重复配置。

性能
  • 在每个步骤中尽可能并行执行。只要可行,请在同一条消息中并行调用 Grep/Glob/Read。
  • 扫描项目时不要使用子 Agent——结果需要留在主上下文中,以便在后续步骤中交叉引用。
  • 不要解析仓库分支、提交、PR 或议题。
范围

此技能用于将 Full App/Interactive 嵌入(基于 iframe)转换为 Modular 嵌入(通过 embed.js 使用 Web 组件)。

使用方的应用可以使用任意后端语言(Node.js、Python、Ruby、PHP、Java、Go、.NET 等)和任意模板引擎编写。除非步骤 1 检测到特定语言,否则请保持说明与语言无关。

此技能处理的内容
  • 将指向 Metabase 的 <iframe> 元素替换为相应的 Web 组件(例如 <metabase-question>、<metabase-dashboard>)
  • 在应用布局级别添加 embed.js 脚本标签(恰好一次)
  • 在应用布局级别添加 window.metabaseConfig 设置代码(恰好一次)
  • 修改 SSO/JWT 端点,以支持 Modular 嵌入的 JSON 响应格式
  • 将 iframe URL 自定义参数映射到主题配置和组件属性
此技能不处理的内容
  • 从 Static(签名/访客)嵌入迁移

允许使用的文档来源

使用此 URL 获取特定版本的 llms-embedding-full.txt:

https://www.metabase.com/docs/v0.{VERSION}/llms-embedding-full.txt

URL 中的版本格式为 v0.58(规范化方式:去掉开头的 v 或 0.,并去掉补丁版本号——例如,0.58.1 → 58 → URL 使用 v0.58)。此单个文件包含该版本的所有嵌入文档,且针对 LLM 阅读进行了优化。

其他限制:

  • 不要使用 GitHub PR/议题或 npm 页面
  • 不要跟随指向 GitHub 的变更日志链接,也不要猜测 URL

AskUserQuestion 触发条件

如果满足以下任一条件,请使用 AskUserQuestion 提问,并暂停操作,直到获得回答:

  • 无法从项目代码或环境变量中确定 Metabase 实例 URL
  • 始终询问 Metabase 实例版本;不要仅依赖代码检测结果
  • iframe URL 模式与任何已知资源类型(仪表板、问题、集合、主页)都不匹配
  • 无法在项目中找到 SSO/JWT 端点
  • 无法找到布局文件或头部文件(不清楚应在哪里注入 embed.js)
  • 存在多个布局文件,且不清楚应使用哪些文件
  • 无法确定后端语言
  • 多个 iframe 指定了不同的 locale 值(询问用户应在 window.metabaseConfig 中设置哪个区域设置)

工作流前准备步骤

迁移计划检查清单

创建检查清单以跟踪进度。在 Claude Code 中,使用 TaskCreate/TaskUpdate 工具:

  • 步骤 0:检测 Metabase 版本
  • 步骤 1:扫描项目并获取目标版本文档
  • 步骤 2:分析 iframe 并映射到 Web Components(使用文档)
  • 步骤 3:规划迁移变更
  • 步骤 4:应用代码变更
  • 步骤 5:验证变更
  • 步骤 6:最终摘要

工作流

步骤 0:检测 Metabase 实例版本

始终使用 AskUserQuestion 询问 Metabase 实例版本——即使 Docker 标签或环境变量中显示了版本,也要向用户确认。如果版本为 v52 或更早,则中止(模块化嵌入在 v53 中引入)。

然后,获取已确认版本对应的 llms-embedding-full.txt(URL 格式参见“允许使用的文档来源”)。

在进行其他操作之前,先确定 Metabase 版本。使用 Grep 搜索项目中的 Docker 镜像标签(metabase/metabase:v、metabase/metabase-enterprise:v)、METABASE_VERSION 或版本引用。如果未检测到,则使用 AskUserQuestion 提问(选项:v52 or older、v53、v54–v58、v59+)。如果版本为 v52 或更早,则中止(模块化嵌入不可用——该功能在 v53 中引入)。记录该版本;后续步骤将根据它确定 jwtProviderUri 的位置。

步骤 1:扫描项目并获取文档(不使用子 Agent)

并行执行项目扫描和文档获取——二者相互独立。凡是不存在依赖关系时,尽可能在同一条消息中并行调用工具。

1a:获取目标版本文档

获取目标版本对应的 llms-embedding-full.txt(URL 格式参见“允许使用的文档来源”)。这些文档是目标版本中 Web Component 属性、window.metabaseConfig 选项和 SSO 端点行为的权威来源。步骤 2 中应使用这些文档进行映射,而不是仅依赖硬编码表格。

与下方的项目扫描步骤并行执行。

1b:识别后端语言和框架
  • 检查依赖项/构建文件(package.json、requirements.txt、Gemfile、pom.xml、go.mod、composer.json 等)。
  • 识别模板引擎,并记录语言和框架。
1c:查找所有 Metabase iframe

使用 Grep 并行搜索以下所有模式:

  • 在所有模板/HTML/JSX/view 文件中搜索 <iframe
  • 在所有服务端代码文件(JS/TS/Python/Ruby/Go/Java/PHP)中搜索 iframe——用于捕获通过字符串拼接或模板字面量构建的 iframe
  • 搜索与 iframe 或 src 属性相邻的 auth/sso。注意:SSO URL 可能在单独的变量或函数中构造,然后传入 iframe 的 src——如果 iframe 的 src 是变量,请追踪其定义以检查是否包含 auth/sso。

对于每个匹配的文件,读取整个文件。

1d:查找 SSO/JWT 身份验证代码

使用 Grep 并行搜索以下所有模式:

  • /auth/sso
  • /sso/metabase 或类似的 SSO 路由模式
  • jwt.sign 或 jwt.encode 或 JWT 或 jsonwebtoken 或 PyJWT 或 jose
  • JWT_SHARED_SECRET 或 METABASE_JWT_SHARED_SECRET
  • return_to(Metabase SSO 重定向参数)
  • redirect(在 auth/sso 附近,用于捕获 SSO 重定向逻辑)

对于每个匹配的文件,读取整个文件。

1e:查找布局/head 文件

找到定义 HTML <head> 部分的单个文件(或通用代码路径)——embed.js 和 window.metabaseConfig 将注入此处。

搜索以下内容:

  • 模板/视图文件中的 <head>、<!DOCTYPE 或 <html
  • 布局/包装器模式:include('head')、<%- include、{% extends、{% block、layout、base.html、_layout、application.html
  • 如果应用通过服务器代码中的内联字符串构建 HTML(例如 res.send(...)),请确定生成 <head> 内容的位置
1f:查找 Metabase 配置

搜索以 METABASE_ 和 MB_ 为前缀的变量。记录每个与 Metabase 相关的变量名称及其读取位置。

输出:结构化项目清单

汇总所有发现并填入:

Backend: {language}, {framework}, {template engine}
Metabase config:
  - Site URL variable: {name} (read at {file}:{line})
  - Dashboard path variable: {name} (read at {file}:{line})
  - JWT secret variable: {name} (read at {file}:{line})
  - Other variables: ...
Layout/head file: {path}:{line range} (or "inline HTML in {file}:{line range}")
Iframes found: {count}
  - {file}:{line} — {brief description}
  - ...
SSO endpoint: {file}:{line} — {route} ({method})
第 2 步:分析 iframe 并映射到 Web 组件(仅在完成第 1 步 ✅ 后)

使用第 1a 步获取的文档作为 Web 组件属性、window.metabaseConfig 选项和 SSO 端点行为的权威参考。以下硬编码表格仅作为备选——如果文档针对目标版本说明了其他属性或不同的行为,请以文档为准。

对于第 1 步中找到的每个 iframe:

2a:解析 iframe URL

从 iframe 的 src 属性中提取以下内容(该属性可能是模板表达式、变量或字面值):

  • Metabase 基础 URL:可能来自环境变量、常量或硬编码值
  • 资源路径:基础 URL 后的路径,例如 /dashboard/1、/question/entity/abc123、/collection/5
  • 资源类型:dashboard、question、collection 或 home(如果路径为 /)
  • 实体 ID 或数字 ID:路径中的资源标识符。
    • ID 可能是:
      • 数字 ID,例如 123
      • 数字 ID + slug,例如 123-slug。必须完全移除 slug;包含 slug 会导致资源无法加载。
      • 实体 ID——符合 /{resource_type}/entity/{entity_id} 模式的 URL 使用实体 ID
  • 用于自定义 UI 的 URL 哈希/查询参数(例如 #logo=false&top_nav=false)
  • SSO 包装:iframe 是否先经过 SSO 端点(例如 /sso/metabase?return_to=...)
2b:将内容类型映射到 Web 组件
完整应用 iframe 路径模式模块化 Web 组件必需属性
/dashboard/{id} 或 /dashboard/entity/{entity_id}<metabase-dashboard>dashboard-id="{id or entity_id}"
/question/{id} 或 /question/entity/{entity_id}<metabase-question>question-id="{id or entity_id}"
/model/{id} 或 /model/entity/{entity_id}<metabase-question>question-id="{id or entity_id}"
/collection/{id} 或 /collection/entity/{entity_id}<metabase-browser>initial-collection="{id or entity_id}"
/(Metabase 主页/根路径)<metabase-browser>initial-collection="root"

如果 iframe 路径由变量动态构建,Web 组件属性应使用相同的变量/表达式。

如果 iframe 路径与任何已知模式都不匹配 → AskUserQuestion。

2c:映射 URL 自定义参数

需要舍弃的参数(不适用 — 模块化 Web 组件不包含 Metabase 应用界面元素):

完整版应用参数舍弃原因
top_navWeb 组件没有 Metabase 顶部导航栏
side_navWeb 组件没有 Metabase 侧边栏
logoWeb 组件没有 Metabase 或白标 Logo
searchWeb 组件没有 Metabase 搜索栏
new_button没有 + New 按钮(如果适用,可在 <metabase-browser> 上使用 with-new-question / with-new-dashboard)
breadcrumbsWeb 组件没有 Metabase 面包屑导航

映射到 Web 组件属性的参数:

完整版应用参数模块化等效项
header=false组件上的 with-title="false"
action_buttons=false组件上的 drills="false"

映射到 window.metabaseConfig 的参数:

完整版应用参数metabaseConfig 属性
locale={code}locale: "{code}"

语言区域迁移规则:

  • 如果所有 iframe 中都找到相同的语言区域值 → 自动将 locale: "{code}" 添加到 window.metabaseConfig
  • 如果不同 iframe 中找到多个不同的语言区域值 → 使用 AskUserQuestion 让用户决定要在 window.metabaseConfig 中设置哪个单一语言区域(模块化嵌入仅支持一个全局语言区域)
2d:输出迁移映射表

针对每个 iframe,输出:

iframe #{n}: {file}:{line}
  Old: {full iframe HTML or code}
  Content type: {dashboard|question|collection|home}
  ID: {static value or variable expression}
  Dropped params: {list}
  Mapped attributes: {list}
  New: {exact replacement web component HTML}
第 3 步:规划迁移变更(仅在第 2 步 ✅ 后执行)

创建完整的逐文件变更计划,涵盖以下所有方面。每项变更都应列出目标文件、旧代码和新代码。

3a:注入 embed.js 脚本 — 每个应用恰好一次
  • 目标:第 1e 步中识别的布局/头部文件
  • 位置:<head> 内(或尽可能靠近其他 <script> 标签)
  • 要添加的代码:
    <script defer src="{METABASE_SITE_URL}/app/embed.js"></script>
    
  • {METABASE_SITE_URL} 应使用项目现有的模板表达式语法动态渲染。
  • 如果 Metabase URL 变量仅在特定路由中可用,请通过中间件或模板上下文将其传递给布局。
  • 确认无论用户访问哪个页面,该脚本在渲染后的 HTML 中都只出现一次 — 如果加载两次,SDK 会重新初始化并破坏身份验证状态。
3b:metabaseConfig — 每个应用恰好一次

模块化嵌入从 window.metabaseConfig 读取其配置。没有 defineMetabaseConfig() 函数 — 直接赋值配置对象。

  • 目标:与 3a 相同的布局/头部文件
  • 位置:embed.js 脚本标签之前(必须先设置配置,再加载 embed.js,否则 SDK 无法读取配置)
  • 要添加的代码(最低限度 — 仅当获取的文档列出了此版本支持的身份验证字段时,才添加这些字段):
    <script>
      window.metabaseConfig = {
        instanceUrl: "{METABASE_SITE_URL}",
        // Add auth fields here only if supported by the confirmed version's docs
      };
    </script>
    
  • 语言区域:如果在第 2c 步中于任何 iframe 上发现了 locale 参数,请在配置对象中添加 locale: "{code}"。如果多个 iframe 使用了不同的语言区域值,用户此前已被询问要使用哪个值(根据 AskUserQuestion 触发条件)。
  • instanceUrl(以及受支持时的 jwtProviderUri)应使用项目现有的模板表达式语法动态渲染。
  • 身份验证配置字段:查阅第 0 步获取的文档,确定 window.metabaseConfig 对已确认版本支持哪些字段。例如,jwtProviderUri 可能可用,也可能不可用。如果文档列出了该字段,请将其设为完整绝对 URL(例如,http://localhost:9090/sso/metabase)— 相对路径无效。如果文档未列出该字段,则必须在 Metabase 管理设置中配置 JWT 身份提供方 URI(参见第 3f 步)。
  • window.metabaseConfig 应仅设置一次 — 如果它出现在每个 iframe 的代码中,而不是布局中,每个组件都会重新初始化 SDK。
3c:修改 SSO 端点

现有的 SSO 端点会将浏览器重定向到 Metabase 的 /auth/sso?jwt={token}&return_to={path}。

对于模块化嵌入,embed.js SDK 会向 JWT 身份提供方 URI 发送 fetch 请求,并期望收到 JSON 响应。该端点应改为仅返回 JSON——不要保留旧的基于重定向的身份验证流程作为回退方案。

这是一次完整迁移,而非渐进式迁移。旧的基于 iframe 的嵌入方式将被完全替换,因此不再需要重定向行为。

请参阅第 0 步获取的身份验证文档,确认对应版本的 SSO 端点预期响应格式。

限制:

  • 不要修改 JWT 签名逻辑——只更改响应的交付方式
  • 完全移除旧的重定向行为——该端点只能返回 JSON
  • JSON 响应正文必须严格为 { "jwt": "<token>" }——不得包含其他字段,因为 SDK 会解析这个确切的结构
  • 移除所有构建重定向 URL 的代码(例如 new URL("/auth/sso", ...)、searchParams.set("return_to", ...)),因为这些代码现已成为死代码
3d:替换 iframe 的计划

对于第 2d 步的迁移映射表中的每个 iframe:

  • 指定:文件路径、要替换的确切旧代码、确切的新代码
  • 新的 Web 组件应保留原始 iframe URL 中的任何动态 ID 表达式
  • 如果 iframe 有明确的 width/height 属性或内联 style,应将它们直接应用于 Web 组件元素(例如 <metabase-dashboard dashboard-id="1" style="width:800px;height:600px">)——不要用 <div> 包裹
  • 如果 iframe 通过 CSS 类设置样式,应将这些类直接应用于 Web 组件
  • 如果 iframe 位于带有样式的容器元素内,应保留该容器
  • 移除仅用于 iframe src 的服务器端 SSO URL 构建代码(例如,构建 /sso/metabase?return_to=...)。但不要移除 SSO 端点本身——模块化嵌入身份验证仍需要该端点。
  • 如果 iframe src 是通过发送内联 HTML 的服务器端路由处理程序构建的(例如 Express res.send('<iframe ...')),应替换该处理程序响应字符串中的 iframe HTML
3e:移除死代码

替换 iframe 并转换 SSO 端点后,请识别并移除以下内容:

  • 构建 iframe src URL 的变量(例如 iframeUrl、mbUrl),前提是这些变量不再在任何地方使用
  • 附加到 iframe URL 的 URL 参数或修饰符字符串(例如 mods = "logo=false"),前提是它们不再在任何地方引用(检查 SSO 端点——如果重定向逻辑已移除,这些字符串也可能成为死代码)
  • 从 SSO 端点移除的重定向相关代码(例如,构建 /auth/sso 的 URL、处理 return_to 参数)——这已在第 3c 步中处理
  • 构建 Metabase iframe URL 的辅助函数,前提是它们不再被调用
  • 不要移除:SSO 端点本身、JWT 签名函数、环境变量读取,或应用程序其他部分使用的任何代码
3f:Metabase 管理员配置说明(用户需手动执行的步骤)

将以下内容列入计划——它们会包含在最终摘要中:

  1. 启用模块化嵌入:Admin > Embedding > 切换“Enable modular embedding”
  2. 配置 CORS 来源:Admin > Embedding > Modular embedding > 添加宿主应用的域名(例如,http://localhost:9090)
  3. 配置 JWT 身份提供方 URI:Admin > Authentication > JWT > 将其设置为 SSO 端点的完整 URL(例如,http://localhost:9090/sso/metabase)。查阅获取的文档,确定对于已确认的版本,此项是必需还是可选(取决于 window.metabaseConfig 是否支持 JWT 提供方字段)。
  4. JWT 共享密钥:无需更改——沿用 Full App 嵌入设置中现有的共享密钥
第 4 步:应用代码更改(仅在第 3 步 ✅ 后)

按以下顺序应用第 3 步中的所有更改(先修改后端,以尽量缩短功能异常的时间窗口):

  1. 首先:修改 SSO 端点,使其返回 JSON(第 3c 步)——这只涉及后端
  2. 其次:在布局/head 文件中添加 window.metabaseConfig 赋值和 embed.js 脚本标签(第 3b + 3a 步,配置要放在 embed.js 之前)
  3. 第三:将每个 iframe 替换为 Web 组件(第 3d 步),一次修改一个文件
  4. 第四:移除无用代码(第 3e 步)

约束:

  • 每项更改都使用 Edit 工具,并通过精确的 old_string / new_string 进行修改
  • 不要添加新的软件包依赖项——模块化嵌入只需要由 Metabase 实例提供的 embed.js 脚本
  • 不要更改环境变量名称
  • 如果一个文件需要多处修改,请按从上到下的顺序进行,以避免偏移问题
第 5 步:验证更改(仅在第 4 步 ✅ 后)

执行以下所有检查。检查 5a–5c 可以并行执行(这些都是独立的 grep 搜索)。检查 5d 和 5e 需要读取特定文件。每项检查都应明确标记通过或失败。

5a:不再存在 Metabase iframe

使用 Grep 在整个项目文件中搜索 <iframe 和 iframe(排除 node_modules、.git 和锁文件)。 确认不再存在指向 Metabase 的 Full App / Interactive Embedding iframe。 非 Metabase iframe 应保持不变。访客嵌入(以前称为“静态嵌入”)或公开嵌入 iframe 也应保持不变——它们使用不同的 URL 模式(例如 /embed/ 或 /public/),不属于此次迁移范围。

通过标准:未找到 Full App / Interactive Embedding iframe(访客/公开嵌入 iframe 除外)。

5b:embed.js 恰好出现一次

使用 Grep 在整个项目文件中搜索 /app/embed.js(排除 node_modules、.git)。此模式专指 Metabase 的嵌入脚本 URL,可避免将其他工具可能使用的通用 embed.js 文件名误判为匹配项。 通过标准:在布局/head 文件中恰好出现一次。

5c:window.metabaseConfig 恰好设置一次

使用 Grep 在整个项目文件中搜索 window.metabaseConfig(排除 node_modules、.git)。 通过标准:恰好出现一次(即布局/head 文件中的赋值)。

5d:SSO 端点仅返回 JSON

读取 SSO 端点文件。确认:

  • 端点返回包含 { jwt: token } 的 JSON 响应
  • 旧的重定向逻辑(res.redirect、new URL("/auth/sso", ...)、return_to)已完全移除
  • 不存在 response=json 条件检查(因为现在仅使用 JSON 响应格式)

通过标准:端点仅返回 JSON,不再保留重定向回退逻辑。

5e:抽查已修改的文件

读取每个已修改的文件并确认:

  • Web 组件具有必需的属性(dashboard-id、question-id 或 initial-collection)
  • 模板语法有效(标签已闭合,表达式正确)
  • 第 3e 步中识别出的无用代码变量已移除

通过标准:所有检查均通过。

如果任何检查失败:

  • 立即修复问题
  • 重新运行相应的检查
  • 如果尝试 3 次后仍无法修复,将第 5 步标记为 ❌ 阻塞,并说明失败的检查及原因
第 6 步:输出摘要

将最终输出整理为以下部分:

  1. 已应用的更改:列出每个已修改文件,并用一句话描述各项更改
  2. Web 组件映射:以表格展示每个旧 iframe → 新 Web 组件
  3. 已删除的参数:列出已删除的 Full App iframe 参数,并简要说明它们为何不适用于模块化嵌入
  4. 主题配置:列出映射到 window.metabaseConfig 中的主题/外观设置
  5. 所需的手动步骤(第 3f 步中的 Metabase 管理员配置):
    • 启用模块化嵌入
    • 配置 CORS 来源
    • 配置 JWT Identity Provider URI
    • 发现的其他管理员步骤
  6. 用户应注意的行为差异:
    • 用户无法再在单个嵌入中浏览仪表板、问题和集合(每个 Web 组件均独立运行)
    • 不再显示 Metabase 应用外壳(导航、侧边栏、搜索)
    • 无法映射的 iframe 参数

重试策略

文档获取:

  • 如果获取 llms-embedding-full.txt 时返回 404,请确认 Metabase 版本号并重试。如果仍然失败,将第 1 步标记为 ❌ 已阻塞。

验证:

  • 如果第 5 步中的任何验证检查在尝试修复 3 次后仍未通过,请将第 5 步标记为 ❌ 已阻塞,并报告失败的检查及原因。
  • 如果 AskUserQuestion 未得到回答,则该步骤继续保持阻塞状态;不要猜测或基于假设继续操作。

评论

支持 Markdown