Files
InfoRSI/src/vocab/submissions.ts
T

654 lines
23 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 { Type, type Static, type TSchema } from "typebox";
import { Id } from "./basics.ts";
import type { SubmissionKind } from "./roster.ts";
import { HypothesisMetric } from "./runs.ts";
/**
* 每种提交的字段定义(方法.md 第五部分)。同一份定义同时是类型、收件时的格式检查、
* 发给模型的提交工具参数,所以字段说明写给模型看,每个字段都写明出口。
* 这里只管单个字段长什么样;跨字段的规则(q₁ < q₅ < q₉ 严格递增、因子数上限、覆盖是否齐全等)
* 在 submissions 模块。
* 程序分配的编号(候选、挑刺、入口编号)不让模型填;因子、实现条款的编号就是它在清单里的位置。
*/
/** 文字字段:不能为空,也不能只有空白。 */
const TEXT = { pattern: "\\S" };
/** 不认识的字段一律不收。 */
const CLOSED = { additionalProperties: false } as const;
// ---------------------------------------------------------------------------
// 共用字段
// ---------------------------------------------------------------------------
/** 三个数:一个分数的事前判断(方法.md 第二部分"范围")。 */
export const Range = Type.Object(
{
q1: Type.Number({ description: "有一成可能低于这个数" }),
q5: Type.Number({ description: "有一半可能低于这个数" }),
q9: Type.Number({ description: "有九成可能低于这个数" }),
},
{
...CLOSED,
description:
"三个数,按分数原值写,不管越大越好还是越小越好。必须严格递增 q1 < q5 < q9,认为几乎不变就写一个很窄的范围。" +
'没有"不知道"这个选项:拿不准就把 q1 和 q9 拉开',
},
);
export type Range = Static<typeof Range>;
const FactorKey = Type.String({
pattern: "^p[1-9][0-9]*$",
description: "因子编号:它在冻结协议因子清单里的位置,第 1 个是 p1",
});
const SafeguardName = Type.String({
pattern: "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$",
description:
"保障短名:字母、数字和 ._-。不能叫 main,也不能写成 p1、p2 这种因子编号的样子," +
"因为答案路径里 main、因子编号和保障短名放在一起;位置表也按它对号",
});
const ClauseKey = Type.String({
pattern: "^c[1-9][0-9]*$",
description: "实现条款编号:它在冻结协议实现条款里的位置,第 1 条是 c1",
});
const CodeLocation = Type.String({
pattern: "^[^:\\s]+(:[1-9][0-9]*(-[1-9][0-9]*)?)?$",
description:
'代码位置:"文件"、"文件:行号"或"文件:起-止",行号以本次提交代码为准。一个值只写一个连续范围,不带括号标签;多处位置拆开写',
});
const CONCERNS = Type.String({
...TEXT,
description:
"照冻结原文实现、但你认为会出问题的地方,尤其是某条保障会挡掉或改变什么。" +
'它会转给研究者,不阻止运行,也不改动协议。没有就写"无"',
});
// ---------------------------------------------------------------------------
// 第零轮:入口、讨论、志愿(方法.md 4.2)
// ---------------------------------------------------------------------------
const Entry = Type.Object(
{
name: Type.String({
...TEXT,
description: "名称:离开这份清单单独看也读得懂",
}),
body: Type.String({
...TEXT,
description:
"Markdown:从哪里开始看(先读哪段代码、先问哪个问题、先质疑哪个设定)、为什么值得有人去走。" +
'只写起点,不写"应该改成什么、能到多少分"',
}),
},
CLOSED,
);
const ENTRIES = Type.Array(Entry, {
minItems: 2,
maxItems: 3,
description:
"2–3 个入口,彼此尽量不重叠;想不出第三个就交两个。编号由程序按顺序分配",
});
export const EntrySubmission = Type.Object(
{
entries: ENTRIES,
},
CLOSED,
);
export const DiscussionSubmission = Type.Object(
{
body: Type.String({
...TEXT,
description:
"讨论正文,Markdown。目标是分工,不是达成共识:哪些入口其实是同一条路," +
"哪条路没人站但值得走,你自己想走哪条",
}),
entries: Type.Optional({
...ENTRIES,
description:
"可选:整份替换你自己的入口清单(2–3 个)。不换就不交这个字段",
}),
},
CLOSED,
);
export const ClaimSubmission = Type.Object(
{
choices: Type.Array(Id, {
minItems: 1,
description:
"志愿:按你想走的先后排入口编号,照材料里列的写,可以排别人的入口;至少排材料里写明的个数",
}),
},
CLOSED,
);
// ---------------------------------------------------------------------------
// 提案(方法.md 4.3 ②、第五部分"提案")
// ---------------------------------------------------------------------------
const Factor = Type.Object(
{
name: Type.String({
...TEXT,
description: "名称:离开这份提案也读得懂",
}),
method: Type.String({
...TEXT,
description:
"做法:改什么、怎么改。配套设定(离开这个因子就没有意义的设定值)写在这里,跟着它一起开、一起关",
}),
keySettings: Type.String({
...TEXT,
description:
'关键设定:决定这个因子效果的设定值,例如"统计窗口 1000 次查找、每次最多删四分之一";没有写"无"',
}),
offBehavior: Type.Optional(
Type.String({
...TEXT,
description: "关掉这个因子时换成什么。省略就回到基线在这件事上的做法",
}),
),
continues: Type.Union([Id, Type.Null()], {
description:
"延续信念本里的哪个因子:写它的因子编号。只用于同一个想法的新版本(换了设定、换了基线);" +
"受它启发想出来的新想法不算延续。新因子写 null",
}),
},
CLOSED,
);
const FACTORS = Type.Array(Factor, {
minItems: 1,
description:
"因子清单:每个因子是单独放到基线上也说得通的改动,编号就是位置(第 1 个是 p1)。" +
"只有一个因子就写一个;拿不准一处改动算因子还是保障,就算因子。" +
"超过因子数上限时把多出来的留到下一轮,不要几个捆成一个",
});
const SAFEGUARDS = Type.Array(SafeguardName, {
description:
"保障清单,每条一个短名。保障在任何一格里都不改变评测打出来的分数,只在评测本来不收的情形下动手," +
"动手时只修到刚好合法、退回基线做法或直接报错;做不到的、拿不准的都是因子。没有保障交空数组",
});
const METRICS = Type.Object(
{
defined: Type.Array(HypothesisMetric, {
description: "新登记的假设专用指标,编号只写短名;没有就交空数组",
}),
reused: Type.Array(Id, {
description:
'引用已登记的假设专用指标,照材料里已登记指标清单的全名("候选编号.短名")写;没有就交空数组。' +
"要测的量清单里已经有,就引用它,不换个名字重新登记:两个名字测同一个东西,会被当成两份独立的证据",
}),
},
{
...CLOSED,
description:
"指标登记:这个候选要采集哪些假设专用指标。最终指标和常规辅助指标程序每次都会采集,不用在这里写。" +
'假设专用指标由候选代码在运行当中算出,候选代码拿不到基线的成绩,"比基线好多少"这类量测不到。' +
"协议在某种情形下会让某个指标写 null(例如某条分支没走到、某个因子被关掉),就别给它选 invalidateRun,否则那次正式运行白跑",
},
);
const PROPOSAL_BODY = Type.String({
...TEXT,
description:
"提案正文,Markdown,带小标题:每个因子为什么可能有效;" +
"每条保障做什么、属于拦 / 补 / 改哪一种、自检时怎么想的;" +
"协议(不许改什么、随机性怎么处理、何时停、什么样的运行不算数);因子之间的关系。" +
'某方面不适用写"不适用:原因"。协议里不要求实验员按正式规模运行程序来核对什么,正式规模只由程序跑。' +
"不写自己估的分数或提升量;引用已测到的分数要写明出自哪次运行;" +
"文献和别人报告的数值只当起点,说明与本任务的区别;从第 2 轮起说明哪些新的观测影响了这条提案",
});
export const ProposalSubmission = Type.Object(
{
factors: FACTORS,
safeguards: SAFEGUARDS,
metrics: METRICS,
authorEstimate: Range,
body: PROPOSAL_BODY,
},
{
...CLOSED,
description:
"一条提案。authorEstimate 是你自己估的这个改法跑出来的最终指标,交上来就封存," +
"到本轮结束前不给任何人看。交不出合格提案时不要凑,在回复里写清卡在哪里",
},
);
// ---------------------------------------------------------------------------
// 两次预测、挑刺(方法.md 4.3 ④⑤⑦)
// ---------------------------------------------------------------------------
/** 第一次、第二次预测用同一个定义:对一个候选的最终指标给三个数。 */
export const PredictionSubmission = Type.Object(
{
range: Range,
body: Type.String({
...TEXT,
description:
"理由,Markdown:参照了什么,这一个和参照差在哪、所以往哪边挪;" +
"同一阶段你给别的候选的数和这一个相同或不同,差别出在机制的哪里",
}),
},
{
...CLOSED,
description: "对这个候选跑出来的最终指标给三个数",
},
);
export const CritiqueSubmission = Type.Object(
{
target: Type.String({
pattern: "^(whole|p[1-9][0-9]*)$",
description:
"针对谁:整条提案写 whole,针对某个因子写它的编号(p1、p2……)",
}),
body: Type.String({
...TEXT,
description:
"挑刺内容,Markdown。每条意见写清是什么、在哪(能对照代码的写出位置)、不处理会怎样," +
"以及作者给出什么就能解决(不替作者选取值)。" +
"确实没找到问题,写查了什么、为什么认为没有",
}),
},
CLOSED,
);
// ---------------------------------------------------------------------------
// 回应与统一(方法.md 4.3 ⑥)
// ---------------------------------------------------------------------------
const REPLIES = Type.Array(
Type.Object(
{
critique: Type.String({
...Id,
description: "挑刺编号,照材料里列的写",
}),
reply: Type.String({
...TEXT,
description:
"先用一句复述这份挑刺说的是什么(照原意,不答弱化的版本),再给采纳、部分采纳或不采纳。" +
"采纳的说出改在改动的哪一项、哪一条条款,只在答复里表态等于没改;不采纳的先摆证据(代码位置、运行编号、协议原文)再讲理由;" +
"部分采纳的说清另一部分为什么不改、还剩什么局限。一份挑刺里有几条意见,在这一条里逐条答",
}),
},
CLOSED,
),
{
description:
'材料"本任务要答复的挑刺"里列出的每一份挑刺各答复一条;没有挑刺交空数组',
},
);
const CLAUSES = Type.Array(
Type.String({
...TEXT,
description:
'一条实现条款:实验员要做的一件事,写到不用猜就能实现。开头写位置:"文件"、"文件:行号"或"文件:函数名",' +
"只写一个行号或一个函数名、不写范围,文件和函数要在基线代码里真实存在",
}),
{
minItems: 1,
description:
"实现条款,编号就是位置(第 1 条是 c1)。实验员照它实现,核验员逐条对号。" +
"条款之间不矛盾;不改任务自带的评分和校验程序,不改最终指标的算法;不让实验员按正式规模运行程序来核对什么",
},
);
export const ResponseSubmission = Type.Object(
{
replies: REPLIES,
changes: Type.Object(
{
factors: Type.Optional(FACTORS),
safeguards: Type.Optional(SAFEGUARDS),
metrics: Type.Optional(METRICS),
body: Type.Optional(PROPOSAL_BODY),
},
{
...CLOSED,
description:
"改动了的部分:写上的一项整项替换原提案的对应部分,没写的沿用原提案。" +
"什么都没改就交空对象。作者自评不能改",
},
),
clauses: CLAUSES,
body: Type.String({
...TEXT,
description:
'答复正文,Markdown:逐条答复之外要补充的话。没有挑刺、也没有要补充的,写"无"',
}),
},
CLOSED,
);
export const UnificationSubmission = Type.Object(
{
factors: FACTORS,
safeguards: SAFEGUARDS,
metrics: METRICS,
body: PROPOSAL_BODY,
replies: REPLIES,
clauses: CLAUSES,
sources: Type.Array(
Type.Object(
{
proposal: Type.String({
...Id,
description: "被合并的原提案编号,照材料里列的写",
}),
coverage: Type.String({
...TEXT,
description:
"这份原提案在统一后的协议里哪里体现;没采纳的部分写明为什么",
}),
},
CLOSED,
),
{
minItems: 1,
description: "逐条写明每份原提案在哪里体现",
},
),
},
{
...CLOSED,
description:
"几条提案合成的候选,由你写成一份完整的冻结协议。只能用原提案和挑刺里有的改动,不能另加新改动",
},
);
// ---------------------------------------------------------------------------
// 实现、修复、核验(方法.md 4.3 ⑨)
// ---------------------------------------------------------------------------
const LOCATIONS = Type.Array(CodeLocation, { minItems: 1 });
/** 实现和修复用同一个定义。代码本身由程序从实验员的工作目录冻结,不作为字段交。 */
export const ImplementationSubmission = Type.Object(
{
factorLocations: Type.Array(
Type.Object(
{
factor: FactorKey,
locations: LOCATIONS,
},
CLOSED,
),
{
description:
"因子位置表:冻结协议里每个因子一项,列出本次代码中它实际所在的全部位置,不只列这次修复的部分。一处代码服务两个因子,两边都写",
},
),
safeguardLocations: Type.Array(
Type.Object(
{
safeguard: SafeguardName,
locations: LOCATIONS,
},
CLOSED,
),
{
description:
"保障位置表:每条保障一项,短名照冻结清单写,位置以本次代码为准。同一行既属于因子又属于保障时,两边都写。冻结协议没有保障就交空数组",
},
),
unassigned: Type.Array(
Type.Object(
{
location: CodeLocation,
note: Type.String({
...TEXT,
description: "为什么不属于任何因子或保障,一句说明",
}),
},
CLOSED,
),
{
description:
"不属于任何因子或保障的改动,按本次代码相对本批基线的完整差异列出,并说明服务哪项要求;没有就交空数组",
},
),
concerns: CONCERNS,
body: Type.String({
...TEXT,
description:
'实现说明,Markdown:位置表之外需要说明的事。修复时逐条回应退回意见:改在哪里,或以协议原文和代码位置说明为什么不改。协议要求自行求解核数而未执行的,说明数值留给正式实验核对,不声称已经验证。没有要补充的就写"无"',
}),
},
CLOSED,
);
const FOUND = Type.Boolean({
description:
"找到代码且实际行为符合该项要求写 true;找不到、找到但不一致或材料不足以核清,写 false(记为没落实)",
});
const WHERE = Type.String({
...TEXT,
description:
"代码位置和足以复核的依据,对应冻结要求。没落实时区分具体偏差与核不了:写明看过哪里、哪里不符或还缺什么,不把猜测写成事实",
});
export const VerificationSubmission = Type.Object(
{
clauses: Type.Array(
Type.Object(
{
clause: ClauseKey,
found: FOUND,
where: WHERE,
},
CLOSED,
),
{
description: "每条实现条款一项",
},
),
factors: Type.Array(
Type.Object(
{
factor: FactorKey,
found: FOUND,
where: WHERE,
},
CLOSED,
),
{
description: "每个因子一项",
},
),
safeguards: Type.Array(
Type.Object(
{
safeguard: SafeguardName,
found: FOUND,
where: WHERE,
},
CLOSED,
),
{
description: "每条保障一项;冻结协议没有保障就交空数组",
},
),
unassigned: Type.Array(
Type.Object(
{
location: Type.String({
...CodeLocation,
description:
"材料列出的改动块位置,逐字照抄位置本身,不带展示用的括号标签,不合并不同改动块",
}),
outOfScope: Type.Boolean({
description:
"超出冻结协议、改了最终指标的测量或校验、未按登记定义采集或用占位数掩盖缺测,写 true;落实冻结要求所需的辅助代码写 false",
}),
reason: Type.String({
...TEXT,
description:
"指出该块代码及对应的冻结要求,说明它为什么必要或哪里超出要求",
}),
},
CLOSED,
),
{
description:
"材料里列出的没有归属的改动,逐条判断是否越界;材料说没有就交空数组",
},
),
body: Type.String({
...TEXT,
description:
"说明,Markdown:逐项结果之外需要说明的事。材料里列出的同一行既属于因子又属于保障的地方,在这里各写一句提醒(只是提醒,不判越界)。" +
'没有要补充的就写"无"',
}),
},
{
...CLOSED,
description:
"只交逐项结果,通过与否由程序推出:任何一项没落实或判为越界,就退回实验员修复",
},
);
// ---------------------------------------------------------------------------
// 解释、消融、收尾(方法.md 4.3 ⑭⑯⑱)
// ---------------------------------------------------------------------------
const Explanation = Type.Object(
{
body: Type.String({
...TEXT,
description:
"Markdown:机制是什么、哪个因子在出力、什么情况下不成立、因子之间在效果上怎么互相依赖" +
'(例如"没有 p2,p3 就没用",当作预测交给消融检验)、结论覆盖哪些设定、没覆盖哪些。' +
"没有具体的机制判断就直说",
}),
cells: Type.Optional(
Type.Array(
Type.Object(
{
factor: FactorKey,
range: Range,
},
CLOSED,
),
{
description:
"每一格三个数:假如这条解释是对的,关掉这个因子后最终指标的范围。" +
"材料说要给每格的数时(开着消融),材料里列出的每一格都要有;" +
"没有具体的机制判断时,照实把范围写宽。材料说不给每格的数时(关掉了消融),不交这个字段",
},
),
),
},
CLOSED,
);
export const ExplanationSubmission = Type.Object(
{
explanations: Type.Array(Explanation, {
minItems: 1,
description:
"一般交一条就够,真有第二个机制判断时再交第二条——比如你的主判断若错了最可能是哪种错法,答得出来就写成第二条,写明哪一格能把两条分开;条数上限见材料",
}),
},
CLOSED,
);
export const AblationImplementationSubmission = Type.Object(
{
alsoOff: Type.Array(FactorKey, {
description:
"需要一起关的因子:只有关掉指定因子后代码真的跑不起来、必须连带关掉别的因子时才写。" +
"只关指定因子就交空数组",
}),
cannotRun: Type.Union([Type.Null(), Type.String(TEXT)], {
description:
"这一格跑不起来、也给不出一起关的方案时写原因,这一格不启动、不扣次数。跑得起来写 null",
}),
concerns: CONCERNS,
},
{
...CLOSED,
description:
"从完整代码出发,把指定的因子换回关掉时的做法,其余因子和全部保障照留",
},
);
export const AblationVerificationSubmission = Type.Object(
{
factors: Type.Array(
Type.Object(
{
factor: FactorKey,
off: Type.Boolean({
description:
"这份代码里这个因子是关着的写 true,照完整版本保留写 false",
}),
where: Type.String({
...TEXT,
description:
"依据:在哪几行看到的。不属于任何单个因子的总体核对说明(怎么核的、行号怎么对上的),写进被关掉因子那一项的依据里",
}),
},
CLOSED,
),
{
description: "冻结协议里每个因子一项",
},
),
},
{
...CLOSED,
description: "只查一件事:是不是只关了指定的因子",
},
);
export const WrapUpSubmission = Type.Object(
{
body: Type.String({
...TEXT,
description:
"收尾,Markdown:什么被支持了、什么被挑战了、下一步想往哪走:沿某个因子继续,或换一条更可能拿最高分的路线。" +
'推广结论时写清它覆盖哪些设定。某方面没有写"没有:为什么"。' +
"原样放进下一轮的提案材料,所有人都读得到",
}),
},
CLOSED,
);
/** 每种提交用哪个字段定义,键与 roster.ts 的 SUBMISSION_KINDS 相同。 */
export const SUBMISSION_SCHEMAS = {
entry: EntrySubmission,
discussion: DiscussionSubmission,
claim: ClaimSubmission,
proposal: ProposalSubmission,
firstPrediction: PredictionSubmission,
critique: CritiqueSubmission,
response: ResponseSubmission,
unification: UnificationSubmission,
secondPrediction: PredictionSubmission,
implementation: ImplementationSubmission,
repair: ImplementationSubmission,
verification: VerificationSubmission,
explanation: ExplanationSubmission,
ablationImplementation: AblationImplementationSubmission,
ablationVerification: AblationVerificationSubmission,
wrapUp: WrapUpSubmission,
} as const satisfies Record<SubmissionKind, TSchema>;
/** 某种提交的内容类型,例如 Submission<"proposal">。 */
export type Submission<K extends SubmissionKind> = Static<
(typeof SUBMISSION_SCHEMAS)[K]
>;