Files
InfoRSI/src/materials/researcher.ts
T
iomgaa 446e26224e feat: 任务书 09,materials 模块与提示词
- src/materials:每种提交一个材料函数,生成给模型看的材料、第一条消息、对号清单、能读的代码版本;查询只在材料内按编号找;读提示词并算原始字节哈希;信念本按方法.md 6.10 现算。
- 账本里只存成 Json 的冻结协议、位置表、决定、结算的形状和编号规则由 materials 定义并导出。
- vocab 加 Checklist(对号清单)。
- prompts/:参考项目 3 份角色提示词、16 份任务说明,只做必要改动。
- TASK.md 补一句 unversionedNames 不存进代码版本。
- 方法.md 4.3 ① 改为"研究者任务"放信念本;结构.md vocab 上限 1150、补 materials 对外内容。

用户 2026-10-03 开工前的四个决定(选的都是推荐项):"甲:上限改 1150,只放对号清单";"甲:materials 定义并导出";"不放,改方法.md ① 的措辞";"按原提案挑"。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:08:46 -04:00

271 lines
14 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 { compareIds } from "../rules/index.ts";
import type { ChecklistOf } from "../vocab/index.ts";
import {
beliefSection,
codeSection,
Draft,
entryVersions,
factorLines,
metricsSection,
ownSection,
publicRecords,
registeredMetrics,
researcherTaskSection,
runLines,
validEntries,
} from "./blocks.ts";
import { json, num, rangeText, score } from "./format.ts";
import { factorId, factorKey } from "./shapes.ts";
// 研究者的 11 种任务,每种一个函数,明写从账本取哪几样(方法.md 第三部分、4.2、4.3)。
// 顺序:你自己的 → 公共材料(任务与本轮、基线、研究记录、信念本、各任务另加的几节、本任务要处理的东西、已登记指标清单)。
// 别人的提交一律经 Board.published(已关阶段的最后一版)读;作者自评、别人的第一次预测、别人的退回都不读。
type Own = ReadonlyArray<{ heading: string; lines: string[] }>;
/**
* 研究者材料的骨架。middle 放在信念本之后、指标清单之前。
* 第零轮(4.2)只有任务与本轮、基线:不跑实验,信念本不变,也还没有研究记录和指标可列。
*/
function frame(d: Draft, own: Own, middle: () => void): void {
ownSection(d, own);
d.group("公共材料", "下面是大家看到的同一份材料,不隐藏任何正式证据;第一部分已经给过的内容,这里只写一句指引。");
researcherTaskSection(d);
codeSection(d);
if (d.round >= 1) {
publicRecords(d);
beliefSection(d);
}
middle();
if (d.round >= 1) metricsSection(d);
}
/** 提案、回应、统一共用的因子规则(方法.md 第五部分"延续""指标"、4.3 ②)。 */
function factorRules(d: Draft) {
const registered = registeredMetrics(d.board).map((m) => m.id);
return {
factorLimit: d.req.study.factorLimit,
continuable: d.board.protocols().flatMap((p) => p.protocol.factors.map((_, i) => factorId(p.candidate, factorKey(i)))),
reusableMetrics: registered,
takenMetricIds: [...d.req.study.metrics.map((m) => m.id), ...registered],
};
}
const PRESUMPTION =
'挑刺里标"(推测)"的判断,是挑刺人没对照代码或已测运行核实过的猜想;作者在回应里同意它,也不会让它变成已核实。当作别人的猜想看,不当证据。';
// ---------------------------------------------------------------------------
// 第零轮(方法.md 4.2)
// ---------------------------------------------------------------------------
/** 0.1 闭门写入口:任务说明、基线代码、基线分数;看不到别人的入口(任何入口都不读)。 */
export function entry(d: Draft): ChecklistOf<"entry"> {
frame(d, [], () => undefined);
return { kind: "entry", entries: { min: 2, max: 3 } };
}
/** 入口和之前各次讨论:只读已关阶段的(写入口阶段关了才有讨论)。 */
function entriesAndDiscussions(d: Draft): void {
const lines: string[] = [];
for (const e of validEntries(d.board)) {
d.record(e.id, `入口 ${e.id}:${e.name}(作者 ${e.author})`, e.body);
lines.push(`### 入口 ${e.id}:${e.name}(作者 ${e.author})`, "", e.body, "");
}
for (const s of d.board.published("discussion")) lines.push(`### ${s.author} 的讨论(阶段 ${s.stageId})`, "", s.content.body, "");
d.section("本任务要处理的东西", lines);
const table: string[] = ['程序给的号;整份替换后的新清单接着往后编,旧编号作废,不会指到新入口。'];
for (const [author, versions] of entryVersions(d.board)) {
versions.forEach((v, i) => {
const last = i === versions.length - 1;
table.push(`- ${author},${i === 0 ? "写入口时交的" : `第 ${i} 次替换`}${last ? "(最后一版,有效)" : "(已被整份替换,编号作废)"}:`);
v.entries.forEach((e, j) => table.push(` - ${v.ids[j]}${last ? "" : "(作废)"}:${e.name}`));
});
}
d.section("入口编号对照", table);
}
/** 0.2 讨论:全部入口(标明作者)、之前各次的讨论。 */
export function discussion(d: Draft): ChecklistOf<"discussion"> {
frame(d, [], () => entriesAndDiscussions(d));
return { kind: "discussion", entries: { min: 2, max: 3 } };
}
/** 0.3 认领:最终的入口与全部讨论;至少排 min(N, 入口总数) 个。 */
export function claim(d: Draft): ChecklistOf<"claim"> {
frame(d, [], () => entriesAndDiscussions(d));
const entries = validEntries(d.board).map((e) => e.id);
const n = d.board.stage(d.req.stageId).roster.length;
return { kind: "claim", entries, minChoices: Math.min(n, entries.length) };
}
// ---------------------------------------------------------------------------
// 第 r 轮(方法.md 4.3)
// ---------------------------------------------------------------------------
/** 某一轮实验员的提醒:每个候选最新一次实现或修复的 concerns,加消融实现的(只读已关阶段)。 */
function concernLines(d: Draft, round: number): string[] {
const b = d.board;
const candidates = [...new Set([...b.published("implementation"), ...b.published("repair")].filter((s) => s.round === round).map((s) => s.target!))];
const lines = [
...candidates.map((c) => `- 候选 ${c}:${b.implementation(c)!.content.concerns}`),
...b.published("ablationImplementation").filter((s) => s.round === round).map((s) => `- 消融格 ${s.target}:${s.content.concerns}`),
];
return lines.length === 0 ? ["(没有)"] : lines;
}
/** ② 提案:公共材料,加上一轮全部收尾、上一轮实验员的提醒、可以写进"延续"的因子。 */
export function proposal(d: Draft): ChecklistOf<"proposal"> {
frame(d, [], () => {
const prev = d.round - 1;
const wraps = d.board.published("wrapUp").filter((w) => w.round === prev);
d.section(
"上一轮的收尾",
prev < 1 ? ["(这是第 1 轮,没有上一轮)"] : wraps.flatMap((w) => [`### ${w.author} 的收尾`, "", w.content.body, ""]),
);
d.section("上一轮实验员实现时的提醒", prev < 1 ? ["(这是第 1 轮,没有上一轮)"] : concernLines(d, prev));
const lines = d.board.protocols().flatMap((p) =>
p.protocol.factors.map((f, i) => `- ${factorId(p.candidate, factorKey(i))} ${f.name}(关键设定:${f.keySettings};候选 ${p.candidate},基线 ${p.protocol.baseline}${f.continues === null ? "" : `;它自己延续 ${f.continues}`})`),
);
d.section('可以写进"延续"的因子', lines.length === 0 ? ["(还没有任何冻结的因子)"] : lines);
});
return { kind: "proposal", maxProposals: d.req.study.proposalLimit, ...factorRules(d) };
}
/** 一条原提案全文,带作者。 */
function proposalLines(d: Draft, id: string): string[] {
const p = d.board.proposal(id);
return [`### 原提案 ${id}(作者 ${p.author})`, "", json(p.content)];
}
/** 一个候选由哪几条原提案合并而成(判重合并的决定,方法.md 4.3 ③)。 */
function mergeLine(d: Draft, candidate: string): string {
const ids = d.board.proposalsOf(candidate);
return ids.length === 1 ? `候选 ${candidate} 就是原提案 ${ids[0]}。` : `候选 ${candidate} 由判重合并的 ${ids.join("、")} 合成,作者都保留作者关系。`;
}
/** 合并说明,后面跟这几条原提案的全文。 */
function candidateProposals(d: Draft, candidate: string): string[] {
return [mergeLine(d, candidate), "", ...d.board.proposalsOf(candidate).flatMap((id) => proposalLines(d, id))];
}
/** ④ 第一次预测:这个候选的全部原始提案。看不到作者自评(账本里已拆出封存)、看不到别人的预测。 */
export function firstPrediction(d: Draft): ChecklistOf<"firstPrediction"> {
const candidate = d.req.object!;
frame(d, [], () => d.section("本任务要处理的东西", candidateProposals(d, candidate)));
return { kind: "firstPrediction", candidate };
}
/** ⑤ 挑刺:被挑的那条原提案全文(用户 2026-10-03 决定 4:按原提案挑)。 */
export function critique(d: Draft): ChecklistOf<"critique"> {
const id = d.req.object!;
frame(d, [], () => d.section("本任务要处理的东西", proposalLines(d, id)));
return { kind: "critique", proposal: id, factors: d.board.proposal(id).content.factors.map((_, i) => factorKey(i)) };
}
/**
* ⑥ 回应与统一:这个候选的全部原提案、针对它们的全部挑刺(已关的挑刺阶段)。
* 两者材料相同;统一另要逐条体现每份原提案(sources)。
*/
function answer(d: Draft): { candidate: string; critiques: string[] } & ReturnType<typeof factorRules> {
const candidate = d.req.object!;
const ids = d.board.proposalsOf(candidate);
const list = d.board.published("critique").filter((c) => ids.includes(c.target!)).sort((a, b) => compareIds(a.item!, b.item!));
frame(d, [], () => {
d.section("本任务要处理的东西", candidateProposals(d, candidate));
d.section(
"本任务要答复的挑刺",
list.length === 0
? ["(没有针对这个候选的挑刺,replies 交空数组)"]
: [PRESUMPTION, "", ...list.flatMap((c) => [`### 挑刺 ${c.item}(${c.author} 挑提案 ${c.target} 的 ${c.content.target})`, "", c.content.body, ""])],
);
});
return { candidate, critiques: list.map((c) => c.item!), ...factorRules(d) };
}
export const response = (d: Draft): ChecklistOf<"response"> => ({ kind: "response", ...answer(d) });
export function unification(d: Draft): ChecklistOf<"unification"> {
const a = answer(d);
return { kind: "unification", ...a, sources: d.board.proposalsOf(a.candidate) };
}
/**
* ⑦ 第二次预测:冻结协议、挑刺与回应按挑刺编号配对、本人第一次给这个候选的三个数(4.3 ⑦)。
* 第一次预测只取本人本轮的;别人的第一次和作者自评不读。留池候选本轮不做第一次预测(4.3 ④)。
*/
export function secondPrediction(d: Draft): ChecklistOf<"secondPrediction"> {
const candidate = d.req.object!;
const first = () =>
d.board.published("firstPrediction").filter((s) => s.author === d.req.actor && s.target === candidate && s.round === d.round).at(-1)!;
const own = [
{
heading: "你第一次给这个候选的三个数",
lines: d.board.carried(d.round).includes(candidate)
? ["(这是留池候选,本轮没有第一次预测)"]
: [`一成 / 一半 / 九成:${rangeText(first().content.range)}(当时只读了原始提案)`, "", "读了冻结协议和挑刺与回应再给一次:该改就改,不该改就照旧,两份都会存档。"],
},
];
frame(d, own, () => {
const p = d.board.protocol(candidate);
const critiques = d.board.published("critique");
const pairs = p.replies.flatMap((r) => {
const c = critiques.find((x) => x.item === r.critique)!;
return [`### 挑刺 ${r.critique}(${c.author} 挑的)`, "", c.content.body, "", `作者的答复:${r.reply}`, ""];
});
d.section("本任务要处理的东西", [mergeLine(d, candidate), "", `### 候选 ${candidate} 的冻结协议`, "", json(p)]);
d.section("挑刺与回应", pairs.length === 0 ? ["(没有针对这个候选的挑刺)"] : [PRESUMPTION, "", ...pairs]);
});
return { kind: "secondPrediction", candidate };
}
/**
* ⑭ 解释:本轮各候选的运行与分数、实验员的提醒、核验结论、被采纳改法的因子(要给三个数的格)、
* 本轮运行的产物(只放进查询)。本轮正向运行在采纳决定写入后公开。
*/
export function explanation(d: Draft): ChecklistOf<"explanation"> {
const adopted = d.board.decision("adopt", d.round)!.output.adopted!;
const p = d.board.protocol(adopted);
frame(d, [], () => {
const runs = d.board.runs().filter((r) => r.plan.purpose === "forward" && r.plan.round === d.round && d.board.runPublic(r));
const verifications = d.board.published("verification").filter((v) => v.round === d.round);
d.section("本任务要处理的东西", [
"### 本轮正向运行",
"",
...runs.flatMap((r) => [...runLines(d, r), `(判分全文用 research_query 查 ${r.plan.run})`, ""]),
"### 核验结论",
"",
...(verifications.length === 0 ? ["(没有)"] : verifications.map((v) => `- 候选 ${v.target}:${v.content.body}`)),
]);
d.section("本轮实验员实现时的提醒", concernLines(d, d.round));
d.section("本轮要给三个数的格", [
`被采纳的改法:候选 ${adopted},完整代码 ${score(d.board.baselineScore(adopted))};本批基线 ${p.baseline} ${score(d.board.baselineScore(p.baseline))}。`,
"每条解释对下面每一格各给三个数:只关这一个因子、其余因子和保障照完整代码保留时,那次运行的最终指标(按分数原值)。",
"",
...factorLines(p),
]);
const artifacts = d.req.artifacts!;
const listed = Object.keys(artifacts)
.sort(compareIds)
.flatMap((run) => Object.entries(artifacts[run]!).map(([file, text]) => `- ${d.record(`${run}/${file}`, `运行 ${run} 的产物 ${file}`, text)}`));
d.section("本轮运行的产物", ["用 research_query 按编号查全文:", ...(listed.length === 0 ? ["(没有)"] : listed)]);
});
return { kind: "explanation", cells: p.factors.map((_, i) => factorKey(i)), maxExplanations: d.req.study.proposalLimit };
}
/** ⑱ 收尾:本轮的采纳、选格(结算与押的因子在信念本里),实验员的提醒。 */
export function wrapUp(d: Draft): ChecklistOf<"wrapUp"> {
frame(d, [], () => {
const adopt = d.board.decision("adopt", d.round)!.output;
const cells = d.board.decision("cells", d.round);
d.section("本任务要处理的东西", [
adopt.adopted === null
? `本轮没有采纳:最好的有效候选 ${adopt.best === null ? "没有" : `${adopt.best.id},改进 ${score(adopt.best.improvement)}`},采纳线 ${score(adopt.line)}。`
: `本轮采纳候选 ${adopt.adopted},改进 ${score(adopt.best!.improvement)},采纳线 ${score(adopt.line)}。`,
cells === undefined ? "本轮没有选格。" : `本轮选格:测 ${cells.output.chosen.length === 0 ? "(一格都不测)" : cells.output.chosen.join("、")},净值 ${num(cells.output.chosenNet)}。`,
'消融结算、各格结论和押的因子见"信念本"。',
]);
d.section("本轮实验员实现时的提醒", concernLines(d, d.round));
});
return { kind: "wrapUp" };
}