Files
InfoRSI/docs/结构.md
T
iomgaa 64e4fd8aac docs: 整个系统不设默认值
用户 2026-10-02:"全部都不要默认值更好,甚至整个系统都不要默认值,因为默认值可能会掩盖错误";"在 CLAUDE.md'写代码的标准'里加一条可以,其他文档也要修改"。
CLAUDE.md 新增"不设默认值"一节,事实来源表与角色模型一句同步;方法.md 开头说明、6.11 末句、开发入口改为配置必须写明;结构.md config 一行和可替换点"方法参数"一行同步。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 07:39:18 -04:00

38 KiB
Raw Blame History

InfoRSI 代码结构

这份文件说明代码分成哪些模块、模块之间怎么依赖、流程按什么步骤推进、账本记哪些记录。研究方法本身以 docs/方法.md 为准;这里只说代码怎么组织来实现它。

来源:

  • 用户 2026-10-01 认可"按负责一件事来切"、下面这套模块与可替换点,以及步骤清单和账本记录种类。
  • 用户 2026-10-01 同意:提交的字段定义放 vocab,submissions 只判收不收;vocab 可放规范序列化和哈希两个函数,上限调到 600 行;执行后实际 1030 行,用户同意上限改为 1100 行。
  • 用户 2026-10-02 同意:"全部只追加":账本用一张只追加的表,全部记录都不能改。
  • 用户 2026-10-02 指出可见性、截止序号、通用唯一键都是过度设计:"每次给到派的材料有哪些"和"一开始灌进去还是让它去查"是分开的两件事;Jev 本质上是一种可以代替程序的判别器。据此:谁能看什么归 materials(按任务定材料),ledger 只存;只在方法要求"只能一次"处加唯一约束;Jev 不单独成记录。ledger 预期 500–900 行。
  • 依据是两次开发调研。一次是模块切分,参考了 Parnas 1972 与 1979、Ousterhout、Cockburn 和同类项目的源码;一次是同类研究的实验设计。

一、切分原则

不按研究步骤切,按"以后可能会改的设计决定"切,一个模块藏住一个决定。换掉某个决定时,只改这一个模块。

判断一刀切得对不对,问这 7 个问题:

  1. 这个模块藏住的是哪一个决定?说不出来,或者说出来的是"第几步",就是切错了。
  2. 换掉这个决定,要改几个模块?应该只改 1 个。
  3. 对外露出的函数和类型,是否比内部实现简单得多?
  4. 依赖有没有绕成圈?下层能不能离开上层单独测试?
  5. 纯计算的模块里,有没有读写文件、数据库、网络或模型?
  6. 每样外部的东西,是不是只有一个模块直接碰它?
  7. 同一份知识(阶段名单、谁能看见什么、某种提交有哪些字段)是不是只在一处定义?

二、模块

层 模块 藏住的决定 依赖
入口 plugin(插件入口) 用户在 Pi 里怎么操作:命令、状态显示、配置对话;文件锁;把各模块装配起来 装配时用到各模块
组织 flow(流程) 按步骤清单推进研究;每次启动走同一条恢复路径 ledger、rules、materials、submissions、agents、runner、tasks、workspace、config、jev
组织 export(导出) 导出给人看的格式 ledger、rules、materials
组织 config(配置) research.json 的字段和校验(每个字段必填,不补默认值);把配置翻译成步骤清单 无
组织 materials(材料) 每个任务能看到哪些材料(方法.md 第三部分);第一条消息放哪些、什么顺序、怎么写;在这些材料范围内回答模型的查询 ledger、rules
组织 submissions(收件) 一份提交收下还是退回:先程序检查,再问 Jev(字段定义在 vocab) jev
外部 ledger(账本) 记录怎么存、怎么保证不可改、怎么从记录算出当前状态;花钱动作的登记 无
外部 agents(角色会话) Pi 的接口:开子会话、发工具和提示词、收提交、记用量 无
外部 runner(运行器) Docker 的用法:限时、限核、取消、记资源、取回产物 无
外部 tasks(任务) 一个任务怎么跑、怎么从原始产物独立算分(填圆的合法化也在这里) 无
外部 workspace(代码版本) git 的用法:基线快照、实验员工作目录、冻结候选、代码差异 无
外部 jev(Jev 判别) 问法、阈值、校准 无
纯计算 rules(研究规则) 全部公式和常数;信念本、战绩、余额的计算 无

