Files
InfoRSI/eval/route/thinking.ts
T

70 lines
4.0 KiB
TypeScript
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.
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}`);