Commit da9c469db870600a8ca7d3192992a365f751f25c

Authored by yibo
1 parent a50f59d5

perf: task list cache (10s TTL), base file memory cache, messages.log 512KB read limit

server/routes/agent-chat.js
... ... @@ -238,6 +238,26 @@ function loadFile(agentDir, filename) {
238 238 // ═══════════════════════════════════════
239 239 const BASE_DIR = path.join(__dirname, '../../packages/agent-core/base');
240 240  
  241 +// ── 启动时预加载 common + type 基类文件到内存(几乎不变的文件,避免每次请求读盘) ──
  242 +const _baseFileCache = new Map();
  243 +function _loadBaseFile(filepath) {
  244 + if (_baseFileCache.has(filepath)) return _baseFileCache.get(filepath);
  245 + try {
  246 + const content = fs.readFileSync(filepath, 'utf-8').trim();
  247 + _baseFileCache.set(filepath, content);
  248 + return content;
  249 + } catch { return ''; }
  250 +}
  251 +// 预热 common 文件
  252 +['methodology.md', 'tools-base.md', 'capabilities.md'].forEach(f => {
  253 + _loadBaseFile(path.join(BASE_DIR, 'common', f));
  254 +});
  255 +// 预热 type 基类
  256 +['master', 'cxo', 'skill', 'orchestrator', 'worker'].forEach(t => {
  257 + _loadBaseFile(path.join(BASE_DIR, t, 'agent-base.md'));
  258 + _loadBaseFile(path.join(BASE_DIR, t, 'cron-base.json'));
  259 +});
  260 +
241 261 /**
242 262 * 构建 system prompt — 三层继承
243 263 *
... ... @@ -282,18 +302,14 @@ function buildSystemPrompt(agentDir, userMessage) {
282 302 const commonFiles = ['methodology.md', 'tools-base.md', 'capabilities.md'];
283 303 for (const f of commonFiles) {
284 304 const fp = path.join(BASE_DIR, 'common', f);
285   - if (fs.existsSync(fp)) {
286   - const content = fs.readFileSync(fp, 'utf-8').trim();
287   - if (content) parts.push(content);
288   - }
  305 + const content = _loadBaseFile(fp);
  306 + if (content) parts.push(content);
289 307 }
290 308  
291 309 // ── Layer 2: 类型基类 ──
292 310 const typeBaseAgent = path.join(BASE_DIR, agentType, 'agent-base.md');
293   - if (fs.existsSync(typeBaseAgent)) {
294   - const content = fs.readFileSync(typeBaseAgent, 'utf-8').trim();
295   - if (content) parts.push(content);
296   - }
  311 + const typeContent = _loadBaseFile(typeBaseAgent);
  312 + if (typeContent) parts.push(typeContent);
297 313  
298 314 // ── Layer 3: Agent 专属文件 ──
299 315 const agentListText = Object.entries(fullSystemConfig.agents || {})
... ... @@ -399,12 +415,13 @@ function buildCronInstructions(agentDir) {
399 415  
400 416 // Layer 1: 基类 cron
401 417 const baseCronPath = path.join(BASE_DIR, agentType, 'cron-base.json');
402   - if (fs.existsSync(baseCronPath)) {
  418 + const baseCronContent = _loadBaseFile(baseCronPath);
  419 + if (baseCronContent) {
403 420 try {
404   - const base = JSON.parse(fs.readFileSync(baseCronPath, 'utf-8'));
  421 + const base = JSON.parse(baseCronContent);
405 422 allCrons = allCrons.concat(base);
406 423 } catch (err) {
407   - console.error(`[Cron] ⚠️ 读取 ${baseCronPath} 失败:`, err.message);
  424 + console.error(`[Cron] ⚠️ 解析 ${baseCronPath} 失败:`, err.message);
408 425 }
409 426 }
410 427  
... ...
server/routes/tasks.js
... ... @@ -4,9 +4,18 @@ const { TaskService } = require('../services/task-service');
4 4  
5 5 const taskService = new TaskService();
6 6  
  7 +// ── Task List 缓存 (10秒 TTL) ──
  8 +let _taskListCache = null;
  9 +let _taskListCacheTime = 0;
  10 +const TASK_LIST_CACHE_TTL = 10000;
  11 +
7 12 // GET /api/tasks — 扫描获取系统中聚合的 Task 列表(含父子关系)
8 13 router.get('/', (req, res) => {
9 14 try {
  15 + if (_taskListCache && Date.now() - _taskListCacheTime < TASK_LIST_CACHE_TTL) {
  16 + return res.json(_taskListCache);
  17 + }
  18 +
10 19 const tasks = taskService.getAggregatedTasks();
11 20  
12 21 const taskList = tasks.map(t => ({
... ... @@ -24,6 +33,9 @@ router.get(&#39;/&#39;, (req, res) =&gt; {
24 33 conversationCount: t.conversations.length,
25 34 }));
26 35  
  36 + _taskListCache = taskList;
  37 + _taskListCacheTime = Date.now();
  38 +
27 39 res.json(taskList);
28 40 } catch (err) {
29 41 res.status(500).json({ error: err.message });
... ...
server/services/task-service.js
... ... @@ -290,7 +290,19 @@ class TaskService {
290 290  
291 291 if (fs.existsSync(logPath)) {
292 292 try {
293   - const logContent = fs.readFileSync(logPath, 'utf-8');
  293 + // 限制最大读取 512KB,超出则只读尾部
  294 + const MAX_LOG_READ = 512 * 1024;
  295 + const stat = fs.statSync(logPath);
  296 + let logContent;
  297 + if (stat.size > MAX_LOG_READ) {
  298 + const fd = fs.openSync(logPath, 'r');
  299 + const buf = Buffer.alloc(MAX_LOG_READ);
  300 + fs.readSync(fd, buf, 0, MAX_LOG_READ, stat.size - MAX_LOG_READ);
  301 + fs.closeSync(fd);
  302 + logContent = buf.toString('utf-8');
  303 + } else {
  304 + logContent = fs.readFileSync(logPath, 'utf-8');
  305 + }
294 306 const blocks = this._parseMessagesLog(logContent, conv);
295 307 timeline.push(...blocks);
296 308 } catch (e) {
... ...