#!/usr/bin/env node /** * _observer-summary.js (v6.6-rc2 附件, 下划线前缀避开 baseline 保护) * P0 观察期数据摘要 · 读 debug/agent-returns.jsonl 出统计 * * 用法: * node scripts/patches/_observer-summary.js # 人类可读 * node scripts/patches/_observer-summary.js --json # 机器可读 * * 阈值: P1 开工门槛 = 业务样本 >= 100 条 (原规划) * 业务样本 = 排除 session_id 以 'smoke-' / 'test-' 开头的冒烟记录 */ 'use strict'; const fs = require('fs'); const path = require('path'); const CLAUDE_ROOT = path.resolve(__dirname, '..', '..'); const LOG_FILE = path.join(CLAUDE_ROOT, 'debug', 'agent-returns.jsonl'); const THRESHOLD = 100; const AS_JSON = process.argv.includes('--json'); function pct(part, whole) { return whole === 0 ? '0.0%' : (part / whole * 100).toFixed(1) + '%'; } function percentile(sorted, p) { if (sorted.length === 0) return 0; const idx = Math.min(sorted.length - 1, Math.max(0, Math.floor(sorted.length * p))); return sorted[idx]; } function readRecords() { if (!fs.existsSync(LOG_FILE)) return []; const raw = fs.readFileSync(LOG_FILE, 'utf8'); const records = []; for (const line of raw.split(/\r?\n/)) { if (!line.trim()) continue; try { records.push(JSON.parse(line)); } catch {} } return records; } function isSmoke(r) { const sid = (r.session_id || '').toLowerCase(); return sid.startsWith('smoke-') || sid.startsWith('test-') || sid.includes('smoke'); } function summarize(records) { const total = records.length; const smokes = records.filter(isSmoke); const business = records.filter(r => !isSmoke(r)); const agentTypes = {}; const traceIdSet = new Set(); const lengths = []; let tsMin = null, tsMax = null; for (const r of business) { const t = r.agent_type || '(empty)'; agentTypes[t] = (agentTypes[t] || 0) + 1; if (r.traceId) traceIdSet.add(r.traceId); if (typeof r.text_length === 'number') lengths.push(r.text_length); const ts = r.ts; if (ts) { if (!tsMin || ts < tsMin) tsMin = ts; if (!tsMax || ts > tsMax) tsMax = ts; } } lengths.sort((a, b) => a - b); return { total_records: total, smoke_records: smokes.length, business_records: business.length, unique_traceIds: traceIdSet.size, agent_type_distribution: agentTypes, text_length_stats: lengths.length === 0 ? null : { min: lengths[0], max: lengths[lengths.length - 1], avg: +(lengths.reduce((a, b) => a + b, 0) / lengths.length).toFixed(1), p50: percentile(lengths, 0.50), p90: percentile(lengths, 0.90), }, time_span: { from: tsMin, to: tsMax }, threshold: THRESHOLD, progress: pct(business.length, THRESHOLD), p1_eligible: business.length >= THRESHOLD, }; } function renderHuman(s) { const lines = []; lines.push('=== v6.6-rc2 P0 观察期摘要 ==='); lines.push(''); lines.push('总条数 : ' + s.total_records + ' (冒烟 ' + s.smoke_records + ' + 业务 ' + s.business_records + ')'); lines.push('唯一 trace : ' + s.unique_traceIds); lines.push('时间跨度 : ' + (s.time_span.from || '-') + ' ~ ' + (s.time_span.to || '-')); lines.push(''); lines.push('agent_type 分布:'); const dist = s.agent_type_distribution; const keys = Object.keys(dist).sort((a, b) => dist[b] - dist[a]); if (keys.length === 0) lines.push(' (空)'); for (const k of keys) lines.push(' ' + k.padEnd(24) + dist[k]); lines.push(''); if (s.text_length_stats) { const t = s.text_length_stats; lines.push('text_length: min=' + t.min + ' max=' + t.max + ' avg=' + t.avg + ' p50=' + t.p50 + ' p90=' + t.p90); } else { lines.push('text_length: (无业务样本)'); } lines.push(''); lines.push('P1 开工门槛: ' + s.business_records + ' / ' + s.threshold + ' (' + s.progress + ')'); lines.push('P1 可开工 : ' + (s.p1_eligible ? 'YES' : 'NO · 继续观察')); return lines.join('\n'); } function main() { const records = readRecords(); const s = summarize(records); if (AS_JSON) { process.stdout.write(JSON.stringify(s, null, 2)); } else { process.stdout.write(renderHuman(s) + '\n'); } } main();