另有一个 vocab(共用词汇),所有模块都可以引用,上限 1100 行(多出来的主要是发给模型的字段说明)。一样东西要有两个互相不能引用的模块都用到,才放进 vocab;只有一个模块用的,放在那个模块里。它放:

  • 共用的类型和名单:角色、研究者阶段、提交种类、指标定义、运行定义与运行结果、模型用量;
  • 每种提交的字段定义(用 TypeBox 写,同一份定义既是类型,也用来检查收到的数据,也是发给模型的提交工具参数);
  • 只有两个函数:规范序列化和哈希,保证"内容相同就得到同一个指纹"只有一种算法。

依赖只能往下,图中同一行的模块之间不互相依赖。谁具体能依赖谁,以上面的模块表为准,npm run check 按那张表检查。

入口      plugin
组织      flow
          export
          materials  submissions  config
外部      ledger  agents  runner  tasks  workspace  jev
纯计算    rules                  vocab(谁都能用)

几条边界

  • 给模型看什么只归 materials。 每个任务开始时,materials 按方法.md 第三部分定下这个任务能看到的材料。哪些放进第一条消息、哪些等模型用查询工具去读,是同一份材料的两种给法,查询工具查不到这份材料以外的东西。ledger 不判断谁能看什么。
  • 收不收一份提交,只在 submissions 一处判。 跟状态有关的两条由 ledger 在写入时拒绝:这个阶段还开着吗;要替换的那一份是不是本人刚交的。
  • 信念本:数字由 rules 算,写成文字由 materials 负责,不单独存,每次现算。
  • runner 只管怎么跑,tasks 只管怎么判。 两者互不认识,由 flow 串起来:先向 tasks 要运行定义,交给 runner 去跑,再把产物交回 tasks 算分。
  • "下一步做什么"只写在 flow 的一个纯函数里。 每个步骤一个小文件。步骤文件里如果出现公式、字段名单或可见性判断,说明知识漏进来了,要挪回下层。

三、步骤清单

研究开始时,config 根据配置里的开关生成这份研究的步骤清单,连同每个决策点用哪条规则,一起冻结进账本。flow、导出、恢复都只读这份清单,不再读开关。被关掉的步骤不执行,账本里记一条"按配置跳过"。

整份研究

1. 冻结配置
2. 基线运行
3. 第零轮:写入口 → 讨论(1 或 2 次)→ 认领 → 分配          〔开关:第零轮〕
4. 一轮接一轮,直到余额用完
5. 导出

每一轮

 1. 开轮:放信念本,处理留池候选
 2. 提案
 3. 判重合并
 4. 第一次预测(看原始提案)                                〔开关:第一次预测〕
 5. 挑刺                                                    〔开关:挑刺与回应〕
 6. 回应与统一 → 协议冻结                                   〔开关:挑刺与回应〕
 7. 第二次预测(看冻结协议)
 8. 选批                                                    〔规则:选批规则〕
 9. 对每个入选候选:实现 → 核验 →(不通过:修复 → 再核验)→ 正式运行
10. 整批公开,记分数账和信息账
11. 采纳
12. 若采纳的改法有 2 个以上因子:                           〔开关:消融〕
      列格 → 解释 → 选格 → 对每个选中的格:消融实现 → 核验 → 运行 → 结算
                          〔规则:选格规则〕
13. 收尾,更新信念本

四、账本记录

账本只存发生过的事。信念本、战绩、余额、"现在进行到哪一步"都不存,每次从记录现算。

全部只追加。 所有记录放在同一张表里,每一行是:序号(自动递增,就是时间先后)、种类、内容(JSON)、内容的哈希。整张表禁止修改、禁止删除,由数据库触发器保证。要更正就追加一条新记录;"开"和"关"、"替换"也都写成新记录。

恢复时只核对、不重做写入(CLAUDE.md"中断后怎么恢复"),所以记录照写就是,不需要通用的去重。有自己编号、按定义只发生一次的记录,按编号加数据库唯一约束,重复了就报错:阶段开、阶段关、运行登记、确认启动、运行结果、模型调用开始、模型调用结束、消融结算(按轮次:每轮最多采纳一个改法,所以最多一次消融)。没有这种编号的记录(提交的各个版本、事件、用量)照写。前后顺序(比如启动前要先登记)由 flow 的同一个入口保证,ledger 不查。运行编号由 flow 起;确认没启动、退还名额后要重试,就起一个新编号。

