bookworm-smart-assistant/scripts/patches/_observer-summary.js

129 lines
4.1 KiB
JavaScript
Raw Normal View History

#!/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();