Contents

Dream-RSI: Recursive Self-Improvement through Evolving Worlds


作者Tong Zheng, Xidong Wu, Zheng Zhang, Zhankui He, Chaoyi Zhang, Benjamin Coleman, Ruoqiao Wei, Di Bai, Haolin Liu, Rui Liu, Xue Wang, Yue Zhuan, Wang-Cheng Kang, Renkai Xiang, Heng Huang, Xinwu Cheng, Yunsong Guo
机构Google, University of Maryland College Park, Google DeepMind, University of Virginia
日期2026-09-14
链接https://arxiv.org/abs/2609.14858
代码github.com/zhengkid/Dream-RSI

一句话:用 LLM 看历史 GA 运行记录,自动改写 GA 的搜索代码(分支因子、剪枝条件、并发度、停止规则),下一轮用改写后的代码跑。novelty 在于历史 GA 运行记录可以当 simulator 用——测试"如果当时用不同搜索参数会怎样"不用重跑,直接在记录上回放,所以 LLM 可以便宜地试很多种改法再选最好的。

Figure 1
图1 — Dream-RSI 三阶段循环总览。① 上线跑任务,产出带完整执行记录的搜索树;② 把这棵树存入历史池;③ 在历史树上零成本评估大量候选搜索策略,选出最优者用于下一轮。

术语速查

所有术语用同一个贯穿全文的例子解释:任务 = 自动写出一个比 sklearn 更快的 Lasso solver Python 程序

术语具体是什么在 Lasso 例子里
Task
任务
固定不变的目标问题,有自动评测器给分 "写一个 Lasso regularization path solver,在 17 个测试实例上运行时间要比 sklearn 短"。评测器 = 实际跑计时
Coding Agent
编程智能体
实际写代码的 LLM(Gemini-3.1-Pro),受一段固定 prompt 驱动(论文 Listing 1)。整个实验中 prompt 不变,模型权重不变。 Gemini 看到"请写一个比 sklearn 更快的 Lasso solver,已有历史尝试如下…",输出一段 Python 代码
Attempt / Node
一次尝试
Coding agent 写了一版代码 + 评测器跑了一次 → 得到分数。树里每个节点 = 一次这样的尝试,存储:代码文件快照 + 评测结果 + 分数 节点 A:Gemini 写了"用 LARS 算法"的 solver → 跑计时 = 3500ms → 存入节点
Branch
分支
从同一个父节点出发的不同探索方向。
从 root 出发 = 全新独立方向;
从已有节点出发 = refine 那个节点的代码
Branch A: 从头探索 LARS 路线
Branch B: 从头探索坐标下降路线
A→A1: 在 A 的代码基础上加 warm start
Discovery Tree
探索树
一轮完整探索产出的所有 attempt 的树形记录。根 = 空 workspace,子节点 = 各次尝试及其关系。每跑完一轮就产出一棵树。 Round 1 跑完,得到一棵树:根→[A(3500ms), B(4200ms), C(2600ms)],C下面还有C1(2200ms)
History H_t
历史池
截至第 t 轮,所有已产出的树的集合。H₃ = {T₁, T₂, T₃} 跑了 3 轮后,历史池里有 3 棵树,每棵对应一轮探索
Policy(探索策略)
⚠️ 不是 prompt
一段 Python 代码,决定"搜索行为":选哪些节点继续探索、并发度多少、何时停止。整个实验只有这个在变。
输入:当前已 reveal 的树节点;输出:下一批要探索的节点列表
solve(question) 函数里写着:先并行开 3 个 branch,哪个 3 步之内没提升就剪掉,集中资源 refine 最好的那个
Policy Developer 一个 LLM(也是 Gemini),读 replay 轨迹和分数,输出修改后的 policy Python 代码(不是 prompt,是代码)。受论文 Listing 2 的 prompt 驱动,该 prompt 固定不变。 看完 replay 后写:"Branch B 每次都差,3 步后就停;C 方向很有潜力,多开几个并行 refine"
Replay
回放
把一个 policy 代码跑在一棵已存的历史树上:policy 选节点 → 不重新跑代码,直接读树里存好的结果 → 统计这个 policy 在这棵树上的总分。零 LLM 调用,零代码执行。 测试新 policy "只探索前 3 个最快的方向":在 T₁ 上 replay,它选了 A、C,跳过 B → 读出 A=3500ms, C=2200ms → 最优=2200ms,用了 4 次 probe → 算分
Dreaming
做梦
生成很多候选 policy,逐一在历史树上 replay 打分,选最高分的——这整个过程叫 dreaming。类比 Dreamer RL:在世界模型里想象轨迹而不真的执行。 生成 20 个候选 policy 代码变体,各自在 {T₁, T₂} 上 replay,耗时几秒,选出平均分最高的
Round / Iteration
轮次
= 一次 Online Explore + 一次 Dreaming。每轮结束后 policy 更新一次。 Round 1: 用初始 policy 跑 → 得 T₁ → dream → 得 π₂
Round 2: 用 π₂ 跑 → 得 T₂ → dream on {T₁,T₂} → 得 π₃