# 记录 内容 谁写入
1 研究配置 冻结的配置、步骤清单、每个决策点用哪条规则、方法参数、提示词哈希 flow(内容由 config 生成)
2 阶段 每个阶段开一条、关一条;关闭即公开 flow
3 提交 各种提交的每一版:入口、讨论、志愿、提案、两次预测、挑刺、回应、解释、收尾、实现、核验:阶段关闭后以最后一版为准,此后由 ledger 拒绝再写。私人笔记的每一版:不属于任何阶段,以最后写入的一条为准。被退回的提交也记,附退回理由(含 Jev 的原始概率和结论) flow(经 submissions 判定后)
4 冻结协议 每个候选的冻结版本:因子清单、保障清单、实现条款、指标登记 flow
5 作者自评 封存时的三个数;解封时追加一条解封记录 flow
6 冻结代码 每份候选代码和消融格代码的版本号,以及因子位置表 flow(版本号由 workspace 给出)
7 运行 每次正式运行三条:登记要跑(占名额);确认启动(扣次);结果(分数、合法化、指标、答案路径、资源) flow(结果由 runner 与 tasks 给出)
8 决定 程序做的每个决定:选批、采纳、选格、押的因子、入口分配、判重合并。每条存规则名、规则版本、方法参数、种子、全部输入、输出;用到 Jev 的(判重合并),输入里带 Jev 的原始概率 flow(由 rules 计算)
9 结算 每格的结局、撑几成、配合关系标签、每条解释的确认或否定、世界比特、惊讶 flow(由 rules 计算)
10 用量 模型调用三条:开始(附这次任务的全部材料)、每次回复的用量、结束;整个环境的用量。只记录 agents、runner
11 事件 违规记录、"按配置跳过"、"没测"及原因、停下的原因 flow

Jev 是一个判别器,和程序检查地位相同,不单独成一种记录:它的原始概率和结论,记在它判断的那件事里(第 3 种的退回理由、第 8 种的判重合并)。

写入时由 ledger 拒绝的几种情况:阶段已关闭还要写提交;要替换的那一份不是本人在这个阶段刚交的;没有"确认启动"就写运行结果("确认没启动"这种结果除外);这次消融已经有格启动了才冻结解释。


五、可替换点

论文实验要换掉的部件,按种类用最轻的办法换:

种类 例子 换法
外部服务 Pi 会话、Docker 运行器、任务、Jev 接口。Jev 另配一个"永远判不同"的空实现,用来关掉判重
决策规则 选批:批内最好期望、均匀随机。选格:信息价值减成本、全测、随机同格数、都不测。以后可能加合并方式 纯函数,登记在 rules 里的"名字 → 函数"表里,配置写名字
方法参数 N、M、批量、运行次数、因子数上限、u、衰减、开放预留 配置值,不写成常数,也不用环境变量,每个都必须写在配置文件里。分两层:用户配置(配置对话逐项问);方法参数(做实验时才改,配置对话按方法.md 6.11 的现行值写进文件、给用户看过)
步骤开关 消融、挑刺与回应、第零轮、第一次预测 配置值,由 config 翻译进步骤清单
模型与提示词 每个角色、每位研究者用哪个模型;提示词目录 配置值;账本记下每份提示词文件的哈希

对照组"朴素循环"不另写流程,它就是一组开关的命名预设。

为了让换规则的实验做得对,结构上守住下面几条:

  1. 开关只在一处被读取:就是第三部分的步骤清单。互相冲突的组合在配置校验时报错,比如关掉了预测,选批却不是随机。
  2. 决策函数是纯函数,输入全部从账本读。 每条决策记录存下规则名、规则版本、方法参数、随机种子、全部输入和输出。种子由研究编号、轮次、版本算出,不用全局随机数。平票按固定规则处理。决策函数要导出,重放脚本和核对脚本直接调用它,不另抄一份公式。这样同一批记录换一条规则,就能离线重算。
  3. 衡量好坏的尺子只有一把。 比特、惊讶、覆盖率永远用同一个固定算法,从每个人的原始范围算出来,不跟着决策规则一起变。
  4. 一份研究的配置在开始时冻结进账本,恢复时以冻结的那份为准。
  5. 只提供跑通过的组合。 每个命名预设至少有一条小规模的跑通测试。

六、模块化程度与检查

  • 一个 npm 包,每个模块一个目录。别的模块只能通过目录下的 index.ts 引用它。
  • 用 dependency-cruiser 在 npm run check 里强制以下规则:
    • 不许循环引用;
    • 每个模块只能依赖第二部分表里列出的模块;
    • 不许绕过 index.ts 直接引用模块内部的文件;
    • rules 和 vocab 不许引用文件、数据库、网络、子进程和 Pi;
    • 只有 ledger 能用 SQLite,只有 agents 和 plugin 能用 Pi,只有 runner 和 workspace 能起子进程;
    • tasks 不许引用 runner。
  • 不拆成多个包,也不做 TypeScript 分项目编译。

七、规模

下表是粗估。动手写某个模块之前,由用户逐个确认;写的过程中超过预期一倍,就停下来检查。

