Skill: 1447443432/hap-upgrade-guide (version 1.0.3)

--- name: HAP升级指南 description: 明道云 HAP 私有部署版本升级专属 skill。只要用户提到 HAP 私有部署升级、跨版本升级、升级注意事项、升级前后附加操作、单机或集群升级步骤、升级文档生成、版本兼容性或架构镜像支持，就必须触发本 skill。即使用户没有明确说"用 hap-upgrade"，只要任务与 HAP 私有部署升级决策或升级执行有关，也要使用本 skill。 --- # HAP 私有部署升级帮助 用于两类任务： - 回答 HAP 私有部署升级相关咨询 - 生成可执行的升级指南 Markdown 文档，并自动转换为 HTML 文档 核心原则： - 只依据官方文档和实时页面内容回答，不能凭记忆补全版本细节 - 允许跨版本直接升级，但必须合并跨越路径中的附加操作 - 架构兼容性以 `https://docs-pd.mingdao.com/version` 总表实时标记为唯一准则 - 所有命令必须保持官方原意，不擅自改写命令逻辑 ## 何时使用 出现以下任一情形时，必须使用本 skill： - 用户询问某个 HAP 私有部署版本是否能升级到另一个版本 - 用户询问升级前要做什么、会跨过哪些附加操作、是否支持跨版本升级 - 用户要求输出升级步骤、升级 SOP、升级文档、升级手册 - 用户给出当前版本、目标版本、部署模式、架构、联网情况，请求生成文档 - 用户询问某一目标版本是否支持 `AMD64` 或 `ARM64` 如果任务不是 HAP 私有部署升级，而是一般部署、故障排查、功能使用说明，不使用本 skill。 ## 输出类型 根据用户意图，在以下两种输出之间选择： - 咨询答复：给出结论、依据、风险点、缺失信息 - 升级指南：输出完整 Markdown 文档，然后调用 `scripts/md_to_html.py` 自动生成 HTML 文档（带侧边目录、代码复制按钮），包含可执行步骤和末尾声明 如果用户明确要求"生成文档""给我一份操作手册""整理成升级指南"，按升级指南处理。否则默认先给咨询答复；当信息齐全时，也可以直接生成文档。 ## 执行顺序 严格按以下顺序执行，不要跳步。 ### Step 1. 先收集 5 项关键信息 **重要**：在未收集完以下 5 项关键信息之前，不得开始获取网页或生成文档。 如果用户未提供完整信息，先补齐，再继续： 1. 当前版本，例如 `v7.0.4` 2. 目标版本，例如 `v7.2.0` 3. 部署模式：`单机模式` 或 `集群模式` 4. 架构：`AMD64` 或 `ARM64` 5. 服务器是否可访问互联网 **信息补齐方式**： - **当 1~2 项缺失时**：直接询问用户提供具体版本号 - **当 3~5 项缺失时**：必须使用 `ask_followup_question` 工具提供选项供用户选择 - 单独缺失部署模式：提供"单机模式"和"集群模式"两个选项（单选） - 单独缺失架构：提供"AMD64"和"ARM64"两个选项（单选） - 单独缺失网络：提供"可访问互联网"和"离线环境"两个选项（单选） - 同时缺失多项：在同一个 `ask_followup_question` 中提供多个问题，分别列出对应选项 这 5 项不完整时，不得生成最终升级文档，也不得开始获取网页。 ### Step 2. 规范化版本号 在后续所有步骤中，同时维护两种版本号形态： - 应用版本：不带 `v`，用于命令、镜像标签、脚本参数、配置文件 - 显示版本：带 `v`，用于标题、正文说明 规范化规则： - 用户输入 `v7.1` 时，规范化为应用版本 `7.1.0`，显示版本 `v7.1.0` - 用户输入 `7.2.0` 时，应用版本保持 `7.2.0`，显示版本补成 `v7.2.0` - 任何命令、URL、镜像 tag 中都不要带 `v` ### Step 3. 先读取本地参考文件 开始抓取官网前，按需读取这些资源： - `references/site-structure.md` - 只用于了解 URL 规律、页面入口和部署文档位置 - 不能使用其中的兼容性快照替代实时校验 - `references/merge-rules.md` - 用于合并跨版本附加操作 - `references/command-library.md` - 用于生成联网 / 离线、单机 / 集群、AMD64 / ARM64 的示例命令 - `assets/upgrade-guide-template-standalone.md` 或 `assets/upgrade-guide-template-cluster.md` - 仅在需要生成升级指南时读取 - 必须先读取模板，再填充文档 - HTML 由脚本从 Markdown 自动生成，**无需**读取 HTML 模板 ### Step 4. 抓取官方实时页面 必须抓取： - 版本总表：`https://docs-pd.mingdao.com/version` 从总表中识别： - 当前版本到目标版本之间所有被跨过的版本 - 不含当前版本 - 包含目标版本 - 按从旧到新排序 - 每个版本是否"含附加操作" - 目标版本在所选架构列中的实时标记 仅对"含附加操作"的版本，再抓取其详情页： - URL 规律：`https://docs-pd.mingdao.com/upgrade/{应用版本}/` 如果升级详情页中的附加操作只是一个超链接、跳转入口或"详见某文档"的简写，不能停在该页面文本。必须继续打开对应链接，提取实际执行步骤、命令、依赖资源和注意事项，再整理进最终文档。 ### Step 5. 做架构校验，必要时立即中止 架构校验是最高优先级。只以总表中的实时标记为准： - `✅` 才表示支持 - 空白、`❌`、缺失、无法确认，都视为不支持 - 不能因为详情页出现通用命令、arm64 标签页或相似内容，就推断该版本支持该架构 如果目标版本不支持用户指定架构，立即停止生成文档，并直接回复： `抱歉，目标版本 {显示版本} 官方尚未发布 {架构} 镜像（以发布历史总表为准），升级文档生成任务已中止。` ### Step 6. 提取附加操作并分类 仅从"含附加操作"的版本详情页提取实际需要的操作，分成两类： - HAP 微服务升级前操作 - HAP 微服务升级后操作 可能遇到的升级前操作示例： - 镜像命名变更 - 创建 MongoDB 数据库 - 存储组件升级（仅单机） - 文档预览服务升级 - 重新初始化预置文件 - MongoDB 预置数据更新 - 集群 `service.yaml` 新增服务配置 可能遇到的升级后操作示例： - 单机进入容器执行 MySQL DDL - 集群进入 config Pod 执行 MySQL DDL - 集群进入 config Pod 执行 MongoDB DDL - 集群进入 config Pod 执行文件初始化命令 这些只是示例，不是完整列表。不要根据"示例类型"虚构步骤，也不要把未列出的线上实际操作忽略掉。详情页没有的，不生成；详情页实际出现但此处未列出的，也必须纳入处理。 ### Step 7. 合并跨版本附加操作 如果跨越多个含附加操作的版本，必须读取并遵循 `references/merge-rules.md`。要把它理解为"合并原则文档"，不是"允许操作类型清单"。 特别注意： - 同类 MongoDB 建库应合并成一次登录和一次整理后的建库列表 - 存储组件只升级到跨越路径要求的最高版本 - MongoDB 预置数据只执行涉及该操作的最新版本命令 - 升级后脚本按版本从低到高顺序排列 - 集群新增服务配置需要把多版本新增项合并到同一节里 **MongoDB 预置数据更新的文档结构要求**： 此操作必须独立成步骤，且在"第一阶段：HAP 微服务升级前操作"中单独列出： 1. **"提前准备"阶段**： - 仅说明：MongoDB 预置数据更新脚本将在升级前操作阶段通过联网方式直接拉取执行，无需提前下载 - **禁止**在此阶段执行预置数据更新命令 2. **"第一阶段：HAP 微服务升级前操作"阶段**： - 必须单独列为一个编号步骤，标题为"MongoDB 预置数据更新" - 必须包含以下说明： - 此操作在**原版本服务运行状态下**执行，无需停机 - 本次升级路径跨越的含此操作版本列表（如：v6.5.0、v7.0.0、v7.2.0） - 按合并规则仅执行最新版本（如：7.2.0）的命令 - 执行命令示例： ```bash bash -c "$(curl -fsSL https://pdpublic.mingdao.com/private-deployment/data/preset_mongodb_docker.sh)" -s 7.2.0 ``` 3. **禁止事项**： - 禁止将预置数据更新与其他升级前操作合并为一个步骤 - 禁止在"提前准备"阶段直接执行预置数据更新命令 - 禁止遗漏"在原版本服务运行状态下执行，无需停机"的说明 **特殊操作识别规则**： 在提取和合并附加操作时，必须识别以下特殊操作，并按对应章节规范编写： 1. **v5.1.0 镜像拆分操作**：识别到后，按照"特殊升级操作的文档结构要求"章节中"v5.1.0 镜像拆分操作（关键结构变更）"的规范编写 2. **v5.5.0 MongoDB 升级操作**：识别到后，按照"特殊升级操作的文档结构要求"章节中"v5.5.0 MongoDB 升级操作（重大升级）"的规范编写 3. **MongoDB 预置数据更新**：识别到后，按照本章节"MongoDB 预置数据更新的文档结构要求"的规范编写 **特殊升级操作的文档结构要求（v5.1.0 镜像拆分和 v5.5.0 MongoDB 升级）** 以下两个特殊升级操作必须按照官网文档详细编写，禁止简化或概括： 1. **v5.1.0 镜像拆分操作（关键结构变更）**： - **触发条件**：当升级路径跨越 v5.1.0 版本时，必须在"第一阶段：HAP 微服务升级前操作"中单独列为一个编号步骤 - **标题**：必须为"v5.1.0 镜像拆分操作（关键结构变更 ⚠️）" - **必须包含的详细内容**： a. **重要警告**：明确说明这是"重大的结构性变更"，必须严格按照官网文档步骤调整 docker-compose.yaml 配置 b. **镜像拆分说明**： - 原来的 `mingdaoyun-community` 镜像被拆分为两个独立镜像 - `mingdaoyun-sc:1.0.0`：负责存储组件相关服务 - `mingdaoyun-command:node1018-python36`：负责扩展代码块执行环境 - 需要在 `docker-compose.yaml` 中新增 `sc` 和 `command` 两个服务配置 - Python 版本升级至 3.6，可能导致旧代码块依赖不兼容 c. **单机模式调整步骤**（严格按官网文档执行）： - 步骤 1：备份当前配置文件（提供完整命令） - 步骤 2：修改 `/data/mingdao/script/docker-compose.yaml`： * 2.1 在 `app` 服务中追加环境变量引用（提供完整的 YAML 配置示例） * 2.2 新增 `sc` 和 `command` 服务（提供完整的 YAML 配置示例） - 步骤 3：调整存储组件配置（根据实际情况选择）： * 3.1 若自定义存储组件连接地址为 `127.0.0.1`，将其改为 `sc`（提供修改前后对比） * 3.2 若启用自定义文件对象存储，将文件存储挂载从 `app` 移动到 `sc`（提供完整 YAML 示例） * 3.3 若 `app` 服务中映射了存储组件端口，将端口映射从 `app` 移动到 `sc`（提供完整 YAML 示例） * 3.4 若原有代码块依赖库持久化挂载，将挂载从 `app` 移动到 `command`（提供完整 YAML 示例） - 步骤 4：完整修改示例（提供完整的 docker-compose.yaml 修改后的关键部分） - 步骤 5：验证配置文件格式（提供验证命令） - 步骤 6：重启服务并验证（提供完整的重启命令和验证命令） - 步骤 7：验证功能（列出需要验证的功能点） d. **重要注意事项**（必须列出所有关键点）： * 镜像拆分：原 mingdaoyun-community 镜像拆分为 sc（存储计算）和 command（代码执行）两个独立服务 * Python 版本升级：Python 升级至 3.6，可能导致旧代码块依赖不兼容，需重新安装依赖 * 存储组件连接：若原配置使用 `127.0.0.1`，必须改为 `sc` * 文件挂载调整：所有存储相关挂载需移至 `sc` 服务，代码依赖挂载需移至 `command` 服务 * 端口映射：存储组件端口映射需从 `app` 移至 `sc` * Flink 升级（如已启用）：需升级到版本 `1.17.1.510`，参考官网 Flink 升级指南 - **要求**： * 所有 YAML 配置示例必须完整、可直接复制 * 所有命令必须提供完整代码块 * 不能只说"参考官网文档"，必须将官网文档的关键步骤和命令完整展开 2. **v5.5.0 MongoDB 升级操作（重大升级）**： - **触发条件**：当升级路径跨越 v5.5.0 版本时，必须在"第一阶段：HAP 微服务升级前操作"中单独列为一个编号步骤 - **标题**：必须为"v5.5.0 MongoDB 升级操作（重大升级 ⚠️）" - **必须包含的详细内容**： a. **重要警告**：明确说明这是"重大版本升级"，涉及数据格式变更，必须严格按照以下步骤操作 b. **MongoDB 升级说明**： - 升级范围：从 v3.4 升级到 v4.4 - 升级方式：参考官方文档 [MongoDB 3.4 升级到 4.4](https://docs-pd.mingdao.com/deployment/docker-compose/standalone/upgrade/mongodb/3.4_4.4) - 升级完成后必须重建 `mdwsrows` 库下所有自建索引 c. **升级前重要提示**（必须在升级步骤前单独列出）： - 升级时长：与MongoDB集合数量直接相关 * 查看集合数量命令：`find /data/mingdao/script/volume/data/mongodb/ -name '*collection*' | wc -l` * 参考时长：1万集合约1分钟，10万集合约10分钟，30万集合约30分钟 - 版本路径：必须逐版本升级：3.4 → 3.6 → 4.0 → 4.2 → 4.4（共4次升级） - 数据路径：默认路径为 [REDACTED_SECRET]，如自定义请相应调整 d. **升级步骤**（严格按照官网文档执行）： - **步骤 1**：停止所有服务（提供完整命令） ```bash # 在管理器根目录执行（通常在 /usr/local/MDPrivateDeployment/） bash ./service.sh stopall ``` - **步骤 2**：备份数据（提供检查数据大小和创建备份的完整命令） ```bash # 检查磁盘空间 du -sh /data/mingdao/script/volume/data/mongodb # 创建备份（备份目录可自定义） mkdir -p /backup && tar -zcvf /backup/mongodb3.4_$(date +%Y%m%d%H%M%S).tar.gz /data/mingdao/script/volume/data/mongodb ``` - **步骤 3**：拉取升级辅助镜像（提供完整命令） ```bash docker pull registry.cn-hangzhou.aliyuncs.com/mdpublic/mingdaoyun-sc-upgrade:1.0.0 ``` - **步骤 4**：逐版本升级执行（提供完整的 4 个升级命令，每个命令都必须包含完整的参数） ```bash # 3.4 到 3.6 docker run -i --rm -v /data/mingdao/script/volume/data/mongodb:/data/mongodb registry.cn-hangzhou.aliyuncs.com/mdpublic/mingdaoyun-sc-upgrade:1.0.0 <<< 'upgradeMongodb.sh 3.4 3.6' # 3.6 到 4.0 docker run -i --rm -v /data/mingdao/script/volume/data/mongodb:/data/mongodb registry.cn-hangzhou.aliyuncs.com/mdpublic/mingdaoyun-sc-upgrade:1.0.0 <<< 'upgradeMongodb.sh 3.6 4.0' # 4.0 到 4.2 docker run -i --rm -v /data/mingdao/script/volume/data/mongodb:/data/mongodb registry.cn-hangzhou.aliyuncs.com/mdpublic/mingdaoyun-sc-upgrade:1.0.0 <<< 'upgradeMongodb.sh 4.0 4.2' # 4.2 到 4.4 docker run -i --rm -v /data/mingdao/script/volume/data/mongodb:/data/mongodb registry.cn-hangzhou.aliyuncs.com/mdpublic/mingdaoyun-sc-upgrade:1.0.0 <<< 'upgradeMongodb.sh 4.2 4.4' ``` - **步骤 5**：升级成功标志（说明如何判断升级完成） * 当看到输出中包含 `newRunVersion: 4.4` 时表示升级成功 * 其他常见输出： - 成功升级：显示 `newRunVersion: 目标版本` - 已执行过升级：显示 `exit upgrade` - 版本不匹配：显示错误信息，需要重复执行上一个成功的升级命令 - **步骤 6**：故障处理提示（提供查看详细日志的方法） ```bash # 如果升级过程中出现版本不匹配错误，需要重复执行上一个成功的升级命令 # 多次尝试仍失败可查看日志 cat /data/mingdao/script/volume/data/mongodb/upgrade-xxxx.log ``` e. **重建 mdwsrows 库的自建索引**（必须详细展开，不能一笔带过）： - 参考 [MongoDB 索引重建文档](https://docs-pd.mingdao.com/deployment/components/mongodb/reIndex) 执行 - **第一部分：重建 `mdwsrows` 数据库索引**： * 步骤 1：进入存储组件容器（提供完整命令） ```bash docker exec -it $(docker ps | grep mingdaoyun-sc | awk '{print $1}') bash ``` * 步骤 2：创建脚本文件 `reIndex.js`（提供完整的 JavaScript 脚本内容，不能省略） ```javascript var targetDbName = "mdwsrows"; // 集合白名单，白名单中的集合不重建索引 var collectionWhitelist = ["discussion", "rowrelations", "workSheetRowTopic", "workSheetTopic", "wslogs"]; // 索引白名单，白名单中的索引不重建 var indexWhitelist = ["_id_", "idx_ctime", "idx_utime", "uk_rowid", "idx_tp_status", "idx_thirdprimary"]; // 格式化时间函数 function formatDateTime() { var now = new Date(); var utc8Time = new Date(now.getTime() + (8 * 60 * 60 * 1000)); return utc8Time.toISOString().replace('Z', '+08:00'); } // 格式化输出函数 function printHeader(text) { print("\n" + "=".repeat(100)); print(text); print("=".repeat(100)); } function printSection(text) { print("\n" + "-".repeat(80)); print(text); print("-".repeat(80)); } function printTimedAction(time, action) { print(`\n[${time}] ${action}`); } // 格式化 JSON，保持一致的缩进 function formatJSON(obj, indent = 5) { return JSON.stringify(obj, null, 2).split('\n').map((line, i) => i === 0 ? line : ' '.repeat(indent) + line).join('\n'); } // 格式化 createIndex 命令 function formatCreateIndexCommand(collName, key, options) { return `db.${collName}.createIndex(${formatJSON(key)},\n ${formatJSON(options)})`; } function printCommand(command) { print(" └─ Execute:"); print(" " + command); } function printCompletion(seconds) { print(` └─ ✓ Completed in ${seconds.toFixed(3)} seconds\n`); } // 连接到指定数据库 var targetDb = db.getSiblingDB(targetDbName); var startTime = formatDateTime(); printHeader("MongoDB Index Rebuild Process"); print(`\n• Start Time: ${startTime}`); print(`• Target Database: ${targetDb.getName()}`); // 获取所有要重建所有的集合（过滤集合白名单） var collections = targetDb.getCollectionNames().filter(function(collName) { return !collectionWhitelist.includes(collName) && !collName.startsWith('system.'); }); print(`• Total Collections: ${collections.length}`); collections.forEach(function(collName, index) { var coll = targetDb.getCollection(collName); var stats = coll.stats(); // 输出集合信息和进度 printSection(`Processing Collection [${index + 1}/${collections.length}]: ${collName}`); print(`\n• Document Count: ${stats.count}`); print(`• Storage Size: ${stats.storageSize} bytes`); // 获取需要重建的索引（过滤索引白名单） var indexes = coll.getIndexes(); var rebuildIndexes = indexes.filter(function(idx) { return !indexWhitelist.includes(idx.name); }); if (rebuildIndexes.length === 0) { print("\n✓ No indexes need to be rebuilt."); return; } // 输出索引重建计划 print(`\n• Indexes to Rebuild (${rebuildIndexes.length}):`); rebuildIndexes.forEach(function(idx) { print(` ├─ Name: ${idx.name.padEnd(20)}`); print(` │ Key: ${JSON.stringify(idx.key)}`); }); // 重建每个索引 rebuildIndexes.forEach(function(idx) { var key = idx.key; var options = {}; // 复制索引配置（排除系统属性） for (var prop in idx) { if (!["v", "ns", "background"].includes(prop)) { options[prop] = idx[prop]; } } options.background = true; // 后台构建索引 try { // 删除旧索引 var dropTime = formatDateTime(); printTimedAction(dropTime, `Dropping Index: ${idx.name}`); printCommand(`db.${collName}.dropIndex("${idx.name}")`); var dropStart = new Date(); coll.dropIndex(idx.name); var dropEnd = new Date(); printCompletion((dropEnd - dropStart) / 1000); // 创建新索引 var createTime = formatDateTime(); printTimedAction(createTime, `Creating Index: ${idx.name}`); printCommand(formatCreateIndexCommand(collName, key, options)); var createStart = new Date(); coll.createIndex(key, options); var createEnd = new Date(); printCompletion((createEnd - createStart) / 1000); } catch (e) { print(` └─ ✗ Error: ${e.message}`); print(" Skipping this index...\n"); } }); }); var endTime = formatDateTime(); printHeader("Process Completed"); print(`\n• End Time: ${endTime}`); ``` * 步骤 3：执行脚本（根据 MongoDB 认证配置，提供无认证和有认证两种情况的完整命令） ```bash # MongoDB 无认证 nohup mongo mongodb://127.0.0.1:27017/admin --quiet reIndex.js >> reIndex_output.log 2>&1 & # MongoDB 有认证（替换 root:password 为实际用户名和密码） nohup mongo mongodb://root:password@127.0.0.1:27017/admin --quiet reIndex.js >> reIndex_output.log 2>&1 & ``` * 步骤 4：观察日志（提供观察进度的命令） ```bash # 实时查看日志输出 tail -f reIndex_output.log # 等待脚本执行完成（日志结尾会输出 "Process Complete" 与 "End Time"） ``` - **第二部分：重建 HAP 系统依赖索引**： * 步骤 5：创建脚本文件 `reIndexWithCmd.js`（提供完整的 JavaScript 脚本内容，不能省略） ```javascript // ==================================================================== // CONFIGURATION // ==================================================================== // 在这里配置您需要重建索引的数据库及其白名单集合 // 格式: "数据库名": ["要跳过的集合1", "要跳过的集合2..."] // 如果某个库下所有集合都需要重建索引，请使用空数组 [] var targetDatabases = { "mdpost": [], "MDHistory": [] }; // ==================================================================== // 格式化输出函数 (无需修改) function printHeader(text) { print("\n" + "=".repeat(100)); print(text); print("=".repeat(100)); } function printSection(text) { print("\n" + "-".repeat(80)); print(text); print("-".repeat(80)); } function formatDateTime() { var now = new Date(); var utc8Time = new Date(now.getTime() + (8 * 60 * 60 * 1000)); return utc8Time.toISOString().replace('Z', '+08:00'); } function formatJSON(obj, indent = 5) { if (obj === undefined || obj === null) { return "Not available"; } return JSON.stringify(obj, null, 2).split('\n').map((line, i) => i === 0 ? line : ' '.repeat(indent) + line).join('\n'); } function formatFileSize(bytes) { if (bytes === undefined || bytes === null) return "N/A"; return (bytes / 1024 / 1024).toFixed(2) + " MB"; } function printTimedAction(time, action) { print(`\n[${time}] ${action}`); } function printCompletion(seconds) { print(` └─ ✓ Completed in ${seconds.toFixed(3)} seconds\n`); } // ==================================================================== // SCRIPT EXECUTION // ==================================================================== var overallStartTime = formatDateTime(); printHeader("MongoDB Multi-Database Index Rebuild Process Started"); print(`\n• Overall Start Time: ${overallStartTime}`); print(`• Databases to Process: ${Object.keys(targetDatabases).join(', ')}`); // 遍历配置中的所有数据库 for (var dbName in targetDatabases) { if (targetDatabases.hasOwnProperty(dbName)) { printHeader(`Processing Database: [ ${dbName} ]`); // 获取目标数据库连接 var currentDb = db.getSiblingDB(dbName); // 获取当前数据库的集合黑名单（即需要跳过的集合列表） var excludedCollections = targetDatabases[dbName]; print(`\n• Target Database: ${currentDb.getName()}`); print(`• Excluded Collections: ${formatJSON(excludedCollections)}`); // 获取所有集合 var collections = currentDb.getCollectionNames(); // 过滤掉黑名单中的集合和系统集合 var validCollections = collections.filter(collection => !excludedCollections.includes(collection) && !collection.startsWith('system.') ); print(`• Total Collections to Process in this DB: ${validCollections.length}`); if (validCollections.length === 0) { print("\nNo collections to process in this database. Moving to the next one."); continue; } validCollections.forEach(function(collection, index) { try { // 获取集合统计信息 var stats = currentDb[collection].stats(); // 输出集合信息和进度 printSection(`Processing Collection [${index + 1}/${validCollections.length}]: ${collection}`); print(`\n• Collection Statistics:`); print(` ├─ Storage Size: ${formatFileSize(stats.storageSize)}`); print(` └─ Document Count: ${(stats.count || 0).toLocaleString()}`); // 获取当前索引信息 var indexes = currentDb[collection].getIndexes(); print(`\n• Current Indexes (${indexes.length}):`); indexes.forEach(function(idx, i) { const isLast = i === indexes.length - 1; print(`${isLast ? '└' : '├'}─ Name: ${idx.name.padEnd(20)}`); print(`${isLast ? ' ' : '│'} Key: ${formatJSON(idx.key)}`); }); // 执行 reIndex var execTime = formatDateTime(); printTimedAction(execTime, "Executing reIndex()"); print(" └─ Execute:"); print(` db.getSiblingDB('${dbName}').getCollection('${collection}').reIndex()`); var startExec = new Date(); var result = currentDb[collection].reIndex(); var endExec = new Date(); if (result.ok === 1) { printCompletion((endExec - startExec) / 1000); print("• Operation Results:"); print(` ├─ Previous Index Count: ${result.nIndexesWas}`); print(` ├─ Current Index Count: ${result.nIndexes}`); if (result.operationTime !== undefined) { print(` ├─ Operation Time: ${formatJSON(result.operationTime)}`); } else { print(` ├─ Operation Time: Not available (non-replica set deployment)`); } if (result.$clusterTime !== undefined) { print(` └─ Cluster Time: ${formatJSON(result.$clusterTime)}`); } else { print(` └─ Cluster Time: Not available (non-replica set deployment)`); } } else { print(` └─ ✗ ReIndex failed: ${formatJSON(result)}`); } } catch (e) { print(` └─ ✗ Error: ${e.message}`); print(" Skipping this collection...\n"); } }); } } var overallEndTime = formatDateTime(); printHeader("Process Completed"); print(`\n• Overall End Time: ${overallEndTime}`); ``` * 步骤 6：执行脚本（根据 MongoDB 认证配置，提供无认证和有认证两种情况的完整命令） ```bash # MongoDB 无认证 nohup mongo mongodb://127.0.0.1:27017/admin --quiet reIndexWithCmd.js >> reIndexWithCmd_output.log 2>&1 & # MongoDB 有认证（替换 root:password 为实际用户名和密码） nohup mongo mongodb://root:password@127.0.0.1:27017/admin --quiet reIndexWithCmd.js >> reIndexWithCmd_output.log 2>&1 & ``` * 步骤 7：观察日志（提供观察进度的命令） ```bash # 实时查看日志输出 tail -f reIndexWithCmd_output.log # 等待脚本执行完成（日志结尾会输出 "Process Complete" 与 "End Time"） ``` - **操作前最终确认**（必须列出所有确认项）： * 已在测试环境验证过此流程 * 已对生产数据库进行完整备份 * 已选择业务低峰期窗口 * 已通知相关用户可能出现的服务影响 f. **建议和注意事项**： * 在重建索引前，先导出当前所有索引的定义 * 索引重建可能需要较长时间，取决于数据量和索引数量 * 重建期间可能影响系统性能，建议在低峰期执行 * 如果 `mongo` 命令没加入 PATH 变量，需指定绝对路径 - **要求**： * 所有命令必须提供完整代码块，不能省略参数 * JavaScript 脚本内容必须完整，不能只说"参考官网文档" * 必须区分无认证和有认证两种情况 * 必须提供观察日志的命令 * 必须详细列出所有升级步骤，不能一笔带过 3. **禁止事项**： - 禁止简化或概括这两个特殊升级操作的步骤 - 禁止只提供参考链接而不展开详细步骤 - 禁止省略 JavaScript 脚本内容 - 禁止省略命令中的任何参数 - 禁止将这两个操作与其他升级前操作合并为一个步骤 - 禁止遗漏任何警告、注意事项或验证步骤 ## 镜像名称规范化规则 在生成升级文档时，必须严格遵守以下镜像名称映射规则，禁止根据官网显示名称推断实际镜像名称： ### 1. 文档预览服务镜像名称 | 官网显示名称 | 实际镜像名称 | 说明 | |-------------|-------------|------| | mingdaoyun-doc-preview | mingdaoyun-doc | 文档预览服务主镜像 | | mingdaoyun-doc-preview-extension | mingdaoyun-ldoc | 文档预览扩展服务镜像 | **重要**： - 文档预览相关的镜像名称必须使用实际镜像名称（mingdaoyun-doc 和 mingdaoyun-ldoc） - 禁止直接使用官网文档中显示的 mingdaoyun-doc-preview 或 mingdaoyun-doc-preview-extension - 在拉取镜像、修改 docker-compose.yaml 或编写命令时，务必使用正确的实际镜像名称 - **此映射关系为 AI 内部约束，禁止在生成的升级文档中展示给用户**（即不要在文档中出现"实际镜像名称与官网显示名称不同"这类说明，直接使用正确名称即可） ### 2. 其他镜像名称 其他镜像名称（如 mingdaoyun-hap、mingdaoyun-sc、mingdaoyun-command 等）以官方文档为准，按官网显示使用。 ## 镜像准备阶段规则 在生成升级文档的"提前准备"章节时，必须遵循以下规则： ### 1. 镜像版本选择原则 当升级路径跨越多个版本，涉及需要提前拉取的镜像时： - **只拉取目标版本的镜像**，无需拉取中间版本的镜像 - 例如：从 v4.8.1 升级到 v6.2.0，只需要拉取 v6.2.0 版本的镜像，不需要拉取 v5.1.0、v5.5.0 等中间版本的镜像 ### 2. 存储组件和文档预览服务镜像 - **存储组件**：直接升级到跨越路径要求的**最高版本** - **文档预览服务**：直接升级到跨越路径要求的**最高版本** - 例如：从 v4.8.1 升级到 v6.2.0，存储组件需要升级到 v3.1.0（跨越路径中要求的最高的版本），文档预览需要升级到 v2.0.0 ### 3. "提前准备"章节内容规范 "提前准备"章节必须列出： - HAP 微服务镜像（目标版本） - 如果升级路径要求存储组件升级，列出对应的存储组件镜像（最高版本） - 如果升级路径要求文档预览服务升级，列出对应的文档预览镜像（最高版本，使用正确的镜像名称） - 如果升级路径涉及 MongoDB 预置数据更新，列出对应的预置数据下载链接 - 其他官方文档中明确要求提前准备的资源 **禁止**： - 禁止列出不会用到的镜像或资源 - 禁止列出中间版本的镜像 - 禁止遗漏官方文档中明确要求提前准备的资源 ### 4. 文档预览服务离线包下载地址规范 在离线环境升级指南的"提前准备"章节中，必须给出文档预览服务的**实际 tar.gz 文件下载链接**，不得只写文字说明或跳转到离线资源页面。 **AMD64 架构实际下载地址**： | 服务 | 镜像名称 | 版本 | 实际下载链接 | |------|---------|------|------------| | 文档预览服务 | mingdaoyun-doc | 2.0.0 | `https://pdpublic.mingdao.com/private-deployment/offline/mingdaoyun-doc-linux-amd64-2.0.0.tar.gz` | | 文档预览扩展服务（ldoc，可选） | mingdaoyun-ldoc | 2.0.2 | `https://pdpublic.mingdao.com/private-deployment/offline/mingdaoyun-ldoc-linux-amd64-2.0.2.tar.gz` | **ARM64 架构下载地址格式**（将 `amd64` 替换为 `arm64`）： - `https://pdpublic.mingdao.com/private-deployment/offline/mingdaoyun-doc-linux-arm64-2.0.0.tar.gz` - `https://pdpublic.mingdao.com/private-deployment/offline/mingdaoyun-ldoc-linux-arm64-2.0.2.tar.gz` **禁止**： - 禁止在离线场景下只写"请前往离线资源页下载"或"下载地址见官网"等文字说明 - 必须直接在下载表格中填写完整的 `https://` 开头的 tar.gz 文件链接 - 如果官网离线页面有更新的版本，以抓取到的实际页面内容为准，不得使用本规范中过时的版本号 ## 生成升级指南时的要求 ### 1. 先选模板 根据部署模式读取 Markdown 模板： - 单机模式：`assets/upgrade-guide-template-standalone.md` - 集群模式：`assets/upgrade-guide-template-cluster.md` 禁止脱离模板凭记忆重写整个结构。HTML 无需读取模板，由脚本从 MD 自动生成（见 2.3 节）。 ### 2. 再填内容 #### 2.1 Markdown 文档结构要求 Markdown 是唯一事实来源，HTML 从它自动生成，因此 Markdown 内容必须完整、正确。 **文档必须包含以下章节（按顺序）**： 1. 标题（`# HAP 升级指南（单机/集群模式）`） 2. 版本信息表格（两列表格：升级路径、部署模式、架构、网络、生成日期） 3. 提前准备 4. 升级前准备（必须包含"授权有效期检查"和"前端二开注意事项"两个小节，分别列为第一项和第二项，即便无其他步骤也要保留此两节） 5. 升级步骤 - 第一阶段：HAP 微服务升级前操作 - 第二阶段：升级微服务 - 第三阶段：HAP 微服务升级后操作 6. 升级后验证 7. 参考文档 8. 底部声明（AI 生成声明） 单机模式还包含"异常情况排查"章节；集群模式不包含此章节。 #### 2.2 Markdown 文档填充规则 填充 Markdown 文档时，遵守以下规则： - 文档顶部信息必须补全升级路径、部署模式、架构、网络情况、生成日期，**必须使用两列 Markdown 表格格式**（禁止使用行内加粗键值对格式，因为会挤在一起）。格式示例： ```markdown | 项目 | 内容 | |------|------| | **升级路径** | v4.8.1 → v7.2.1 | | **部署模式** | 单机模式（Docker Compose） | | **服务器架构** | AMD64 | | **服务器网络** | 可访问互联网 | | **文档生成日期** | 2026-03-30 | ``` - 备份步骤必须保留在最前面 - "提前准备"必须汇总**本次升级实际会用到的全部资源**，不限于 HAP 微服务镜像。只要升级步骤或附加操作中会用到镜像、离线包、脚本、预置数据、额外组件资源，都应提前列出 - 如果某一阶段完全没有步骤，删除整节，不保留占位提示 - 所有命令必须给出完整代码块，不能写"同上""省略""按前文类似处理" - 生成的是面向执行者的文档，不要把模型的推理过程写进正文 - **禁止**在产物中出现"不要预设资源类型已经列全""如本次升级还涉及其他离线镜像或资源，请继续补充"等面向 AI 自身的指令性免责文本，这些仅作为内部约束，不输出给用户 - 如果线上文档把附加操作写成超链接或跳转说明，最终输出时必须把超链接背后的实际步骤展开写入对应章节，不能只给一个链接让用户自己点进去 - **微服务升级必须包含 ENV_APP_VERSION 变量值设置**： - **单机模式**：在"第二阶段：升级微服务"的"修改镜像版本号"步骤中，必须同时说明： * 修改 `docker-compose.yaml` 中的 HAP 镜像版本号 * 确保 `ENV_APP_VERSION` 环境变量的值与微服务镜像版本号保持一致（例如：微服务版本是 7.1.1，`ENV_APP_VERSION` 的值也应设为 7.1.1） * 提供完整的修改示例： ```yaml # 修改前 image: registry.cn-hangzhou.aliyuncs.com/mdpublic/mingdaoyun-hap:4.8.1 environment: - ENV_APP_VERSION=4.8.1 # 修改后 image: registry.cn-hangzhou.aliyuncs.com/mdpublic/mingdaoyun-hap:6.2.0 environment: - ENV_APP_VERSION=6.2.0 ``` - **集群模式**：在"第二阶段：升级微服务"的升级步骤中，必须同时说明： * `update.sh update hap {目标版本号}` 命令会自动更新镜像版本号 * `ENV_APP_VERSION` 环境变量也会通过 update.sh 自动更新为相同的版本号 * 如果需要手动确认，可以查看部署的 yaml 文件或 Pod 环境变量进行验证 - **升级前准备中必须包含以下两个注意事项小节**（按顺序列为第一项和第二项）： **第一项：授权有效期检查** - **位置**：放置在"升级前准备"章节的**第一项**（编号为第 1 步） - **标题**：`授权有效期检查` - **必须标注重要提示**，使用 `> ⚠️` 引用块 - **必须包含以下说明**： * 请确保您的授权密钥仍在"升级服务"有效期内 * 若目标主版本的发布日期晚于授权到期日，强行升级将触发系统受限提示，并导致授权自动降级为免费版 * 建议在升级前确认版本发布日期与授权期限的匹配情况 * 若授权即将到期或已过期，请联系明道云商务团队续期后再执行升级 - **固定提示语**（必须包含）： ``` > ⚠️ **重要提示**：请确保您的授权密钥仍在"升级服务"有效期内。若目标主版本的发布日期晚于授权到期日，强行升级将触发系统受限提示，并导致授权自动降级为免费版。建议在升级前确认版本发布日期与授权期限的匹配情况。 ``` **第二项：前端二次开发注意事项** - **位置**：放置在"升级前准备"章节的**第二项**（编号为第 2 步） - **标题**：`前端二次开发注意事项` - **必须包含以下说明**： * 若系统中存在前端二次开发（即有基于 HAP 前端源码进行过定制开发），升级后前端代码可能与新版本存在冲突 * 需要**前端二开负责同事**在升级前或升级后（根据实际情况确定时机），执行以下操作： 1. 拉取最新的前端二开基础代码（通常为官方前端仓库对应目标版本的分支或 tag） 2. 将自定义的二开代码合并（merge）进最新基础代码，处理可能存在的冲突 3. 构建并发布更新后的前端服务，使新版本前端生效 * 若系统中**没有**前端二次开发，忽略本注意事项 - **固定提示语**（必须包含）： ``` > ⚠️ **注意**：如有前端二次开发，请联系前端二开负责同事确认此操作已完成，否则可能导致升级后前端功能异常。 ``` #### 2.3 HTML 文档生成 **MD 是唯一事实来源，HTML 完全由 AI 直接生成。不依赖任何本地 Python 脚本，也不需要安装任何依赖。** 生成 Markdown 文档后，AI 直接读取 MD 文件内容，按以下规范转换为 HTML，并用 `write_to_file` 写出 `.html` 文件。 --- **HTML 模板结构（严格遵守）**： ```html <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{文档标题}</title> <style> /* 见下方 CSS 规范 */ </style> </head> <body> <div class="layout"> <nav class="sidebar"> <div class="sidebar-title">文档目录</div> <ul class="toc" id="toc"></ul> </nav> <div class="main"> <div class="content"> {正文 HTML 内容} </div> </div> </div> <script> /* 见下方 JS 规范 */ </script> </body> </html> ``` --- **CSS 规范（完整内嵌，不省略）**： ```css * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; font-size: 15px; line-height: 1.75; color: #24292f; background: #f6f8fa; } .layout { display: flex; min-height: 100vh; } .sidebar { width: 260px; flex-shrink: 0; background: #fff; border-right: 1px solid #d0d7de; position: fixed; top: 0; left: 0; height: 100vh; overflow-y: auto; padding: 24px 0; } .sidebar-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; color: #57606a; padding: 0 20px 10px; border-bottom: 1px solid #d0d7de; margin-bottom: 12px; } .toc { list-style: none; padding: 0; } .toc li a { display: block; padding: 4px 20px; font-size: 13px; color: #24292f; text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-left: 3px solid transparent; } .toc li a:hover { background: #f6f8fa; color: #0969da; } .toc li a.active { border-left-color: #0969da; color: #0969da; font-weight: 600; background: #f0f6ff; } .toc li.h3 a { padding-left: 34px; font-size: 12.5px; color: #57606a; } .toc li.h4 a { padding-left: 48px; font-size: 12px; color: #57606a; } .main { flex: 1; margin-left: 260px; background: #f6f8fa; min-width: 0; } .content { max-width: 980px; margin: 0 auto; padding: 36px 48px 60px; background: #fff; border-left: 1px solid #d0d7de; border-right: 1px solid #d0d7de; min-height: 100vh; } h1 { font-size: 28px; font-weight: 700; margin: 0 0 16px; color: #1f2328; } h2 { font-size: 22px; font-weight: 600; margin: 36px 0 14px; padding-bottom: 8px; border-bottom: 1px solid #d0d7de; color: #1f2328; } h3 { font-size: 18px; font-weight: 600; margin: 28px 0 10px; color: #1f2328; } h4 { font-size: 16px; font-weight: 600; margin: 22px 0 8px; color: #1f2328; } h5 { font-size: 14px; font-weight: 600; margin: 18px 0 6px; color: #1f2328; } p { margin-bottom: 14px; color: #24292f; } ul, ol { margin: 8px 0 14px 24px; color: #24292f; } li { margin-bottom: 5px; line-height: 1.7; } li > ul, li > ol { margin: 4px 0 4px 20px; } blockquote { border-left: 4px solid #d0d7de; margin: 16px 0; padding: 4px 16px; color: #57606a; } blockquote p { margin-bottom: 4px; } /* 特别注意高亮样式 */ blockquote.attention { border-left: 4px solid #cf222e; background: #fff1f0; padding: 12px 16px; color: #1f2328; border-radius: 0 6px 6px 0; } blockquote.attention p { color: #1f2328; margin-bottom: 4px; } table { width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 14px; } th, td { border: 1px solid #d0d7de; padding: 8px 12px; text-align: left; } th { background: #f6f8fa; font-weight: 600; } tr:nth-child(even) td { background: #f6f8fa; } /* 版本信息表格（文档第一个表格）特殊样式 */ table.meta-block { border-radius: 8px; overflow: hidden; margin-bottom: 28px; } table.meta-block th { color: #57606a; font-size: 12.5px; text-transform: uppercase; letter-spacing: .4px; width: 140px; } code { font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; font-size: 85%; background: #eef0f3; border: 1px solid #d0d7de; border-radius: 4px; padding: 2px 5px; color: #cf222e; } .code-block { position: relative; margin: 16px 0; border-radius: 6px; overflow: hidden; border: 1px solid #d0d7de; } .code-block pre { background: #f6f8fa; color: #24292f; margin: 0; padding: 16px; overflow-x: auto; font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.5px; line-height: 1.5; } .code-block pre code { background: none; border: none; padding: 0; color: inherit; font-size: inherit; border-radius: 0; } .code-block::before { content: attr(data-lang); position: absolute; top: 8px; left: 12px; font-size: 11px; font-family: "SFMono-Regular", Consolas, monospace; color: #57606a; text-transform: uppercase; letter-spacing: .5px; pointer-events: none; } .code-block[data-lang]:not([data-lang=""]) pre { padding-top: 32px; } .copy-btn { position: absolute; top: 6px; right: 8px; padding: 3px 10px; background: rgba(175,184,193,.8); color: #24292f; border: 1px solid rgba(27,31,35,.15); border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: 500; transition: background .15s, color .15s; z-index: 2; } .copy-btn:hover { background: rgba(175,184,193,1); } .copy-btn.copied { background: #2ea44f; color: #fff; border-color: transparent; } hr { border: none; border-top: 1px solid #d0d7de; margin: 28px 0; } a { color: #0969da; text-decoration: none; } a:hover { text-decoration: underline; } ``` --- **JS 规范（完整内嵌，不省略）**： ```javascript // 复制按钮 document.querySelectorAll('.copy-btn').forEach(function(btn) { btn.addEventListener('click', function() { var pre = btn.closest('.code-block').querySelector('pre'); navigator.clipboard.writeText(pre.innerText).then(function() { btn.textContent = '已复制'; btn.classList.add('copied'); setTimeout(function() { btn.textContent = '复制'; btn.classList.remove('copied'); }, 1500); }); }); }); // 目录生成 & 滚动高亮 (function() { var headings = document.querySelectorAll('.content h2, .content h3, .content h4'); var toc = document.getElementById('toc'); if (!toc || headings.length === 0) return; headings.forEach(function(h, idx) { if (!h.id) h.id = 'heading-' + idx; var li = document.createElement('li'); li.className = h.tagName.toLowerCase(); var a = document.createElement('a'); a.href = '#' + h.id; a.textContent = h.textContent; li.appendChild(a); toc.appendChild(li); }); var links = toc.querySelectorAll('a'); function onScroll() { var scrollY = window.scrollY + 80; var active = null; headings.forEach(function(h) { if (h.offsetTop <= scrollY) active = h.id; }); links.forEach(function(a) { a.classList.toggle('active', a.getAttribute('href') === '#' + active); }); } window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); })(); ``` --- **Markdown → HTML 转换规则（AI 执行）**： | Markdown 语法 | HTML 输出 | |---------------|-----------| | `# 标题` | `<h1 id="...">标题</h1>` | | `## 标题` | `<h2 id="...">标题</h2>`（id 由标题文字生成，用于目录锚点） | | `### / ####` | `<h3> / <h4>`（同上） | | `` ` `` 行内代码 `` ` `` | `<code>内容</code>` | | ` ```lang\n代码\n``` ` | `<div class="code-block" data-lang="lang"><pre><code>代码内容</code></pre><button class="copy-btn">复制</button></div>` | | `**粗体**` | `<strong>粗体</strong>` | | `- 列表项` | `<ul><li>列表项</li></ul>` | | `1. 列表项` | `<ol><li>列表项</li></ol>` | | `> 引用` | `<blockquote><p>引用</p></blockquote>` | | `> ⚠️ **特别注意**：...` | `<blockquote class="attention"><p>⚠️ <strong>特别注意</strong>：...</p></blockquote>`（醒目红底样式） | | 表格 | `<table>` 标准结构，首个表格额外加 `class="meta-block"` | | `[文字](url)` | `<a href="url">文字</a>` | | `---` | `<hr>` | | 空行分段 | `<p>段落内容</p>` | **代码块特别要求**（最关键，不得出错）： - 所有代码内容**必须原样保留**，不得截断、省略或改写，不论代码有多长（包括 4000+ 字符的 JS 脚本） - `<` `>` `&` `"` 需 HTML 转义为 `&lt;` `&gt;` `&amp;` `&quot;` - `data-lang` 属性值为代码块语言标识（如 `bash`、`yaml`、`javascript`），无语言标识时写 `data-lang=""` - **禁止**用 `...` 或 `省略` 代替代码内容 - **禁止**在代码块中插入换行符或修改缩进 **标题 id 生成规则**：取标题文字，保留中文和字母数字，空格转 `-`，其余字符删除，全部小写。例：`第一阶段：HAP 微服务升级前操作` → `id="第一阶段hap-微服务升级前操作"` **禁止事项**： - 禁止手动填充任何旧 HTML 模板 - 禁止省略或截断任何代码块内容 - 禁止在输出 HTML 前将文件交给用户（必须先写入文件） ### 3. 严格裁剪模板块 模板中的可选段落必须清理干净： - 联网场景：只保留联网版本的段落，删除离线版本的段落 - 离线场景：只保留离线版本的段落，删除联网版本的段落 - 最终输出中不得保留"以下二选一""按场景保留其一""删除不适用内容"之类模板提示，也不得保留 `{...}` 占位说明或模板注释语 ### 4. 命令与内容来源 生成命令时，优先顺序如下： 1. 当前抓取到的官方升级详情页原文 2. `references/command-library.md` 3. 模板中的固定结构 如果三者出现冲突，以官方实时页面为准；但不要擅自拼接出官网没有表达过的新命令逻辑。 ### 5. 针对不同部署模式的硬规则 - 集群模式不能生成"存储组件升级"步骤，除非官方实时页面明确要求 - 单机模式不能生成 `kubectl`、`crictl`、`ctr` 命令 - 集群模式不能生成 `docker exec`、`service.sh restartall` 这类单机命令 - ARM64 时，涉及镜像名或更新命令时，必须检查是否需要 `-arm64` ## 仅做咨询答复时的要求 如果用户只是咨询，不一定要输出完整升级指南，但仍要遵循： - 先补齐缺失信息，或明确指出因为哪些信息缺失暂时无法下结论 - 涉及具体版本结论时，必须先抓取官方页面再回答 - 回答里要明确区分"已确认事实"和"基于缺失信息的待确认项" - 如果判断结果依赖架构支持或是否联网，要把这一点直接说清楚 ## 禁止事项 禁止出现以下行为： - 凭训练记忆回答具体版本是否支持某架构 - 直接使用 `references/site-structure.md` 中的兼容性快照当作结论 - 杜撰官网未出现的升级步骤、版本路径、下载地址或脚本命令 - 在最终文档中保留模板占位符、AI 推理说明、合并逻辑说明 - 在信息不完整时伪造缺失参数 ## 最终检查清单 在输出前，自检以下项目： - 已确认 5 项前置信息 - 版本号已同时规范化为应用版本和显示版本 - 已抓取 `/version` 实时页面 - 已按目标架构完成兼容性校验 - 已收集跨越路径中的含附加操作版本 - 已按 `merge-rules.md` 合并 - 需要生成文档时，已读取正确模板 - 已按网络场景裁剪模板中的可选段落 - 已把升级详情页或其跳转链接中的实际附加操作完整展开到正文 - 最终内容中无占位符、无逻辑注释、无残留条件标记 - 文末已追加声明 ## 固定声明 无论输出咨询答复还是升级指南，只要给出与升级执行相关的正式结论，末尾都附加以下声明： ```md --- 💡 声明：内容由 AI 生成。尽管已努力确保信息的合理性，但 AI 模型仍可能产生不准确、过时或存在偏差的内容。请在执行关键操作前，务必对照[官方文档](https://docs-pd.mingdao.com)进行核实校验。 ```


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)