gangvy 5 месяцев назад
Родитель
Сommit
fd7aaa293c

+ 318 - 88
scripts/deploy/upload-skills.js

@@ -1,18 +1,22 @@
 #!/usr/bin/env node
 /**
- * OpenClaw Skills CDN 上传脚本
+ * OpenClaw Skills + Workshop CDN 上传脚本 v4.1
  * 
  * 功能:
  *   1. 将所有 skills 上传到七牛云 CDN,生成下载 URL
  *   2. 将每个 skill 的元数据写入 Parse 数据库 Skill 表
- *   3. 生成 README.md(包含所有 skill 简介 + 下载链接)并上传到 CDN
+ *   3. 将 workshop 文件(playbook + session + memory templates)上传到 CDN
+ *   4. 生成 README.md(包含所有 skill 简介 + workshop 文件 + 下载链接)并上传到 CDN
  * 
  * 使用方法:
- *   node scripts/deploy/upload-skills.js
+ *   node scripts/deploy/upload-skills.js              # 上传全部(skills + workshop)
+ *   node scripts/deploy/upload-skills.js --skills-only # 只上传 skills
+ *   node scripts/deploy/upload-skills.js --workshop-only # 只上传 workshop
  * 
  * 前置条件:
  *   1. npm install qiniu
  *   2. 配置下方 QINIU_ACCESS_KEY 和 QINIU_SECRET_KEY
+ *   3. 先运行 deploy-to-openclaw.ps1 确保 ~/.openclaw 是最新的
  * 
  * 参考:hongben-server/modules/fmode-tikhub-server/upload.sh
  */
@@ -37,8 +41,16 @@ const PARSE_BASE = 'https://server.fmode.cn/parse';
 const APP_ID = 'ncloudmaster';
 const MASTER_KEY = process.env.PARSE_MASTER_KEY || 'SnkK12*&sunq2#@20!';
 
-// Skills 源目录(部署目录)
-const DEPLOYED_SKILLS_DIR = path.join(process.env.USERPROFILE || process.env.HOME, '.openclaw', 'skills');
+// 源目录(部署目录)
+const OPENCLAW_DIR = path.join(process.env.USERPROFILE || process.env.HOME, '.openclaw');
+const DEPLOYED_SKILLS_DIR = path.join(OPENCLAW_DIR, 'skills');
+const WORKSHOP_DIR = path.join(OPENCLAW_DIR, 'workspace');
+const MEMORY_TEMPLATES_DIR = path.join(WORKSHOP_DIR, 'memory');
+
+// CLI 参数
+const args = process.argv.slice(2);
+const SKILLS_ONLY = args.includes('--skills-only');
+const WORKSHOP_ONLY = args.includes('--workshop-only');
 
 // 要上传的技能目录列表(如果为空,则上传所有 skills)
 const SKILL_FILTER = []; // 例如 ['tiktok-video-search', 'jimeng-img-v4'] 指定只上传某些
@@ -77,6 +89,38 @@ const SKILL_PACKAGES = {
     }
 };
 