模块 预期行数
plugin 600–1000
config 300–600
flow 2000–3500
ledger 500–900
rules 1500–2500
submissions 600–1000
jev 300–500
materials 1000–1800
agents 800–1300
workspace 300–600
runner 500–900
tasks 契约 150–300;填圆 600–1000,另有 Python 文件
export 500–900
vocab 上限 1100

八、各模块对外的函数与类型

写到哪个模块时,再把它对外的函数和类型补在这里。

vocab

下面是 src/vocab/index.ts 导出的全部内容。标"字段定义"的,是一份 TypeBox 定义,同名的 TypeScript 类型也一起导出:同一份东西既当类型用,也能在运行时检查数据(Value.Check),还能直接当发给模型的工具参数。

两个函数

  • canonical(value):规范序列化。对象的键排序后再拼成字符串,数字必须是有限数,只接受纯 JSON;内容相同就一定得到同一个字符串。
  • hash(value):对规范序列化的结果算 sha256,得到 64 位十六进制的指纹。

基础

  • Json(类型):任意纯 JSON 值。
  • Id(字段定义):编号的格式,只许字母、数字和 ._-,首字符是字母或数字,最长 128 个字符,因为编号会被拼进文件路径和命令行。
  • DIRECTIONS、Direction(字段定义):指标方向,只有越大越好(maximize)和越小越好(minimize)两种。
  • Measured(字段定义):测到 / 测不到。测到带值,测不到带原因,所有可能测不到的量都用它,不拿 0 或 null 冒充。

名单

  • ROLES、Role:三个角色,研究者、实验员、核验员。
  • STAGES、Stage:有人交提交的各个阶段,按流程先后排;程序自己做的步骤不在里面。
  • SUBMISSION_KINDS、SubmissionKind:每种提交属于哪个阶段、由哪个角色交,各模块都从这张表取。

指标

  • METRIC_LAYERS、MetricLayer:指标的三个层次,最终、常规辅助、假设专用。
  • MISSING_POLICIES、MissingPolicy:测不到时怎么办的两档,缺了只记缺失、缺了整次作废。
  • MISSING_POLICY_EFFECTS:两档各自后果的白话说明,字段说明和给模型的材料共用这一份。
  • HypothesisMetric(字段定义):研究者在提案里登记的一条假设专用指标。
  • MetricDefinition(字段定义):一条完整的指标定义,带编号和层次。

运行

  • RunDefinition(字段定义):tasks 交给 runner 的运行定义,写明命令、工作目录、按 sha256 钉死的镜像和运行时、时限、核数和环境变量;候选进程不继承宿主的环境变量。
  • ProcessResult(字段定义):runner 交出的进程层面结果,分没启动、正常退出、超时、被取消四种;启动过的带资源用量。
  • VERDICTS、Verdict:一次运行的判定,算数、不算数、判不了三选一。
  • ANSWER_PATH_KEY、SAFEGUARD_TRIGGERS_KEY:候选在指标文件里多写的两个保留键名,answer-path 和 safeguard-triggers。
  • AnswerPath(字段定义):读到的答案路径原样字符串,或读不到及原因;在不在位置表里由拿得到位置表的模块现算。
  • SafeguardTriggers(字段定义):读到的"保障短名 → 动手次数"原样内容,或读不到及原因。
  • JudgedResult(字段定义):tasks 从原始产物独立判分的结果,包括判定和理由、分数、各指标取值、答案路径、保障动手次数和任务自己的附加记录。
  • ModelUsage(字段定义):一次模型调用的用量,四种 token 数和价格,每一项都可能测不到。

提交

  • Range(字段定义):三个数 q1、q5、q9,即一个分数有一成、一半、九成可能低于多少。
  • SUBMISSION_SCHEMAS:每种提交的字段定义,键与 SUBMISSION_KINDS 相同;同一份定义是类型、收件格式检查和提交工具参数。
  • Submission<K>(类型):某种提交的内容类型,例如 Submission<"proposal">。

rules

下面是 src/rules/index.ts 导出的全部内容。rules 是纯计算:输入数字,输出数字,不碰文件、数据库和模型,也不用全局随机数。λ、ε、已知最好、每批上限、余额这些参数都由调用方传进来,rules 里只写死方法.md 6.11 标"固定"的几个常数。三个数一律用 vocab 的 Range。编号(候选、研究者、入口、因子、解释)一律按自然序比较:数字部分按数值大小比,所以 c2 排在 c10 前面。

几个反复出现的词:折线是把一个人的三个数画成"分数低于 x 的把握"的分段直线(6.2);合并分布是几条折线按权重加起来的结果(6.3);改进量是分数比基线好了多少,正数表示变好,越小越好的任务也换算成正数为好(6.1)。决定类函数的返回值里带着写决定记录要用的中间数,不只返回"选了谁"。

