bookworm-smart-assistant/scripts/production-sim.js

437 lines
18 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Bookworm Smart Assistant — 生产活动全量模拟器
* 路径: scripts/production-sim.js
* 版本: v1.0 (2026-02-20)
*
* 用途:
* 模拟一个完整工作日的 67 个对话事件,验证 activity-logger 日志链路完整性。
* 以「SaaS 支付网关重构」为主线,覆盖全部 50 Skills + 10 Agents + 7 MCPs。
*
* 用法:
* node scripts/production-sim.js # 执行模拟并写入活动日志
* node scripts/production-sim.js --dry-run # 仅打印场景,不写入日志
* node scripts/production-sim.js --verify # 执行后校验日志完整性
*
* 日志输出: debug/activity-YYYY-MM-DD.jsonl
*
* 覆盖清单:
* Skills (50): ai-ml-expert → zero-defect-guardian
* Agents (10): orchestrator, self-auditor, self-healer, full-stack-builder,
* research-analyst, quality-gate, canvas-ui-designer,
* code-reviewer, pre-deploy-checker, test-writer
* MCPs (7): deep-research, context7, sequential-thinking, playwright,
* chrome-devtools, selenium, browserbase
*/
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
// ─── 配置 ───────────────────────────────────────────────
// 动态检测配置根目录
const detectClaudeRoot = () => require('./paths.config.js').PATHS.root;
const CLAUDE_ROOT = detectClaudeRoot();
const HOOK = path.join(CLAUDE_ROOT, 'hooks', 'activity-logger.js');
const DEBUG_DIR = path.join(CLAUDE_ROOT, 'debug');
// ─── 参数解析 ───────────────────────────────────────────
const args = process.argv.slice(2);
const DRY_RUN = args.includes('--dry-run');
const VERIFY = args.includes('--verify');
// ─── 计数器 ─────────────────────────────────────────────
const stats = { skill: 0, agent: 0, mcp: 0, total: 0, failed: 0 };
// 记录注入前的日志行数,用于 --verify
let logLinesBefore = 0;
if (VERIFY || !DRY_RUN) {
const dateStr = new Date().toISOString().slice(0, 10);
const logFile = path.join(DEBUG_DIR, `activity-${dateStr}.jsonl`);
try {
logLinesBefore = fs.readFileSync(logFile, 'utf8').trim().split('\n').length;
} catch {
logLinesBefore = 0;
}
}
// ─── 触发函数 ───────────────────────────────────────────
function trigger(payload, scenario) {
const evLabel = (
payload.tool_name === 'Skill' ? 'SKILL' :
payload.tool_name === 'TaskCreate' ? 'AGENT' :
payload.tool_name.startsWith('mcp__') ? 'MCP ' :
payload.tool_name === 'Bash' ? 'BASH ' : 'WRITE'
);
const detail =
payload.tool_input.skill ||
payload.tool_input.subject ||
payload.tool_name.replace('mcp__', '').split('__').join('/') ||
payload.tool_input.command?.slice(0, 60) ||
payload.tool_input.file_path || '';
if (DRY_RUN) {
console.log(` [${evLabel}] ${detail}`);
stats[payload.tool_name === 'Skill' ? 'skill' :
payload.tool_name === 'TaskCreate' ? 'agent' : 'mcp']++;
stats.total++;
return;
}
try {
const json = JSON.stringify(payload).replace(/'/g, "\\'");
execSync(`echo '${json}' | node "${HOOK}"`, { stdio: 'pipe', timeout: 5000 });
stats[payload.tool_name === 'Skill' ? 'skill' :
payload.tool_name === 'TaskCreate' ? 'agent' : 'mcp']++;
stats.total++;
console.log(` [${evLabel}] ${detail}`);
} catch (e) {
stats.failed++;
console.log(` [FAIL] ${detail}${e.message.slice(0, 80)}`);
}
}
function skill(name, scenario) {
console.log(`\n 用户: "${scenario}"`);
trigger({ tool_name: 'Skill', tool_input: { skill: name } }, scenario);
}
function agent(name, subject, scenario) {
console.log(`\n 系统: ${scenario}`);
trigger({ tool_name: 'TaskCreate', tool_input: { subject: `[agent:${name}] ${subject}` } }, scenario);
}
function mcp(toolName, input, scenario) {
console.log(`\n MCP: ${scenario}`);
trigger({ tool_name: toolName, tool_input: input }, scenario);
}
// ═══════════════════════════════════════════════════════════
// 场景剧本: SaaS 支付网关重构 — 完整工作日
// ═══════════════════════════════════════════════════════════
// 场景仅在直接运行时执行 (被 require 时跳过)
if (require.main !== module) {
// 模块导出 (供测试使用)
if (typeof module !== 'undefined') {
module.exports = { detectClaudeRoot, trigger, skill, agent, mcp, stats, verifyStats, verifyLogIntegrity };
}
return; // 被其他模块 require 时不执行场景
}
console.log('');
console.log('================================================================');
console.log(' Bookworm 生产活动全量模拟器');
console.log(` 日期: ${new Date().toISOString().slice(0, 10)}`);
console.log(` 模式: ${DRY_RUN ? '预览 (--dry-run)' : '写入活动日志'}`);
console.log(` 钩子: ${HOOK}`);
console.log('================================================================');
// ── Phase 1: 晨会与规划 (09:00-09:30) ──────────────────
console.log('\n\n--- Phase 1: 晨会与项目规划 (09:00-09:30) ---');
skill('product-manager-expert',
'帮我梳理 Q1 的产品路线图,重点是用户增长和留存功能');
skill('project-coordinator',
'安排本周冲刺任务,包括支付模块重构和移动端适配');
skill('tech-lead-mentor',
'新来的初级开发不太理解 SOLID 原则,帮我准备一份指导材料');
agent('orchestrator', '协调 Q1 支付模块重构项目',
'编排多团队并行任务: 前端支付UI + 后端网关 + 数据库迁移');
// ── Phase 2: 架构与需求分析 (09:30-10:30) ──────────────
console.log('\n\n--- Phase 2: 架构设计与需求分析 (09:30-10:30) ---');
skill('ai-ml-expert',
'用户支付行为数据训练一个欺诈检测模型,要求 recall > 95% 且误报率 < 1%');
skill('architect-expert',
'设计一个支持多租户的 SaaS 支付网关架构,要求 99.99% 可用性');
skill('cloud-native-expert',
'我们要把支付服务迁移到 K8s帮我设计 Helm Chart 和 HPA 策略');
skill('database-tuning-expert',
'PostgreSQL 的 orders 表已经 5000 万行了,查询越来越慢,怎么优化分区');
skill('edge-computing-expert',
'海外用户延迟太高,考虑在边缘节点部署支付预校验服务');
skill('impact-analyst',
'评估一下把支付从同步改成异步消息队列模式的影响范围');
skill('diagram-as-code-expert',
'用 Mermaid 画一张支付系统的 C4 架构图');
mcp('mcp__sequential-thinking__sequentialthinking',
{ thought: '分析支付架构的关键决策点: 同步vs异步、单体vs微服务、SQL vs NoSQL', thoughtNumber: 1, totalThoughts: 5, nextThoughtNeeded: true },
'顺序推理分析支付架构决策树');
// ── Phase 3: 市场与商业分析 (10:30-11:00) ──────────────
console.log('\n\n--- Phase 3: 市场与商业分析 (10:30-11:00) ---');
skill('business-plan-skill',
'写一份 SaaS 支付平台的商业计划书,目标是种子轮融资');
skill('finance-advisor',
'帮我做一个 3 年财务预测模型,包括 MRR 增长和获客成本');
skill('pricing-strategist',
'我们的 API 调用计费模式合理吗?竞品都是怎么定价的');
skill('investor-review-guide',
'投资人问我们的护城河是什么,帮我准备 pitch deck 的差异化部分');
skill('industry-research-cn',
'调研一下 2025-2026 年中国第三方支付行业的监管趋势和市场规模');
skill('growth-hacker',
'月活增长放缓了,帮我设计一套 PLG 增长飞轮策略');
skill('sales-consultant',
'企业客户谈判卡在年费定价上了,给一些 B2B SaaS 的谈判技巧');
skill('legal-review-skill',
'审查一下我们的用户服务条款,特别是支付退款和数据隐私部分');
agent('research-analyst', '竞品支付网关技术栈深度调研',
'启动调研智能体,分析 Stripe/Adyen/PayPal 的技术架构差异');
mcp('mcp__deep-research__search',
{ query: '2026 payment gateway architecture trends microservices event-driven' },
'深度研究支付网关行业趋势');
// ── Phase 4: 前端开发 (11:00-12:00) ────────────────────
console.log('\n\n--- Phase 4: 前端开发 (11:00-12:00) ---');
skill('frontend-expert',
'用 React 18 + TanStack Query 重写支付收银台组件,要求支持暗色模式');
skill('designer-expert',
'设计支付成功页的 UI要有品牌感和信任感');
skill('ux-researcher',
'我们的支付转化率才 67%,分析一下结账流程哪一步流失最严重');
skill('miniprogram-expert',
'微信小程序端的支付组件需要适配新版 JSAPI帮我改一下');
skill('mobile-expert',
'React Native 端的支付页面在 Android 低端机上卡顿,怎么优化渲染');
agent('canvas-ui-designer', '设计支付收银台 UI 原型',
'生成收银台组件的响应式设计方案和交互原型');
skill('copywriter-expert',
'写支付页面的文案,包括按钮文字、错误提示、加载状态的 UX writing');
skill('social-media-manager',
'新版支付体验上线了,帮我写一篇产品更新的推文和公众号文章');
mcp('mcp__playwright__browser_navigate',
{ url: 'http://localhost:3000/checkout' },
'自动化浏览器测试支付收银台页面渲染');
mcp('mcp__chrome-devtools__take_snapshot',
{},
'用 DevTools 抓取收银台页面 DOM 快照排查布局问题');
// ── Phase 5: 后端与数据开发 (13:00-14:30) ──────────────
console.log('\n\n--- Phase 5: 后端与数据开发 (13:00-14:30) ---');
skill('backend-builder',
'用 NestJS + Prisma 实现支付回调的幂等处理逻辑');
skill('developer-expert',
'TypeScript 里怎么优雅地处理支付状态机的类型推导');
skill('api-integration-specialist',
'集成支付宝和微信支付的统一回调接口,需要签名验证');
skill('data-engineer-expert',
'设计支付数据的 ETL 管道,从 PostgreSQL 实时同步到 ClickHouse');
skill('data-analyst-expert',
'分析上个月的支付数据,哪些渠道的失败率最高,原因是什么');
skill('regex-shell-wizard',
'写一个正则提取支付日志里的交易 ID 和金额: "TXN-2026-[A-Z0-9]+ amount=\\d+.\\d+"');
skill('ultimate-code-expert',
'这段支付回调处理代码有竞态条件,帮我重构成无锁设计');
skill('email-communicator',
'帮我写一封邮件给支付渠道的技术对接人,催促沙箱环境的开通');
agent('full-stack-builder', '实现支付回调幂等处理模块',
'全栈构建: 后端幂等键 + Redis 分布式锁 + 前端重试机制');
mcp('mcp__context7__resolve-library-id',
{ libraryName: '@nestjs/core' },
'查询 NestJS 最新文档以实现支付模块');
// ── Phase 6: 安全与合规 (14:30-15:00) ──────────────────
console.log('\n\n--- Phase 6: 安全审计与合规 (14:30-15:00) ---');
skill('security-expert',
'对支付 API 做一次安全审计,检查 OWASP Top 10 和 PCI-DSS 合规');
skill('devsecops-expert',
'在 CI/CD 管道里集成 SAST/DAST 扫描,特别是密钥泄露检测');
skill('customer-success-expert',
'用户投诉支付扣款成功但订单未更新,帮我排查并写一个安抚话术');
// ── Phase 7: 测试与质量 (15:00-16:00) ──────────────────
console.log('\n\n--- Phase 7: 测试与质量保障 (15:00-16:00) ---');
skill('tester-expert',
'帮我设计支付模块的测试方案,包括单元测试、集成测试和 E2E 测试');
skill('browser-automation-expert',
'用 Playwright 写一个支付流程的 E2E 测试,覆盖成功/失败/超时三种场景');
skill('performance-expert',
'支付接口的 P99 延迟从 200ms 涨到 800ms 了,帮我定位瓶颈');
skill('debugger-expert',
'支付回调偶发 500 错误,日志里有 "deadlock detected",帮我排查');
agent('test-writer', '生成支付模块测试套件',
'自动生成: 支付创建/回调/退款/对账 4 个模块的 Vitest 测试用例');
agent('quality-gate', '支付模块质量门控检查',
'执行: 覆盖率 > 80% + 无 Critical 漏洞 + 性能基线达标');
agent('code-reviewer', '审查支付回调 PR #347',
'Review: 幂等处理逻辑 + Redis 锁超时 + 错误重试策略');
mcp('mcp__selenium__navigate',
{ url: 'http://localhost:3000/checkout?test=e2e' },
'Selenium 跨浏览器兼容性测试支付页面');
mcp('mcp__browserbase__browserbase_session_create',
{},
'创建云端浏览器会话进行多地域支付页面测试');
// ── Phase 8: 部署与运维 (16:00-17:00) ──────────────────
console.log('\n\n--- Phase 8: 部署与运维 (16:00-17:00) ---');
skill('devops-expert',
'帮我写 GitHub Actions 的 CI/CD 配置,支付模块要蓝绿部署');
skill('sre-expert',
'设计支付系统的 SLO: 可用性 99.99%P99 延迟 < 300ms错误率 < 0.1%');
skill('git-operation-master',
'支付分支 feature/payment-v2 需要 rebase 到 main有 3 个冲突文件');
skill('technical-seo-expert',
'支付成功页需要正确的 canonical URL 和结构化数据标记');
skill('prompt-optimizer',
'优化支付客服机器人的 system prompt让它更准确地处理退款咨询');
agent('pre-deploy-checker', '支付模块 v2.0 上线前检查',
'预发布检查: 数据库迁移脚本 + 环境变量 + 回滚方案 + 监控告警');
// ── Phase 9: 系统自进化 (17:00-17:30) ──────────────────
console.log('\n\n--- Phase 9: 系统自进化闭环 (17:00-17:30) ---');
skill('project-audit-expert',
'对今天的支付模块重构做一个项目复盘,识别技术债和改进点');
skill('reviewer-expert',
'帮我 review 今天提交的所有 PR检查代码风格和架构一致性');
skill('genesis-engine',
'根据今天的开发经验,生成一个"支付系统开发"的技能模板');
skill('zero-defect-guardian',
'全面扫描支付模块的代码质量: 类型安全、错误处理、边界情况');
skill('tech-writer-expert',
'帮支付模块写 API 文档,用 OpenAPI 3.0 格式');
agent('self-auditor', '每日系统一致性审计',
'8 维度扫描: 配置 → 技能 → 智能体 → 钩子 → MCP → 路由 → 安全 → 磁盘');
agent('self-healer', '自动修复审计发现的元数据偏差',
'修复: 版本号同步 + 技能计数校正 + 注册表更新');
// ═══════════════════════════════════════════════════════════
// 结果汇总
// ═══════════════════════════════════════════════════════════
console.log('\n');
console.log('================================================================');
console.log(' 模拟完成');
console.log('----------------------------------------------------------------');
console.log(` Skills: ${stats.skill}/50`);
console.log(` Agents: ${stats.agent}/10`);
console.log(` MCPs: ${stats.mcp}/7`);
console.log(` ----------------`);
console.log(` Total: ${stats.total}/67${stats.failed ? ` (${stats.failed} failed)` : ''}`);
console.log('================================================================');
// 完整性校验函数 (供测试使用)
function verifyStats(s) {
return s.skill === 50 && s.agent === 10 && s.mcp === 7;
}
// 日志完整性校验函数
function verifyLogIntegrity(logFile, linesBefore) {
const allLines = fs.readFileSync(logFile, 'utf8').trim().split('\n');
const newLines = allLines.slice(linesBefore);
const skills = new Set(), agents = new Set(), mcps = new Set();
for (const line of newLines) {
const o = JSON.parse(line);
if (o.event === 'skill') skills.add(o.detail);
else if (o.event === 'agent') {
const m = o.detail.match(/\[agent:(\S+)\]/);
agents.add(m ? m[1] : o.detail);
}
else if (o.event === 'mcp') mcps.add(o.detail.split('/')[0]);
}
return { newLines: newLines.length, skills: skills.size, agents: agents.size, mcps: mcps.size };
}
if (require.main === module) {
const passed = verifyStats(stats);
if (!passed) {
console.log('\n [WARN] 覆盖不完整,请检查失败项');
process.exit(1);
}
console.log(`\n [PASS] 全量覆盖验证通过 — 67/67 事件已${DRY_RUN ? '预览' : '写入活动日志'}`);
// ─── --verify: 日志完整性校验 ───────────────────────────
if (VERIFY && !DRY_RUN) {
console.log('\n--- 日志完整性校验 ---');
const dateStr = new Date().toISOString().slice(0, 10);
const logFile = path.join(DEBUG_DIR, `activity-${dateStr}.jsonl`);
try {
const result = verifyLogIntegrity(logFile, logLinesBefore);
console.log(` 新增日志行: ${result.newLines}`);
console.log(` Unique Skills: ${result.skills}/50`);
console.log(` Unique Agents: ${result.agents}/10`);
console.log(` Unique MCPs: ${result.mcps}/7`);
if (result.skills === 50 && result.agents === 10 && result.mcps === 7) {
console.log('\n [PASS] 日志完整性校验通过');
} else {
console.log('\n [WARN] 日志记录数与预期不符');
process.exit(1);
}
} catch (e) {
console.log(` [ERROR] 无法读取日志: ${e.message}`);
process.exit(1);
}
}
}