guanghulab/scripts/dc-agent-behavior.js
2026-05-10 13:12:44 +08:00

148 lines
4.2 KiB
JavaScript
Raw 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.

/**
* scripts/dc-agent-behavior.js
* DC-03 · Agent 行为记录器
*
* 采集时机:每次铸渊处理 Notion 工单回写后附带写入
* 存储位置data/dc-reports/agent-behavior-YYYY-MM.json按月累积
*
* 用法:
* 1. 作为模块引入:
* const { appendAgentBehaviorLog } = require('./dc-agent-behavior');
* appendAgentBehaviorLog({ type: '规则同步', auto_resolved: true, processing_sec: 18 });
*
* 2. 命令行调用:
* node scripts/dc-agent-behavior.js --type "规则同步" --auto --sec 18
* node scripts/dc-agent-behavior.js --type "工单回执" --human --sec 45 --llm-calls 3 --tokens 1200
*/
'use strict';
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const DC_DIR = path.join(ROOT, 'data/dc-reports');
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
// ━━━ 获取当月标识 ━━━
function getCurrentMonth() {
const now = new Date();
return new Date(now.getTime() + BEIJING_OFFSET_MS).toISOString().slice(0, 7);
}
// ━━━ 加载或初始化月度文件 ━━━
function loadMonthlyReport(month) {
const filePath = path.join(DC_DIR, `agent-behavior-${month}.json`);
try {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch {
return {
month: month,
tickets: [],
llm_calls_total: 0,
avg_tokens_per_call: 0,
human_intervention_rate: 0
};
}
}
// ━━━ 重新计算统计指标 ━━━
function recalcStats(report) {
const total = report.tickets.length;
if (total === 0) return;
const humanCount = report.tickets.filter(t => t.human_intervention).length;
report.human_intervention_rate = parseFloat((humanCount / total).toFixed(2));
// llm_calls_total 和 avg_tokens_per_call 在 append 时累加
}
// ━━━ 核心:追加行为记录 ━━━
function appendAgentBehaviorLog(ticketData) {
fs.mkdirSync(DC_DIR, { recursive: true });
const month = ticketData.month || getCurrentMonth();
const report = loadMonthlyReport(month);
const record = {
type: ticketData.type || 'unknown',
auto_resolved: ticketData.auto_resolved !== false,
human_intervention: ticketData.human_intervention === true,
processing_sec: ticketData.processing_sec || 0,
timestamp: new Date().toISOString()
};
report.tickets.push(record);
// 累加 LLM 调用统计
if (ticketData.llm_calls) {
const prevTotal = report.llm_calls_total;
report.llm_calls_total += ticketData.llm_calls;
if (ticketData.tokens_per_call) {
// 加权平均
const prevTokens = report.avg_tokens_per_call * prevTotal;
const newTokens = ticketData.tokens_per_call * ticketData.llm_calls;
report.avg_tokens_per_call = Math.round(
(prevTokens + newTokens) / report.llm_calls_total
);
}
}
recalcStats(report);
const filePath = path.join(DC_DIR, `agent-behavior-${month}.json`);
fs.writeFileSync(filePath, JSON.stringify(report, null, 2));
console.log(`🤖 DC-03 · 行为记录已追加: ${record.type} (${month})`);
return report;
}
// ━━━ CLI 模式 ━━━
function parseCli() {
const args = process.argv.slice(2);
if (args.length === 0) return null;
const data = {};
for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case '--type':
data.type = args[++i]; break;
case '--auto':
data.auto_resolved = true; break;
case '--human':
data.human_intervention = true;
data.auto_resolved = false; break;
case '--sec':
data.processing_sec = parseInt(args[++i], 10) || 0; break;
case '--llm-calls':
data.llm_calls = parseInt(args[++i], 10) || 0; break;
case '--tokens':
data.tokens_per_call = parseInt(args[++i], 10) || 0; break;
case '--month':
data.month = args[++i]; break;
}
}
return data.type ? data : null;
}
// ━━━ 入口 ━━━
if (require.main === module) {
const cliData = parseCli();
if (cliData) {
appendAgentBehaviorLog(cliData);
} else {
console.log('用法: node dc-agent-behavior.js --type "类型" [--auto|--human] [--sec N] [--llm-calls N] [--tokens N]');
process.exit(1);
}
}
module.exports = { appendAgentBehaviorLog };