常数与基础

  • INTERACTION_CUT:配合关系标签里相对交互量的界限 0.2(6.6"配合关系标签")。
  • INDISPENSABLE_SUPPORT:判"缺一不可"的撑几成界限 0.9(6.6)。
  • NET_TOLERANCE:选格时比较净值的容差 1e-12,差距在它以内的组合当作一样好(6.7"选格")。
  • GRID_COMBINATION_CAP:只测一部分格时,网格组合总数的上限 4,000,000(6.7"信息价值")。
  • GRID_SUBDIVISIONS:网格里每段最多再等分 20 份(6.7"信息价值")。
  • epsilonFloor(ε):算 ε/100;采纳线下限、剩余空间下限、比特格宽三处共用这一个函数,保证末位一致(6.11"跟随 ε")。
  • improvement(from, to, direction):从 from 到 to 按指标方向好了多少,越大越好时是 to − from,越小越好时是 from − to(6.1)。
  • compareIds(a, b):按自然序比较两个编号,供所有排序和"编号最小"的判断使用;p01 和 p1 这类逐段相同的编号再按整串定先后,不会判成相等。
  • combinations(n, k):列出从 0..n−1 里选 k 个的全部组合,按字典序排,选批和选格列组合时用。

范围与合并分布(6.1–6.3)

  • Point、Knots(类型):折线上的一个点(横坐标,低于它的把握),以及按横坐标严格递增排列的一串点。
  • Component、Mixture(类型):合并分布的一个成分(一条折线加它的权重),以及成分的列表,权重加起来是 1。
  • personKnots(range):把一个人的三个数变成五个点的折线,两端各补一段与相邻半宽相同的尾巴(6.2)。
  • below(knots, x):读折线在 x 处"低于 x 的把握"。
  • knotsMean(knots):折线所代表的分数的平均值(期望)。
  • density(knots, x):折线在 x 处的斜率,也就是把握在这一点有多集中(密度);押的因子更新权重时用(6.8)。
  • mapKnots(knots, f, decreasing):把折线的横坐标换到另一把刻度上;换算是反向时点序倒过来、把握换成 1 减原把握。
  • reserveKnots(lines):开放预留,即给"大家都没想到的结果"留的那一份:取所有折线的最低点到最高点,左右各再放宽一半,在上面均匀铺开(6.3)。
  • authorShares(authors):每条解释的占比,研究者之间等权,同一人的几条平分(6.3"合并"、6.7"成分与权重")。
  • mixtureBelow(m, x):合并分布在 x 处"低于 x 的把握"。
  • breakpoints(m):合并分布全部折点的横坐标,去重、升序。
  • mapMixture(m, f, decreasing):把合并分布的每个成分都换到另一把刻度上,权重不变。
  • forwardMixtures(candidates, λ):正向每个候选的合并分布,(1 − λ) × 各人折线平均 + λ × 开放预留,开放预留由这次选批的全部候选、全部人共用(6.3"正向")。
  • ablationCellMixture(entries, λ):消融一格的合并分布,各条解释按 authorShares 分 1 − λ,这一格自己的开放预留占 λ(6.3"消融")。
  • toImprovement(m, b, direction):把合并分布从分数原值换算成相对基线 b 的改进量;越小越好时刻度反向,一成和九成随之对调(6.1)。
  • mixtureQuantile(m, p):合并分布的分位点,即"有 p 的把握低于多少";只给人看,不参与决定。
  • mixtureRange(m):合并分布的一成、一半、九成,按分数原值,消融格显示"大家合起来的范围"用。
  • forwardMergedRange(imp, b, direction):正向候选"大家合起来的范围",在改进量上读三个数再换回分数原值。
  • integrateUpperTail(ds, from):按段精确算 ∫[1 − ∏F] dt,正向的批内最好期望和消融全测时的信息价值共用这一个积分。

正向:入池、选批、采纳(6.4、6.5,4.3 ⑫)

  • lossProbability(imp, ε):候选"比基线低 ε 以上"的把握;严格小于一半才入池(6.4"入池")。
  • batchValue(imps):一组候选的批内最好期望 V(S),即这批里最好的那个预计比基线好多少,低于基线按 0 算(6.4)。
  • BatchInput(类型):两条选批规则共用的输入:候选及各人的三个数、基线、方向、ε、λ、每批上限、余额、种子。
  • BatchDecision(类型):选批结果,含 6.4"选批记录写入"的全部内容:参数、批量 k、每个候选的合并范围和是否入池、各组合的 V、选中的组合及其 V、候选池的 V、分数尺读数。
  • selectBestExpected(input):选批规则"批内最好期望",在候选池里列出全部组合取 V 最大的,V 完全相等时取编号小的组合(6.4)。
  • selectUniformRandom(input):选批规则"均匀随机",用种子从全部候选里抽,不看入池门槛;入池、合并范围、分数尺读数照样算出来记下(第十一部分)。
  • AdoptionLineInput(类型):算采纳线要的基线 B、已知最好 K*(没有时为 null)、ε 和方向。
  • adoptionLine(input):采纳线(6.5),返回线的值和 (K* − B) ÷ 4 的原值;没有已知最好时是 ε,结果不低于 ε/100。
  • adopt(input):采纳判断,只看有有效分数的候选,最好的那个比基线高出采纳线以上才采纳;返回采纳线、最好的候选及其改进量和采纳结果,够写采纳记录。

