Files
InfoRSI/docs/结构.md
T
iomgaa 6cf9f0ced8 docs: 建立 docs/结构.md
从 inbox/结构.md 迁入模块、边界、可替换点、模块化程度与规模,删除 inbox/结构.md。
新增步骤清单与账本 12 种记录(用户 2026-10-01 认可),写明每种记录由谁写入、哪些禁止修改。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:41:06 -04:00

194 lines
12 KiB
Markdown
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.
# InfoRSI 代码结构
这份文件说明代码分成哪些模块、模块之间怎么依赖、流程按什么步骤推进、账本记哪些记录。研究方法本身以 `docs/方法.md` 为准;这里只说代码怎么组织来实现它。
来源:
- 用户 2026-10-01 认可"按负责一件事来切"、下面这套模块与可替换点,以及步骤清单和账本记录种类。
- 依据是两次开发调研。一次是模块切分,参考了 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(材料) | 给模型看什么、什么顺序、怎么写;回答研究者的查询 | ledger、rules |
| 组织 | submissions(收件) | 每种提交的字段;收下还是退回(先程序检查,再问 Jev) | jev |
| 外部 | ledger(账本) | 记录怎么存、怎么保证不可改、怎么从记录算出当前状态;谁在什么时候能看到哪些记录;花钱动作的登记 | 无 |
| 外部 | agents(角色会话) | Pi 的接口:开子会话、发工具和提示词、收提交、记用量 | 无 |
| 外部 | runner(运行器) | Docker 的用法:限时、限核、取消、记资源、取回产物 | 无 |
| 外部 | tasks(任务) | 一个任务怎么跑、怎么从原始产物独立算分(填圆的合法化也在这里) | 无 |
| 外部 | workspace(代码版本) | git 的用法:基线快照、实验员工作目录、冻结候选、代码差异 | 无 |
| 外部 | jev(Jev 判别) | 问法、阈值、校准 | 无 |
| 纯计算 | rules(研究规则) | 全部公式和常数;信念本、战绩、余额的计算 | 无 |
另有一个 **vocab**(共用词汇):只放各模块共用的类型和名单,不放函数,所有模块都可以引用,上限 400 行。
依赖只能往下,同一层之间不互相依赖:
```
入口 plugin
组织 flow export config
materials submissions
外部 ledger agents runner tasks workspace jev
纯计算 rules vocab(谁都能用)
```
### 几条边界
- **能不能看归 ledger,这次给不给看归 materials。** ledger 只交出"截至某一刻、这位角色能看到的快照"。materials 和查询工具都只拿得到快照,拿不到数据库。
- **收不收一份提交,只在 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. 收尾,更新信念本
```
---
## 四、账本记录
账本只存发生过的事。信念本、战绩、余额、"现在进行到哪一步"都不存,每次从记录现算。
| # | 记录 | 内容 | 谁写入 | 禁止修改 |
|---|---|---|---|---|
| 1 | 研究配置 | 冻结的配置、步骤清单、每个决策点用哪条规则、方法参数、提示词哈希 | flow(内容由 config 生成) | 是 |
| 2 | 阶段 | 每个阶段的开和关、什么时候公开 | flow | 否 |
| 3 | 提交 | 各种提交的每一版:入口、讨论、志愿、提案、两次预测、挑刺、回应、解释、收尾、实现、核验。阶段关闭后以最后一版为准,此后不能再追加新版 | flow(经 submissions 收下后) | 替换时追加新版;阶段关闭后由 ledger 拒绝再写 |
| 4 | 冻结协议 | 每个候选的冻结版本:因子清单、保障清单、实现条款、指标登记 | flow | 是 |
| 5 | 作者自评 | 封存时的三个数;解封时追加一条解封记录 | flow | 是 |
| 6 | 冻结代码 | 每份候选代码和消融格代码的版本号,以及因子位置表 | flow(版本号由 workspace 给出) | 是 |
| 7 | 运行 | 每次正式运行三条:登记要跑;确认启动(扣次);结果(分数、合法化、指标、答案路径、资源) | flow(结果由 runner 与 tasks 给出) | 是 |
| 8 | 决定 | 程序做的每个决定:选批、采纳、选格、押的因子、入口分配、判重合并。每条存规则名、规则版本、方法参数、种子、全部输入、输出 | flow(由 rules 计算) | 是 |
| 9 | 结算 | 每格的结局、撑几成、配合关系标签、每条解释的确认或否定、世界比特、惊讶 | flow(由 rules 计算) | 是 |
| 10 | Jev 判别 | 每次问了什么、原始概率、结论 | 提问方(submissions 或 flow) | 否 |
| 11 | 用量 | 模型调用和整个环境的用量,只记录 | agents、runner | 否 |
| 12 | 事件 | 违规记录、"按配置跳过"、"没测"及原因、停下的原因 | flow | 否 |
**为什么这几种禁止修改**
- CLAUDE.md 定的核心表是冻结协议、预测、证据、运行与扣次,分别对应第 4 种、第 3 种里的两次预测(阶段关闭后不再接受新版本)、第 7 和第 9 种、第 7 种。
- 另外四种也禁止修改:研究配置、作者自评、冻结代码、决定。它们被改了以后,事后没人察觉得到:配置要在开始时冻结,作者自评要封存,代码冻结后才能启动运行,决定一经写入、恢复时就按原决定走。
禁止修改的意思是:数据库里加约束,只能追加,不能改也不能删;要更正,就追加一条新记录。
---
## 五、可替换点
论文实验要换掉的部件,按种类用最轻的办法换:
| 种类 | 例子 | 换法 |
|---|---|---|
| 外部服务 | Pi 会话、Docker 运行器、任务、Jev | 接口。Jev 另配一个"永远判不同"的空实现,用来关掉判重 |
| 决策规则 | 选批:批内最好期望、均匀随机。选格:信息价值减成本、全测、随机同格数、都不测。以后可能加合并方式 | 纯函数,登记在 rules 里的"名字 → 函数"表里,配置写名字 |
| 方法参数 | N、M、批量、运行次数、因子数上限、u、衰减、开放预留 | 配置值,不写成常数,也不用环境变量。分两层:用户配置(配置对话会问);方法参数(做实验时才改,配置对话不问) |
| 步骤开关 | 消融、挑刺与回应、第零轮、第一次预测 | 配置值,由 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 | 1500–2500 |
| 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 | 200–400 |
---
## 八、各模块对外的函数与类型
写到哪个模块时,再把它对外的函数和类型补在这里。