研究动机

核心方法(逐步拆解)

Step 1:一轮 Online Exploration 长什么样

当前 policy π(一段 Python 代码)控制 coding agent 跑任务,产出一棵 discovery tree:

# policy 代码示例(Round 1 初始策略)
class OptimalPolicy:
    def solve(self, question, budget=None):
        # 并行开 10 个全新方向(从 root 出发)
        roots = question.legal_roots()[:10]
        question.probe_batch(roots)          # 触发 10 次 coding agent 调用
        # 对每个方向,继续 refine 最多 11 次
        for step in range(11):
            leaves = question.legal_actions()
            question.probe_batch(leaves)

每次 probe_batch 在 online 阶段 = 真实调用 coding agent(LLM 写代码 + 评测器计时),结果存入树节点。

跑完后产出:

T₁(Round 1 的树):
Root
├── Node A: 代码=LARS算法,        分数=3500ms, 文件快照已存
│   └── Node A1: 代码=LARS+warmstart, 分数=3100ms, 文件快照已存
│       └── Node A2: 代码=LARS+调参,   分数=2900ms, 文件快照已存
├── Node B: 代码=坐标下降,        分数=4200ms, 文件快照已存
│   └── Node B1: 代码=坐标下降+向量化, 分数=3800ms, 文件快照已存
└── Node C: 代码=active set,      分数=2600ms, 文件快照已存
    └── Node C1: 代码=active set+KKT, 分数=2200ms ← 本轮最优
Figure 2
图2 — 历史树作为 replay simulator。左:policy 上线跑任务,产出带完整执行记录的树。右:数千个候选 policy 在同一棵树上"回放"——选不同子集的节点、不同顺序、不同并发批次,直接读已存结果,零执行成本。

Step 2:Dreaming——在历史树上测试新 policy

Online 结束,T₁ 存入历史池 H₁ = {T₁}。现在 policy developer(LLM)想测试一个新 policy:"先并行看 3 个 branch,分差的立刻剪掉"。

把这个候选 policy 代码在 T₁ 上 replay:

# replay 时 probe_batch = 读树里存好的结果,不调用任何 LLM
question.probe_batch([A, B, C])   → 立刻返回 3500, 4200, 2600(从 T₁ 读出)
# policy 看 B 最差,剪掉;继续 A 和 C
question.probe_batch([A1, C1])    → 立刻返回 3100, 2200(从 T₁ 读出)
# policy 决定停止

replay score = 最优(2200ms换算分数) - 成本惩罚(用了5个节点) + 并发奖励

整个过程:0 次 LLM 调用,0 次代码执行,耗时毫秒级

policy developer 可以这样测试几十个候选 policy 变体,选出平均 replay 分最高的,作为 π₂ 用于下一轮 online。

Step 3:完整多轮循环

flowchart LR
  A["Online"] -->|"new tree T_t"| B["History Pool"]
  B -->|"replay all trees"| C["Dreaming"]
  C -->|"pick best"| D["New Policy"]
  D -->|"next round"| A
          
drag to pan · scroll to zoom
Dream-RSI 多轮循环:Online 阶段用当前 policy 跑真实 LLM 产出搜索树 T_t,存入历史池;Dreaming 阶段 policy developer LLM 生成 M 个候选 policy 代码,各自在历史池全部树上零成本 replay,选最优的作为下一轮 policy。
轮次上线用的 policy产出dreaming 在哪些树上得到新 policy
Round 1初始固定策略(开10并行,refine 11次)T₁{T₁}π₂
Round 2π₂T₂{T₁, T₂}π₃
Round 3π₃T₃{T₁, T₂, T₃}π₄
Round 4π₄T₄{T₁…T₄}π₅
Round 5π₅T₅{T₁…T₅}(实验结束)