消融结算(6.6,4.3 ⑬ ⑰)

  • CellOutcome(类型):一格的五种结局:有效分数、证据不足、没测、跑不起来、一起关(带实际关掉的因子和可能的分数)。
  • AblationCell(类型):一格,即一个因子和它的结局。
  • AblationExplanation(类型):一条解释,带作者和它对每一格给的三个数。
  • listCells(factors):列格,被采纳改法的每个因子各一格,按因子编号排(4.3 ⑬)。
  • totalGain(baseline, full, direction):总提升 G = f(完整) − f(基线);G ≤ 0 说明上游选错了改法,直接报错(6.6"撑几成")。
  • supportOf(baseline, full, off, direction):某个因子撑几成,即关掉它后损失的分数占总提升的比例,不截断(6.6)。
  • SynergyLabel(类型):配合关系标签的代号,六种:关掉反而更好、不全、缺一不可、互补、替代、各管各;中文说明以后由 materials 写。
  • synergyLabel(supports):按 6.6 那张表从上往下判出标签,同时返回各格撑几成之和与相对交互量(有格没测出有效分数时这两个数为 null)。
  • sharedSettings(cells):判断哪些"一起关"的格其实是同一种设置(关的因子集合相同),给出该沿用哪一格的结果(6.6)。
  • ExplanationVerdict(类型):一条解释在一格上的判定,确认或否定。
  • judgeExplanations(explanations, cells):每条解释在每个有效格上,实测落在它的一成到九成之间(含两端)记确认,否则记否定(6.6)。
  • inseparableGroups(judgments):找出在所有有效格上确认与否定完全相同的解释,归成"现有测法分不开"的组(6.6)。
  • ablationFinding(input):消融中发现,找出比当前基线高出采纳线以上的格,几格都过线时取分数最好的、同分取因子编号小的;返回采纳线、(K* − B) ÷ 4、全部过线的格和选中的格(4.3 ⑰)。
  • SettleInput(类型):一次消融结算的输入:基线、完整分数、方向、ε、已知最好、λ、被采纳改法的因子清单、各格结局、各条解释。清单里每个因子都要有一格,缺了直接报错。
  • settleAblation(input):一次消融的完整结算,给出 G、每格的撑几成和合并范围、有效格的世界比特、惊讶和分歧量、配合关系标签、确认与否定、分不开的组、消融中发现(4.3 ⑰、6.6、6.9);押的因子另由 chooseBet 算。

