From 9f5229d5f40ded1d30637de91eeb89b1fdb8d6fd Mon Sep 17 00:00:00 2001 From: yibo Date: Sat, 28 Mar 2026 02:21:34 +0800 Subject: [PATCH] feat: add Quota Monitor dashboard traversing all FDE LLM usage and API calls token costs across agents --- data/system/agents/agent-creator/memory/hippocampus-index.json | 4 ++-- data/system/agents/agent-creator/memory/memory.md | 1 + data/system/agents/agent-creator/memory/short-term/session-1774635384262.md | 33 +++++++++++++++++++++++++++++++++ data/system/agents/master/memory/hippocampus-index.json | 2 +- data/system/agents/master/memory/memory.md | 1 + data/system/agents/master/memory/short-term/session-1774635398379.md | 43 +++++++++++++++++++++++++++++++++++++++++++ data/system/agents/master/plan.md | 1 + packages/acp-core/protocol/ClaudeCliProtocol.js | 14 ++++++++++++++ server/routes/agent-chat.js | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ web/src/components/AppNav.vue | 4 ++++ web/src/router/index.js | 5 +++++ web/src/views/QuotaMonitor.vue | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 502 insertions(+), 9 deletions(-) create mode 100644 data/system/agents/agent-creator/memory/short-term/session-1774635384262.md create mode 100644 data/system/agents/master/memory/short-term/session-1774635398379.md create mode 100644 web/src/views/QuotaMonitor.vue diff --git a/data/system/agents/agent-creator/memory/hippocampus-index.json b/data/system/agents/agent-creator/memory/hippocampus-index.json index 8c49712..35f9f29 100644 --- a/data/system/agents/agent-creator/memory/hippocampus-index.json +++ b/data/system/agents/agent-creator/memory/hippocampus-index.json @@ -13,8 +13,8 @@ ], "planFile": "task-from-master.md", "confidence": 0.9, - "useCount": 0, - "lastUsed": null, + "useCount": 1, + "lastUsed": "2026-03-27", "createdAt": "2026-03-27" } ] \ No newline at end of file diff --git a/data/system/agents/agent-creator/memory/memory.md b/data/system/agents/agent-creator/memory/memory.md index 79751b5..bc16f8c 100644 --- a/data/system/agents/agent-creator/memory/memory.md +++ b/data/system/agents/agent-creator/memory/memory.md @@ -2,3 +2,4 @@ ## 关键事实 - [2026-03-27] [TASK from master] → ... | +- [2026-03-27] [TASK_CALLBACK] → 收到了 innovator 的任务完成回调。weibo-scraper 技能训练已完成: diff --git a/data/system/agents/agent-creator/memory/short-term/session-1774635384262.md b/data/system/agents/agent-creator/memory/short-term/session-1774635384262.md new file mode 100644 index 0000000..c16340d --- /dev/null +++ b/data/system/agents/agent-creator/memory/short-term/session-1774635384262.md @@ -0,0 +1,33 @@ +# Session 摘要 + +- **时间**: 2026-03-27 18:16:24 +- **对话ID**: callback-T-WEIBO-TRAINING +- **状态**: completed +- **工具调用**: 1 次 +- **耗时**: 14秒 + +## 任务 +[TASK_CALLBACK] +-TRAINING +执行者: innova + +## 结果 +收到了 innovator 的任务完成回调。weibo-scraper 技能训练已完成: +- 最终分数: **93.50** ✓ +- 版本: v1.6.0 +- 训练迭代: 3轮 +- 状态: 已部署 + +根据工作流程,我需要向任务分发者汇报完成。 + + +收到!weibo-scraper 技能训练已成功完成: + +| 指标 | 值 | +|------|-----| +| 最终分数 | 93.50 | +| 版本 | v1.6.0 | +| 训练迭代 | 3轮 | +| 状态 | ✅ 已部署 | + +已向 master 汇报任务完成。weibo-scraper Agent 现在已经具备从微博平台爬取热点话题、热门微博、热搜榜等内容的能耐,可以正式上岗了! diff --git a/data/system/agents/master/memory/hippocampus-index.json b/data/system/agents/master/memory/hippocampus-index.json index f367cd4..c60bd7b 100644 --- a/data/system/agents/master/memory/hippocampus-index.json +++ b/data/system/agents/master/memory/hippocampus-index.json @@ -12,7 +12,7 @@ ], "planFile": "task-callback.md", "confidence": 0.76, - "useCount": 23, + "useCount": 24, "lastUsed": "2026-03-27", "createdAt": "2026-03-27" }, diff --git a/data/system/agents/master/memory/memory.md b/data/system/agents/master/memory/memory.md index 1dbc489..5b63cac 100644 --- a/data/system/agents/master/memory/memory.md +++ b/data/system/agents/master/memory/memory.md @@ -32,3 +32,4 @@ - [2026-03-27] [TASK_CALLBACK] → 收到 innovator-001 的任务完成回调。让我先检查 plan.md 了解当前整体进度。 - [2026-03-27] hi → 你好,我是 Master Agent,LemonBoss 组织的管理者。告诉我你想做什么,我会把任务分配给合适的 Agent 来执行。 - [2026-03-27] [TASK_CALLBACK] → 收到 Innovator 任务完成回调。让我先检查 plan.md 了解当前整体进度。 +- [2026-03-27] [TASK_CALLBACK] → 收到 weibo-scraper 训练完成回调。让我更新记录并汇报。 diff --git a/data/system/agents/master/memory/short-term/session-1774635398379.md b/data/system/agents/master/memory/short-term/session-1774635398379.md new file mode 100644 index 0000000..2132f31 --- /dev/null +++ b/data/system/agents/master/memory/short-term/session-1774635398379.md @@ -0,0 +1,43 @@ +# Session 摘要 + +- **时间**: 2026-03-27 18:16:38 +- **对话ID**: callback-T-WEIBO-TRAINING +- **状态**: completed +- **工具调用**: 2 次 +- **耗时**: 18秒 + +## 任务 +[TASK_CALLBACK] +-TRAINING +执行者: agent- + +## 结果 +收到 weibo-scraper 训练完成回调。让我更新记录并汇报。 + + +收到任务完成回调。weibo-scraper 技能训练已完成! + +**训练结果汇总:** + +| 项目 | 结果 | +|------|------| +| **最终 Benchmark 得分** | 93.50 / 100 ✅ | +| **最终版本** | v1.6.0 | +| **部署状态** | 已部署至生产环境 ✅ | + +--- + +**当前系统状态:** + +| 公司/服务 | 状态 | +|-----------|------| +| ABC Inc. | ✅ 运营中 (CEO/CTO/COO/HR + 执行层) | +| weibo-scraper | ✅ 训练完成,已部署 | +| 系统 Agent | 全部正常 | + +--- + +告诉我下一步你想做什么?我可以: +1. 为 ABC Inc. 部署新的业务能力 +2. 创建新的公司或技能模板 +3. 查看系统整体健康度 diff --git a/data/system/agents/master/plan.md b/data/system/agents/master/plan.md index dc1dcad..304d1c1 100644 --- a/data/system/agents/master/plan.md +++ b/data/system/agents/master/plan.md @@ -43,6 +43,7 @@ - 2026-03-27 15:55: 定时巡检 - cto 15:37/coo 15:39 活跃,qa-judge 15:33,hr 15:18,系统正常 - 2026-03-27 16:00: 定时巡检 - cto 15:43/coo 15:43/qa-judge 15:43 活跃,hr 15:18,系统正常 - 2026-03-27 17:55: [TASK_CALLBACK] Innovator weibo-scraper FDE 训练完成 → Benchmark 得分 92.65/100 (≥90 达标), SKILL.md 已升级到 v1.3.0 +- 2026-03-27 18:04: [TASK_CALLBACK] weibo-scraper 训练最终完成 → 最终分数 93.50, 版本 v1.6.0, 已部署至生产环境 --- diff --git a/packages/acp-core/protocol/ClaudeCliProtocol.js b/packages/acp-core/protocol/ClaudeCliProtocol.js index 19f31bd..9c0dc6c 100644 --- a/packages/acp-core/protocol/ClaudeCliProtocol.js +++ b/packages/acp-core/protocol/ClaudeCliProtocol.js @@ -331,10 +331,13 @@ class ClaudeCliProtocol extends EventEmitter { cost_usd: event.total_cost_usd, session_id: event.session_id, stop_reason: event.stop_reason, + usage: this._lastUsage || { input: 0, output: 0 }, }, timestamp: Date.now(), }); + this._lastUsage = null; + if (this._promptResolve) { if (this._idleTimer) { clearTimeout(this._idleTimer); this._idleTimer = null; } this._promptResolve(); @@ -391,6 +394,17 @@ class ClaudeCliProtocol extends EventEmitter { _handleInnerStreamEvent(innerEvent, sessionId) { if (!innerEvent) return; + if (innerEvent.usage) { + this._lastUsage = this._lastUsage || { input: 0, output: 0 }; + this._lastUsage.input += (innerEvent.usage.input_tokens || 0); + this._lastUsage.output += (innerEvent.usage.output_tokens || 0); + } + if (innerEvent.message?.usage) { + this._lastUsage = this._lastUsage || { input: 0, output: 0 }; + this._lastUsage.input += (innerEvent.message.usage.input_tokens || 0); + this._lastUsage.output += (innerEvent.message.usage.output_tokens || 0); + } + switch (innerEvent.type) { case 'content_block_delta': if (innerEvent.delta?.type === 'text_delta' && innerEvent.delta.text) { diff --git a/server/routes/agent-chat.js b/server/routes/agent-chat.js index 6009655..baa2e74 100644 --- a/server/routes/agent-chat.js +++ b/server/routes/agent-chat.js @@ -631,6 +631,52 @@ function buildPrompt(agentDir, agentId, convId, userMessage, isNewSession) { // 路由 // ═══════════════════════════════════════ +router.get('/quota', (req, res) => { + try { + const DATA_ROOT = path.resolve(__dirname, '../../data'); + const records = []; + + // 读取所有 billing.log + const readBilling = (dir) => { + const p = path.join(dir, 'billing.log'); + if (fs.existsSync(p)) { + fs.readFileSync(p, 'utf-8').split('\n').forEach(line => { + line = line.trim(); + if (line.startsWith('{')) { + try { records.push(JSON.parse(line)); } catch(e) {} + } else if (line) { + // 旧的纯文本兼容: 2026-03-28T18:00:00.000Z | Agent: master | Conv: 123 | Cost: $0.123 | Duration: 123ms + try { + const parts = line.split('|').map(p => p.trim()); + const ts = parts[0]; + const ai = parts[1].replace('Agent:', '').trim(); + const ci = parts[2].replace('Conv:', '').trim(); + const cost = parseFloat(parts[3].replace('Cost: $', '').trim()); + const dur = parseInt(parts[4].replace('Duration:', '').replace('ms', '').trim()); + records.push({ timestamp: ts, agentId: ai, convId: ci, costUsd: cost || 0, durationMs: dur || 0, usage: { input: 0, output: 0 } }); + } catch(e) {} + } + }); + } + }; + + readBilling(path.join(DATA_ROOT, 'system/agents/master/logs')); + const compDir = path.join(DATA_ROOT, 'companies'); + if (fs.existsSync(compDir)) { + const comps = fs.readdirSync(compDir, { withFileTypes: true }); + for (const c of comps) { + if (c.isDirectory() && !c.name.startsWith('.')) { + readBilling(path.join(compDir, c.name, 'logs')); + } + } + } + + res.json(records); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + router.get('/list', (req, res) => { try { const allAgents = [] @@ -1862,11 +1908,10 @@ router.post('/:agentId/chat', async (req, res) => { currentToolInput = ''; } } else if (event.method === 'session/result') { - if (event.params?.cost_usd) { - sendSSE('session_cost', { cost: event.params.cost_usd, duration: event.params.duration_ms }); - // Phase 5: Economic Audit - 记录账单 (Token Profiling) + if (event.params?.duration_ms) { + sendSSE('session_cost', { cost: event.params.cost_usd, duration: event.params.duration_ms, usage: event.params.usage }); + // Phase 5: Economic Audit - 记录账单 (Token Profiling) JSON lines try { - // billing.log 写到公司目录下 (company agent) 或 master 目录下 (system agent) let billingDir; if (agentId && agentId.includes(':')) { const compId = agentId.split(':')[0]; @@ -1875,8 +1920,15 @@ router.post('/:agentId/chat', async (req, res) => { billingDir = path.resolve(__dirname, '../../data/system/agents/master/logs'); } if (!fs.existsSync(billingDir)) fs.mkdirSync(billingDir, { recursive: true }); - const billingEntry = `${new Date().toISOString()} | Agent: ${agentId} | Conv: ${convId} | Cost: $${event.params.cost_usd.toFixed(6)} | Duration: ${event.params.duration_ms}ms\n`; - fs.appendFileSync(path.join(billingDir, 'billing.log'), billingEntry, 'utf-8'); + const logEntry = { + timestamp: new Date().toISOString(), + agentId, + convId, + costUsd: event.params.cost_usd || 0, + durationMs: event.params.duration_ms, + usage: event.params.usage || { input: 0, output: 0 } + }; + fs.appendFileSync(path.join(billingDir, 'billing.log'), JSON.stringify(logEntry) + '\n', 'utf-8'); } catch (err) {} } if (!fullOutput && event.params?.result) { diff --git a/web/src/components/AppNav.vue b/web/src/components/AppNav.vue index 748a7fa..b5ea5b5 100644 --- a/web/src/components/AppNav.vue +++ b/web/src/components/AppNav.vue @@ -13,6 +13,10 @@ 📦 Templates + + 📊 Quota + + diff --git a/web/src/router/index.js b/web/src/router/index.js index dbd828d..72d9afb 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -59,6 +59,11 @@ const routes = [ name: 'PipelineEditor', component: () => import('@/views/PipelineEditor.vue'), }, + { + path: '/quota', + name: 'QuotaMonitor', + component: () => import('@/views/QuotaMonitor.vue'), + }, ] const router = createRouter({ diff --git a/web/src/views/QuotaMonitor.vue b/web/src/views/QuotaMonitor.vue new file mode 100644 index 0000000..4e1e81d --- /dev/null +++ b/web/src/views/QuotaMonitor.vue @@ -0,0 +1,339 @@ + + + + + -- libgit2 0.21.4