+// ============================================
+// Workshop 文件定义 (v4.1)
+// ============================================
+const WORKSHOP_FILES = {
+    playbooks: [
+        'workshop-voc-playbook.md',
+        'workshop-global-rules.md',
+        'workshop-session-0-intake.md',
+        'workshop-session-1-category.md',
+        'workshop-session-2-brand.md',
+        'workshop-session-3-voc.md',
+        'workshop-session-4-deep.md',
+        'workshop-session-5-report.md'
+    ],
+    supporting: [
+        'product-analysis-playbook.md',
+        'synthesis-prompts.md',
+        'voc-report-schema-spec.md'
+    ],
+    memoryTemplates: [
+        'workshop-progress.json',
+        'brand-context.json',
+        'analysis-request.json',
+        'calibration-notes.json',
+        'stage-1-output.json',
+        'stage-2-output.json',
+        'stage-3-output.json',
+        'stage-4-output.json',
+        'stage-5-output.json'
+    ]
+};
+
 function getPackageForCategory(category) {
     for (const [pkgId, pkg] of Object.entries(SKILL_PACKAGES)) {
         if (pkg.categories.includes(category || 'other')) return pkgId;
@@ -272,6 +316,83 @@ function extractSkillMeta(skillDir, skillName) {
     };
 }
 
+// ============================================
+// Collect workshop files
+// ============================================
+
+function collectWorkshopFiles() {
+    const files = [];
+
+    // Playbooks + supporting docs
+    const allMdFiles = [...WORKSHOP_FILES.playbooks, ...WORKSHOP_FILES.supporting];
+    for (const fileName of allMdFiles) {
+        const filePath = path.join(WORKSHOP_DIR, fileName);
+        if (fs.existsSync(filePath)) {
+            files.push({
+                localPath: filePath,
+                cdnKey: `${CDN_PREFIX}/workshop/${fileName}`,
+                fileName,
+                type: WORKSHOP_FILES.playbooks.includes(fileName) ? 'playbook' : 'supporting'
+            });
+        } else {
+            console.log(`    ⚠️  跳过 ${fileName}(不存在)`);
+        }
+    }
+
+    // Memory templates
+    for (const fileName of WORKSHOP_FILES.memoryTemplates) {
+        const filePath = path.join(MEMORY_TEMPLATES_DIR, fileName);
+        if (fs.existsSync(filePath)) {
+            files.push({
+                localPath: filePath,
+                cdnKey: `${CDN_PREFIX}/workshop/memory-templates/${fileName}`,
+                fileName: `memory-templates/${fileName}`,
+                type: 'memory-template'
+            });
+        } else {
+            console.log(`    ⚠️  跳过 memory-templates/${fileName}(不存在)`);
+        }
+    }
+
+    return files;
+}
+
+function generateWorkshopManifest(workshopResults) {
+    const playbooks = {};
+    const supporting = {};
+    const memoryTemplates = {};
+
+    for (const item of workshopResults) {
+        if (item.status !== 'ok') continue;
+        if (item.type === 'playbook') {
+            playbooks[item.fileName] = item.url;
+        } else if (item.type === 'supporting') {
+            supporting[item.fileName] = item.url;
+        } else if (item.type === 'memory-template') {
+            const baseName = item.fileName.replace('memory-templates/', '');
+            memoryTemplates[baseName] = item.url;
+        }
+    }
+
+    return {
+        package: 'workshop',
+        label: 'VOC虾工作坊',
+        description: 'VOC 工作坊完整流程文件(v4.1 Side Quest 架构)',
+        version: '4.1.0',
+        generatedAt: new Date().toISOString(),
+        architecture: {
+            sessions: 6,
+            sideQuests: 17,
+            stateFlow: 'pending → in_progress → awaitingCalibration → exploring → completed'
+        },
+        installTarget: {
+            playbooks: '~/.openclaw/workspace/',
+            memoryTemplates: '~/.openclaw/workspace/memory/'
+        },
+        files: { playbooks, supporting, memoryTemplates }
+    };
+}
+
 // ============================================
 // Collect skills
 // ============================================
@@ -320,7 +441,7 @@ function collectSkills() {
 // Generate README.md
 // ============================================
 
-function generateReadme(skillEntries) {
+function generateReadme(skillEntries, workshopManifestUrl) {
     const now = new Date().toISOString().replace('T', ' ').substring(0, 19);
 
     // 按技能包分组
@@ -338,9 +459,9 @@ function generateReadme(skillEntries) {
     }
 
     const lines = [
-        '# OpenClaw Skills 技能市场',
+        '# OpenClaw Skills 技能市场 + Workshop',
         '',
-        `> 自动生成于 ${now},共 ${visibleCount} 个技能(分 ${Object.keys(pkgMap).filter(k => SKILL_PACKAGES[k]?.status !== 'hidden').length} 个套件)`,
+        `> 自动生成于 ${now},共 ${visibleCount} 个技能(分 ${Object.keys(pkgMap).filter(k => SKILL_PACKAGES[k]?.status !== 'hidden').length} 个套件)+ VOC工作坊 v4.1`,
         '',
     ];
 
@@ -418,6 +539,29 @@ function generateReadme(skillEntries) {
     lines.push('下载对应套件的 `.json` 安装清单,包含该套件所有技能的 CDN 下载地址。');
     lines.push('');
 
+    // Workshop section
+    if (workshopManifestUrl) {
+        lines.push('---');
+        lines.push('');
+        lines.push('## 🦐 VOC虾工作坊 v4.1');
+        lines.push('');
+        lines.push('> VOC 工作坊完整流程文件,含 Side Quest 支线探索系统');
+        lines.push('>');
+        lines.push(`> 📥 安装清单:[\`workshop.json\`](${workshopManifestUrl})`);
+        lines.push('');
+        lines.push('**架构**:6个Session + 17个Side Quest,主线感知 + 支线深耕');
+        lines.push('');
+        lines.push('| 文件 | 说明 |');
+        lines.push('|------|------|');
+        lines.push('| `workshop-voc-playbook.md` | 入口路由 |');
+        lines.push('| `workshop-global-rules.md` | 全局规则 + Side Quest Protocol |');
+        lines.push('| `workshop-session-0~5-*.md` | Session 0-5 流程文件 |');
+        lines.push('| `memory-templates/*.json` | 记忆模板(9个JSON) |');
+        lines.push('');
+        lines.push('安装方式:将 playbook 文件放入 `~/.openclaw/workspace/`,memory templates 放入 `~/.openclaw/workspace/memory/`');
+        lines.push('');
+    }
+
     return lines.join('\n');
 }
 
@@ -461,9 +605,12 @@ function generatePackageManifests(skillEntries) {
 
 async function main() {
     console.log('==================================================');
-    console.log('   OpenClaw Skills CDN 上传 + Parse 同步脚本');
+    console.log('   OpenClaw Skills + Workshop CDN 上传 v4.1');
     console.log('==================================================');
     console.log('');
+    if (SKILLS_ONLY) console.log('  📌 模式: 仅上传 Skills');
+    if (WORKSHOP_ONLY) console.log('  📌 模式: 仅上传 Workshop');
+    console.log('');
 
     // 检查凭证
     if (QINIU_ACCESS_KEY === 'YOUR_ACCESS_KEY_HERE' || QINIU_SECRET_KEY === 'YOUR_SECRET_KEY_HERE') {
@@ -473,94 +620,179 @@ async function main() {
         process.exit(1);
     }
 
-    // 收集技能
-    const skills = collectSkills();
-    console.log(`📦 找到 ${skills.length} 个技能待上传`);
-    console.log(`📂 源目录: ${DEPLOYED_SKILLS_DIR}`);
     console.log(`☁️  Bucket: ${BUCKET}`);
     console.log(`🔗 CDN前缀: ${CDN_PREFIX}`);
     console.log('');
 
+    // 收集技能
+    const skills = WORKSHOP_ONLY ? [] : collectSkills();
+    if (!WORKSHOP_ONLY) {
+        console.log(`📦 找到 ${skills.length} 个技能待上传`);
+        console.log(`📂 Skills源: ${DEPLOYED_SKILLS_DIR}`);
+    }
+
+    // 收集 workshop 文件
+    const workshopFiles = SKILLS_ONLY ? [] : collectWorkshopFiles();
+    if (!SKILLS_ONLY) {
+        console.log(`� 找到 ${workshopFiles.length} 个 workshop 文件待上传`);
+        console.log(`📂 Workshop源: ${WORKSHOP_DIR}`);
+    }
+
     let totalFiles = 0;
     skills.forEach(s => { totalFiles += s.files.length; });
+    totalFiles += workshopFiles.length;
     console.log(`📄 总计 ${totalFiles} 个文件`);
     console.log('');
 
-    // ── Step 1: 上传文件到七牛 ──
-    console.log('── Step 1: 上传文件到七牛云 ──');
+    // ── Step 1: 上传 Skill 文件到七牛 ──
     let successCount = 0;
     let failCount = 0;
     const results = [];
 
-    for (const skill of skills) {
-        process.stdout.write(`  📦 ${skill.name} ... `);
-        const skillResults = [];
-        const cdnUrls = {};
+    if (!WORKSHOP_ONLY && skills.length > 0) {
+        console.log('── Step 1: 上传 Skills 到七牛云 ──');
+
+        for (const skill of skills) {
+            process.stdout.write(`  📦 ${skill.name} ... `);
+            const skillResults = [];
+            const cdnUrls = {};
+
+            for (const file of skill.files) {
+                try {
+                    const result = await uploadFile(file.localPath, file.cdnKey);
+                    skillResults.push({ ...result, fileName: file.fileName, status: 'ok' });
+                    cdnUrls[file.fileName] = result.url;
+                    successCount++;
+                } catch (err) {
+                    skillResults.push({ fileName: file.fileName, status: 'fail', error: err.message });
+                    failCount++;
+                }
+            }
+
+            const allOk = skillResults.every(r => r.status === 'ok');
+            console.log(allOk ? '✅' : '⚠️');
+            results.push({ name: skill.name, files: skillResults, meta: skill.meta, cdnUrls });
+        }
+
+        console.log('');
+        console.log(`   Skills 上传完成!成功: ${successCount}  失败: ${failCount}`);
+        console.log('');
+    } else if (!WORKSHOP_ONLY) {
+        console.log('── Step 1: 跳过(无 Skills)──');
+        console.log('');
+    }
 
-        for (const file of skill.files) {
+    // ── Step 1b: 上传 Workshop 文件到七牛 ──
+    let wsSuccessCount = 0;
+    let wsFailCount = 0;
+    const workshopResults = [];
+
+    if (!SKILLS_ONLY && workshopFiles.length > 0) {
+        console.log('── Step 1b: 上传 Workshop 文件到七牛云 ──');
+
+        for (const file of workshopFiles) {
+            process.stdout.write(`  📝 ${file.fileName} ... `);
             try {
                 const result = await uploadFile(file.localPath, file.cdnKey);
-                skillResults.push({ ...result, fileName: file.fileName, status: 'ok' });
-                cdnUrls[file.fileName] = result.url;
-                successCount++;
+                workshopResults.push({ ...result, fileName: file.fileName, type: file.type, status: 'ok' });
+                wsSuccessCount++;
+                console.log('✅');
             } catch (err) {
-                skillResults.push({ fileName: file.fileName, status: 'fail', error: err.message });
-                failCount++;
+                workshopResults.push({ fileName: file.fileName, type: file.type, status: 'fail', error: err.message });
+                wsFailCount++;
+                console.log(`❌ ${err.message}`);
             }
         }
 
-        const allOk = skillResults.every(r => r.status === 'ok');
-        console.log(allOk ? '✅' : '⚠️');
-        results.push({ name: skill.name, files: skillResults, meta: skill.meta, cdnUrls });
+        console.log('');
+        console.log(`   Workshop 上传完成!成功: ${wsSuccessCount}  失败: ${wsFailCount}`);
+        console.log('');
     }
 
-    console.log('');
-    console.log(`   上传完成!成功: ${successCount}  失败: ${failCount}`);
-    console.log('');
-
     // ── Step 2: 写入 Parse Skill 表 ──
-    console.log('── Step 2: 同步到 Parse 数据库 (Skill 表) ──');
-    await ensureSkillClass();
     let dbSuccess = 0;
     let dbFail = 0;
 
-    for (const entry of results) {
-        if (Object.keys(entry.cdnUrls).length === 0) continue;
-        process.stdout.write(`  💾 ${entry.name} ... `);
-        try {
-            // Parse 不允许嵌套 key 含 '.',将文件名中的 '.' 替换为 '_'
-            const safeCdnUrls = {};
-            for (const [k, v] of Object.entries(entry.cdnUrls)) {
-                safeCdnUrls[k.replace(/\./g, '_')] = v;
+    if (!WORKSHOP_ONLY && results.length > 0) {
+        console.log('── Step 2: 同步到 Parse 数据库 (Skill 表) ──');
+        await ensureSkillClass();
+
+        for (const entry of results) {
+            if (Object.keys(entry.cdnUrls).length === 0) continue;
+            process.stdout.write(`  💾 ${entry.name} ... `);
+            try {
+                // Parse 不允许嵌套 key 含 '.',将文件名中的 '.' 替换为 '_'
+                const safeCdnUrls = {};
+                for (const [k, v] of Object.entries(entry.cdnUrls)) {
+                    safeCdnUrls[k.replace(/\./g, '_')] = v;
+                }
+                const skillData = {
+                    name: entry.meta.name,
+                    displayName: entry.meta.displayName,
+                    description: entry.meta.description,
+                    category: entry.meta.category,
+                    version: entry.meta.version,
+                    author: entry.meta.author,
+                    cdnUrls: safeCdnUrls,
+                    cdnPrefix: `${CDN_DOMAIN}/${CDN_PREFIX}/${entry.name}`
+                };
+                const result = await upsertSkill(skillData);
+                console.log(`${result.action === 'created' ? '🆕 创建' : '🔄 更新'} (${result.objectId})`);
+                dbSuccess++;
+            } catch (err) {
+                console.log(`❌ ${err.message}`);
+                dbFail++;
             }
-            const skillData = {
-                name: entry.meta.name,
-                displayName: entry.meta.displayName,
-                description: entry.meta.description,
-                category: entry.meta.category,
-                version: entry.meta.version,
-                author: entry.meta.author,
-                cdnUrls: safeCdnUrls,
-                cdnPrefix: `${CDN_DOMAIN}/${CDN_PREFIX}/${entry.name}`
-            };
-            const result = await upsertSkill(skillData);
-            console.log(`${result.action === 'created' ? '🆕 创建' : '🔄 更新'} (${result.objectId})`);
-            dbSuccess++;
-        } catch (err) {
-            console.log(`❌ ${err.message}`);
-            dbFail++;
         }
+
+        console.log('');
+        console.log(`   数据库同步完成!成功: ${dbSuccess}  失败: ${dbFail}`);
+        console.log('');
     }
 
-    console.log('');
-    console.log(`   数据库同步完成!成功: ${dbSuccess}  失败: ${dbFail}`);
-    console.log('');
+    // ── Step 3: 生成清单 + README 并上传 ──
+    console.log('── Step 3: 生成清单 + README 并上传 ──');
+    const packageUrls = {};
+
+    // 上传每个技能包的 install.json
+    if (!WORKSHOP_ONLY && results.length > 0) {
+        const manifests = generatePackageManifests(results);
+        for (const [pkgId, manifest] of Object.entries(manifests)) {
+            try {
+                const key = `${CDN_PREFIX}/packages/${pkgId}.json`;
+                const buf = Buffer.from(JSON.stringify(manifest, null, 2), 'utf8');
+                const res = await uploadBuffer(buf, key);
+                packageUrls[pkgId] = res.url;
+                const pkg = SKILL_PACKAGES[pkgId];
+                const statusTag = pkg.status === 'testing' ? ' 🧪' : ' ✅';
+                console.log(`  ${statusTag} ${pkg.label} (${manifest.skillCount}个技能): ${res.url}`);
+            } catch (err) {
+                console.log(`  ❌ ${pkgId}.json 上传失败: ${err.message}`);
+            }
+        }
+    }
 
-    // ── Step 3: 生成 README.md + 技能包清单 并上传 ──
-    console.log('── Step 3: 生成 README.md + 技能包清单 并上传 ──');
+    // 上传 workshop manifest
+    let workshopManifestUrl = '';
+    if (!SKILLS_ONLY && workshopResults.length > 0) {
+        try {
+            const wsManifest = generateWorkshopManifest(workshopResults);
+            const key = `${CDN_PREFIX}/packages/workshop.json`;
+            const buf = Buffer.from(JSON.stringify(wsManifest, null, 2), 'utf8');
+            const res = await uploadBuffer(buf, key);
+            workshopManifestUrl = res.url;
+            packageUrls['workshop'] = res.url;
+            const okCount = workshopResults.filter(r => r.status === 'ok').length;
+            console.log(`  🦐 VOC虾工作坊 (${okCount}个文件): ${res.url}`);
+        } catch (err) {
+            console.log(`  ❌ workshop.json 上传失败: ${err.message}`);
+        }
+    }
+
+    // 上传 README.md
     let readmeUrl = '';
     try {
-        const readme = generateReadme(results);
+        const readme = generateReadme(results, workshopManifestUrl);
         const readmeKey = `${CDN_PREFIX}/README.md`;
         const readmeResult = await uploadBuffer(Buffer.from(readme, 'utf8'), readmeKey);
         readmeUrl = readmeResult.url;
@@ -568,32 +800,20 @@ async function main() {
     } catch (err) {
         console.log(`  ❌ README.md 上传失败: ${err.message}`);
     }
-
-    // 上传每个技能包的 install.json
-    const manifests = generatePackageManifests(results);
-    const packageUrls = {};
-    for (const [pkgId, manifest] of Object.entries(manifests)) {
-        try {
-            const key = `${CDN_PREFIX}/packages/${pkgId}.json`;
-            const buf = Buffer.from(JSON.stringify(manifest, null, 2), 'utf8');
-            const res = await uploadBuffer(buf, key);
-            packageUrls[pkgId] = res.url;
-            const pkg = SKILL_PACKAGES[pkgId];
-            const statusTag = pkg.status === 'testing' ? ' 🧪' : ' ✅';
-            console.log(`  ${statusTag} ${pkg.label} (${manifest.skillCount}个技能): ${res.url}`);
-        } catch (err) {
-            console.log(`  ❌ ${pkgId}.json 上传失败: ${err.message}`);
-        }
-    }
     console.log('');
 
     // ── 输出 URL 汇总 ──
-    console.log('📋 技能包下载地址:');
+    console.log('📋 下载地址:');
     console.log('');
     for (const [pkgId, url] of Object.entries(packageUrls)) {
-        const pkg = SKILL_PACKAGES[pkgId];
-        const statusTag = pkg.status === 'testing' ? '🧪' : '✅';
-        console.log(`  ${statusTag} ${pkg.label}`);
+        if (pkgId === 'workshop') {
+            console.log(`  🦐 VOC虾工作坊`);
+        } else {
+            const pkg = SKILL_PACKAGES[pkgId];
+            if (!pkg) continue;
+            const statusTag = pkg.status === 'testing' ? '🧪' : '✅';
+            console.log(`  ${statusTag} ${pkg.label}`);
+        }
         console.log(`    👉 ${url}`);
     }
     console.log('');
@@ -606,17 +826,27 @@ async function main() {
             urlMap[entry.name] = entry.cdnUrls;
         }
     }
+    if (workshopResults.length > 0) {
+        urlMap['_workshop'] = {};
+        for (const item of workshopResults) {
+            if (item.status === 'ok') urlMap['_workshop'][item.fileName] = item.url;
+        }
+    }
     const outputPath = path.resolve(__dirname, '..', 'skills-cdn-urls.json');
     fs.writeFileSync(outputPath, JSON.stringify(urlMap, null, 2), 'utf8');
     console.log(`💾 URL 列表已保存到: ${outputPath}`);
     console.log('');
 
     console.log('==================================================');
-    console.log('📌 技能市场索引:');
+    console.log('📌 索引:');
     console.log(`  README:    ${CDN_DOMAIN}/${CDN_PREFIX}/README.md`);
     for (const [pkgId, url] of Object.entries(packageUrls)) {
-        const pkg = SKILL_PACKAGES[pkgId];
-        console.log(`  ${pkg.emoji} ${pkg.label}: ${url}`);
+        if (pkgId === 'workshop') {
+            console.log(`  🦐 VOC虾工作坊: ${url}`);
+        } else {
+            const pkg = SKILL_PACKAGES[pkgId];
+            if (pkg) console.log(`  ${pkg.emoji} ${pkg.label}: ${url}`);
+        }
     }
     console.log('==================================================');
 }

+ 24 - 1
scripts/skills-cdn-urls.json

@@ -2,7 +2,8 @@
   "_packages": {
     "voc-core": "https://repos.fmode.cn/x/openclaw-skills/packages/voc-core.json",
     "social-media": "https://repos.fmode.cn/x/openclaw-skills/packages/social-media.json",
-    "ai-creation": "https://repos.fmode.cn/x/openclaw-skills/packages/ai-creation.json"
+    "ai-creation": "https://repos.fmode.cn/x/openclaw-skills/packages/ai-creation.json",
+    "workshop": "https://repos.fmode.cn/x/openclaw-skills/packages/workshop.json"
   },
   "_README": "https://repos.fmode.cn/x/openclaw-skills/README.md",
   "asin-reverse-keywords": {
@@ -307,5 +308,27 @@
   "voc-proposal": {
     "SKILL.md": "https://repos.fmode.cn/x/openclaw-skills/voc-proposal/SKILL.md",
     "api-config.json": "https://repos.fmode.cn/x/openclaw-skills/voc-proposal/api-config.json"
+  },
+  "_workshop": {
+    "workshop-voc-playbook.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/workshop-voc-playbook.md",
+    "workshop-global-rules.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/workshop-global-rules.md",
+    "workshop-session-0-intake.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/workshop-session-0-intake.md",
+    "workshop-session-1-category.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/workshop-session-1-category.md",
+    "workshop-session-2-brand.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/workshop-session-2-brand.md",
+    "workshop-session-3-voc.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/workshop-session-3-voc.md",
+    "workshop-session-4-deep.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/workshop-session-4-deep.md",
+    "workshop-session-5-report.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/workshop-session-5-report.md",
+    "product-analysis-playbook.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/product-analysis-playbook.md",
+    "synthesis-prompts.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/synthesis-prompts.md",
+    "voc-report-schema-spec.md": "https://repos.fmode.cn/x/openclaw-skills/workshop/voc-report-schema-spec.md",
+    "memory-templates/workshop-progress.json": "https://repos.fmode.cn/x/openclaw-skills/workshop/memory-templates/workshop-progress.json",
+    "memory-templates/brand-context.json": "https://repos.fmode.cn/x/openclaw-skills/workshop/memory-templates/brand-context.json",
+    "memory-templates/analysis-request.json": "https://repos.fmode.cn/x/openclaw-skills/workshop/memory-templates/analysis-request.json",
+    "memory-templates/calibration-notes.json": "https://repos.fmode.cn/x/openclaw-skills/workshop/memory-templates/calibration-notes.json",
+    "memory-templates/stage-1-output.json": "https://repos.fmode.cn/x/openclaw-skills/workshop/memory-templates/stage-1-output.json",
+    "memory-templates/stage-2-output.json": "https://repos.fmode.cn/x/openclaw-skills/workshop/memory-templates/stage-2-output.json",
+    "memory-templates/stage-3-output.json": "https://repos.fmode.cn/x/openclaw-skills/workshop/memory-templates/stage-3-output.json",
+    "memory-templates/stage-4-output.json": "https://repos.fmode.cn/x/openclaw-skills/workshop/memory-templates/stage-4-output.json",
+    "memory-templates/stage-5-output.json": "https://repos.fmode.cn/x/openclaw-skills/workshop/memory-templates/stage-5-output.json"
   }
 }

+ 11 - 1
workshop/memory-templates/stage-1-output.json

@@ -1,5 +1,5 @@
 {
-  "$schema": "stage-output-v2",
+  "$schema": "stage-output-v3",
   "stage": 1,
   "name": "品类全景分析",
   "clientId": "",
@@ -66,6 +66,16 @@
     }
   },
 
+  "_sideQuestTriggers": [],
+
+  "sideQuests": {
+    "_comment": "Side Quest 完成后的深度输出存储在此。questId 作为 key。",
+    "keyword-deep-dive": null,
+    "category-tree-explorer": null,
+    "price-band-mining": null,
+    "top-player-decode": null
+  },
+
   "calibrationTable": {
     "marketSizeConfirmed": false,
     "topBrandsConfirmed": false,

+ 10 - 1
workshop/memory-templates/stage-2-output.json

@@ -1,5 +1,5 @@
 {
-  "$schema": "stage-output-v2",
+  "$schema": "stage-output-v3",
   "stage": 2,
   "name": "品牌画像+竞品分析",
   "clientId": "",
@@ -82,6 +82,15 @@
     "competitors": []
   },
 
+  "_sideQuestTriggers": [],
+
+  "sideQuests": {
+    "_comment": "Side Quest 完成后的深度输出存储在此。questId 作为 key。",
+    "competitor-stalker": null,
+    "asin-clinic": null,
+    "pricing-war-room": null
+  },
+
   "calibrationTable": {
     "brandProfileConfirmed": false,
     "competitorsConfirmed": false,

+ 10 - 1
workshop/memory-templates/stage-3-output.json

@@ -1,5 +1,5 @@
 {
-  "$schema": "stage-output-v2",
+  "$schema": "stage-output-v3",
   "stage": 3,
   "name": "评论采集+VOC初体验(Day1晚)",
   "description": "批量采集评论+初步情感分析+关键词地图。深度分析(痛点/功能/场景)在stage-4进行。",
@@ -83,6 +83,15 @@
     "sentimentGapAnalysis": []
   },
 
+  "_sideQuestTriggers": [],
+
+  "sideQuests": {
+    "_comment": "Side Quest 完成后的深度输出存储在此。questId 作为 key。",
+    "negative-review-deep-dive": null,
+    "five-star-decoder": null,
+    "review-battle": null
+  },
+
   "calibrationTable": {
     "sentimentAccuracy": "",
     "userMarkedKnown": [],

+ 12 - 1
workshop/memory-templates/stage-4-output.json

@@ -1,5 +1,5 @@
 {
-  "$schema": "stage-output-v2",
+  "$schema": "stage-output-v3",
   "stage": 4,
   "name": "深度VOC+社媒洞察+用户画像(Day2上午)",
   "description": "基于stage-3评论数据做深度分析(痛点/亮点/功能/场景),扫描社媒渠道,生成用户画像。",
@@ -133,6 +133,17 @@
     "contentMarketingAngles": []
   },
 
+  "_sideQuestTriggers": [],
+
+  "sideQuests": {
+    "_comment": "Side Quest 完成后的深度输出存储在此。questId 作为 key。",
+    "tiktok-expedition": null,
+    "instagram-recon": null,
+    "douyin-patrol": null,
+    "persona-workshop": null,
+    "trend-radar": null
+  },
+
   "calibrationTable": {
     "painPoints": {
       "userMarkedKnown": [],

+ 14 - 1
workshop/memory-templates/stage-5-output.json

@@ -1,5 +1,5 @@
 {
-  "$schema": "stage-output-v2",
+  "$schema": "stage-output-v3",
   "stage": 5,
   "name": "最终报告",
   "clientId": "",
@@ -97,6 +97,19 @@
     "reportStats": null
   },
 
+  "sideQuestEnrichments": {
+    "_comment": "Step 5.0 收集的所有已完成 Side Quest 输出,按报告章节分组",
+    "totalCompleted": 0,
+    "enrichedChapters": [],
+    "enrichmentMapping": {}
+  },
+
+  "sideQuests": {
+    "_comment": "Session 5 的 Side Quest 完成后的深度输出",
+    "chapter-enhance": null,
+    "action-plan-workshop": null
+  },
+
   "calibrationTable": {
     "proposalReviewed": false,
     "actionPlanConfirmed": false,

+ 213 - 11
workshop/memory-templates/workshop-progress.json

@@ -1,9 +1,19 @@
 {
-  "$schema": "workshop-progress-v3",
-  "_statusEnum": ["pending", "in_progress", "awaitingCalibration", "completed"],
+  "$schema": "workshop-progress-v4",
+  "_statusEnum": ["pending", "in_progress", "awaitingCalibration", "exploring", "completed"],
   "_statusRules": {
-    "awaitingCalibration": "Agent已输出分析结果+校准提问,HARD STOP等待用户回复。系统消息不算用户回复。",
-    "transitionRule": "awaitingCalibration -> completed 只能由用户明确文字回复触发"
+    "awaitingCalibration": "Agent已输出主线分析结果+校准提问,HARD STOP等待用户回复。系统消息不算用户回复。",
+    "exploring": "主线+校准已完成,用户正在Side Quest菜单中选择深入或继续。SOFT GATE——用户可随时说'继续'进入下一Session。",
+    "transitionRule": "awaitingCalibration -> exploring 由用户回复校准触发;exploring -> completed 由用户说'继续/下一步'触发"
+  },
+  "_sideQuestStatusEnum": ["locked", "discovered", "available", "in_progress", "completed", "skipped"],
+  "_sideQuestRules": {
+    "locked": "尚未满足前置条件,不可见",
+    "discovered": "主线分析中发现了有趣线索,Agent内部标记但尚未向用户展示",
+    "available": "已在Side Quest菜单中向用户展示,可选择",
+    "in_progress": "用户已选择,正在执行",
+    "completed": "已完成,输出已写入stage-N-output.sideQuests",
+    "skipped": "用户选择跳过或直接进入下一Session"
   },
   "_playbookFiles": {
     "_comment": "Agent每次对话必须先加载 workshop-global-rules.md,再根据 currentPhase 加载对应的 session 文件。禁止一次加载多个 session 文件。",
@@ -15,14 +25,26 @@
     "session-4": "workspace/workshop-session-4-deep.md",
     "session-5": "workspace/workshop-session-5-report.md"
   },
+  "_designPhilosophy": {
+    "metaphor": "主线感知 + 支线深耕",
+    "mainQuest": "每个Session的主线是'体验氛围'——快速扫描、感受品类/品牌/评论的全貌,不必面面俱到",
+    "sideQuest": "主线中发现的有趣线索变成支线任务,用户选择性深入。偏离主线的探索往往获得感最强",
+    "userAgency": "用户在每个Session结束后自主选择:深入当前Session的某条支线,或继续主线",
+    "noForcedGrinding": "主线不超过5分钟即可走完,Side Quest才是真正的价值产出",
+    "rewardForExploring": "每完成一个Side Quest,最终报告自动获得更深度的对应章节"
+  },
   "clientId": "",
   "brandName": "",
   "startedAt": "",
   "currentPhase": "intake",
+  "activeSideQuest": null,
+  "totalSideQuestsCompleted": 0,
   "sessions": {
     "intake": {
       "name": "品牌建档",
       "status": "pending",
+      "_noSideQuests": true,
+      "_statusFlow": "pending → in_progress → completed(无校准、无探索)",
       "startedAt": null,
       "completedAt": null
     },
@@ -39,7 +61,49 @@
         "价格带分布",
         "趋势判断+风险预警"
       ],
-      "skillsCalled": []
+      "skillsCalled": [],
+      "sideQuests": {
+        "keyword-deep-dive": {
+          "name": "🔑 关键词深潜",
+          "description": "对主线中发现的高潜力关键词做深度趋势分析、季节性拆解、竞争度评估",
+          "skills": ["keyword-search-trend", "keyword-product-ranking"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "2-3min"
+        },
+        "category-tree-explorer": {
+          "name": "🌳 品类树探索",
+          "description": "顺着品类树往下钻,发现被忽视的细分赛道和蓝海子品类",
+          "skills": ["category-tree", "category-products"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "3-5min"
+        },
+        "price-band-mining": {
+          "name": "💰 价格带掘金",
+          "description": "深入分析价格空白带,找到利润最高且竞争最小的价格区间",
+          "skills": ["product-search", "asin-sales-volume"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "2-3min"
+        },
+        "top-player-decode": {
+          "name": "🏆 头部玩家解密",
+          "description": "挑一个Top品牌或Top ASIN,深度拆解它的成功策略",
+          "skills": ["product-detail-query", "product-deep-analysis", "asin-reverse-keywords"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "3-5min"
+        }
+      }
     },
     "session-2": {
       "name": "品牌画像+竞品分析",
@@ -54,10 +118,42 @@
         "竞品对比矩阵",
         "定价策略分析"
       ],
-      "skillsCalled": []
+      "skillsCalled": [],
+      "sideQuests": {
+        "competitor-stalker": {
+          "name": "⚔️ 竞品深度侦察",
+          "description": "挑一个竞品做全方位侦察——全ASIN扫描、反查关键词、流量结构拆解",
+          "skills": ["product-deep-analysis", "asin-reverse-keywords", "product-detail-query"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "3-5min"
+        },
+        "asin-clinic": {
+          "name": "🔍 ASIN体检中心",
+          "description": "对你的其他ASIN逐个做深度诊断(主线只诊断了主力ASIN)",
+          "skills": ["product-deep-analysis", "product-detail-query"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "2-4min per ASIN"
+        },
+        "pricing-war-room": {
+          "name": "💰 价格战推演",
+          "description": "模拟不同定价策略对销量和利润的影响,找到最优价格点",
+          "skills": ["competitor-pricing-analysis", "asin-sales-volume"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "2-3min"
+        }
+      }
     },
     "session-3": {
-      "name": "评论采集+VOC初体验(Day1晚)",
+      "name": "评论采集+VOC初体验",
       "status": "pending",
       "startedAt": null,
       "completedAt": null,
@@ -68,10 +164,42 @@
         "关键词情感地图",
         "品牌vs竞品情感对比"
       ],
-      "skillsCalled": []
+      "skillsCalled": [],
+      "sideQuests": {
+        "negative-review-deep-dive": {
+          "name": "😤 差评深潜",
+          "description": "挑一个差评重灾区做深度挖掘——追溯根因、关联退货、量化影响",
+          "skills": ["review-pain-point-extraction"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "2-3min"
+        },
+        "five-star-decoder": {
+          "name": "⭐ 好评解码",
+          "description": "分析5星好评的共性模式——哪些卖点让用户自发推荐?",
+          "skills": ["review-highlight-extraction"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "2-3min"
+        },
+        "review-battle": {
+          "name": "🆚 评论对决",
+          "description": "挑一个竞品做面对面评论对比——找到你的差异化优势和劣势",
+          "skills": ["review-batch-collection", "review-sentiment-analysis"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "3-5min"
+        }
+      }
     },
     "session-4": {
-      "name": "深度VOC+社媒洞察+用户画像(Day2上午)",
+      "name": "深度VOC+社媒洞察+用户画像",
       "status": "pending",
       "startedAt": null,
       "completedAt": null,
@@ -85,7 +213,59 @@
         "竞品BSR追踪",
         "用户画像手册(3-5个画像)"
       ],
-      "skillsCalled": []
+      "skillsCalled": [],
+      "sideQuests": {
+        "tiktok-expedition": {
+          "name": "📱 TikTok远征",
+          "description": "深入TikTok探索——追踪特定话题标签、爆款视频、头部创作者的内容策略",
+          "skills": ["tiktok-video-search", "tiktok-hashtag-detail", "tiktok-hashtag-videos", "tiktok-user-posts"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "3-5min"
+        },
+        "instagram-recon": {
+          "name": "📸 Instagram侦察",
+          "description": "侦察Instagram上的品牌声量、UGC内容、KOL合作机会",
+          "skills": ["instagram-search", "instagram-user-info", "instagram-user-posts"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "3-5min"
+        },
+        "douyin-patrol": {
+          "name": "🎵 抖音巡逻",
+          "description": "扫描抖音上的品类内容生态——热门话题、带货达人、评论区真实反馈",
+          "skills": ["douyin-general-search", "douyin-hashtag-search", "douyin-video-comments"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "3-5min"
+        },
+        "persona-workshop": {
+          "name": "👤 画像工坊",
+          "description": "交互式用户画像打磨——你来挑选最像你客户的画像,虾帮你补充细节",
+          "skills": ["user-persona"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "3-5min"
+        },
+        "trend-radar": {
+          "name": "🔥 趋势雷达",
+          "description": "跨平台趋势交叉验证——Amazon评论趋势 × 社媒热度 × 搜索趋势,找到下一个爆点",
+          "skills": ["social-trend-analysis", "keyword-search-trend"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "2-3min"
+        }
+      }
     },
     "session-5": {
       "name": "最终报告",
@@ -99,7 +279,29 @@
         "30天行动时间线",
         "ROI估算"
       ],
-      "skillsCalled": []
+      "skillsCalled": [],
+      "sideQuests": {
+        "chapter-enhance": {
+          "name": "📝 章节深化",
+          "description": "挑一个报告章节做深度补充——跑更多数据、加更多案例、细化行动建议",
+          "skills": [],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "3-5min"
+        },
+        "action-plan-workshop": {
+          "name": "🎯 行动计划工坊",
+          "description": "交互式行动计划细化——按你的资源和优先级定制30/60/90天路线图",
+          "skills": ["voc-proposal"],
+          "status": "locked",
+          "trigger": null,
+          "startedAt": null,
+          "completedAt": null,
+          "estimatedTime": "3-5min"
+        }
+      }
     }
   }
 }

+ 206 - 22
workshop/workshop-global-rules.md

@@ -1,7 +1,7 @@
 ---
 name: workshop-voc-global-rules
-description: VOC虾工作坊全局规则(角色设定、记忆管理、SESSION GATE、进度恢复等),所有 Session 文件共用。
-version: 2.2.0
+description: VOC虾工作坊全局规则(角色设定、记忆管理、SESSION GATE、Side Quest、进度恢复等),所有 Session 文件共用。
+version: 3.0.0
 author: nkkj-BrainHack
 ---
 
@@ -188,25 +188,27 @@ Agent 在完成一个 Session 的分析输出 + 校准提问后,**必须立即
 
 ### Session 状态机
 
-每个 Session 有个状态,必须严格按顺序流转:
+每个 Session 有个状态,必须严格按顺序流转:
 
 ```
-pending → in_progress → awaitingCalibration → completed
-                              ↑
-                         HARD STOP 点
-                    (必须等待用户回复)
+pending → in_progress → awaitingCalibration → exploring → completed
+                              ↑
+                         HARD STOP 点          SOFT GATE
+                    (必须等待用户回复)   (Side Quest 菜单)
 ```
 
 - **pending**: 尚未开始
-- **in_progress**: 正在执行技能调用和数据分析
-- **awaitingCalibration**: 分析完成,校准问题已向用户展示,**等待用户回复**
-- **completed**: 用户已回复校准意见,校准已记录,可以进入下一个 Session
+- **in_progress**: 正在执行主线技能调用和数据分析
+- **awaitingCalibration**: 主线分析完成,校准问题已向用户展示,**等待用户回复**
+- **exploring**: 校准已完成,用户在 Side Quest 菜单中自由探索(详见「Side Quest 协议」)
+- **completed**: 用户选择进入下一 Session(说"继续/下一步"),或所有 Side Quest 已探索完毕
 
 **状态转换规则:**
-- `awaitingCalibration → completed` 只能由**用户的明确文字回复**触发
+- `awaitingCalibration → exploring` 由**用户的明确文字回复**触发(回复校准意见后,自动进入 Side Quest 菜单)
+- `exploring → completed` 由**用户说"继续/下一步/下一阶段"**触发(用户主动选择推进主线)
 - 系统消息、异步任务完成通知、空消息等**不算**用户回复
-- 如果用户回复 "继续" / "没问题" / "OK" 等确认词,视为校准通过,记录后可进入下一 Session
-- 如果用户提供了具体校准意见,必须先记录到 calibration-notes.json,然后才能进入下一 Session
+- 如果用户回复 "继续" / "没问题" / "OK" 且当前 Session 有 Side Quest → 先展示 Side Quest 菜单再等用户选择
+- 如果用户提供了具体校准意见,必须先记录到 calibration-notes.json,然后展示 Side Quest 菜单
 
 ### HARD STOP 输出模板
 
@@ -233,10 +235,181 @@ pending → in_progress → awaitingCalibration → completed
 但绝不能将其视为"用户同意继续下一阶段"的信号。
 ```
 
+---
+
+## Side Quest 协议(开放世界探索机制)
+
+> **设计哲学:主线感知 + 支线深耕**
+> - 每个 Session 的**主线**是"体验氛围"——快速扫描全貌,让用户感受到这个维度的可能性
+> - **Side Quest**是主线中发现的有趣线索——用户选择性深入,偏离主线的探索往往获得感最强
+> - 主线不应超过5分钟;Side Quest 才是真正的价值产出
+> - 每完成一个 Side Quest,最终报告自动获得更深度的对应章节
+
+### SQ1: 发现协议(主线执行中)
+
+Agent 在执行主线分析时,必须关注"有趣的线索"并内部标记为 Side Quest 触发器:
+
+```
+触发信号类型:
+- 🔑 数据异常:某个关键词搜索量暴增、某个价格带竞争真空、某个竞品BSR骤变
+- 💡 意外发现:用户品牌在某维度意外领先/落后、某竞品策略独特、某差评模式罕见
+- 🌊 深水区信号:某个数据维度表面正常但细看值得深挖(如品类健康但子品类分化严重)
+- 🔥 用户兴趣信号:用户在校准中对某个话题追问、表达强烈兴趣或惊讶
+
+标记方式:
+- 在 stage-N-output.json 中写入 `_sideQuestTriggers` 数组
+- 每个触发器格式:{ "questId": "xxx", "signal": "发现原因", "dataRef": "关联数据路径" }
+- 同时更新 workshop-progress.json 中对应 sideQuest 的 status: "locked" → "discovered",写入 trigger
+```
+
+### SQ2: Side Quest 菜单(校准完成后展示)
+
+当 Session status 从 `awaitingCalibration` 转为 `exploring` 时,Agent 必须展示 Side Quest 菜单:
+
+```
+虾:🗺️ 主线任务完成!在刚才的分析中,虾发现了几条值得深入的线索:
+
+┌─────────────────────────────────────────────┐
+│  🎮 Side Quest 支线任务                      │
+├─────────────────────────────────────────────┤
+{{#each availableSideQuests}}
+│  {{emoji}} {{name}}                          │
+│  📍 {{trigger}}                              │
+│  ⏱️ 预计 {{estimatedTime}}                   │
+│                                              │
+{{/each}}
+├─────────────────────────────────────────────┤
+│  ⏭️ 说"继续"直接进入下一阶段                   │
+│  💾 说"暂停"保存进度下次再探索                   │
+└─────────────────────────────────────────────┘
+
+你对哪条线索感兴趣?选一个深入,或者直接继续主线。
+```
+
+**菜单展示规则:**
+- 只展示 status 为 `discovered` 或 `available` 的 Side Quest
+- 如果主线分析中没有触发任何 Side Quest(极少情况),也展示预设 Side Quest 但标注"虾建议"
+- 菜单展示后,所有 `discovered` 的 Side Quest 状态变为 `available`
+- 用户可以选择多个 Side Quest 依次完成,每完成一个回到菜单
+- 用户说"继续/下一步"时,所有未选择的 Side Quest 标记为 `skipped`
+
+### SQ3: Side Quest 执行流程
+
+```
+用户选择 Side Quest → Agent 执行:
+1. 更新 workshop-progress.json:
+   - sessions.session-N.sideQuests.{questId}.status = "in_progress"
+   - activeSideQuest = "session-N/questId"
+2. 展示 Side Quest 开场:
+   虾:🎮 开始支线任务:{{questName}}
+   ━━━━━━━━━━━━━━━━━━━━
+   {{questDescription}}
+   线索来源:{{trigger}}
+   
+   让虾深入挖掘...
+3. 调用对应技能(定义在 session 文件的 sideQuest 区块中)
+4. 展示发现结果(比主线更详细、更聚焦)
+5. 轻量校准(1-2个问题,不需要 HARD STOP):
+   虾:这个发现对你有用吗?有什么补充吗?
+6. 存储输出到 stage-N-output.json 的 sideQuests.{questId} 下
+7. 更新 workshop-progress.json:
+   - sideQuests.{questId}.status = "completed"
+   - sideQuests.{questId}.completedAt = timestamp
+   - activeSideQuest = null
+   - totalSideQuestsCompleted++
+8. 回到 Side Quest 菜单(更新已完成状态)
+```
+
+**Side Quest 与主线的区别:**
+| 维度 | 主线 | Side Quest |
+|------|------|-----------|
+| 范围 | 全景扫描 | 聚焦单点深挖 |
+| 时间 | 3-5min | 2-5min |
+| 校准 | HARD STOP + 多问题 | 轻量确认(1-2问题) |
+| 必要性 | 必做 | 用户选择 |
+| GATE | awaitingCalibration | 无GATE,完成即回菜单 |
+| 输出 | stage-N-output.json 主体 | stage-N-output.sideQuests.{id} |
+
+### SQ4: Side Quest 完成后回到菜单
+
+```
+虾:✅ 支线「{{questName}}」完成!
+{{briefFinding}}
+
+🏆 探索进度:{{completedCount}}/{{totalAvailable}} 支线已完成
+
+{{#if remainingSideQuests.length > 0}}
+┌─────────────────────────────────────────────┐
+│  🎮 还有以下支线可以探索:                      │
+├─────────────────────────────────────────────┤
+{{#each remainingSideQuests}}
+│  {{emoji}} {{name}} — {{trigger}}            │
+{{/each}}
+├─────────────────────────────────────────────┤
+│  ⏭️ 说"继续"进入下一阶段                       │
+└─────────────────────────────────────────────┘
+{{else}}
+🎉 所有支线任务都已完成!自动进入下一阶段...
+{{/if}}
+```
+
+**边界情况:所有 Side Quest 已完成/跳过**
+- 当 `remainingSideQuests.length === 0`(所有 Side Quest 为 `completed` 或 `skipped`)时:
+  - 自动将 Session status 从 `exploring` 转为 `completed`
+  - 展示完成总结后直接进入下一 Session,无需用户说"继续"
+  - 更新 `workshop-progress.json`: `currentPhase` 切到下一阶段
+
+### SQ5: Side Quest 输出对报告的影响
+
+每个完成的 Side Quest 会自动增强最终报告的对应章节:
+
+| Side Quest | 增强报告章节 |
+|-----------|-------------|
+| 🔑 关键词深潜 | 品类全景 — 增加关键词趋势深度分析 |
+| 🌳 品类树探索 | 品类全景 — 增加细分赛道机会地图 |
+| 💰 价格带掘金 | 品类全景 + 定价策略 — 增加价格空白机会详析 |
+| 🏆 头部玩家解密 | 竞品对比 — 增加头部玩家策略拆解 |
+| ⚔️ 竞品深度侦察 | 竞品对比 — 增加单竞品全维度报告 |
+| 🔍 ASIN体检中心 | 品牌画像 — 增加多ASIN诊断对比 |
+| 💰 价格战推演 | 定价策略 — 增加价格弹性模拟 |
+| 😤 差评深潜 | 痛点洞察 — 增加单维度根因分析 |
+| ⭐ 好评解码 | 卖点方案 — 增加好评驱动因素分析 |
+| 🆚 评论对决 | VOC对比 — 增加单竞品评论深度对比 |
+| 📱 TikTok远征 | 社媒洞察 — 增加TikTok深度报告 |
+| 📸 Instagram侦察 | 社媒洞察 — 增加Instagram深度报告 |
+| 🎵 抖音巡逻 | 社媒洞察 — 增加抖音深度报告 |
+| 👤 画像工坊 | 用户画像 — 增加定制化画像详情 |
+| 🔥 趋势雷达 | 行动计划 — 增加跨平台趋势验证 |
+| 📝 章节深化 | 用户选择的章节 — 补充数据点+案例+细化洞察 |
+| 🎯 行动计划工坊 | 行动计划 — 定制化30/60/90天路线图 |
+
+Session 5 生成报告时,必须检查 `totalSideQuestsCompleted` 和各 `sideQuests.{id}.status`,将完成的 Side Quest 输出整合到对应章节中。
+
+### SQ6: 中断与恢复
+
+```
+如果用户在 Side Quest 执行中说"暂停":
+1. 保存当前 Side Quest 进度(status 保持 "in_progress")
+2. activeSideQuest 保留当前值
+3. 下次恢复时,Agent 检测到 activeSideQuest 非空 → 提示用户:
+   虾:上次你正在探索支线「{{questName}}」,要继续还是换一个?
+
+如果用户在 exploring 状态说"暂停":
+1. 保存当前状态(status 保持 "exploring")
+2. 下次恢复时,Agent 重新展示 Side Quest 菜单
+```
+
+---
+
 ## 触发词
 - 用户说"开始工作坊" / "启动VOC分析" / "开始品类调研" → 从头开始
-- 用户说"继续" / "下一步" / "下一阶段" → 从 workshop-progress 记录的位置继续
-- 用户说"回顾" / "看进度" → 展示当前进度和已产出的交付物
+- 用户说"继续" / "下一步" / "下一阶段" → 根据当前状态判断:
+  - 如果当前 status = `awaitingCalibration` → 视为校准通过,进入 Side Quest 菜单 (exploring)
+  - 如果当前 status = `exploring` → 跳过剩余 Side Quest,进入下一 Session
+  - 如果当前 status = `completed` → 开始下一 Session
+- 用户说"回顾" / "看进度" → 展示当前进度和已产出的交付物(含 Side Quest 完成情况)
+- 用户说"深入" / "我想看看..." / "能不能详细说说..." → 在 exploring 状态时视为选择 Side Quest
+- 用户说"暂停" / "保存" → 保存当前进度,下次继续
 
 ## 容错机制
 - 如果某个技能调用失败,记录错误并告知用户,提供跳过/重试选项
@@ -251,20 +424,31 @@ pending → in_progress → awaitingCalibration → completed
 
 ```
 1. 读取 memory/workshop-progress.json
-2. 找到最后一个 status != "completed" 的 session
-3. 读取对应的 brand-context.json 和前置 stage output
-4. 加载对应的 Session 文件(只加载该 Session,不加载后续)
-5. 展示进度摘要:
+2. 检查 activeSideQuest — 如果非空,提示用户上次正在探索的支线
+3. 找到最后一个 status != "completed" 的 session
+4. 读取对应的 brand-context.json 和前置 stage output
+5. 加载对应的 Session 文件(只加载该 Session,不加载后续)
+6. 根据 session status 决定行为:
+   - pending/in_progress → 继续或开始主线任务
+   - awaitingCalibration → 提示用户回复校准
+   - exploring → 重新展示 Side Quest 菜单
+7. 展示进度摘要:
 
 虾:欢迎回来老板!上次我们完成到了阶段{{N}}。
 当前进度:
-✅ 阶段一:品类全景 — 已完成
-✅ 阶段二:品牌+竞品 — 已完成
-⏳ 阶段三:评论采集+VOC初体验 — 进行中
+✅ 阶段一:品类全景 — 已完成(🎮 2/4 支线已探索)
+✅ 阶段二:品牌+竞品 — 已完成(🎮 1/3 支线已探索)
+🗺️ 阶段三:评论采集+VOC — 探索中(正在 Side Quest 菜单)
 ⬜ 阶段四:深度VOC+社媒+画像 — 待开始
 ⬜ 阶段五:最终报告 — 待开始
 
+🏆 总探索进度:{{totalSideQuestsCompleted}} 个支线任务已完成
+
+{{#if activeSideQuest}}
+📍 上次你正在探索支线「{{activeSideQuestName}}」,要继续还是换一个?
+{{else}}
 要继续阶段三吗?
+{{/if}}
 ```
 
 ---

+ 90 - 8
workshop/workshop-session-1-category.md

@@ -1,7 +1,7 @@
 ---
 name: workshop-session-1-category
-description: VOC虾工作坊 Session 1 — 教虾认品类(品类全景分析)
-version: 2.2.0
+description: VOC虾工作坊 Session 1 — 教虾认品类(品类全景分析)+ Side Quests
+version: 3.0.0
 session: 1
 prevSession: workshop-session-0-intake.md
 nextSession: workshop-session-2-brand.md
@@ -14,6 +14,17 @@ nextSession: workshop-session-2-brand.md
 ## 前置检查
 - ✅ `brand-context.json` 已存在且 categoryKeywords 非空
 
+## Side Quest 触发条件速查(主线执行时参考)
+
+> Agent 在执行主线分析时,**必须同时对照此表检测触发信号**,发现匹配时写入 `_sideQuestTriggers`。
+
+| questId | 名称 | 触发信号(满足任一即触发) |
+|---------|------|-------------------------|
+| `keyword-deep-dive` | 🔑 关键词深潜 | ① 某关键词月搜索量 >10,000 但首页品牌数 <5 ② 某关键词近3月搜索量环比增长 >30% ③ 用户校准中对某关键词追问 |
+| `category-tree-explorer` | 🌳 品类树探索 | ① CR5 <30%(分散市场暗示细分机会) ② 品类树中某子节点产品数/评论数比值异常 ③ 主品类与相邻品类存在交叉机会 |
+| `price-band-mining` | 💰 价格带掘金 | ① 存在价格空白带(某 $5 区间内产品数 <3 但相邻区间 >15) ② 黄金价格带均价与用户品牌价格差距 >25% ③ 某价格带利润空间显著高于均值 |
+| `top-player-decode` | 🏆 头部玩家解密 | ① Top1 品牌市占率 >20% 且策略独特(高溢价+高销量) ② CR5 中某品牌近6月增长率 >50% ③ 某头部ASIN评分 >4.5 且月销量 >5000 |
+
 ## 对话开场
 
 ```
@@ -177,13 +188,13 @@ nextSession: workshop-session-2-brand.md
 ```
 
 ## 阶段交付
-1. 存储输出到 `memory/stage-1-output.json`(含 `_goalAlignment` 和 `_anomalies`)
-2. 更新 `workshop-progress.json` → session1.status = "completed"
+1. 存储输出到 `memory/stage-1-output.json`(含 `_goalAlignment`、`_anomalies`、`_sideQuestTriggers`)
+2. 更新 `workshop-progress.json` → session-1.status = "awaitingCalibration"
 3. 更新 `calibration-notes._forwardPassSummary`
 4. 展示交付物清单
 
 ```
-虾:✅ 阶段一完成!
+虾:✅ 阶段一主线完成!
 
 📦 已产出交付物:
 • 《品类认知校准表》
@@ -193,9 +204,80 @@ nextSession: workshop-session-2-brand.md
 • 趋势判断 + 风险预警
 
 所有数据已存入记忆,后续阶段会自动引用。
-准备好了告诉我,我们进入阶段二——品牌画像 + 竞品分析!
 ```
 
-**🛑 SESSION GATE: 此处 HARD STOP。Agent 必须停止并等待用户回复校准意见。禁止自动进入 Session 2。**
+**🛑 SESSION GATE: 此处 HARD STOP。Agent 必须停止并等待用户回复校准意见。禁止自动进入 Side Quest 菜单或 Session 2。**
 **更新 workshop-progress.json: session-1.status = "awaitingCalibration"**
-**只有当用户明确回复后,才将 status 改为 "completed" 并开始 Session 2。**
+
+---
+
+## Side Quest 发现(主线分析中自动标记)
+
+Agent 在执行主线分析时,根据数据发现标记 Side Quest 触发器。以下为各 Side Quest 的典型触发条件:
+
+### 🔑 关键词深潜 (keyword-deep-dive)
+```
+技能调用:keyword-search-trend(keywords=[触发关键词], months=12) → keyword-product-ranking(keyword, domain)
+关键输出:月均搜索量+12月趋势+季节性峰值+竞争度+建议出价 | Top10产品表(ASIN+品牌+售价+评分+月销量) | 机会判断
+轻量校准:这个关键词方向对你有价值吗?
+```
+
+### 🌳 品类树探索 (category-tree-explorer)
+```
+技能调用:category-tree(rootCategory) → category-products(categoryId, topN=20)
+关键输出:品类结构地图(树形可视化) | 蓝海子品类列表(名称+竞品数+均价+月销量+机会评估)
+轻量校准:这些细分赛道有你感兴趣的吗?
+```
+
+### 💰 价格带掘金 (price-band-mining)
+```
+技能调用:product-search(keyword, priceRange=目标价格带) → asin-sales-volume(价格带内Top产品)
+关键输出:价格空白机会表(价格带+产品数+月销量+竞争密度+利润空间+评分) | 最佳切入价格点+理由
+轻量校准:这个价格策略建议实际可行吗?
+```
+
+### 🏆 头部玩家解密 (top-player-decode)
+```
+技能调用:product-detail-query(asin) → product-deep-analysis(asin) → asin-reverse-keywords(asin)
+关键输出:策略拆解(Listing+定价+流量+关键词覆盖) | 反查关键词Top10表(关键词+排名+搜索量+你的排名) | 可借鉴策略+竞争风险
+轻量校准:这个分析有帮助吗?有想进一步了解的吗?
+```
+
+---
+
+## 校准后进入探索模式
+
+**当用户回复校准意见后,Agent 执行以下流程:**
+
+1. 记录校准到 `calibration-notes.json`
+2. 更新 `workshop-progress.json`: session-1.status = "exploring"
+3. 将所有 `discovered` 的 Side Quest 状态改为 `available`
+4. 按照全局规则 SQ2 展示 Side Quest 菜单
+
+```
+虾:校准意见已记录!
+
+🗺️ 在刚才的品类分析中,虾发现了几条值得深入的线索:
+
+┌─────────────────────────────────────────────┐
+│  🎮 Side Quest 支线任务                      │
+├─────────────────────────────────────────────┤
+{{#each availableSideQuests}}
+│  {{emoji}} {{name}}                          │
+│  📍 {{trigger}}                              │
+│  ⏱️ 预计 {{estimatedTime}}                   │
+│                                              │
+{{/each}}
+├─────────────────────────────────────────────┤
+│  ⏭️ 说"继续"直接进入阶段二:品牌画像+竞品       │
+│  💾 说"暂停"保存进度下次再探索                   │
+└─────────────────────────────────────────────┘
+
+你对哪条线索感兴趣?选一个深入探索,或者直接继续主线。
+虾建议:如果你对某个关键词或某个价格区间特别好奇,现在是最佳深入时机!
+```
+
+**当用户说"继续"时:**
+- 将所有 `available` 的 Side Quest 标记为 `skipped`
+- 更新 session-1.status = "completed"
+- 进入 Session 2

+ 83 - 9
workshop/workshop-session-2-brand.md

@@ -1,7 +1,7 @@
 ---
 name: workshop-session-2-brand
-description: VOC虾工作坊 Session 2 — 教虾认品牌·找竞品(品牌画像+竞品分析)
-version: 2.2.0
+description: VOC虾工作坊 Session 2 — 教虾认品牌·找竞品(品牌画像+竞品分析)+ Side Quests
+version: 3.0.0
 session: 2
 prevSession: workshop-session-1-category.md
 nextSession: workshop-session-3-voc.md
@@ -12,11 +12,21 @@ nextSession: workshop-session-3-voc.md
 > **前置依赖:必须先加载 `workshop-global-rules.md`**
 
 ## 前置检查
-- 🛑 **GATE CHECK**: 检查 `workshop-progress.json` 中 `session-1.status`。如果为 `awaitingCalibration`,**禁止开始 Session 2**,必须先等待用户回复 Session 1 的校准问题
+- 🛑 **GATE CHECK**: 检查 `workshop-progress.json` 中 `session-1.status`。如果为 `awaitingCalibration` 或 `exploring`,**禁止开始 Session 2**,必须先等待用户完成 Session 1 的校准和探索
 - ✅ `stage-1-output.json` 存在
 - ✅ `brand-context.json` 中 brandAsins 非空
 - ✅ 读取 `calibration-notes.sessions.session-1.forwardActions` → 获取需要在本阶段应用的调整
 
+## Side Quest 触发条件速查(主线执行时参考)
+
+> Agent 在执行主线分析时,**必须同时对照此表检测触发信号**,发现匹配时写入 `_sideQuestTriggers`。
+
+| questId | 名称 | 触发信号(满足任一即触发) |
+|---------|------|-------------------------|
+| `competitor-stalker` | ⚔️ 竞品深度侦察 | ① 某竞品漏洞评分 >70(高可利用性) ② 某竞品6月内BSR涨幅 >100% ③ 某竞品策略与品牌形成直接对抗(同价格带+同关键词) |
+| `asin-clinic` | 🔍 ASIN体检中心 | ① 品牌有 >3 个ASIN但主线只诊断了1个 ② 主力ASIN listing评分 <60 ③ 用户在校准中提到其他ASIN的问题 |
+| `pricing-war-room` | 💰 价格战推演 | ① 品牌价格在黄金价格带外(偏高或偏低 >15%) ② 近3月品类均价波动 >10% ③ 竞品定价策略呈现明显趋势(集体涨价/降价) |
+
 ## 校准前向传递
 读取 session-1 的校准意见,在分析中应用:
 - 如果用户校准了 CR5/topBrands → 竞品发现时优先覆盖用户确认的头部品牌
@@ -357,13 +367,13 @@ Step 2.5: competitor-pricing-analysis(
 ```
 
 ## 阶段交付
-1. 存储输出到 `memory/stage-2-output.json`(含 `_goalAlignment` + `_anomalies` + `_contextFromPreviousStages`)
-2. 更新 `workshop-progress.json` → session2.status = "awaitingCalibration"
+1. 存储输出到 `memory/stage-2-output.json`(含 `_goalAlignment` + `_anomalies` + `_contextFromPreviousStages` + `_sideQuestTriggers`
+2. 更新 `workshop-progress.json` → session-2.status = "awaitingCalibration"
 3. 更新 `calibration-notes._forwardPassSummary`
 4. 展示交付物清单
 
 ```
-虾:✅ 阶段二完成!
+虾:✅ 阶段二主线完成!
 
 📦 已产出交付物:
 • 📊《品牌六维画像》— 综合得分 {{overallScore}}/100 + 六维雷达
@@ -374,9 +384,73 @@ Step 2.5: competitor-pricing-analysis(
 • 💰《定价策略分析》— 建议定价 ${{recMin}}-${{recMax}} + 毛利估算
 
 所有数据已存入记忆,后续阶段会自动引用。
-准备好了告诉我,阶段三——虾要下海捕评论了!
 ```
 
-**🛑 SESSION GATE: 此处 HARD STOP。Agent 必须停止并等待用户回复校准意见。禁止自动进入 Session 3。**
+**🛑 SESSION GATE: 此处 HARD STOP。Agent 必须停止并等待用户回复校准意见。禁止自动进入 Side Quest 菜单或 Session 3。**
 **更新 workshop-progress.json: session-2.status = "awaitingCalibration"**
-**只有当用户明确回复后,才将 status 改为 "completed" 并开始 Session 3。**
+
+---
+
+## Side Quest 发现(主线分析中自动标记)
+
+Agent 在执行主线分析时,根据数据发现标记 Side Quest 触发器。以下为各 Side Quest 的典型触发条件:
+
+### ⚔️ 竞品深度侦察 (competitor-stalker)
+```
+技能调用:product-deep-analysis(竞品主力ASIN) → asin-reverse-keywords(竞品ASIN) → product-detail-query(竞品其他ASIN)
+关键输出:产品线概览+Listing策略+流量结构(自然/广告/关联%) | 核心关键词Top10表(关键词+排名+搜索量+你的排名+差距) | 可利用弱点+需防范优势
+轻量校准:这个竞品分析有帮你看清对手吗?
+```
+
+### 🔍 ASIN体检中心 (asin-clinic)
+```
+技能调用:对每个额外ASIN执行 product-deep-analysis + product-detail-query
+关键输出:多ASIN对比看板(ASIN+Listing分+评分+评论数+月销量+BSR+主要问题) | 产品线健康度+自相残杀风险+优化建议
+轻量校准:这些ASIN的诊断结果符合预期吗?
+```
+
+### 💰 价格战推演 (pricing-war-room)
+```
+技能调用:competitor-pricing-analysis(深度模式+历史价格) → asin-sales-volume(不同价格时期对比)
+关键输出:定价策略模拟表(策略+目标价+预估销量+收入+毛利率+风险) | 最优策略推荐+理由+执行时机
+轻量校准:这些定价模拟对你有参考价值吗?
+```
+
+---
+
+## 校准后进入探索模式
+
+**当用户回复校准意见后,Agent 执行以下流程:**
+
+1. 记录校准到 `calibration-notes.json`
+2. 更新 `workshop-progress.json`: session-2.status = "exploring"
+3. 将所有 `discovered` 的 Side Quest 状态改为 `available`
+4. 按照全局规则 SQ2 展示 Side Quest 菜单
+
+```
+虾:校准意见已记录!
+
+🗺️ 在品牌和竞品分析中,虾发现了几条值得深入的线索:
+
+┌─────────────────────────────────────────────┐
+│  🎮 Side Quest 支线任务                      │
+├─────────────────────────────────────────────┤
+{{#each availableSideQuests}}
+│  {{emoji}} {{name}}                          │
+│  📍 {{trigger}}                              │
+│  ⏱️ 预计 {{estimatedTime}}                   │
+│                                              │
+{{/each}}
+├─────────────────────────────────────────────┤
+│  ⏭️ 说"继续"直接进入阶段三:评论采集+VOC       │
+│  💾 说"暂停"保存进度下次再探索                   │
+└─────────────────────────────────────────────┘
+
+你对哪条线索感兴趣?选一个深入探索,或者直接继续主线。
+虾建议:如果你想摸清某个竞品的底细,现在是最佳侦察时机!
+```
+
+**当用户说"继续"时:**
+- 将所有 `available` 的 Side Quest 标记为 `skipped`
+- 更新 session-2.status = "completed"
+- 进入 Session 3

+ 84 - 9
workshop/workshop-session-3-voc.md

@@ -1,7 +1,7 @@
 ---
 name: workshop-session-3-voc
-description: VOC虾工作坊 Session 3 — 教虾抓评论·VOC初体验(Day1晚)
-version: 2.2.0
+description: VOC虾工作坊 Session 3 — 教虾抓评论·VOC初体验 + Side Quests
+version: 3.0.0
 session: 3
 prevSession: workshop-session-2-brand.md
 nextSession: workshop-session-4-deep.md
@@ -16,12 +16,22 @@ nextSession: workshop-session-4-deep.md
 > **关键点**:本阶段只做采集+初步感知,深度分析(痛点/功能/场景)在阶段四进行
 
 ## 前置检查
-- 🛑 **GATE CHECK**: 检查 `workshop-progress.json` 中 `session-2.status`。如果为 `awaitingCalibration`,**禁止开始 Session 3**,必须先等待用户回复 Session 2 的校准问题
+- 🛑 **GATE CHECK**: 检查 `workshop-progress.json` 中 `session-2.status`。如果为 `awaitingCalibration` 或 `exploring`,**禁止开始 Session 3**,必须先等待用户完成 Session 2 的校准和探索
 - ✅ `stage-2-output.json` 存在
 - ✅ 已确认的竞品ASIN列表
 - ✅ 读取 `calibration-notes.sessions.session-2.forwardActions` → 获取需要在本阶段应用的调整
 - ✅ 读取 `stage-2-output._anomalies` → 关注竞品异常信号
 
+## Side Quest 触发条件速查(主线执行时参考)
+
+> Agent 在执行主线分析时,**必须同时对照此表检测触发信号**,发现匹配时写入 `_sideQuestTriggers`。
+
+| questId | 名称 | 触发信号(满足任一即触发) |
+|---------|------|-------------------------|
+| `negative-review-deep-dive` | 😤 差评深潜 | ① 某差评维度提及率 >15%(如"漏液"在所有差评中被提到 >15%) ② 某维度差评近3月环比增长 >50% ③ 品牌独有的差评模式(竞品无此类投诉) |
+| `five-star-decoder` | ⭐ 好评解码 | ① 品牌5星好评率与竞品差距 >10个百分点(高或低) ② 好评中某关键词出现频率 >20% ③ 好评中出现竞品未提及的独特卖点 |
+| `review-battle` | 🆚 评论对决 | ① 品牌vs某竞品情感分差距 >15分 ② 品牌与某竞品在同维度上情感极性相反 ③ 用户校准中对某竞品的评论表现表示惊讶 |
+
 ## 校准前向传递
 读取 session-1 + session-2 的校准意见:
 - 用户拒绝的竞品 → 从采集列表中移除
@@ -274,7 +284,7 @@ ASIN覆盖:{{asinsCovered.length}} 个
 ```
 
 ## 阶段交付
-存入 `stage-3-output.json`(含 `_goalAlignment` + `_anomalies` + `_contextFromPreviousStages`)
+存入 `stage-3-output.json`(含 `_goalAlignment` + `_anomalies` + `_contextFromPreviousStages` + `_sideQuestTriggers`
 记录校准到 calibration-notes → 填写 `forwardActions`(特别是 `knownPainPointsToDeprioritize`、`newDiscoveriesToHighlight`、`misjudgedItemsToRemove`)→ 更新 `_forwardPassSummary`
 
 **注意:stage-3-output.json 保存以下数据供阶段四使用:**
@@ -294,7 +304,7 @@ ASIN覆盖:{{asinsCovered.length}} 个
 ```
 
 ```
-虾:✅ 阶段三完成!
+虾:✅ 阶段三主线完成!
 
 📦 已产出交付物:
 • 📥《结构化评论语料库》— {{totalReviews}}条评论,覆盖{{asinCount}}个ASIN,已清洗去重
@@ -305,10 +315,75 @@ ASIN覆盖:{{asinsCovered.length}} 个
 • 🔤《关键词情感地图》— 正面/负面/趋势关键词Top10 + 情感强度
 • 📂《问题维度细分表》— 差评按维度分类 + 严重度 + 典型原文
 
-所有评论数据已存入记忆。明天上午我会基于这些数据做深度痛点分析。
-准备好了告诉我,阶段四——深度VOC分析 + 社媒洞察 + 用户画像!
+所有评论数据已存入记忆。
 ```
 
-**🛑 SESSION GATE: 此处 HARD STOP。Agent 必须停止并等待用户回复校准意见。禁止自动进入 Session 4。**
+**🛑 SESSION GATE: 此处 HARD STOP。Agent 必须停止并等待用户回复校准意见。禁止自动进入 Side Quest 菜单或 Session 4。**
 **更新 workshop-progress.json: session-3.status = "awaitingCalibration"**
-**只有当用户明确回复后,才将 status 改为 "completed" 并开始 Session 4。**
+
+---
+
+## Side Quest 发现(主线分析中自动标记)
+
+Agent 在执行主线分析时,根据数据发现标记 Side Quest 触发器。以下为各 Side Quest 的典型触发条件:
+
+### 😤 差评深潜 (negative-review-deep-dive)
+```
+技能调用:review-pain-point-extraction(reviews=目标维度差评子集, focusDimension=触发维度)
+关键输出:根因追溯(表面投诉+根本原因+影响范围+关联退货率) | 差评时间线(按月/季) | 修复方案(短期0成本+中期迭代+长期战略) | 预计评分提升
+轻量校准:这个差评根因分析准确吗?修复优先级合理吗?
+```
+
+### ⭐ 好评解码 (five-star-decoder)
+```
+技能调用:review-highlight-extraction(reviews=4-5星好评, mode="deep")
+关键输出:用户自发推荐核心原因Top5(原因+提及次数+代表原文+Listing植入建议+竞品是否提到) | 好评vs竞品好评关键词差异 | Listing优化优先级
+轻量校准:这些好评驱动因素和你的产品优势一致吗?
+```
+
+### 🆚 评论对决 (review-battle)
+```
+技能调用:review-batch-collection(asins=[品牌+竞品], pages=5) → review-sentiment-analysis(mode="comparative")
+关键输出:一对一情感对比表(维度+双方分数+差距+判定) | 你赢/对手赢/势均力敌的维度 | 最有价值对比发现 | 差异化策略建议
+
+轻量校准:这个对比结果符合你的体感吗?
+```
+
+---
+
+## 校准后进入探索模式
+
+**当用户回复校准意见后,Agent 执行以下流程:**
+
+1. 记录校准到 `calibration-notes.json`
+2. 更新 `workshop-progress.json`: session-3.status = "exploring"
+3. 将所有 `discovered` 的 Side Quest 状态改为 `available`
+4. 按照全局规则 SQ2 展示 Side Quest 菜单
+
+```
+虾:校准意见已记录!
+
+🗺️ 在评论分析中,虾发现了几条值得深入的线索:
+
+┌─────────────────────────────────────────────┐
+│  🎮 Side Quest 支线任务                      │
+├─────────────────────────────────────────────┤
+{{#each availableSideQuests}}
+│  {{emoji}} {{name}}                          │
+│  📍 {{trigger}}                              │
+│  ⏱️ 预计 {{estimatedTime}}                   │
+│                                              │
+{{/each}}
+├─────────────────────────────────────────────┤
+│  ⏭️ 说"继续"直接进入阶段四:深度VOC+社媒       │
+│  💾 说"暂停"保存进度下次再探索                   │
+└─────────────────────────────────────────────┘
+
+你对哪条线索感兴趣?选一个深入探索,或者直接继续主线。
+虾建议:如果某个差评维度让你特别在意,现在深入挖掘可以为明天的深度分析指明方向!
+```
+
+**当用户说"继续"时:**
+- 将所有 `available` 的 Side Quest 标记为 `skipped`
+- 更新 session-3.status = "completed"
+- 进入 Session 4

+ 99 - 9
workshop/workshop-session-4-deep.md

@@ -1,7 +1,7 @@
 ---
 name: workshop-session-4-deep
-description: VOC虾工作坊 Session 4 — 深度VOC + 社媒洞察 + 用户画像(Day2上午)
-version: 2.2.0
+description: VOC虾工作坊 Session 4 — 深度VOC+社媒洞察+用户画像 + Side Quests
+version: 3.0.0
 session: 4
 prevSession: workshop-session-3-voc.md
 nextSession: workshop-session-5-report.md
@@ -16,12 +16,24 @@ nextSession: workshop-session-5-report.md
 > **关键点**:这是经过一晚消化后的深度工作,养虾人带着昨晚的校准意见来审阅深度结果
 
 ## 前置检查
-- 🛑 **GATE CHECK**: 检查 `workshop-progress.json` 中 `session-3.status`。如果为 `awaitingCalibration`,**禁止开始 Session 4**,必须先等待用户回复 Session 3 的校准问题
+- 🛑 **GATE CHECK**: 检查 `workshop-progress.json` 中 `session-3.status`。如果为 `awaitingCalibration` 或 `exploring`,**禁止开始 Session 4**,必须先等待用户完成 Session 3 的校准和探索
 - ✅ `stage-3-output.json` 存在(含reviewCorpus原始数据)
 - ✅ `calibration-notes.json` 中 session3 校准意见已记录
 - ✅ 读取 `calibration-notes.sessions.session-3.forwardActions` → **这是最关键的前向传递**
 - ✅ 读取 `stage-3-output._anomalies` → 需要在深度分析中验证的异常
 
+## Side Quest 触发条件速查(主线执行时参考)
+
+> Agent 在执行主线分析时,**必须同时对照此表检测触发信号**,发现匹配时写入 `_sideQuestTriggers`。
+
+| questId | 名称 | 触发信号(满足任一即触发) |
+|---------|------|-------------------------|
+| `tiktok-expedition` | 📱 TikTok远征 | ① TikTok品类VOC返回相关视频数 >100 ② 发现品类相关话题标签累计播放量 >10M ③ 竞品在TikTok有明显内容营销布局 |
+| `instagram-recon` | 📸 Instagram侦察 | ① 品牌或竞品在Instagram有 >1000 粉丝的官方账号 ② 品类相关UGC帖子数量 >500 ③ 发现KOL合作机会(品类KOL粉丝量 >50K) |
+| `douyin-patrol` | 🎵 抖音巡逻 | ① 品类关键词在抖音搜索有结果 ② 发现品类相关带货达人 ③ 用户在校准中提到国内社媒布局需求 |
+| `persona-workshop` | 👤 画像工坊 | ① 自动生成的画像 >3 个且差异度大 ② 用户对某个画像表示"这很像我的客户" ③ 画像间存在矛盾的购买行为模式 |
+| `trend-radar` | 🔥 趋势雷达 | ① Amazon评论趋势与社媒热度出现背离(如Amazon差评增加但社媒口碑良好) ② 某关键词在社媒和Amazon同时爆发 ③ 跨平台数据中发现新兴使用场景 |
+
 ## 校准前向传递(Session 3→4 核心机制)
 读取 session-3 校准中的三个标记列表,直接影响本阶段分析:
 
@@ -459,7 +471,7 @@ Step 4.13: 将以上所有输出写入 memory/stage-4-output.json
 ```
 
 ## 阶段交付
-存入 `stage-4-output.json`(含 `_goalAlignment` + `_anomalies` + `_contextFromPreviousStages`)
+存入 `stage-4-output.json`(含 `_goalAlignment` + `_anomalies` + `_contextFromPreviousStages` + `_sideQuestTriggers`
 记录校准到 calibration-notes → 填写 `forwardActions`(特别是 `proposalEmphasis`、`proposalDeemphasis`、`userDefinedPriority`)→ 更新 `_forwardPassSummary`
 
 **stage-4-output.json 包含:**
@@ -490,7 +502,7 @@ Step 4.13: 将以上所有输出写入 memory/stage-4-output.json
 ```
 
 ```
-虾:✅ 阶段四完成!这是整个工作坊信息量最大的一个阶段。
+虾:✅ 阶段四主线完成!这是整个工作坊信息量最大的一个阶段。
 
 📦 已产出交付物:
 • ⚡《痛点深度洞察》— {{criticalCount}}个严重痛点 + 权重排行
@@ -501,10 +513,88 @@ Step 4.13: 将以上所有输出写入 memory/stage-4-output.json
 • 📊《竞品BSR追踪》— 增长路径 + 风险预警
 • 👤《用户画像手册》— {{personaCount}}个画像 + 购买行为
 
-所有数据已存入记忆。这些分析会直接驱动最终报告的行动建议。
-准备好了告诉我,最终阶段——虾要交作业了!
+所有数据已存入记忆。
 ```
 
-**🛑 SESSION GATE: 此处 HARD STOP。Agent 必须停止并等待用户回复校准意见。禁止自动进入 Session 5。**
+**🛑 SESSION GATE: 此处 HARD STOP。Agent 必须停止并等待用户回复校准意见。禁止自动进入 Side Quest 菜单或 Session 5。**
 **更新 workshop-progress.json: session-4.status = "awaitingCalibration"**
-**只有当用户明确回复后,才将 status 改为 "completed" 并开始 Session 5。**
+
+---
+
+## Side Quest 发现(主线分析中自动标记)
+
+Agent 在执行主线分析时,根据数据发现标记 Side Quest 触发器。以下为各 Side Quest 的典型触发条件:
+
+### 📱 TikTok远征 (tiktok-expedition)
+```
+技能调用:tiktok-video-search → tiktok-hashtag-detail → tiktok-hashtag-videos → tiktok-user-posts
+关键输出:话题热度地图(hashtag+播放量+趋势) | 爆款视频Top5(标题+播放+创作者) | 值得关注创作者(粉丝+合作价值) | TikTok营销建议(内容方向+KOL+话题策略)
+轻量校准:这些TikTok发现对你有启发吗?
+```
+
+### 📸 Instagram侦察 (instagram-recon)
+```
+技能调用:instagram-search → instagram-user-info → instagram-user-posts
+关键输出:品牌声量对比表(账号+粉丝+互动+内容类型) | UGC分析(数量+类型+情感) | Instagram营销建议(内容策略+UGC激励+KOL优先级)
+轻量校准:Instagram上的品牌表现符合预期吗?
+```
+
+### 🎵 抖音巡逻 (douyin-patrol)
+```
+技能调用:douyin-general-search → douyin-hashtag-search → douyin-video-comments
+关键输出:品类内容生态概览(播放量+达人数+带货占比) | 热门内容Top5(标题+播放+评论热词) | 评论区真实声音(洞察+频率) | 抖音策略建议
+轻量校准:抖音上的发现有参考价值吗?
+```
+
+### 👤 画像工坊 (persona-workshop)
+```
+技能调用:user-persona(mode = "interactive-refine", existingPersona = 主线初始画像)
+关键输出:细化画像卡片(基础属性+购买行为+痛点需求+典型评论原文) | 针对该画像的营销建议
+轻量校准:这个画像像你认识的客户吗?需要调整什么?
+```
+
+### 🔥 趋势雷达 (trend-radar)
+```
+技能调用:social-trend-analysis(mode = "cross-platform-validation") → keyword-search-trend(12月)
+关键输出:跨平台趋势矩阵(Amazon×TikTok×Instagram×抖音+确信度) | 已验证强趋势(名称+行动窗口) | 新兴趋势(早期信号+监测建议) | 趋势驱动行动建议
+轻量校准:这些趋势信号和你的市场感知一致吗?
+```
+
+---
+
+## 校准后进入探索模式
+
+**当用户回复校准意见后,Agent 执行以下流程:**
+
+1. 记录校准到 `calibration-notes.json`
+2. 更新 `workshop-progress.json`: session-4.status = "exploring"
+3. 将所有 `discovered` 的 Side Quest 状态改为 `available`
+4. 按照全局规则 SQ2 展示 Side Quest 菜单
+
+```
+虾:校准意见已记录!
+
+🗺️ 这一阶段的分析触发了最多的探索机会——社媒、画像、趋势,每一条都值得深入:
+
+┌─────────────────────────────────────────────┐
+│  🎮 Side Quest 支线任务                      │
+├─────────────────────────────────────────────┤
+{{#each availableSideQuests}}
+│  {{emoji}} {{name}}                          │
+│  📍 {{trigger}}                              │
+│  ⏱️ 预计 {{estimatedTime}}                   │
+│                                              │
+{{/each}}
+├─────────────────────────────────────────────┤
+│  ⏭️ 说"继续"直接进入最终阶段:报告生成          │
+│  💾 说"暂停"保存进度下次再探索                   │
+└─────────────────────────────────────────────┘
+
+你对哪条线索感兴趣?选一个深入探索,或者直接继续主线。
+虾建议:这是最后的探索机会了!如果对某个社媒平台好奇,强烈建议现在深入看看,完成的支线会让最终报告更丰富!
+```
+
+**当用户说"继续"时:**
+- 将所有 `available` 的 Side Quest 标记为 `skipped`
+- 更新 session-4.status = "completed"
+- 进入 Session 5

+ 167 - 3
workshop/workshop-session-5-report.md

@@ -1,7 +1,7 @@
 ---
 name: workshop-session-5-report
-description: VOC虾工作坊 Session 5 — 虾交作业(最终报告)
-version: 2.2.0
+description: VOC虾工作坊 Session 5 — 最终报告生成(含 Side Quest 探索成果整合
+version: 3.0.0
 session: 5
 prevSession: workshop-session-4-deep.md
 nextSession: null
@@ -12,13 +12,22 @@ nextSession: null
 > **前置依赖:必须先加载 `workshop-global-rules.md`**
 
 ## 前置检查
-- 🛑 **GATE CHECK**: 检查 `workshop-progress.json` 中 `session-4.status`。如果为 `awaitingCalibration`,**禁止开始 Session 5**,必须先等待用户回复 Session 4 的校准问题
+- 🛑 **GATE CHECK**: 检查 `workshop-progress.json` 中 `session-4.status`。如果为 `awaitingCalibration` 或 `exploring`,**禁止开始 Session 5**,必须先等待用户完成 Session 4 的校准和探索
 - ✅ `stage-1~4-output.json` 全部存在
 - ✅ `calibration-notes.json` 全部 session 校准已记录
 - ✅ 读取 `calibration-notes._forwardPassSummary` → 全量校准摘要
 - ✅ 读取所有 stage 的 `_goalAlignment` 和 `_anomalies` → 目标进度 + 异常汇总
 - 如果缺少某个阶段,提示用户先补完
 
+## Side Quest 触发条件速查(报告生成时参考)
+
+> Agent 在生成报告时,**必须同时对照此表检测触发信号**,发现匹配时写入 `_sideQuestTriggers`。
+
+| questId | 名称 | 触发信号(满足任一即触发) |
+|---------|------|-------------------------|
+| `chapter-enhance` | 📝 章节深化 | ① 某报告章节仅基于主线数据(该Session的Side Quest全部skipped) ② 用户在校准中对某章节追问或表达强烈兴趣 ③ 某章节数据量明显少于其他章节 |
+| `action-plan-workshop` | 🎯 行动计划工坊 | ① 用户对30天行动计划的优先级提出质疑 ② 用户提到预算/团队/时间线等资源约束 ③ 行动计划中 P0 项超过5个(需要帮用户做取舍) |
+
 ## 校准前向传递(全量汇总)
 这是最终报告,必须整合所有前置校准:
 
@@ -58,11 +67,42 @@ nextSession: null
 {{/if}}
 
 我会把前面4个阶段的所有数据 + 你的校准意见整合起来。
+
+{{#if totalSideQuestsCompleted > 0}}
+🎮 探索成果加成:你完成了 {{totalSideQuestsCompleted}} 个支线任务!
+以下章节将获得深度加成:
+{{#each completedSideQuestEnhancements}}
+• {{chapterName}} ← {{sideQuestName}} 的深度数据
+{{/each}}
+{{else}}
+💡 提示:你跳过了所有支线任务,报告将基于主线数据生成。
+如果后续想深入某个方向,随时可以回来探索!
+{{/if}}
 ```
 
 ## 技能调用序列
 
 ```
+Step 5.0: Side Quest 成果收集(报告生成前必做)
+  读取 workshop-progress.json → 收集所有 sessions.*.sideQuests 中 status = "completed" 的条目
+  读取对应 stage-N-output.json 的 sideQuests.{questId} 数据
+  构建 sideQuestEnrichments 对象:
+  {
+    "completedQuests": [{ "questId": "...", "session": "...", "outputData": {...} }],
+    "enrichmentMapping": {
+      "category-landscape": [...],   // 关键词深潜/品类树/价格带 的深度数据
+      "brand-profile": [...],        // ASIN体检 的多ASIN诊断
+      "competitor-matrix": [...],    // 竞品侦察 的深度报告
+      "pricing-strategy": [...],     // 价格战推演/价格带掘金 的模拟数据
+      "voc-pain-point": [...],       // 差评深潜 的根因分析
+      "voc-highlight": [...],        // 好评解码 的驱动因素
+      "voc-comparison": [...],       // 评论对决 的一对一对比
+      "social-insights": [...],      // TikTok/Instagram/抖音 的深度报告
+      "user-persona": [...],         // 画像工坊 的定制画像
+      "action-plan": [...]           // 趋势雷达 的跨平台验证
+    }
+  }
+
 Step 5.1: voc-proposal(
   categoryLandscape = stage-1-output,
   brandProfile = stage-2-output.brandProfile,
@@ -71,11 +111,13 @@ Step 5.1: voc-proposal(
   vocDeepAnalysis = stage-4-output.{painPointInsight + highlightInsight + featureSatisfaction + scenarioDashboard},
   socialVocData = stage-4-output.{socialTrend + competitorBsr} || null,
   userPersona = stage-4-output.userPersona,
+  sideQuestEnrichments = step5.0 output.enrichmentMapping,
   brandName = brand-context.brandName,
   categoryKeyword = brand-context.categoryKeywords[0],
   calibrationNotes = memory/calibration-notes.json
 )
 → 输出: proposal (11章节提案 + actionableInsights + overallHealthScore + ROI + 30天行动线)
+→ 注意: 如果某章节有 sideQuestEnrichments 数据,该章节自动获得深度增强标记 🎮
 
 Step 5.2: html-report-generator(
   industryName = 从categoryKeywords推断,
@@ -87,6 +129,7 @@ Step 5.2: html-report-generator(
   vocDeepAnalysis = stage-4-output (痛点+亮点+功能+场景),
   socialVocData = stage-4-output.{socialTrend + competitorBsr} || null,
   userPersona = stage-4-output.userPersona,
+  sideQuestEnrichments = step5.0 output.enrichmentMapping,
   actionableInsights = step5.1 output.actionableInsights,
   overallHealthScore = step5.1 output.overallHealthScore,
   reportSections = ["cover", "executive-summary", "category-landscape", "brand-profile",
@@ -95,6 +138,7 @@ Step 5.2: html-report-generator(
     "user-persona", "action-plan", "roi-estimate"]
 )
 → 输出: HTML报告文件(13屏)
+→ 注意: 有 sideQuestEnrichments 的章节会显示 🎮 深度探索标记和额外数据面板
 ```
 
 ## 提案展示
@@ -199,6 +243,108 @@ HTML报告已生成(13屏),可在浏览器打开直接演示。
 你的反馈我会记录下来,作为提案的"老板批注"附在最后。
 ```
 
+**🛑 SESSION GATE: 此处 HARD STOP。Agent 必须停止并等待用户回复校准意见。禁止自动进入 Side Quest 菜单或直接结束工作坊。**
+**更新 workshop-progress.json: session-5.status = "awaitingCalibration"**
+
+---
+
+## Side Quest 发现(报告生成中自动标记)
+
+Agent 在生成报告时,根据数据发现标记 Side Quest 触发器。以下为各 Side Quest 的典型触发条件:
+
+### 📝 章节深化 (chapter-enhance)
+**触发条件**:某报告章节的数据深度不足(如仅基于主线数据,缺少 Side Quest 增强),或用户在校准中对某章节表达强烈兴趣
+```
+技能调用:
+根据用户选择的章节,调用该章节对应的原始技能(如选品类全景则调用 category-landscape 相关技能),
+以更细粒度的参数重跑,补充更多案例和数据点。
+
+展示模板:
+虾:📝 章节「{{chapterName}}」深化完成!
+
+📊 新增数据:
+• {{addedDataPoints}} 个新数据点
+• {{addedCases}} 个补充案例
+• {{refinedInsights}} 条细化洞察
+
+📋 章节已更新,报告中对应部分已重新生成。
+
+轻量校准:补充的内容到位吗?还需要再深入哪个方向?
+```
+
+### 🎯 行动计划工坊 (action-plan-workshop)
+**触发条件**:用户对30天行动计划的优先级排序提出疑问,或用户资源/预算条件与默认假设不同
+```
+技能调用:
+1. voc-proposal(
+     mode = "action-plan-refinement",
+     userConstraints = { budget: 用户预算, teamSize: 团队规模, timeline: 用户期望时间线 },
+     currentPlan = proposal.actionTimeline,
+     userFeedback = 校准中的反馈
+   )
+   → 输出: 定制化行动路线图
+
+展示模板:
+虾:🎯 行动计划工坊完成!
+
+📋 定制版行动路线图(基于你的资源条件):
+• 预算约束:{{budgetConstraint}}
+• 团队规模:{{teamSize}}人
+
+🗓️ 30天路线图(已调整):
+{{#each weeks}}
+Week {{@index+1}}: {{tasks}}
+ 预计投入:{{effort}} | 预期产出:{{expectedResult}}
+{{/each}}
+
+📊 60/90天延展计划:
+{{extendedPlan}}
+
+💰 调整后 ROI 预估:{{adjustedROI}}
+
+轻量校准:这个计划和你的实际情况匹配吗?
+```
+
+---
+
+## 校准后进入探索模式
+
+**当用户回复校准意见后,Agent 执行以下流程:**
+
+1. 记录校准到 `calibration-notes.json`(作为"老板批注")
+2. 更新 `workshop-progress.json`: session-5.status = "exploring"
+3. 将所有 `discovered` 的 Side Quest 状态改为 `available`
+4. 按照全局规则 SQ2 展示 Side Quest 菜单
+
+```
+虾:校准意见已记录为"老板批注"!
+
+🗺️ 报告已生成,但虾发现还有几个方向可以继续打磨:
+
+┌─────────────────────────────────────────────┐
+│  🎮 Side Quest 支线任务                      │
+├─────────────────────────────────────────────┤
+{{#each availableSideQuests}}
+│  {{emoji}} {{name}}                          │
+│  📍 {{trigger}}                              │
+│  ⏱️ 预计 {{estimatedTime}}                   │
+│                                              │
+{{/each}}
+├─────────────────────────────────────────────┤
+│  ⏭️ 说"结束"直接完成工作坊                      │
+│  💾 说"暂停"保存进度下次再探索                   │
+└─────────────────────────────────────────────┘
+
+你想打磨哪个方向?或者说"结束"完成整个工作坊。
+```
+
+**当用户说"结束/继续/完成"时:**
+- 将所有 `available` 的 Side Quest 标记为 `skipped`
+- 更新 session-5.status = "completed"
+- 进入工作坊结束流程
+
+---
+
 ## 工作坊结束
 
 ```
@@ -242,9 +388,27 @@ HTML报告已生成(13屏),可在浏览器打开直接演示。
 ✅ 30天行动时间线 + ROI估算
 ✅ 《HTML可演示报告》(13屏完整报告)
 
+{{#if totalSideQuestsCompleted > 0}}
+🎮 支线探索成果:
+━━━━━━━━━━━━━━━━━━━━
+共完成 {{totalSideQuestsCompleted}} 个支线任务:
+{{#each completedSideQuests}}
+✅ {{emoji}} {{name}}({{session}})
+{{/each}}
+{{#if skippedSideQuests.length > 0}}
+📋 未探索的支线(已记录,供后续参考):
+{{#each skippedSideQuests}}
+• {{emoji}} {{name}}({{session}})
+{{/each}}
+{{/if}}
+{{/if}}
+
 ━━━━━━━━━━━━━━━━━━━━
 所有数据和报告都已保存在我的记忆中。
 以后你随时可以叫我重新分析、更新数据、或者换一个品类再跑一遍。
+{{#if skippedSideQuests.length > 0}}
+💡 未探索的 {{skippedSideQuests.length}} 个支线已记录在报告附录中,后续如需深入可以重新开启对应阶段。
+{{/if}}
 
 虾会一直在这里,有问题随时叫我!🦐
 ```