信息价值与选格(6.7、6.8)

  • OPEN_RESERVE:成分名单里开放预留的名字,排在最后。
  • AblationModelInput(类型):建成分模型要的输入:基线、完整分数、方向、已知最好、ε、λ、因子编号、各条解释。
  • AblationModel(类型):成分模型:因子、成分(各条解释加开放预留)、起始权重、G、剩余空间 h、每格每成分的折线(分数原值和撑几成两种刻度)及期望、每格的事前期望撑几成。
  • remainingSpace(full, knownBest, ε, direction):剩余空间 h,有已知最好时是 max(K* − f(完整), ε/100),没有时是 1(6.7"方向收益")。
  • ablationModel(input):建成分模型,每条解释一个成分、开放预留一个成分,解释合起来占 1 − λ(研究者等权、同一人平分),开放预留占 λ(6.7"成分与权重")。
  • gridSize(k):测 k 格时每格的小段数 m,即满足 m^k ≤ 4,000,000 的最大整数(6.7"信息价值")。
  • cellGrid(knots, k):把一格按折点切成小段,返回各小段中点和每个成分在各小段上的把握;小段数的规则按 6.7 原文。
  • measuredGrid(model, c, k):模型里第 c 格的网格,在分数原值上切段,再把中点换算成撑几成。
  • informationValueExact(model):全部格都测时的信息价值,逐段精确积分。
  • informationValueGrid(model, S):用网格算的信息价值,不论测几格都走网格,主要用来和精确算法互相对照。
  • informationValue(model, S):信息价值 VOI(S),即测了 S 这几格之后,挑出最该押的因子能比现在多挣多少(6.7);全测走精确积分,只测一部分走网格,空集是 0。
  • ComboParams(类型):算价值与成本要的余额、每批上限、知识被用上的概率 u、衰减 r。
  • ComboValue(类型):一个测格组合的 VOI、R̂、m′、价值、成本、净值(6.7"价值减成本")。
  • evaluateCombos(model, params):列出格数不超过"余额 − 1"的全部非空组合并算价值、成本、净值,另给 ĝ₀ 的原值。
  • CellRuleInput(类型):四条选格规则共用的输入,即成分模型的输入加 ComboParams 和种子。
  • UntestedReason(类型):一格没测的原因:剩余次数不够、信息价值不足以抵过成本、随机没抽中、规则是"都不测"。
  • CellDecision(类型):选格结果,含 6.7 要求附上的全部数字:参数、种子、G、h、成分与起始权重、各因子的事前期望收益、ĝ₀、全部组合、选中的格及其净值,以及每一格测不测、没测的原因和含它的净值最大组合。
  • topCombo(combos):在给定的组合里挑净值最大的,差在 1e-12 以内算一样好,再先取格数少的、后按因子编号比(6.7"选格"第 2 条)。它不管净值是否大于 1e-12,所以也用来挑"含这一格的最好组合"。
  • pickByNet(combos):按净值挑要测的组合。先只留净值大于 1e-12 的组合,一个都没有就一格都不测;再在留下的组合里用 topCombo 挑。
  • selectByValue(input):选格规则"信息价值减成本"(6.7"选格")。
  • selectAll(input):选格规则"全测",按因子编号测前"余额 − 1"格(第十一部分)。
  • selectRandomSameCount(input):选格规则"随机抽同样多的格",格数和"信息价值减成本"会选的一样,用种子抽(第十一部分)。
  • selectNone(input):选格规则"都不测"(第十一部分)。
  • BetResult(类型):押的因子的结果:更新后的成分权重、权重是否无从更新、各因子的方向收益(实测换算的或估计的)、挑出的因子及其预测收益、押或不押的两种情况、关掉反而更好的格。
  • chooseBet(input):押的因子,用有效格的实测更新各成分的权重,再挑方向收益最大的因子;有格关掉反而更好或估计收益不为正时不押,实测落在所有成分之外时保持起始权重(6.8)。

信息账(6.9)

这一组是方法里"唯一的尺子",不随选批、选格规则变,一律在分数原值上从本批基线 b 起按格宽切小格。

  • bitWidth(ε):格宽 Δ = ε/100。
  • entropyBits(ps):一组把握的拿不准程度 −Σ p log₂ p,单位是比特。
  • worldBits(m, origin, width):世界比特,即合并分布在宽 Δ 的小格上有多拿不准。
  • cellIndex(origin, width, v):实测 v 落在第几个小格,和切格用同一套边界。
  • surprise(m, origin, width, value):惊讶 = −log₂(事前给实测所在小格的把握),返回格号、事前把握和惊讶值;事前把握为 0 时惊讶记"测不到"并写明原因。
  • disagreement(entries, origin, width):分歧量,大家平均后的世界比特减去各人世界比特的平均,量的是人与人之间的差别,不掺开放预留,只记录。
  • Placement(类型):落点四段:低于一成、一成到一半、一半到九成、高于九成。
  • placement(range, v):实测落在一个人范围的哪一段。
  • covers(range, v):实测是否落在一成到九成之间(含两端),也是 6.6 里"确认"的判据。
  • coverage(samples):落点四段的计数和覆盖率;一条样本都没有时覆盖率记"测不到",不记 0。

余额与战绩(第七部分、6.10 第 4 条)

  • RunState(类型):一次正式运行在账上的三种状态:已预留还没核对出结果、确认启动、确认没启动。
  • runBalance(total, runs):从运行记录数出已扣次数、占用名额和余额;确认启动的扣一次,已预留的先占着名额,确认没启动的退还。只数运行次数,不算计算时间。
  • forwardBatchSize(batchCap, balance):本轮正向批量 k = min(每批上限, 余额)。
  • ablationCellLimit(balance):消融最多测"余额 − 1"格,余额为 0 或 1 时是 0。
  • Phase(类型):正向或消融。
  • Tally(类型):一个人的战绩:总数、中位数偏高几次、偏低几次、被穿过上沿几次、下沿几次、包住几次、落点四段计数。
  • trackRecord(samples):按研究者、按正向与消融分开数战绩,只用来显示,不改任何权重(6.9、6.10)。
  • authorVsMerged(items, direction):"作者自评 vs 大家合并",给出作者比大家乐观多少,以及实测落在两个范围的哪一段;只收已经跑过、有有效成绩的候选(6.10 第 4 条)。

