← 工作原理 · 原文件 notes/architecture/10_harness_era_architecture.md(Markdown 源文件已渲染;链接到其他文档的会跳转,指向源码的只显示路径)
10. 算法搜索架构:D = ERA + tune
一句话:在 DGX Spark 上跑锁定的算法搜索 run。
- 选父节点:ERA 的
futs.py(原样拷贝)+ 包装层; - 每个节点:一次抽签决定算子(draft / debug / improve;tune 代码保留、现行配置关闭)。照 DrugEvolve 分步:Researcher 出方案(可查文献与数据集元数据)→ Compliance 审方案 →(方案带数据请求时)Data agent 下载、删窗、入库 → Engineer(opencode 会话,不设固定时限)实现、调试、查分 → 权威执行 + 关闭机制对照 → Analyst 写分析;
- 终选:锁定的控制器全自动完成,候选按代码树 / 输出去重,在最终数据纪元内比较,带对照 copy_last 的护栏;
- run 之后:package → verify → 轨迹审计(G50,提交门)→ 邮件通知(G51);轨迹分析(G44)给下一环出提案。
本文按 2026-10-03 下午的定稿更新(G39-G43 任务书 §9,集成记录 2026-10-03_integration.md):tune 默认关、Engineer 不设时限、Compliance 与 Data 角色、单轮角色经 VM 中继用 Opus(relay_cli)、数据纪元、审计门、通知、平台期策略、stop_reason。相关设计见 15(Compliance / Data / vec-data)、16(中继与回退链)、17(run 外审计与分析)。
工程定为 D(decisions.tsv 2026-10-03“工程定为 D(ERA + tune);B、C 停止投入”)。D 是 A 的超集:tune.p = 0 时与 A 的决策逐位一致。T1 初筛和两个 T2 插值榜上,同时跑、只差 tune 的三组对照 D 都更高(T1 53.4 vs 52.0;心脏插值 63.9 vs 61.1;全胚插值 61.7 vs 59.9),每榜只比了一次。以后不再常规跑 A 对照。
相关文档:
- 决策:decisions.tsv(2026-10-02 起各行:方向库与 draft 调度、种子三态、护栏、目标;2026-10-03 尺子校准、泄漏审计、主线 D);提交台账 submissions.tsv;
- 尺子与目标:14_objective_rulers.md、T1 尺子校准、T2 外推 / T3 尺子校准、泄漏审计、分项反馈;
- 数据规则:11_data_use_scope.md;P3 规程:12_p3_protocol.md;记录与轨迹:13_records.md;
- 候选程序契约:
agent/search/CONTRACT.md;现行配置:agent/configs/experiments/t1_round2/D.yaml、g24_t2_t3/t2_*_D*.yaml、g24_t2_t3/t3_spark.yaml; - 历史与对照:见 §11。
1. 背景
1.1 ERA、OpenEvolve、DrugEvolve 各取什么
| 来源 | 取什么 | 不取什么 |
|---|---|---|
ERA futs.py(Apache-2.0) | 选父节点的算法原样拷贝:排名归一化 R_i = r_i/(M-1),U_i = R_i + c·(1/M)·√Σv/(1+v_i),在全树里取最大者,只回传访问次数;附带它的单元测试 | 单次 LLM 调用式生成、只返回一个数的执行器、只在内存里的树 |
| DrugEvolve | 每个节点分研究员 / 工程师 / 分析师三个角色;分析报告存库供后续节点读;外部知识库;失败时修复(debug 算子) | Inspector 方案去重;“只继承思路、代码从模板重写”(我们继承代码) |
| OpenEvolve | tune 借鉴了它修改轮的思路(SEARCH/REPLACE、便宜的多轮),但在 agent/search/tune.py 里独立实现,不依赖 OpenEvolve | 岛屿、MAP-Elites、种群(B、C 两臂已停,见 §11) |
用户实测 ERA(加州房价任务)时看到的问题与对策:
| 实测现象 | 对策 |
|---|---|
| 同一父节点连续被选中,多次超时 | 告诉会话父节点的耗时与预算;虚拟访问、忙父节点回避、连续失败降权(§4.3) |
| 同一父节点,几次改法几乎一样 | 全树实验表 + Analyst 报告 + 方法族库与 draft 覆盖调度(§4.3、§4.4) |
| 大部分轮次只是调参 | 调参单独交给 tune 算子(§4.6),improve 专做结构修改 |
| 分数偏乐观 | 真值分 A/B 两半;终选在没用过的新 seed 上做配对护栏(§4.8);官网分作外部校准 |
| grok-4.7 长 prompt 失败 | Spark 上用 qwen3.8-max 直连百炼 Token Plan,中间没有代理 |
1.2 为什么不用原来的单 Agent 长会话
run1、run2 的问题是提前收工(run2 只用了预算的 8%)、只走一条路、评分器 Agent 能改、隔离不严。新架构保留“会话内能查分”,但查分走沙箱外的服务、有次数上限、看的是另一半真值。唯一的例外是 T3(§7.1)。
2. 合规边界
原则:按官网规则执行(用户 2026-10-03:“不要过于谨慎,根据官网要求就好”)。官网允许的就用,禁止的不用,不再自己加额外界限;所有外部来源全部披露。项目自己的口径和边界问题见 11 号文。
| 规则 | 我们的做法 |
|---|---|
| §9:锁定前什么都能改,锁定后不能干预 | 每个 run 都是锁定 run(配置 + 代码快照 + 输入,§4.1)。人只在 run 之间改 harness,run 内不看不改 |
| §9:锁定后由 Agent 自己评估、选择最终输出 | 终选由锁定的控制器自动完成(§4.8),人不参与 |
| §9 例子:锁定前可以看结果、往配置里加方法 | 种子准入、方向库、方法卡、评分规则简报都由人在锁定前整理,来源写进锁和证据 |
| 保留阶段(含禁窗)与保留基因型的信息一律不能用 | 禁窗内的外部细胞在策展时删掉;检索结果涉及禁窗阶段的不得写进方案(researcher_spec.md);审查检查是否读取或内嵌实测数据 |
| 禁窗按任务设置计算 | 每个任务的视图只挂本任务允许的数据;同一阶段不能既做训练又做测试 |
| 外部数据、文献必须披露 | catalog 与知识库都记录来源,prepare_submission.py 生成披露文本 |
| 证据:每个文件 ≤ 200 MB,全队 ≤ 600 MB | prepare_submission.py 检查单文件大小和全队累计(台账 + 本次) |
3. D 循环总览
flowchart TB
LOCK["锁定 launch(lock v8)<br/>配置 + 代码快照 code/ + 输入硬链接 inputs/<br/>评测指纹 · draft 覆盖顺序 · 种子状态 · 回退链 models_fallback<br/>视图里挂上以往 run 入库的数据(catalog_auto)"]
SEEDS["种子执行 + 打分(不做分析)<br/>copy_last = anchor(护栏基线)<br/>exploration 种子(准入记录 ADMISSION_*.md)"]
DRAW{"每个节点抽签一次<br/>T1:draft 0.30 · debug 0.15 · improve 其余(tune.p 0)<br/>T2:draft 0.15 · debug 0.15 · improve 其余<br/>平台期(8 个节点没刷新前 5):draft 临时升到 0.5"}
SEL["FUTS 选父节点(原样 futs.py)<br/>虚拟访问 · 忙父节点回避 · 连续失败降权<br/>多个数据纪元时按纪元分组排名"]
DRAFT["draft:coverage_bandit<br/>失败的 draft 不占覆盖名额,同族失败满 2 次不再抽"]
RES["Researcher(单轮,relay_cli → Opus,回退千问)<br/>lit-search ≤ 10 次 · vec-data search(只读元数据)<br/>→ PLAN.json(可带 data_requests)"]
COMP["Compliance(正则 + 单轮 LLM)<br/>删保留阶段测量值句子,过半被删 → Researcher 重写一次"]
DATA["Data agent(仅有 data_requests 时,多轮)<br/>vec-data fetch → 隔离区删窗 → Compliance → 挂 external/dynamic/<br/>数据纪元 +1;失败不影响节点"]
ENG["Engineer:opencode + qwen3.8-max,bwrap,llm_only<br/>不设固定时限:180 s 无动作记一次停顿,连续 3 次结束<br/>预写 METHOD 占位;vec-score ≤ 20 次(A 半);可 vec-data fetch"]
EXEC["权威执行 + 打分(无网络沙箱,seed 0,B 半;记 data_epoch)<br/>代码树与父节点相同 → gen_empty,不执行<br/>--ablate 对照 → mechanism_active"]
VC["视图盲检查"]
ANA["Analyst(relay_cli)→ ANALYSIS.json"]
REV["Reviewer(relay_cli,材料内联)<br/>前 10% 且高于最好种子;method_auto 节点晋级前必审"]
PROMO["晋级:去重后排名分前 5 → seed 1、2<br/>(seed 0 在旧纪元时一并重跑)"]
ARCH[("存档 archive.sqlite + programs.git<br/>refuted_direction · same_as_parent · data_epoch")]
FINAL["终选:停止挂载 → 去重后前 5 → 最终纪元里 3 seed → 视图盲检查<br/>护栏:新 seed 上配对下界 > 0,否则交 copy_last → SELECTION.md"]
PACK["package → verify → G50 轨迹审计(提交门)→ G51 邮件<br/>stop_reason:budget / plateau / infra / agent_early_exit / operator"]
OUTER["run 外:G44 轨迹分析 → 白名单改动自动应用、下一环配置<br/>prepare_submission(审计门)→ 人挑 run 上传"]
LOCK --> SEEDS --> DRAW
DRAW -- "improve / debug" --> SEL
DRAW -- "draft(新根)" --> DRAFT --> RES
SEL --> RES --> COMP
COMP -- "无数据请求" --> ENG
COMP -- "data_requests" --> DATA --> ENG
ENG --> EXEC --> VC --> ANA --> ARCH
ARCH --> REV
ARCH --> PROMO
ARCH -- "预算未用完" --> DRAW
ARCH -- "截止 / 无可选父节点" --> FINAL --> PACK --> OUTER几点说明:
- 抽签是一次
rng.random():[0, p_draft)draft,接着p_debug一段,再接着tune.p一段(现行配置为 0,区间为空),其余是 improve。draft / debug 做不了时退回 improve。 - 平台期策略(
plateau.py,G45.2):最近连续 8 个已打分节点都没有刷新前 5 时,draft.p临时升到 0.5,draft 优先抽还没有已打分 draft 的族,被子节点上限挡住的 exploration 种子回到父节点池;有节点刷新前 5 即恢复。状态每次选择时从存档重算,resume 安全;不会停止 run。 - stop_reason:
stop_manifest.json与run_meta.json记录 run 为何结束:budget(max_nodes 或墙钟)、agent_early_exit(没有可选父节点、也没有在跑的节点)、infra(异常、重启用尽、launch / resume 被拒)、operator(SIGTERM / SIGINT);plateau预留。 - 并发:每个 run 同时 2 个节点(
concurrency.nodes: 2)。晋级复测在后台单独排队(一次 1 个)。
4. 组件
4.1 锁、代码快照与输入(lock v8)
harness.py launch生成config.lock.json:规范化后的配置(含draft.order与覆盖规则、各种子的状态、tune 块、目标与护栏)、harness git sha、代码 / 提示词 /search_hashes、评分器快照哈希、pip freeze哈希、scoring_fingerprint、view_files、各角色由提供方回报的模型 id、code_snapshot、pinned_inputs;dirty 树或假后端的 run 标为不可提交(submittable: false)。- run 代码快照(
runcode.py):锁定提交的agent/、modeling/、scripts/等导出成只读的<run_dir>/code/。控制器、各角色、执行器、打分服务都从这里 import;提示词、方向库、知识、方法卡也只从这里读。resume进入同一快照并校验树哈希。读入的数据硬链接到<run_dir>/inputs/。sync_spark.sh更新主 clone 不影响正在跑的 run。 - 评测指纹(
evalspec.py):视图文件、各尺子各半真值、各评分器 seed 的锚点(都按内容 sha256),加上评分器快照、venv、执行参数、候选可 import 的 modeling 快照。指纹相同,分数才可跨 run 比较;续跑接续时指纹不同就重新打分。
4.2 起点:种子与三态
每个种子 agent/seeds/<board>/<seed>/SEED.json 写明 state、family_id、provenance、admission(T1 有 seed_states: 块时必需;状态在加载时锁进配置):
| 状态 | 含义 | 现状(T1) |
|---|---|---|
anchor | 只有 copy_last,必须有;是护栏基线和 tune 的根参照 | copy_last |
exploration | 已准入(合规、机制生效、没有可靠的负证据)。和普通父节点一样,但子节点到 exploration_max_children(3)后移出父节点池,除非它或它的子树有复测收益 > 0(此后在本 run 内转为 active) | ot_moscot(local_ot)、composition_trend(2026-10-03 准入,family_id 为 null,故不算覆盖 composition_program) |
active | 可重复的收益;不受子节点上限限制 | 目前没有静态的 active 种子 |
reference_only | 只作回归和负对照,seeds: all 时自动去掉,显式列出时报错 | pseudobulk_shift、heart_jcf_peri、cfm_flow、composition、lineage_newtypes、scvi_shift |
T2 三个榜没有 seed_states::种子 = copy_last + G15 方法种子(心脏插值、全胚插值是 mix,心脏外推是 damped_shift)。
4.3 选父节点(FUTS + 包装层,select.py)
futs.py原样拷贝,公式不动。树 = 已结束的节点(失败节点分数 −∞,访问次数保留;interrupted不进树);分数用排名分(晋级后为 3 seed 均值,否则为 seed 0)。- 包装层:虚拟访问(选中即给父节点及祖先各 +1,子节点结束后结算);忙父节点回避(有子节点在跑的父节点,只要还有别的可选就跳过);连续失败降权(最近 2 个子节点都失败就暂停,直到别处出现新节点);同分取平均排名;只有
scored、审查没判 fail、没被视图盲检查排除的节点才有资格。 - 保留的 improve 名额(
reserve_improve):每个方法族第一个“合格”的 draft(输出不同于 copy_last,且 seed 0 分数 ≥ copy_last − 10)保证有一次 improve 深化。之后每次 improve 先给最早的这种 draft,再按 argmax U。 - 无进展访问:输出与父节点完全相同的节点照常计访问,但不占晋级名额。
- 候选身份(
candidates.py,G39.2):可执行代码树相同(runtime_key,不含 METHOD.md)、或目标视图 seed-0 预测 digest 相同、或(集成补充)两节点在 ≥ 2 个共同 seed 上分数、各组分、各分量都相同(到 4 位小数,用来合并只差浮点噪声的输出),即为同一候选,分组可传递。用于终选、晋级和无进展判断。 - 已证否方向(G39.3):输出与父节点相同、或 METHOD 首行以
NO_CHANGE:开头的节点写refuted_direction;之后该父节点的 Researcher 与 Engineer 都会看到这张列表。 - 数据纪元(G52,15 §6):run 中途挂上新数据后,树里有多个纪元的节点时,排名分在每个纪元内单独计算(各纪元的 futs 排名归一化分别做);只有一个纪元时与原来逐位一致。
4.4 draft 调度与方法族库(drafts.py,T1)
- 方法族库
agent/prompts/ideas_T1__val.md(v2):11 个族,分三类(continuous_dynamics/fate_branching/distribution_structure)。前言写合规、共用要求、两个优先方向(composition_program、local_ot)、算子分工(draft = 新族最小版,improve = 结构修复,tune = 已证实机制的调参,debug = 运行修复)和禁做清单。库里不写分数,带分数的实验账本另外管理。 - 覆盖:类别按
search_seed左转,类内各族也按search_seed左转,三类交错排成覆盖顺序。每个 draft 取顺序里第一个还没有 draft、也没被种子覆盖的族(种子覆盖 = 已打分、输出不同于 copy_last)。search_seed 0 的顺序以ot_cfm, local_ot, composition_program, manifold_ode, …开头。 - bandit(全部覆盖之后,
coverage_bandit):按min_explore0.25 的配额,部分 draft 给 draft 数最少的族(探索),其余给复测收益最大的族(收益 = 该族节点晋级后 3 seed 均值 − copy_last 排名分,只看目标,不看 record / holdout 尺子)。没有族收益 > 0 时也去探索。 - 终选时写
DRAFT_COVERAGE.json。T2 没有draft:块:p_draft0.15,按方向库条目轮流起草(各 9 条)。
4.5 Researcher
- 单轮调用,上下文全部内联,没有文件工具;时限 15 分钟。现行配置走 relay_cli(宿主机直接 POST VM 中继的订阅路由,Opus
claude-opus-5-5),回退 Token Plan 千问(opencode,thinking_budget8192,180 秒没有动静就杀掉重试一次)。relay_cli 下的工具走文本协议:模型在回复里写一个 ```tool 代码块,控制器用本次调用自己的 socket 执行(允许范围与 opencode 的工具策略相同),把输出作为下一轮发回。 - 输入:父节点的分数、四项排名指标分解(原始值 / skill / 得分)、文件列表、
METHOD.md、run.py前 6000 字符、ANALYSIS、变化量表、debug 的报错尾部;全树实验表;兄弟节点和前 5 名的分析摘要;本节点指定的方法族(draft);有draft:时还有父节点所属的族、族一览(含各族已有的 draft 数)和库前言;方向库条目名;知识库 BM25 前 3 条(agent/knowledge/,k014 排除);本任务的评分规则简报(scoring_rules_t1/t2.md,Engineer、Analyst 也有,Reviewer 没有)。 - 在线文献检索
lit-search:宿主机服务持 key;现行 T1 / T2 配置开启(research.lit_search.enabled: true,每节点 10 次)。返回给 Researcher 的文本与 PLAN 一起过 Compliance。 - 数据集元数据检索
vec-data search(data.search: true):只读元数据(样本、阶段换算、本 run 是否允许、文件大小、许可),不能下载;需要数据时在 PLAN 写data_requests(最多 3 条,accession+purpose)。 - Compliance(PLAN 校验通过后,
data_role.compliance_plan):正则表找出疑似句子,单轮 LLM 逐句判 measurement / mechanism / rule / method,删掉测量值句子并标[compliance: removed]、写_compliance;主要段落过半被删 → 带反馈让 Researcher 重写一次(单独计数),仍需重写就用删过的 PLAN(approach 被删空则 gen_failed)。 - 输出
PLAN.json:name / motivation / approach / expected_groups / risks,有draft:时另需family_id(库 id 或other)、mechanism、vs_constant_shift、mechanism_evidence、mechanism_off_control。不合格就带上错误信息重试一次,仍不合格记gen_failed。
4.6 Engineer
- opencode +
alibaba-token-plan-cn/qwen3.8-max,thinking_budget4096。在父节点 worktree 的副本上工作(draft 从空白solution/开始)。 - 沙箱:bwrap;网络
llm_only(经 egress 代理只放行token-plan.cn-beijing.maas.aliyuncs.com);可写的只有本节点工作目录;只读挂载各目标分量的视图、候选 modeling 快照、venv;有 GPU;内存上限 32 GB(engineer_memory_gb),进入前在全机 admission 账本里预留(§9)。 - 时限:不设固定时限(§9 定稿):会话一直跑到“截止 − 节点收尾时间”,只要健康;
engineer_stall180 s 没有任何活动记一次停顿,连续 3 次结束会话(不重试),有活动即重新计数。显式写了engineer_minutes的旧配置仍当作上限。 - METHOD 占位(G39.4):会话开始前 harness 预写
solution/METHOD.md(首行[auto: Engineer 未完成] <PLAN.name>)。结束时仍是占位:有run.py→ 照常执行打分、标method_auto(晋级前必须 Reviewer 通过);没有 → gen_failed。代码树与父节点相同(只改了 METHOD)→ gen_empty,不执行。 - 数据(G52):
data.fetch时多一个可下载的vec-datasocket(vec-data fetch --purpose由后台 Compliance 线程审查),并只读绑定<run_dir>/inputs/dynamic/,会话中途入库的数据经视图里的external/dynamic/<id>/可见。 - 能看到:
search_spec.md+ 本任务方法卡(method_cards_t1/t2.md)+CONTRACT.md+ 评分规则简报(scoring_rules_t1/t2.md),以及程序生成的本节点块(PLAN、目标各分量的视图路径和权重、父节点分数 / 耗时 / 内存、父节点 ANALYSIS、变化量表、全树实验表)。方向库不直接给 Engineer,方法族信息经 PLAN 传过来。 - 工具:
vec-score(每节点 ≤ 20 次,所有分量共用额度;查的是各分量真值的 A 半,评分 seed 0;返回四项排名指标的score_parts)、vec-check(格式检查,不限次数)、vec-gpu -- <命令>(全机 GPU 槽排队 + 进程内显存上限)。 - 结束时提交
solution/,METHOD.md首行是方法摘要(缺失记gen_failed,没有改动记gen_empty,提供方故障记interrupted)。大于 20 MB 的权重在ARTIFACTS.json声明,按 sha256 存进 run 的产物库;运行时才训练的权重声明为per_view(§4.8“训练一次多 seed 复用”)。 - 停顿判定(G45.7):Engineer 的停顿探针除 CPU / I/O / GPU 排队外,还把会话进程树自己的 GPU 计算(
nvidia-smi pmon,每 60 s 读一次)算作活动,长时间无输出的 GPU 训练不会被当成停顿结束。
跨环续做(G45.6,2026-10-03;carryover.py,设计 18 §5)
- 交接点:搜索阶段的 cutoff −
node_tail_minutes。会话的截止随 cutoff 移动(deadline_fn:终选预留按实测加大时,在飞的会话和执行同样提前结束)。每项工作绑定启动时的阶段(节点线程、晋级线程绑定 search,work_phase),主线程进入终选不会把搜索执行的截止换成硬截止,也不会关掉它的续做;前移的截止经LiveDeadline传到训练锁、admission、GPU 槽等待与评分请求,并经打分服务的截止组(请求带deadline_key,控制器在开跑和每次前移 cutoff 时发set_deadline,服务确认前保持待发、每 30 s 重发,进入终选前再确认一次,确认不了记deadline_unconfirmed;服务端的排队、内存准入和评分 worker 每 2 s 重读,worker 到点被杀;客户端在移动后的截止过去 30 s 仍无应答就不再等待,记 interrupted)传到已经发出的评分;等待资源时遇到截止返回的interrupted都带budget_cut,节点照样进入续做(训练锁超时与产物损坏区分);节点收尾与晋级收尾共用同一个绝对收尾截止(settle_deadline:cutoff + 宽限 + 180 s,不晚于 stop_at − 60 s);晋级线程到点仍未结束就在提交锁下把它的晋级结算为 failed(promotion_abandoned,代次加一),旧线程之后的提交一律丢弃,线程引用留作回收;终选获取视图盲检查的节点锁最多等 300 s(不超过终选截止),等不到跳过该候选;晋级线程的代次在线程启动时绑定,启动登记、结果与异常写入都经同一代次与停止检查;截止确认持续失败时,进入终选前杀掉本 run 的全部评分 worker scope(vec-score-<run>-*,直到 10 s 内不再出现新的,最多 120 s),事件scoring_workers_reaped;到 cutoff 时主线程先等搜索线程完成进程回收(整组宽限term_grace_s)、封存和节点结算(最多宽限 +SETTLE_EXTRA_S180 s,且不晚于 stop_at − 60 s)再进入终选(Codex 实现审阅 P0-1)。进程组宽限期内整组未退出才 KILL;有进程回收不掉时不登记权重、不封存。 - 什么会被带走:会话在交接点被切断、且没有留下可执行程序(无
run.py、无改动、无 METHOD)→ 节点记carried_over(kind engineer);会话被切但run.py可用 → 照常提交、立即执行(G39 的即时验收),快照只作证据;权威执行被搜索阶段截止切断(interrupted+budget_cut)→carried_over(kind execution,下一环重跑同一提交;SIGTERM 宽限 60 s 内程序写出的最后检查点一并封存,只作证据,没有训练续跑协议)。 - 封存是一个事务:写入者停止(会话 / 程序的进程组已回收、打分 client 已移除)→ 文件检查 → 不可变快照
nodes/<id>/carryover/(solution/、PLAN.json、原始轨迹trajectory/、checkpoint/、HANDOFF.json;只读)+SEAL.json(逐文件 sha256)→ 登记进写入账本 → 节点结算(carried_over,与interrupted同样搁置:释放虚拟访问、无访问、不进 FUTS 树)→ 控制器停止时写 stop_manifest。 - 下一环接续:
--continue-from导入时,carried_over节点保留状态;快照逐文件按 SEAL 校验通过才复制(失败记carryover_rejected,不续做)。校验通过的进resume队列,优先于 improve 抽签,每环最多carryover.max_resume_per_ring(4),其余留到再下一环。 - 多环去重(Codex 实现审阅 P0-2):一段被带走的工作以封存它的
run#node为稳定身份(work_key);任何一环(本环或导入的历史环)已续做过的工作不再续做;导入时resume_origin.imported_id随节点重编号;本环名额只数origin_run IS NULL的 resume 节点;resume_origin.run / id记直接上一段(封存它的那一环)。 - 祖先完整性(P0-4):launch(
--continue-from)、package、prepare_submission都沿接续链递归校验每个祖先 run(continuation.ancestor_integrity:未 tainted、ops.verify_run通过、目录与锁都在);有问题就拒绝接续 / 拒绝打包 / 列为不能提交。 resume算子:本环的新节点(计入max_nodes与本环预算),父节点是被带走节点的真实父节点;resume_origin记上一环 run id、节点 id、导入后的 id、kind 与有序segments[]。kind engineer:不调 Researcher,新开 Engineer 会话(不续旧 opencode 会话)——本环完整提示 + 原 PLAN + 程序生成的交接摘要(carryover.resume_block:上一段的改动文件、run.py / METHOD 状态、会话统计、最后的消息节选)+ 只读挂载的整个快照(含原始轨迹);工作区 = 快照里的solution/;METHOD 已写过就保留;查分额度 = 每节点额度 − 各段已用:会话结束时先移除打分 client(检查服务应答、有界重试;关闭与请求准入在服务的同一把锁下完成,关闭后到达的请求一律拒绝;服务只对已关闭的 client 出具最终结算,关闭无法证明时计数为未知),再用服务的settle_client等它在途的评分结清(最多 90 s)后取最终计数;结不清或服务不答复都记为未知(None,节点列与所有封存一致;续做不发新额度),不回退成日志计数。旧格式的resume_origin(没有work)导入时补上稳定身份run#id,同样参与去重。kind execution:同一提交直接执行打分。父节点审查判 fail 的不续做(resume_refused)。- 证据:续做节点的轨迹路径以各段快照里的原始轨迹开头;打包时祖先链里的续做节点把各段快照(轨迹、HANDOFF、SEAL、PLAN)并入
lineage_trajectories,MANIFEST.json的lineage_segments按顺序列出每一段的 run、节点、锁哈希、封存哈希与预算;三环以上的工作逐段累加,不丢中间段。 - 配置:
carryover: {enabled, max_resume_per_ring: 4, max_file_mb: 2048};默认关,run 链配置(T1 / T2)打开。
4.7 tune 算子(tune.py)
| 项 | 现行值(T1 / T2 D 配置相同) |
|---|---|
| 概率 | 现行配置 tune.p 0(默认关闭,G39.1):屏选 D 的 40 个 tune 节点只接受 1 个;tune.py 保留。以下为打开时的行为 |
| 父节点 | FUTS 池里 U 最高、且比参照高 > eligible_gain(= min_gain 1.0)的节点:参照是它的父节点,根节点的参照是 copy_last;每个父节点最多 max_per_parent 2 个 tune 子节点 |
| 基线 | 父节点程序在各分量视图上跑一次(程序 seed 0),用评分 seed 0、1、2 在 A 半上打分并取均值,按父节点 commit 缓存 |
| 轮次 | 最多 max_rounds 3 轮,同一段对话。每轮一次纯文本调用(bwrap,无工具,只放行 LLM),模型交回 CHANGE: + SEARCH/REPLACE → 应用到 run.py → 提交 → 在各视图上跑 → 3 个评分 seed 打分 → 分项分数和 stderr 尾部回到对话 |
| 模型 | Engineer 的模型和选项(有回退链时按链 engineer@tune 选择并记账),thinking_budget 1024;每次调用 ≤ 10 分钟 |
| 额度 | 每次打分都算进本节点的 max_queries_per_node(20),超额度的轮次不开 |
| 接受 | 最好的一轮(同分取最早)均值比父节点基线高 > min_gain 1.0 才接受;崩溃的轮次不算候选。不接受 → gen_empty;基础设施故障 → interrupted |
| 之后 | 接受的提交照常走权威执行(B 半、seed 0)→ Analyst → 审查 → 晋级 → 终选 |
tune 不能和 Codex Engineer 同用(配置加载时报错,§7.2);护栏的新 seed 不得与 tune 的评分 seed 重叠(加载时检查)。
4.8 执行、打分、检查、晋级与终选
权威执行(execute.py):harness 从 programs.git 取出已提交的 solution/,在无网络沙箱里重跑 run.py(seed 0),在各目标分量的视图上各跑一次(任一视图崩溃就算节点崩溃)。程序跑在自己的进程组和 systemd scope 里(MemoryMax = 视图 manifest 的上限,在 vec.slice 内),时限取 min(兜底上限, 当前阶段终点),阶段终点随 cutoff 前移而前移。
候选执行上限(G45.7,2026-10-03):搜索配置 limits: {memory_gb, time_limit_min}(缺省 28 GB / 360 min;可写成 {<榜>: {...}}),按榜规范化进锁(config.limits),build_run_views 把它写进本 run 每个视图(proxy、proxy10 所在视图、final、各测试题视图)manifest 的 limits,执行器和评测规格(evalspec 的 execution.programs)都从 manifest 读。360 min 只是兜底:执行由健康监督(health.py)管——每 60 s 读一次程序自己的进程组(累计 CPU、I/O、属于它的 GPU SM% 与显存、GPU 排队、进程状态与 wchan、cgroup 内存与余量、stdout/stderr、可写目录 mtime、solution/progress.json、各探针是否可用),每 5 min 汇总一条进 <exec_dir>/health.jsonl / health_latest.json。活动只认 CPU、自己的 GPU 计算、I/O、D 态加 I/O、GPU 排队;只写心跳 / 进度不算。现阶段 exec_health.enforce: false:只观测,30 min 无活动记 would_hang(统计误判率),程序仍受 360 min 与阶段终点约束;enforce: true 时 30 min 无活动判 hung(候选失败,与 timeout 同类),360 min 只在监督器失效(连续 failed_after_s = 10 min 读不到进程组)时生效。不再有“内存连续逼近上限即不健康”的判据(内存只记录,OOM 由 scope 与 slice 压力判)。任何一次终止先 SIGTERM,宽限 term_grace_s 60 s 再 SIGKILL。exec_health 进锁,也进评测规格(旧锁无此块,规格不变)。
训练一次、多 seed 复用(G45.7;契约见 CONTRACT“seed、训练状态与权重复用”):ARTIFACTS.json 里 per_view: true 的权重由 run.py 在运行时训练。执行器按缓存键 = sha256(提交的 solution 树、训练种子(EXECUTION.json 的 train_seed,或 seed_affects_training 时的 --seed)、视图内容(evalspec.view_content)、依赖环境(venv、pip freeze、modeling 快照)、--ablate 等额外参数)查 artifacts/per_view/<tree>/<key>.json:有 → 权重只读硬链回原路径(weights.mode = replay);没有 → 先取该键的训练锁(同键的并发首跑等它),把权重所在子目录变成可写(其中指向产物库的硬链先换成私有副本),执行成功后验收并不可变登记(train)。已登记的权重缺失、哈希不符或索引不完整 → interrupted,不悄悄重训;首跑写出的权重验收不过 → 本次执行 crash、不打分(Codex 实现审阅 P0-3)。哪些执行复用:同一视图上的晋级 seed、终选护栏 seed、--continue-from 下一环(产物库连同 per_view 索引一起硬链导入);--ablate 对照训练它自己的一组(参数在键里);视图盲检查的伪装与复核、终选末尾的冷启动重现检查(repro_check:入围的训练型候选 seed 0 不放回权重再跑一次,记 repro_json / REPRO.json,只作诊断,排在所有必要终选步骤之后)用 fresh(训练、不回放、不登记)。护栏的 LCB 因此是“给定冻结权重和当前评测数据”的结论;--seeds 批量接口与“每节点提前三 seed”按 Codex 审阅后置。
打分:打分服务(沙箱外)在真值 B 半上打分,评分 seed = 程序 seed。节点分 = 目标各分量的加权均值:
| 榜 | 目标分量(B 半) | 只记录(终选时) | 护栏 |
|---|---|---|---|
| T1:val(round 2) | proxy10×1 + X3×2 | X1、X4、X5、X6、proxy | 非劣护栏:select_on / require_gain_on = objective,gain_bound: lcb,noninferior: {},新 seed 3–7,α 0.025 |
| T2:heart:val_interp、T2:embryo:val_interp | 本榜 proxy | holdout X2 | 旧式护栏:rulers: [proxy],margin: 1 |
| T2:heart:val_extrap | proxy_noscale(scale_log_ratio 固定为 copy_last 的 skill 0.5) | proxy;holdout X2 | 旧式护栏:rulers: [proxy_noscale],margin: 1 |
proxy10 = 在 T1 proxy 视图(E8.5 → E9.5)的预测上模拟官方 10% 抽样流程(5 次抽取取均值),没有自己的视图。
关闭机制对照(ablation.py,G39.7):权威执行后、Analyst 之前,在目标各视图上用 --ablate <name>(取自 PLAN 的 mechanism_off_control)再跑一次 seed 0,比较输出 digest 与分项,写 mechanism_active: yes / no / unclear 进节点记录和变化量表;程序不支持该参数记 unclear。现有种子都已实现 --ablate(copy_last 的对照等于自身)。只作证据,不参与排名。
数据纪元:节点开始执行时记 data_epoch(晋级重跑、导入节点重打分同样记在每个 seed 的记录里)。
失败归属:候选的错 crash / oom / timeout / hung / invalid_format,计入连续失败降权;评分失败 score_error、基础设施 infra_oom / interrupted 都被搁置,不算候选的错。
视图盲检查(viewcheck.py):在伪装过的尺子视图(换路径、改 manifest 键序、所有阶段时间 +1 天;T2 视图另把 mode proxy ↔ final 互换、输入改名为 input_<k>)上重跑 seed 0,要求输出内容与真实视图相同。不同的先在真实视图上复跑确认:确认 → view_dependent(不能当父节点、不能晋级、不能进终选);复跑也不同 → nondeterministic(只记录)。触发时机:抽样(sample 0.1)、每次晋级前、每个终选候选。默认检查的尺子:T1 是测试题分量(X3),T2 是 proxy。静态扫描只给警告。
Analyst(relay_cli → Opus,回退千问 thinking_budget 2048;10 分钟):读变化量表(含分项)、PLAN、diff、Engineer 最后的消息、失败时的 stderr 尾部 → ANALYSIS.json(可写 family_id)。后续节点能看到父节点的完整报告,以及兄弟节点和前几名的摘要。
Reviewer(relay_cli → Opus,回退千问;10 分钟,只读;relay_cli 下 solution/、PLAN、视图 manifest、pitfalls 全部内联进提示词):节点进入前 10%、且排名分高于最好的种子时触发;method_auto 节点进入晋级前 k 名时也触发。检查越界读取、硬编码由替代目标算出的统计量、读取或内嵌保留阶段的实测数据、钻评分器漏洞(notes/pitfalls/04_scorer_invariance.md)、知识来源、确定性,以及 PLAN 里 [compliance: removed] 删掉的内容是否在 METHOD 或代码里重新出现。判 fail 的节点不能当父节点,也不能进终选。
晋级:按候选身份去重后、排名分前 top_k 5 的合格节点(不含无进展访问,method_auto 须先审查通过)先过视图盲检查,再用 seed 1、2 复跑(seed 0 的数据纪元不是当前纪元时一并重跑);之后排名分 = 3 个 seed 的均值。FUTS、审查触发、bandit 收益、终选候选都按它排序。
终选预留按实测(G45.7,Codex 审阅 P0-3):控制器每分钟(搜索与收尾阶段)按当前排名前 final_top_k 的候选估算终选需要的时间——未做的视图盲检查(各尺子视图 seed 0 的实测时间,伪装视图要重新训练)、未审查的 Reviewer 时间、缺的晋级 seed(每个按实测复跑时间)、护栏全部任务(最坏候选与基线各 fresh_seeds 次)、final 视图冷启动(最长单视图时间 × 1.5)与基线的 final 运行,每次执行加 60 s 评分排队;按串行累加(不除并发)。执行时间用各视图合计(加评分时间),不用最大值。reserve = max(配置值, 估计 + 10 min),上限半个挂钟;只会把 cutoff 往前移(Budget.extend_reserve,记 final_reserve 事件并存 meta,resume 后保持),在飞工作通过 deadline_fn 跟着提前。
链级已暴露评测 seed(G45.7):launch 时沿 --continue-from 链读出此前各环护栏用过的新 seed(及它们继承的集合),本环配置的 fresh_seeds 与之重叠时换成大于所有已用 seed 的最小未用整数(例如第二环 3–7 → 8–12),并把整条链的集合锁进 config.chain_exposed_seeds;在 inputs 锁定前确定,评分锚点按新 seed 预建。
搜索分数可比性指纹(G45.7,Codex 实现审阅 P1-9、二审 P1-1 / P1-3):评测规格(evalspec.spec)另算 search_hash = 去掉只属于终选或运行监督的部分后的规格——护栏的 fresh_seeds / seeds、只为这些 seed 建的锚点(按字段解析 seed,兼容 T1 proxy10 的 B:s3:d0)、execution.health。候选看得见或可能改变输出的都留在比较里:视图内容(含 manifest 的 limits)、时间上限、内存、线程数、评分器、真值、其余锚点。--continue-from 按 search_hash 判断导入分数是否可比(旧锁没有时由锁里的规格直接重算,不读磁盘);完整 hash 仍描述本 run。只换护栏 seed 时导入分数照常保留;导入重评被搜索阶段截止切断时不出队(仍是 imported_unscored、记 rescore_cut),下一环导入时随来源 run 的未完成重评队列一起重新入队;重评期间主线程照常更新实测终选预留;时间上限(30 → 360 min)或线程数改变时不可比,导入节点按 G18 只重评 continue_rescore_top_k 个(T1 链配置提到 40),其余成为只作历史的节点——这是“分数含义一致”优先于“保留旧树”的取舍。
终选(final.py,在 stop_at − final_reserve(配置值或上面的实测值)之后,受同一截止时间约束):
- 终选开始即停止数据审查线程,不再挂载新数据,当前纪元即最终纪元;
- 有护栏时,先落盘的是基线 copy_last 在 final 视图上的预测(
final/<board>.h5ad+ 临时SELECTION.md),在任何候选审查 / 视图盲检查之前(G45.7);配置了护栏却找不到基线提交时拒绝任何候选替换:不写final/,记final_guard_refused(原先是跳过护栏); - 候选 = 按候选身份去重后、排名分前
final_top_k5 个审查通过的节点(缺审查的当场审查),每个都过视图盲检查; - 补跑各候选的 seed 1、2(晋级结果只在同一纪元时复用;seed 0 来自旧纪元的一并重跑),按 3 seed 均值排序;
- 护栏:
- 非劣护栏(T1):只选一个候选(
select_on的 3 seed 均值最高),它和 copy_last 在新 seed 3–7 上配对运行(程序 seed = 评分 seed,B 半)。配对增益均值的单侧 97.5% t 下界 > 0 才替换 copy_last;否则保留 copy_last,不再试别的候选(避免多重检验); - 旧式护栏(T2):在每把护栏尺子上,3 seed 均值都比 copy_last 高 >
margin的、排名最高的候选胜出,否则交 copy_last;
- 非劣护栏(T1):只选一个候选(
4b. 补跑 seed 与护栏的截止都给“放置胜者”留出时间:基线 final 运行的 1.5 倍与相关候选冷启动 final 运行估计(final.cold_final_estimate:没有 per-view 权重的程序 = 最长单视图实测 × 1.5;有 per-view 权重且有训练实测 = 最长训练实测 × 1.5;有 per-view 权重但只有回放、没有冷启动实测 = final_cold_fraction(默认 0.25)× limits.time_limit_min)取大,再加 120 s 格式检查与落盘。控制器的实测终选预留用同一个估计(含默认值、护栏任务、落盘开销),所以预留够终选内部扣除;
- 有时间时在 holdout / record 尺子上(整份真值)给候选打分,只记录进
SELECTION.md,不参与选择;之后仍有时间才做入围训练型候选的冷启动重现检查(只记录);护栏不完整就保留基线,不降样本数、不跳护栏、不改试别的候选; - 用实际跑完的内容重写
SELECTION.md(含逐 seed 分数、差值、下界、结论、最终数据纪元、外部数据与文献披露;dynamic/auto_admitted数据带各自的披露文本)。
4.9 存档、证据与提交
- 存档:
archive.sqlite(节点 id、父节点、算子、族、commit、状态、各 seed 分数、分组与分项、变化量、PLAN、ANALYSIS、审查、视图检查、各阶段时间、轨迹路径;events 表记录控制器的每个决定);程序提交到programs.git的refs/nodes/<id>(tune 各轮在refs/tune/<id>/<k>);权重在<run_dir>/artifacts/。四类轨迹的存放见 13_records.md。 - 写入账本(
ledger.py):写进 run 目录的每个文件当场登记到只追加、带哈希链的ledger.jsonl。resume、package、prepare_submission、被--continue-from接续之前,都由ops.verify_run核对账本、代码快照、输入、评分器、视图、programs.git引用;不符就追加到TAINTED.json(不能提交)。harness.py verify <run> --reevaluate-taint只能在原因都能由重算解释、且 run 现在核对干净时追加“已解决”事件,原记录保留。 - 证据:锁、提示词与知识快照、harness、存档、打分日志、网络日志、
scores_progression.csv、中选节点祖先链上的完整轨迹。 - 打包之后:
harness.py package依次做 package → verify →(Spark 上的正式 run)后台启动 G50 轨迹审计(notes/reports/audit/<run>/AUDIT.json)→ G51 run 结束邮件(agent/notify)。verify 失败则 package 返回失败、不启动审计。 - 提交:
scripts/prepare_submission.py --run <id>(默认带审计门:fail 拒绝、review 需人放行,见 17 号文;逐榜格式检查、证据大小与全队配额、披露文本、T3 稠密度护栏,写submission/CHECKLIST.md;tainted、dirty、假后端的 run 一律拒绝)→ 人从已完成的 run 里挑一个上传(官网没有 API)→scripts/record_submission.py写台账。官网评分器固定,同一文件不重复提交(decisions 2026-10-02)。P3 的“每榜两枪”见 12 号文。
4.10 代码与 run 目录
agent/
├── harness.py launch / resume / status / package / verify(mode: agent 与 mode: search 共用)
├── paths.py run 根目录与 run id 查找
├── search/ controller · select(+futs)· drafts · tune · roles · context · execute · viewcheck
│ scoring_def · final · evalspec · runcode · ledger · ops · budget · codex_cli
│ ├── openevolve_adapter/ B/C 臂的代码(已停止投入,保留可复现)
│ └── tests/
├── scoring/ service(打分服务)· parts(分项)· density(T3 护栏)· admission(内存账本)
├── prompts/ *_spec.md · method_cards_t1/t2/t3.md · scoring_rules_t1/t2/t3.md · ideas_<board>.md
├── seeds/<board>/ 种子程序 + SEED.json + 准入记录
└── configs/experiments/ t1_round2/ · g24_t2_t3/ · screen_abcd/ · model_test/ · …新 run 建在 default_root(kind):Spark 上是 ~/vec/runs/formal/ 或 ~/vec/runs/test/,VM 上是 agent/runs/。所有命令都按 run id 在同一组根目录里查找。
4.11 模型
| 角色 | 模型(回退链) | 思考强度 / 时限 |
|---|---|---|
| Researcher | relay_cli claude-opus-5-5(订阅)→ qwen3.8-max | 千问时 8192 / 15 min |
| Compliance | relay_cli Opus → qwen3.8-max | 千问时 2048 / 5 min |
| Engineer | qwen3.8-max(Token Plan;可选 Codex,§7.2);draft 用 Opus 待中继 API key 模式启用(engineer_draft_model 占位) | 4096 / 不设固定时限 |
| Data agent | qwen3.8-max(opencode 多轮) | 2048 / 30 min |
| tune(关闭) | 同 Engineer | 1024 / 每次 10 min |
| Analyst | relay_cli Opus → qwen3.8-max | 千问时 2048 / 10 min |
| Reviewer | relay_cli Opus → qwen3.8-max | 千问时 2048 / 10 min |
- 凭据中继(16 号文):Claude 凭据只在 VM;Spark 经 ssh 反向转发连 VM 上的中继(
127.0.0.1:18790),只持中继令牌。relay_cli 由宿主机控制器直接 POST,带X-Vec-Run / Node / Role / Attempt头供中继记账。 - 回退链(
model_fallback.py):每个模型一个熔断器(健康按模型共享);额度类错误(含relay_mode_disabled、relay_cli_quota)立即跳过 60 min;当前调用失败且链已切换时,同一调用在下一个模型上立即重试一次;只有整条链不可用时才让 run 熔断器计数。链写进锁的顶层字段models_fallback;launch 时探测链里每个模型(中继模型查/healthz)。
5. 尺子与校准(摘要)
详见 14_objective_rulers.md;数据来自 2026-10-03 的两份校准报告。
| 榜 | 结论 | 现在的用法 |
|---|---|---|
| T1 | 11 个有官网分的程序中,X3 是唯一前瞻有效的尺子:与官网 Spearman ρ ≈ 0.77(p 0.005),前瞻 4 个符号全对,但合并后只有 5/11 个符号对;收益幅度约为官网的 0.4 倍。proxy10 对两输入程序完全看不见,只对重抽样 / 组成类程序有排序信息(回顾集 n = 6 时 ρ 0.99,合并后 −0.12)。proxy10 + 2·X3 留一 RMSE 最低(1.30),符号 10/11(权重 2 是定的,不是拟合的)。X1、X4、X5、X6 都没有可用的正相关 | 目标 proxy10×1 + X3×2;X1/X4–X6/proxy 只记录。护栏的回放:官网 ≥ +2.6 的 5 个程序全部通过;但台账里没有低于 copy_last 的程序,护栏挡坏程序的能力没被检验过 |
| T2 插值(心脏、全胚) | proxy 与官网大致一致 | 目标 = 本榜 proxy |
| T2 心脏外推 | proxy 的 +4.15 全部来自尺寸项,而尺寸项用的是从 proxy 目标泄漏来的常数(§6);没有任何已发布数据能验证尺寸外推 | 目标 proxy_noscale;proxy 只记录 |
| T3:gata4 | 没有可靠的本地尺子:Mab21l2 留一基因尺子在 3 个官网点上完全反序(ρ = −1)。打分服务里改名为 T3:gata4/proxy_mab21l2,每个分数都带 “local proxy” 标签 | 不设本地目标,跑 agent 模式(§7.1);最终输出须过稠密度护栏:非零比例 / WT 输入的非零比例在 0.8–1.25 之间(density.py;harness verify 与 prepare_submission 都检查) |
6. 本地尺子的目标泄漏规则
起因:T2 心脏外推的方向库和方法卡写了 E9.5(proxy 目标)的 RMS,程序据此写出回退增长率 0.58/天,proxy 尺寸项白拿 +4.15,官网 49.6。这不违反官方规则(E9.5 已发布),但这把尺子因此失效(泄漏审计)。
- 规则:智能体可见的文本(角色提示、方向库、方法卡、评分规则简报、知识库、种子的 README / METHOD / 代码、候选可 import 的 modeling 快照)不得给出任何本地尺子目标阶段的统计量(细胞数、尺寸 / RMS、组成、同名类型数等),一律改为“由程序从视图输入现场计算”。
search_spec.md同时要求不得把从某阶段算出的统计量硬编码进程序。 - 检查:
scripts/check_knowledge.py的 proxy-target 检查默认只警告(用户 2026-10-03 决定),--strict-proxy-targets才会让它失败。 - 已知余留:T1 proxy / proxy10 的类型级文字已改,但
modeling/src/task1_temporal/reweight.py保持字节不变(为了评测规格不变);composition_trend 的超参数是在旧 proxy 上调的,所以 proxy10 对这一系偏乐观(已披露)。T1 round 2 两个 run 用的是修复之前的 master 659a312。T3 在 agent 模式下能直接读到 Mab21l2 KO 文件,这是结构性问题。
7. 例外
7.1 T3 跑 agent 模式
g24_t2_t3/t3_spark.yaml:mode 缺省,即 agent 模式,不走树搜索(没有可靠的本地尺子,选不了节点)。由 agent/harness.py 起一个长 opencode 会话(qwen3.8-max),bwrap,llm_only,4 h,最多 400 轮;提示词是 initial_spec.md + method_cards_t3.md + scoring_rules_t3.md。只挂官方数据(脱敏副本)和 reference panels,不挂外部数据和 prior/。每个 client 最多查分 20 次(本地 proxy,带标签)。最终输出必须过稠密度护栏。
7.2 可选的 Codex Engineer
models.engineer.framework: codex(codex_cli.py):Engineer 改用 codex exec --json,跑在同一个 bwrap 沙箱里,工具、视图、查分、网络策略都与 opencode 版相同。egress 白名单另加 chatgpt.com;凭据放在每次调用私有的 CODEX_HOME,token 在流和工作目录里打码。现在用的是 gpt-6-astra + reasoning_effort: xhigh(model_test/astra_engineer*.yaml)。限制:
- 不能与 tune 同用(
tune.p > 0时加载报错:tune 经 opencode 调 Engineer 的模型),也不能与openevolve_native同用;只有 Engineer 可以换; - 只能在 VM 上跑:DGX Spark 连不到 OpenAI(
model_test/README.md)。
8. 数据
data/external/
├── catalog.yaml # 来源、许可、阶段换算、删窗记录、哈希、用途(train / test / bench / prior)、允许的设置
├── train/<dataset>/ # 窗外训练数据
├── test/<task>/ # 外部尺子:inputs/、target/(只有打分服务能读)、manifest、anchors
└── prior/ # 生物先验(Reactome、GO、CollecTRI、STRING、MSigDB、MGI),只读挂进视图 prior/- 视图(契约见
CONTRACT.md):入口solution/run.py --data <view> --out <pred.h5ad> --seed <int>。T1 的所有视图(proxy、proxy2、final、X1/X3–X6)用同一套中性 schema;同一份代码必须在单输入(T1 proxy)和两输入视图上都能跑。
| 榜 | proxy:输入 → 目标 | final:输入 → 目标 |
|---|---|---|
T1:val | E8.5 → E9.5(proxy10 也在这里打分) | E8.5、E9.5 → E10.5 |
T2:heart:val_interp | E8.25_late、E9.5 → E8.75 | 三个阶段 → E8.5 |
T2:heart:val_extrap | E8.25_late、E8.75 → E9.5 | 三个阶段 → E10.5 |
T2:embryo:val_interp | E6.75、E8.0 → E7.25 | 三个阶段 → E7.5 |
T3:gata4 | agent 模式,不建搜索视图 | — |
- 外部数据:训练集 = Qiu 2024 早期心脏的 E8.75 / E9.0(G20 起 E9.25 / E9.5 只作测试)和 MOSTA E9.5;T1 尺子 X1(Qiu 晚期心脏)、X3(Qiu E8.75 + E9.0 → E9.5)、X4 / X5 / X6(Imaz 2024);T2 尺子 X2(MOSTA E14.5 + E16.5 → E15.5)。每个阶段只标一种用途;
check_catalog.py保证训练和测试不重叠。P3 的暂定方案(Qiu E9.25 进训练)见 decisions 2026-10-02。 - 禁窗按任务计算:T1 (9.5, 13.5];心脏 (8.25, 8.75) ∪ (9.5, 13.5];全胚 (7.25, 8.0)。
- run 内数据(G52):Data agent 经 vec-data 服务下载到 Spark 的隔离区
data/external/_quarantine/<qid>/,先按元数据删禁窗样本,下载后逐文件 / 逐细胞再删(data.post_filter: true),原始下载随即删除;Compliance 放行后移入data/external/<id>/、写catalog_auto.yaml,硬链接到<run_dir>/inputs/dynamic/并挂进各视图external/dynamic/<id>/,数据纪元 +1。以后的 run 在 launch 时把catalog_auto.yaml里本任务允许的文件挂进视图(data.link_auto_admitted,默认开)。规则改写:下载不违规、使用才违规(用户 2026-10-03,11 号文 §3、SPARK_EXCLUDE)。
9. 运维与资源
| 项 | 现行设置 |
|---|---|
| 机器 | DGX Spark(GB10、aarch64、121 GB 统一内存,与他人共用);VM 经 Mac 反向隧道 ssh spark;代码只在 VM 仓库改,用 scripts/sync_spark.sh 推送 |
| cgroup | 所有进程跑在 vec.slice(MemoryMax 96G、CPUQuota 1800%);run 的 budget.memory_max 48G |
| 内存准入 | 全机共享账本(admission.py,容量 = slice 的 memory.high,否则 memory.max):Engineer 会话、权威执行、打分子进程先预留再启动(D 配置:Engineer 6 GB、执行 6 GB)。打分走优先通道,可用 headroom。slice 有压力时的 OOM 记为 infra_oom |
| GPU | GPU 显存不计入 cgroup,所以 GPU 任务一律经 vec-gpu:全机文件锁 gpu.lock*(gpu.slots 1,所有 run 共用)+ 进程内显存上限 32 GB |
| 并发 | 每个 run 2 个节点;打分服务每个 run 最多 2 个并发评分(快速评分器,4 线程,T1 12G);晋级一次 1 个 |
| 预算 | 一个截止时间(budget.py):T1 round 2 4.5 h、max_nodes 120;T2 4 h、max_nodes 80;final_reserve_minutes 20,node_tail_minutes 10 |
| 运行保障 | 模型回退链(§4.11);LLM 熔断(整条链不可用时计数,连续 5 次;退避 1 → 30 min);磁盘 70% 警告、85% 不再开新节点;心跳 + systemd watchdog 600 s;失败后自动 resume(max_restarts 1) |
| 分析任务 | 与正式 run 同时做分析时,最多 2 个进程、BLAS 2 线程,走 ~/vec/scratch/heavy_run.sh |
| 通知 | Spark 上 vec-notify:每 3 h 健康汇报(2026-10-03 用户决定;G45.7 加入每个在飞候选执行的健康采样摘要:已运行时长、CPU、属于它的 GPU、内存 / 上限、进度、是否达到挂死判据)、run 结束邮件(package 之后)、额度耗尽 / 合规拦截 / tainted / 崩溃即时告警;花费只统计不设限 |
| 候选执行上限 | G45.7:limits(28 GB / 360 min 兜底)+ 健康监督 exec_health(只观测,enforce 后 30 min 无活动判 hung);run 链 T1 改为 24 h 一环(final_reserve_minutes 40 起、max_nodes 600),T2 仍 12 h;每条 run 并发节点 2 不变,执行器并发另由打分服务 max_concurrent 2 与 GPU 槽约束 |
| 外层循环 | G44 轨迹分析出提案:白名单(提示词、方向库、种子取舍、搜索参数、回退链顺序)自动应用并写下一环配置,黑名单等人批;不自动启动 run |
| 人 | 看看板、审计结论与 G44 提案 → 批黑名单提案 → 从已完成的 run 里挑一个提交 |
9.1 候选执行上限(G45.7)
- 配置:
limits: {memory_gb: 28, time_limit_min: 360}(默认值,不写即可;按榜进锁并写进视图 manifest);exec_health: {enabled: true, enforce: false, collect_s: 60, interval_s: 300, hang_minutes: 30, cpu_core_frac: 0.05, gpu_util_pct: 5, io_bytes: 1 MiB, failed_after_s: 600, term_grace_s: 60}。 - 运维看什么:
nodes/<id>/<exec>/health.jsonl(每 5 min 一条)、健康邮件(每 3 h)里的“在飞”行;would_hang的记录用来统计误判率,确认后再把enforce打开(打开即改变评测规格,下一环导入节点会重打分)。 - 改默认上限(30 → 360 min)与写入
exec_health都改变评测规格:从 2026-10-03 以前的 run 接续时,导入节点按 G18 规则重打分。
9.2 训练一次多 seed 复用(G45.7)
- 产物库:
<run_dir>/artifacts/<sha256>(只读)+ 索引artifacts/per_view/<tree>/<key>.json(首写者为准,进账本);训练锁在$XDG_RUNTIME_DIR/vec/<run>/train_locks/(不在 run 目录内)。 --continue-from把产物库与索引整体硬链进新 run;环境(venv / pip freeze / modeling 快照)不变时下一环直接回放,变了就重训。- 记录:每次执行的
proxy.<视图>.weights(train / replay / fresh);冷启动重现检查的结果在repro_json/REPRO.json与repro_check事件。
9.3 跨环续做(G45.6)
- 第一环结束:
carried_over节点的快照在nodes/<id>/carryover/(只读,进账本);harness.py package正常打包(快照的.py/.json/.jsonl也进run_trajectories)。 - 下一环:
harness.py launch --config <链配置> --continue-from <上一环>;continued_from.json的carryover段列出每个快照的校验结果,控制器日志打印 resume 队列。每环最多续做 4 个,更早的先续。 - 合规:新环的锁承接的是已结束 run 的封存输入(与 G18 导入节点同类);每段保留自己的锁与全部提示,续做节点不是原锁定 run 的继续执行,证据按
segments[]逐段列出。健康邮件与 G44 只用于两环之间改配置,不在锁定的环内人工指挥。
10. 风险
| 风险 | 对策 |
|---|---|
| 本地尺子和官网不一致 | 校准账本(官网分 vs 尺子分);目标按校准结果定权重;T3 不设本地目标 |
| 尺子目标泄漏进提示词或程序 | §6 的规则与警告级检查;审查查硬编码统计量 |
| 评分噪声被当成进步 | 晋级后用 3 seed 均值排序;tune 要求 > 1.0;终选在新 seed 上看配对下界 |
| 过拟合目标尺子 | 视图盲检查;record 尺子只记录、给人看;copy_last 护栏 |
| 护栏挡坏程序的能力没被检验过 | 官网结果持续入账;需要时调整护栏 |
| tune 追噪声 | min_gain 1.0、每个父节点最多 2 次、A 半选择、B 半记分 |
| 用到保留阶段数据 | 外部数据删窗;视图只挂允许的数据;审查;知识检查 |
| 共用的 Spark 过载 | 每个 run 2 个节点、内存准入、GPU 锁、分析任务限进程数 |
| 7×24 运行中断 | systemd、watchdog、自动 resume、熔断、账本核对 |
11. 历史
- A/B/C/D 对照(2026-10-01 – 10-03):A = ERA,B = OpenEvolve 种群选父节点,C = OpenEvolve 原生生成,D = A + tune。深度轨迹报告 abc_deep_2026-10-02.md 发现 A、B 分不出来,C 快但浅;四组初筛官网分 D 53.4、A 52.0、B 49.5、C 47.8(copy_last 46.9),轨迹分析见
notes/reports/runs/abcd_screen_trajectories_2026-10-03.md(待写)。2026-10-03 起主线定为 D,B、C 停止投入(代码留在agent/search/openevolve_adapter/;OpenEvolve 版的设计在并列仓库2026_virtual_embryo_openevolve)。 - 已弃用的目标:proxy / proxy2(2026-10-02,G36);X3 单独作目标、X3 + X6 平均、X1 / X6 非劣护栏(2026-10-02 各行,被 10-03 的校准取代)。
- 已改动的旧设定:默认并发 3 → 2;Engineer 45 → 30 min;draft 15% 轮流起草 → T1 30% coverage_bandit;终选“前 5 名 → 3 seed → 直接交最高的” → copy_last 护栏;“每个 run 交最佳 + 次佳到 P2” → 人挑一个,同一文件不重复提交;lit-search 在现行配置里关闭。
- 2026-10-03 定稿后的改动(本次同步):tune 0.25 → 0(默认关);Engineer 30 min → 不设固定时限;lit-search 关闭 → 开启;新增 Compliance 与 Data agent、vec-data、数据纪元;单轮角色 qwen3.8-max → Opus(relay_cli)回退千问;新增审计门、邮件通知、平台期策略、stop_reason;“禁窗数据不得落到 Spark” → 下载到隔离区先删窗、使用才违规。
- 本文 2026-10-01 版(ERA + R/E/A、G29 更新)见 git 历史。