read-tools.js
5.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/**
* read-tools.js — Master Agent 的读取工具
*
* 让 Master Agent 可以:
* - 读取 retrain job 的 checkpoint 状态
* - 读取 sub-agent 的 log 文件尾部
* - 列出活跃的 retrain jobs
*/
const fs = require('fs');
const path = require('path');
const CONVERSATIONS_DIR = path.resolve(__dirname, '../../../conversations');
/**
* read_checkpoint 工具 — 读取 retrain job 的 checkpoint
*/
function createReadCheckpointTool() {
return {
name: 'read_checkpoint',
description: `读取 retrain 任务的 checkpoint 状态。返回当前进行到哪一步 (hunt/digest/evolve/publish),状态 (running/done/failed),重试次数等。
适用场景:
- 检查 retrain 任务进度
- 巡检时判断任务是否卡住
- 恢复任务前了解断点位置`,
parameters: {
type: 'object',
required: ['jobId'],
properties: {
jobId: {
type: 'string',
description: 'Retrain Job ID,如 "job-1773311603902-8d5a39c0"',
},
},
},
async execute(params) {
const { jobId } = params;
const checkpointPath = path.join(CONVERSATIONS_DIR, `retrain-${jobId}`, 'checkpoint.json');
if (!fs.existsSync(checkpointPath)) {
return { status: 'not_found', message: `Job ${jobId} 没有 checkpoint 文件` };
}
try {
const checkpoint = JSON.parse(fs.readFileSync(checkpointPath, 'utf-8'));
return { status: 'ok', checkpoint };
} catch (err) {
return { status: 'error', message: `读取 checkpoint 失败: ${err.message}` };
}
},
};
}
/**
* read_log 工具 — 读取 sub-agent 的 log 文件尾部
*/
function createReadLogTool() {
return {
name: 'read_log',
description: `读取 retrain 任务中 sub-agent 的日志尾部。可以查看 l3-hunt、l2-digest、innovator-evolver 的最新输出。
适用场景:
- 检查 sub-agent 的执行进度
- 判断 agent 是否产出了有价值的内容
- 分析失败原因`,
parameters: {
type: 'object',
required: ['jobId'],
properties: {
jobId: {
type: 'string',
description: 'Retrain Job ID',
},
agent: {
type: 'string',
description: '要读取的 agent 名称 (如 "innovator-hunter"),不填则读取所有 agent 日志',
},
lines: {
type: 'number',
description: '读取最后 N 行,默认 30',
},
},
},
async execute(params) {
const { jobId, agent, lines = 30 } = params;
const trainDir = path.join(CONVERSATIONS_DIR, `retrain-${jobId}`, 'train');
if (!fs.existsSync(trainDir)) {
return { status: 'not_found', message: `Job ${jobId} 没有 train 目录` };
}
const logFiles = fs.readdirSync(trainDir).filter(f => f.endsWith('.log'));
if (logFiles.length === 0) {
return { status: 'empty', message: '没有 log 文件' };
}
const results = {};
for (const logFile of logFiles) {
const agentName = logFile.replace('.log', '');
if (agent && agentName !== agent) continue;
try {
const content = fs.readFileSync(path.join(trainDir, logFile), 'utf-8');
const allLines = content.split('\n').filter(l => l.trim());
const tail = allLines.slice(-lines);
results[agentName] = {
totalLines: allLines.length,
fileSize: `${Math.round(content.length / 1024)}KB`,
tail: tail.join('\n'),
};
} catch (err) {
results[agentName] = { error: err.message };
}
}
return { status: 'ok', logs: results };
},
};
}
/**
* list_retrain_jobs 工具 — 列出活跃的 retrain jobs
*/
function createListRetrainJobsTool() {
return {
name: 'list_retrain_jobs',
description: `列出当前所有 retrain 任务及其状态。
适用场景:
- 查看有哪些正在进行的 retrain 任务
- 巡检时扫描所有任务状态`,
parameters: {
type: 'object',
properties: {},
},
async execute(_params, context) {
const { jobManager } = context;
if (!jobManager) {
// fallback: 扫描 conversations 目录
if (!fs.existsSync(CONVERSATIONS_DIR)) return { status: 'ok', jobs: [] };
const dirs = fs.readdirSync(CONVERSATIONS_DIR)
.filter(d => d.startsWith('retrain-') && fs.statSync(path.join(CONVERSATIONS_DIR, d)).isDirectory());
const jobs = [];
for (const dir of dirs) {
const cpPath = path.join(CONVERSATIONS_DIR, dir, 'checkpoint.json');
let checkpoint = null;
try { checkpoint = JSON.parse(fs.readFileSync(cpPath, 'utf-8')); } catch {}
jobs.push({
conversationId: dir,
jobId: dir.replace('retrain-', ''),
checkpoint,
});
}
return { status: 'ok', jobs };
}
const jobs = jobManager.listJobs({ limit: 20 });
return { status: 'ok', jobs };
},
};
}
module.exports = {
createReadCheckpointTool,
createReadLogTool,
createListRetrainJobsTool,
};