eval: 完成任务书15路线对照与先想后读核查
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
// 任务书 15 第 4.4 节思考核查的材料:研究者第一次用 read_source 读基线代码之前,思考里想了什么。
|
||||
// 对每个"闭门写入口"调用(calls/r0-entryDraft.*)和第 1 轮提案调用(calls/r1-proposal.*),
|
||||
// 读 calls/<调用>/sessions/ 下的 Pi 会话文件,按时间顺序列出每条助手消息里的思考、文字和工具调用(名字和参数摘要),
|
||||
// 到第一次读取基线源码文件(含)为止;目录列表、资料文件不截断。另标出首次 read_source,保留两种口径。
|
||||
// 路径类型按保留的调用工作目录核查;每条消息标原始 JSONL 行号,便于回查。
|
||||
// node eval/route/thinking.ts <研究目录> <输出文件>
|
||||
// 研究目录是 /home/iomgaa/inforsi-eval/<运行>/study 这一层(下面有 calls/)。
|
||||
// 会话文件每行一个 JSON;type 为 "message" 的行里 message.role 是 "assistant" 时,message.content 是一串片段:
|
||||
// {type:"thinking", thinking}、{type:"text", text}、{type:"toolCall", name, arguments}。
|
||||
|
||||
const [study, out] = process.argv.slice(2);
|
||||
if (study === undefined || out === undefined) throw new Error("用法:node eval/route/thinking.ts <研究目录> <输出文件>");
|
||||
const callsDir = join(study, "calls");
|
||||
const calls = readdirSync(callsDir)
|
||||
.filter((c) => c.startsWith("r0-entryDraft.") || c.startsWith("r1-proposal."))
|
||||
.sort();
|
||||
if (calls.length === 0) throw new Error(`${callsDir} 下没有闭门写入口或第 1 轮提案调用`);
|
||||
|
||||
type Part = { type: "thinking"; thinking: string } | { type: "text"; text: string } | { type: "toolCall"; name: string; arguments: unknown };
|
||||
const ARG_CHARS = 300;
|
||||
|
||||
/** 一个会话文件到第一次读取基线源码(含)为止的文字。 */
|
||||
function session(path: string): string {
|
||||
const L: string[] = [];
|
||||
let step = 0;
|
||||
let firstRead = true;
|
||||
const lines = readFileSync(path, "utf8").split("\n");
|
||||
for (const [lineIndex, line] of lines.entries()) {
|
||||
if (line.trim() === "") continue;
|
||||
const entry = JSON.parse(line) as { type: string; timestamp?: string; message?: { role: string; content: Part[] } };
|
||||
if (entry.type !== "message" || entry.message?.role !== "assistant") continue;
|
||||
L.push(`---- 助手第 ${++step} 条(${entry.timestamp};原始 JSONL:${lineIndex + 1})`);
|
||||
for (const p of entry.message.content) {
|
||||
if (p.type === "thinking") L.push(`【思考】\n${p.thinking}`);
|
||||
else if (p.type === "text") L.push(`【文字】\n${p.text}`);
|
||||
else {
|
||||
const args = JSON.stringify(p.arguments);
|
||||
L.push(`【工具】${p.name} ${args.length > ARG_CHARS ? args.slice(0, ARG_CHARS) + "……" : args}`);
|
||||
if (p.name === "read_source") {
|
||||
if (firstRead) L.push("(首次 read_source;此处不一定读取源码)");
|
||||
firstRead = false;
|
||||
const target = (p.arguments as { path: string }).path;
|
||||
if (statSync(target).isDirectory()) L.push("(仅列目录,继续保留思考)");
|
||||
else if (/\.(py|ts|js|cpp|c|h|rs)$/.test(target)) {
|
||||
return [...L, "(到首次读取基线源码文件为止)"].join("\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...L, "(整个会话没有读取基线源码文件,以上为全部助手消息)"].join("\n\n");
|
||||
}
|
||||
|
||||
const sections: string[] = [`# 思考核查材料:${study}\n`];
|
||||
for (const call of calls) {
|
||||
const dir = join(callsDir, call, "sessions");
|
||||
const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl")).sort();
|
||||
if (files.length === 0) throw new Error(`${dir} 下没有会话文件`);
|
||||
// 文件名以开始时间开头,按名字排就是先后。
|
||||
files.forEach((f, i) => {
|
||||
const order = files.length > 1 ? `(第 ${i + 1} 个,共 ${files.length} 个,按开始时间)` : "";
|
||||
sections.push(`## ${call} / ${f}${order}\n\n${session(join(dir, f))}\n`);
|
||||
});
|
||||
}
|
||||
writeFileSync(out, sections.join("\n"));
|
||||
console.log(`${calls.length} 个调用,写到 ${out}`);
|
||||
Reference in New Issue
Block a user