From 3861fd873fa0572f3c64f4fb40cd2dbbc7e4c4ab Mon Sep 17 00:00:00 2001 From: iomgaa Date: Thu, 1 Oct 2026 11:21:27 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=BB=BA=E7=AB=8B=20InfoRSI=20?= =?UTF-8?q?=E7=9A=84=20CLAUDE.md=20=E4=B8=8E=20inbox?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLAUDE.md 按官方写法:两层标准、遇到问题先找原因、错误两类、单一恢复路径、怎么改一份文件、方法约束、Pi 与角色、工作方式。 来源:用户 2026-10-01 逐条认可(改名 InfoRSI、文件锁、删重开与替换、违规处理表、修根因原则、来源写进提交说明)。 inbox:方法(违规处理表)、结构(13 个模块与可替换点)、调研(待迁清单)。 Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++ inbox/方法.md | 13 ++++ inbox/结构.md | 108 +++++++++++++++++++++++++++++++++ inbox/调研.md | 8 +++ 4 files changed, 290 insertions(+) create mode 100644 CLAUDE.md create mode 100644 inbox/方法.md create mode 100644 inbox/结构.md create mode 100644 inbox/调研.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..9ce108c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,161 @@ +# InfoRSI + +一个 Pi 插件,让多个模型角色在有限的实验预算内自动做研究。研究者提出改法并预测效果,实验员写代码,程序启动正式运行、独立核对成绩、记账,再把结果写进下一轮的信念本。第一个任务是填圆。nanochat 和 AI4AI 只预留接口。 + +用户是唯一的使用者,也是研究方法的设计者。讲解和文档用简体中文,写给普通研究生看得懂的程度:不用行业黑话,不自造词,专有名词第一次出现时解释它。代码标识符用英文;提交说明用英文类型前缀加中文正文。 + +## 事实来源 + +| 问题 | 看哪里 | +|---|---| +| 研究方法:流程、公式、信念本、记账含义 | `docs/方法.md` | +| 代码结构与各模块的预期规模 | `docs/结构.md` | +| 配置字段与默认值 | 配置的类型定义和校验代码;文档只解释含义,不抄数值 | +| 当前进度、下一步、运行中的任务 | `WORK_LOG.md` | +| 同类系统和书籍的调研 | `docs/调研/` | +| Pi 的接口与行为 | 本机安装的包、它的文档和实测 | + +已经定下、但对应的正式文件还没建好的内容,先放进 `inbox/`。每个文件对应一个目标位置,文件名就用目标文件名,每条都注明来源。迁到正式位置后,就把 inbox 里对应的那条删掉,同一件事不在两处各留一份。 + +文档之间有冲突时,以上表为准,找用户确认,不要自行调和。`docs/` 和 `inbox/` 里新写的要求,要注明是用户哪句话、方法文档哪一节,还是开发中自己提的;本文件的改动,来源写在提交说明里。自己提的规则失去来源标签后,读起来和用户的要求一样硬,会越长越大。 + +## 参考项目 + +`/home/iomgaa/Projects/RivalBits` 实现过同一套研究方法,真实跑通过填圆研究。它在这里只是参考,不在里面改任何东西。 + +- 它的代码为罕见情况建了很多专门路径,长到人读不懂。所以只借鉴它的行为和教训,按本文件的标准重写,不整段搬代码。 +- 它在 `tests/fixtures/` 里有独立算出的预期值,数学规则和记账可以直接拿来对照。 +- 它的真实运行记录在 `/home/iomgaa/rb-acceptance/`,只能复制出来读。 +- 同类开源项目的本地克隆在 `/home/iomgaa/Projects/RivalBits/reference/`。 + +## 写代码的标准 + +本项目的代码分两类,用不同的标准: + +- **账本**:冻结的协议与预测、运行是否启动和结果、预算扣减、证据、信念本更新。这部分出错会悄悄污染研究结论,所以按会计系统的标准写: + - 更正就追加一条新记录,不改旧记录; + - 当前状态都能从记录重新算出来; + - 每条规则都有独立预期值的测试。 +- **其余部分**:阶段推进、提示词、给模型的材料、任务适配、导出、配置对话。这部分写错了马上就能看出来,按研究原型的标准写:用最直接的写法;遇到罕见情况就停下来,把原因写清楚交给用户。 + +只有冻结协议、预测、证据、运行与扣次这几张核心表,在数据库里加禁止修改和删除的约束。其余表照常读写。 + +### 遇到问题先找原因 + +出了问题,先查清它为什么发生,再到原因所在的地方去改。重试、跳过、换默认值、解析时放宽格式、捕获异常后接着跑,这些做法只是让症状不再出现,原因还留在那里。在本项目里这样做代价很大:兜住的结果会以"看起来正常"的样子进入账本,之后没人分得清哪些是猜出来的;分支越积越多,代码最后谁也读不懂。 + +模型有一个已被记录的倾向:几次正面尝试失败后,会转去特判、改测试,或做一个"看起来能用"的方案(Anthropic,Claude 3.7 Sonnet 系统卡第 6 节)。所以当你在第二、三次失败后开始想"先加个分支让它过去",就该停下来重新找原因了。 + +例子:模型的提交时好时坏,有时交错内容。原因多半在提示词或任务拆法上:要求没写清、一个任务塞了几件事,或者给的材料不够。应该改提示词和拆法,再用真实运行确认改好了,而不是加重试、宽松解析或补默认值。按"一个 Agent、一个对象、一件事"来拆任务,缺交在结构上就不容易出现。 + +动手前问自己:这个改动是让问题不再发生,还是让问题发生了也没人看见? + +只有原因在我们控制之外时(网络断开、服务超时、进程被系统杀掉),才按"错误怎么处理"做有限次重试,并把失败记下来。原因查不清,或者要修就得动这次范围之外的东西时,停下来把查到的情况告诉用户,不要先上一个临时的绕法。 + +### 什么情况值得为它写代码 + +罕见情况的处理代码平时很少被执行到,坏了也没人知道。分布式系统的故障研究发现,92% 的严重故障出在这类代码里(Yuan 等,OSDI 2014)。所以每加一条专门路径之前,先问自己: + +1. 能不能改一下数据的表示,让这种情况变成正常情况的一种? +2. 它防的情况在参考项目的真实运行里出现过吗?没出现过的先不写。 +3. 调用方拿到这个错误能做什么?说不出来,就直接停下,不定义这个错误。 +4. 有没有测试或真实运行会执行到它?执行不到就不写。 +5. 它会不会散到好几个文件里?会的话,先想办法把它收在一处。 + +值得防的只有三类:没人操作也会发生的、模型正常工作时就会做的、出错后无法察觉的(比如测不到的数值被记成 0)。 + +以下这些不防: +- 用户本人中途改配置、改提示词、同时开两个进程、随时终止; +- 有人在同一个账号下故意用 shell 绕开规则; +- 出错后马上就能看出来的问题。 + +同一个研究目录同时只允许一个控制进程,用一把文件锁实现。 + +### 错误怎么处理 + +- **外部错误**,比如模型接口超时、网络断开、子进程被系统杀掉:只在调用外部服务的那一处做有限次重试,仍然失败就把这一步记为失败,写明状态。 +- **内部错误**,比如数据不满足规则、出现不该有的状态组合、方法文档没说该怎么办的情况:立刻停下,报错写清是哪条规则、哪条记录出了问题。不要写"猜一个合理值继续跑"的代码。 + +中间层不写 try/catch,除非它能把错误完整处理掉,而且上层不需要知道。不写空 catch,不吞异常,不悄悄改用默认值。 + +模型交不出合格提交,按内部错误处理。 + +### 中断后怎么恢复 + +- 每次启动都走同一条恢复路径:从账本读出当前状态,找出已登记要做、但还没有结果的动作,逐个核对。不为不同的中断原因分别写分支。 +- 启动正式运行、调用模型这类花钱的动作,都按"先登记,再执行,再登记结果"的顺序走同一个入口。恢复时只核对,不重做。 +- 核对不出结果,就记为"状态未知"并停下,由用户决定。 +- 研究不支持中途作废重做某一轮。想重来,就开一份新研究。 + +### 测试 + +- 账本和数学规则的预期值,来自手算、独立脚本或参考项目的固定数据,不从实现里复制公式。 +- 每个阶段结束时核对几条总账,不符就停。比如:扣掉的运行次数等于已确认启动的正式运行数;每条证据只记一次账。 +- 其余部分测到能跑通就够。真实模型链路要用真实运行来证明,假模型测试证明不了。 +- 修缺陷时,附一条能复现原问题的测试。 +- 不靠删断言、改预期值,或只保留成功的运行来让测试通过。 + +### 规模与注释 + +- 每个模块在 `docs/结构.md` 里写明预期规模,动手写之前由用户确认。超过预期一倍时停下来检查:是需求本身复杂,还是专门路径在变多。 +- 一个概念如果在三个以上模块里反复出现,先调整结构,把它收拢到一处。 +- 注释只写这段代码现在做什么、为什么这样做。修改过程写进提交说明和 `WORK_LOG.md`。引用方法依据时,写 `docs/方法.md` 里的小节名。 + +## 怎么改一份文件 + +改代码和改文档都按同一个标准:**范围要窄,改得要深**。 + +- **范围由这次要改的事决定。** 先找出受这次改动影响的那一块:一个函数、一个模块、文档里的一节。范围之外的东西,就算看着不顺眼也不碰。顺手的重构会让改动变大、出错的地方变多,也让用户没法一眼看清这次改了什么。看到范围外有值得改的地方,记下来告诉用户。 +- **范围之内要改彻底。** 把新的要求融进这一块,改完以后读起来要像一开始就是这么设计的,看不出补丁的痕迹。要做到这一点,往往得重写这一块,而不是在旧写法旁边加一个分支、一个参数、一段"另外,如果……"。被新要求取代的旧内容,在同一次改动里删掉。只往里加、不往外删,文件会越来越长,最后谁也读不懂。 +- **检验办法**:如果一开始就知道这个新要求,从头写这一块会写成什么样?改完的结果应该接近那个样子。 +- 要改得干净,必须动到范围外的东西时(比如一个接口不改,这一块就写不干净),先停下来,向用户说明要动什么、为什么要动,再继续。 + +## 研究方法上不能变的约束 + +公式和细节以 `docs/方法.md` 和生效的配置为准。下面几条是方法的根基,改代码时不能动: + +- 程序负责推进阶段、执行数学规则、保存不可改的记录;模型负责提案、实现和解释。模型在对话里说"已经提交"不算数,以正式记录为准。 +- 研究者人数 N、每人提案上限 M 都可配置。每轮的名单和上限冻结;收齐 N 个人的范围后,程序按等权合并。 +- 当前轮还没公开的提案和范围,不能通过上下文或查询接口提前泄露给别人。 +- 正向候选都从同一批基线出发,彼此独立。"改法有效"和"机制解释正确"分开记录。 +- 最终指标由任务确定,宿主独立校验实际产物,不采信候选自报的成绩。研究者可以自己定义假设专用指标,在执行前登记,不限定只能从内置清单里选。 +- 冻结的协议、预测和原始证据都保留版本,更正只追加。同一份证据不重复记账;事后的解释不能冒充事前的预测。 +- 正式运行确认启动后才扣次数: + - 启动后失败或取消,照样计数; + - 确认没有启动的取消,退还次数; + - 不确定有没有启动,先核对再决定。 + + 轻量检查不能变成免费试跑。 +- 变化很小,在 ±ε 以内,也是正常结果,照样记账。某一格没测出有效分数,记"证据不足"。两条解释在一次消融里总是一起被确认或一起被否定,记"现有测法分不开"。 +- 配置缺失、测量失败、价格未知,都要留下明确的状态,不用 0 或默认值顶替。 +- 防作弊靠结构:宿主重新算分、硬规则校验、评测文件由宿主保管。不用模型去审查实验员的命令或运行结果。同类系统里真实发生的作弊,大多是钻了评分程序的漏洞,所以发现漏洞就修评分程序。发现违规后怎么处理,见 `docs/方法.md`。 +- 方法的三层都要保住:正向找改法加反向查原因的闭环;信息度量(范围、信念本、比特、信息价值);以"点"为单位做消融、记录知识。 + +## Pi 与三个角色 + +- 研究者、实验员、核验员都用 Pi 子会话、Pi 工具和 Pi 的模型服务配置,不自己写直接调用模型接口的 Agent。 +- Pi 固定用本机的 `@earendil-works/pi-coding-agent` 0.85.1,Node ≥ 22.19。只照这个版本的包和文档写代码,不升级全局 Pi。 +- 角色模型默认 glm-5.3。 +- 每个角色有一份自己的角色提示词,同一角色的所有任务共用;每类任务另有一份任务消息,写目标、要求和要交什么。 +- 实验员有完整的文件和 shell 权限,因为它的工作就是写代码、跑代码。研究者和核验员只有只读工具和提交工具。这是分工,不是安全边界。 +- 提示词只引导模型怎么想,不设必填栏目。逼模型填栏目,它会为了填而编。只有程序确实要读取的值,才做成提交字段。 +- 判断"两段话是不是同一件事""这处改动有没有某个性质"这类封闭问题,直接用 Jev 判别,并据此合并、拒收或退回;配好校准,保存原始概率。不要用 Jev 预测代码运行结果,也不要让它做算术。 +- 研究子会话不加载搜索插件,也不自己去网上搜研究方案;按配置下载依赖可以。子会话不继承本文件,也不递归加载研究插件。设置好工作目录后,核对它实际加载了哪些资源。 + +## 工作方式 + +- 在用户授权的范围内,自行完成调查、安装依赖、编码、测试、修复、更新文档和本地 Git 提交。不推送到远程,不发布包,不购买服务,不删除不能重建的研究证据。 +- 新的设计方案先在终端讲给用户,顺序是背景、问题、方案;用户认可后再写成文件。需要用户做决定时,一次列出全部问题和选项,并给出你的倾向;选项里用到的词,先在正文里解释清楚。 +- 设计让人觉得像在打补丁时,先派子 Agent 去查同类系统和书里的成熟做法,必要时推翻现有结构。调研时先把参考代码克隆到本地读源码;几个子 Agent 都结束后,再给用户一份完整报告。 +- 你主要当项目经理:拆分任务、整合结果、核对交付。调查、编码、测试、独立审阅交给子 Agent,委托时写清范围、相关文件、验收标准和要交的证据。涉及账本、数学规则、预算或恢复的改动,另派一个全新上下文的子 Agent 审阅。子 Agent 说"通过"不算数,要自己核对实际改动和测试产物。 +- `WORK_LOG.md` 只记当前状态:目标、已完成的事和证据、未完成的事、关键决定(附用户原话)、运行中的任务编号、下一步。一个阶段完成后,压缩成几行。会话恢复后,先读它,再核对文件、Git 状态和正在运行的进程。 +- 汇报时分开写已验证的事实、推断和未验证的部分。每个实验数字都要能追溯到实际运行。 +- 完成以真实产物为准: + - 可安装的插件; + - 填圆真实研究的正反向闭环,以及下一轮的证据; + - 预算与恢复的验证; + - 任务接口; + - 缺少 research.json 时,与用户对话生成配置; + - 研究导出; + - 使用说明; + - 验收报告。 diff --git a/inbox/方法.md b/inbox/方法.md new file mode 100644 index 0000000..dc9363d --- /dev/null +++ b/inbox/方法.md @@ -0,0 +1,13 @@ +# 待迁入 docs/方法.md + +## 违规处理 + +来源:用户 2026-10-01 认可。依据 RivalBits `research-wiki/reference/调研_违规与防作弊_同类系统做法.md`。 + +| 情况 | 处理 | +|---|---| +| 能确认的违规(硬规则或宿主重算判出来的) | 这次运行不进记账,次数照扣,写一条记录,研究继续 | +| 只是模型怀疑的 | 记下来,在导出里列出,不暂停 | +| 宿主自己说不清的,比如这次运行到底启动没有 | 停下,等用户核对 | + +不设"暂停整个研究、等人处置"的流程。 diff --git a/inbox/结构.md b/inbox/结构.md new file mode 100644 index 0000000..e285e15 --- /dev/null +++ b/inbox/结构.md @@ -0,0 +1,108 @@ +# 待迁入 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 里"下一步做什么"只写在一个纯函数里。** 每个步骤一个小文件。步骤文件里出现公式、字段名单或可见性判断,说明知识漏进来了,要挪回下层。 + +## 模块化程度 + +- 一个 npm 包,每个模块一个目录。别的模块只能通过目录下的 `index.ts` 引用它。 +- 用 dependency-cruiser 在 `npm run check` 里强制以下规则: + - 不许循环引用; + - 每个模块只能依赖上表里列出的模块; + - 不许绕过 `index.ts` 直接引用模块内部的文件; + - rules 和 vocab 不许引用文件、数据库、网络、子进程和 Pi; + - 只有 ledger 能用 SQLite,只有 agents 和 plugin 能用 Pi,只有 runner 和 workspace 能起子进程; + - tasks 不许引用 runner。 +- 不拆成多个包,也不做 TypeScript 分项目编译。 + +## 可替换点 + +论文实验要换掉的部件,按种类用最轻的办法换: + +| 种类 | 例子 | 换法 | +|---|---|---| +| 外部服务 | Pi 会话、Docker 运行器、任务、Jev | 接口。Jev 另配一个"永远判不同"的空实现,用来关掉判重 | +| 决策规则 | 选批(批内最好期望、均匀随机);选消融格(信息价值减成本、全测、随机同格数、都不测);以后可能加合并方式 | 纯函数,登记在 rules 里的"名字 → 函数"表里,配置写名字 | +| 方法参数 | N、M、批量、总次数、轮数、点数上限、消融份额、u、衰减、开放预留 | 配置值,不写成常数,也不用环境变量。分两层:用户配置(配置对话会问),方法参数(做实验时才改,配置对话不问) | +| 步骤开关 | 消融、挑刺与回应、第零轮、第一次预测 | 配置值 | +| 模型与提示词 | 每个角色、每位研究者用哪个模型;提示词目录 | 配置值;账本记下每份提示词文件的哈希 | + +对照组"朴素循环"不另写流程,它就是一组开关的命名预设。 + +为了让换规则的实验做得对,结构上守住下面几条: + +1. **开关只在一处被读取。** 研究开始时,config 把配置翻译成"这份研究的步骤清单",加上"每个决策点用哪条规则",存进账本。flow、导出、恢复只读这份清单,不再读开关。被关掉的步骤,在账本里记一条"按配置跳过"。互相冲突的组合(比如关掉了预测,选批却不是随机)在配置校验时就报错。 +2. **决策函数是纯函数,输入全部从账本读。** 每条决策记录存下:规则名、规则版本、方法参数、随机种子、全部输入、输出。种子由研究编号、轮次、版本算出来,不用全局随机数。平票按固定规则处理。决策函数要导出,重放脚本和核对脚本直接调用它,不另抄一份公式。这样同一批记录换一条规则就能离线重算。 +3. **衡量好坏的尺子只有一把。** 比特、惊讶、覆盖率永远用同一个固定算法,从每个人的原始范围算出来,不跟着决策规则一起变。 +4. **一份研究的配置在开始时冻结进账本。** 恢复时以冻结的那份为准。 +5. **只提供实际跑通过的组合。** 每个命名预设至少有一条小规模的跑通测试。 + +## 规模(粗估,动手写某个模块之前由用户逐个确认) + +| 模块 | 预期行数 | +|---|---| +| 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 | diff --git a/inbox/调研.md b/inbox/调研.md new file mode 100644 index 0000000..dd315ad --- /dev/null +++ b/inbox/调研.md @@ -0,0 +1,8 @@ +# 待迁入 docs/调研/ + +来源:开发中自己提的。 + +以下 RivalBits 里的调研记录与 InfoRSI 的写法和结构直接相关,迁入时只写结论和出处,不整篇照搬: +- `research-wiki/reference/调研_研究型代码1_参考项目怎么处理边界.md` +- `research-wiki/reference/调研_研究型代码2_书籍与指南.md` +- `research-wiki/reference/调研_违规与防作弊_同类系统做法.md`