入口分配、种子与规则登记表(4.2、第九部分、第十一部分)

  • EntryAssignment(类型):一个研究者分到的入口,以及它是第几志愿(按剩余入口分到的为 null)。
  • assignEntries(input):第零轮的入口分配,按志愿一轮一轮分,争同一个入口时先给作者、再给编号最小的人,志愿用完的人按编号拿剩下的入口;结果里带志愿序号,够写分配记录(4.2"分配规则")。
  • decisionSeed(input):决定的种子,由研究编号、轮次、版本经 vocab 的 hash 算出(第九部分)。
  • seededRandom(seed):从种子确定地展开一串随机整数,同一个种子永远得到同一串数,不用 Math.random。
  • seededSample(items, size, seed):用种子从一组编号里不放回地均匀抽 size 个,结果只取决于种子和编号集合,与传入次序无关;候选和因子都这样抽。
  • BATCH_RULES、BatchRuleName:选批规则登记表,名字 → 函数和版本号,两条:批内最好期望、均匀随机。
  • CELL_RULES、CellRuleName:选格规则登记表,四条:信息价值减成本、全测、随机抽同样多的格、都不测。
  • runBatchRule(name, input):按名字跑一条选批规则,结果带上规则名和版本,flow 写决定记录时从这里取。
  • runCellRule(name, input):按名字跑一条选格规则,结果带上规则名和版本。

ledger

下面是 src/ledger/index.ts 导出的全部内容。账本是一个 SQLite 文件里的一张只追加的表;外面拿不到数据库连接,只能经下面这些函数读写。ledger 不判断谁能看到哪些记录,那归 materials。

打开与写入

  • Ledger.open(path):打开一份研究的账本,新文件就建表,旧文件核对表结构版本号,对不上就报错。
  • ledger.close():关闭数据库。
  • ledger.append(kind, content):追加一条记录并返回它。先按种类检查内容,再做第四部分说的四种拒收检查(第 1 条是只收开着的阶段的提交,即有开阶段记录、没有关阶段记录),都在同一个事务里。有自己编号、按定义只发生一次的记录按编号加了唯一约束,重复写就报错:阶段开、阶段关、运行登记、确认启动("结果是没启动"也算在这一条里,一次运行两样只能有一样)、运行结果、模型调用开始、模型调用结束、消融结算;其余记录照写。记录之间的先后(例如结果前要先有登记)不查。写提案时,作者自评从提案里拆出来,在同一个事务里另存成一条封存。
  • ledger.transaction(fn):里面的几次追加要么全部写进去,要么一条都不写;fn 必须是同步函数,返回值类型排除了 Promise,异步函数在类型检查时就被挡住;不能嵌套。

读取

  • ledger.records():全部记录,按序号排,读出时按种类重新检查内容。
  • ledger.stageClosed(stageId):这个阶段关了没有。
  • ledger.latestSubmission(slot):某份提交(阶段、作者、对象、编号)的最新一版。
  • ledger.pending():未完成的动作,即登记了还没有结果的运行(带着有没有确认启动)和开始了还没有结束的模型调用,恢复时由 flow 逐个核对。
  • Pending(类型):pending() 的返回值。

类型

  • Kind:记录的种类,共 19 种:config(研究配置)、stageOpen / stageClose(阶段开、关)、submission(提交的每一版)、rejection(退回,带原样内容和理由,理由来自 Jev 的带原始概率和结论)、note(私人笔记的每一版)、protocol(冻结协议)、seal / unseal(作者自评的封存、解封,都用记录序号指明是哪一条)、code(冻结代码)、runPlanned / runStarted / runResult(运行的登记要跑、确认启动、结果)、decision(决定)、settlement(一次消融的结算)、callStarted / callUsage / callEnded(模型调用的开始,附这次任务的全部材料;每次回复的用量;结束)、event(事件)。
  • AppendKind:调用方能追加的种类,除了 seal(封存只由 ledger 在写提案时一并写)。
  • Content<K>:某种记录存下的内容。
  • Input<K>:追加时交的内容;只有提交和存下的不同:提案带着作者自评。
  • LedgerRecord<K>:一条记录:序号、种类、内容的哈希、内容。
  • SubmissionSlot(类型):一份提交不论第几版:阶段、作者、提交种类(版本号只在同一种提交里往下接)、针对谁(候选或格,没有就空)、这一份的编号(只有提案、挑刺这两种一人能交好几份的才有,其余必须为空)。
  • StoredSubmission、SubmissionInput(类型):存下的提交和追加时交的提交,按提交种类区分内容类型;版本号从 1 起,替换时加 1,被替换的就是上一版。