历史树越积越多 → replay 的参考越来越丰富 → policy 改进信号越来越准。

Step 4:Replay 打分公式

分项含义Lasso 例子
max score这次 replay 看到的最好结果C1 节点:2200ms(换算越小越高分)
−β₁ × N惩罚 reveal 的节点总数(代理真实执行成本)reveal 了 5 个节点,扣 5β₁ 分
+β₂ × N/轮数奖励并发:每轮平均 reveal 多个节点(并行比串行好)2 轮 reveal 了 5 个节点,平均 2.5/轮,加分

最终选出在所有历史树平均 replay 分最高的 policy,保证不比当前策略差。

三类组件各自的 prompt / 代码

组件类型固定/变化作用
Coding agent prompt(Listing 1)Prompt 文本固定不变驱动 Gemini 写候选代码
Policy developer prompt(Listing 2)Prompt 文本固定不变驱动 Gemini 修改 policy 代码
Policy(探索策略)Python 代码每轮更新决定搜索行为(选哪些节点/并发/停止)

主要实验结果

对比:Dream-RSI(每轮更新 policy)vs. Recursive Fixed Exploration(policy 固定不变),其余完全相同。

算法工程 — Lasso Regularization Path(5 轮)

方法模型总调用次数平均运行时 (ms)↓
sklearn44,180
glmnet13,768
SimpleTESGPT-OSS-120B51,2003,805
Recursive FixedGemini-3.1-Pro5503,587
Recursive FixedGemini-3.7-Flash3,2002,517
Dream-RSIGemini-3.1-Pro3172,931
Dream-RSIGemini-3.7-Flash1,8792,351

Dream-RSI 用 SimpleTES 约 1/162 的调用次数达到更好性能。同样用 Gemini-3.1-Pro,Dream-RSI 比 Fixed 少用 42% 调用(550→317),且结果更好(3587→2931ms)。

数学优化 — 三个问题(10 轮)

方法Sum–Diff ↑AutoCorr ↓Circle Pack ↑
AlphaEvolve1.45572.635862
SimpleTES (51,200 调用)1.1439751.4536752.635983
Recursive Fixed (Gemini-3.1-Pro)1.1440471.4560012.635983
Dream-RSI (<1000 调用)1.1454271.4563752.635983

GPU Kernel 工程 — KernelBench

KernelBench:绿 = 用更少 generations 达相同性能,蓝 = 相同 budget 性能更高
任务Dream-RSI 优势条件
VGG162.43× 更少 generations达到相同性能
LayerNorm1.79× 更少 generations达到相同性能
ConvDiv2.09× 更高性能相同 budget
ConvMax1.44× 更高性能相同 budget

深入分析

为什么不直接把历史总结成文字注入 prompt?

对比实验:把历史树压缩成"方向性建议"文字,注入 coding agent 的 prompt("之前试过 LARS,建议探索 active set 方向")。结果:这样反而更差,在 Fixed 和 Dream-RSI 两种范式下均不如不注入。

原因:文字建议会过度缩小搜索空间,让 agent 都往同一个方向走,失去多样性。而 replay 不告诉 agent 往哪走——它只改变 policy 决定"何时停、怎么分配并发",agent 本身仍自由探索。

Policy 演化行为(ConvDiv 案例,9 轮)

Policy 呈现自适应模式——不是固定策略,而是根据当前进展动态调整:

  1. E0→E4:性能快速提升(0.427→1.488),policy 主动减少 attempt 数(110→80→50),省算力
  2. E4→E5:性能停滞(1.488→1.499),policy 察觉瓶颈,增加 attempt 数(50→92)
  3. E6→E8:重新突破(1.770→1.898),policy 再次优化并发策略
ConvDiv 任务 9 轮 policy 演化:性能停滞时 policy 自动增加 attempt 数重新突破

局限与展望

Replay 打分公式完整形式
对 policy π^m 在历史树 T_i 上的 replay:

V_i^m = max_{v ∈ revealed_nodes} score(v)   # 最优解分数
       - β₁ × N_i^m                          # 惩罚 reveal 节点总数
       + β₂ × N_i^m / max(1, 轮数)           # 奖励并发(每轮 reveal 越多越好)

policy 总分 = 对所有历史树的平均 V_i^m

β₁、β₂ 固定,选出总分 ≥ 当前 policy 的最优候选。