RD-Agent 数据科学实验生成策略:draft / idea / merge / router 与 select 机制设计解析
【免费下载链接】RD-AgentResearch and development (R&D) is crucial for the enhancement of industrial productivity, especially in the AI era, where the core aspects of R&D are mainly focused on data and models. We are committed to automating these high-value generic R&D processes through R&D-Agent, which lets AI drive>项目地址: https://gitcode.com/GitHub_Trending/rd/RD-Agent
本文围绕 RD-Agent 数据科学场景中的实验生成(exp_gen)模块设计文档展开,系统讲解其“draft(新建解)、idea(改进假设)、merge(合并解)”三种核心优化策略与 router(元策略路由)的设计思想,以及 select 工具中 expand(扩展点选择)与 submit(最终方案挑选)两类能力。读完本文,你可以理解该模块的完整策略分层、各策略在源码中的落地位置(rdagent/scenarios/data_science/proposal/exp_gen/目录),并掌握max_trace_num、merge_hours、sota_count_window等关键配置项的默认值与作用机制。
1. 背景:数据科学场景下的“方案优化”
在 RD-Agent 的数据科学(Kaggle 式竞赛)场景中,Agent 会不断生成、执行、评估实验。所有历史实验及反馈被组织成一棵 DAG 形式的DSTrace(定义于 base.py),其中记录了:
hist:已提交的实验-反馈序列;dag_parent:每个节点(实验)的父节点索引,支持多分支并行探索;sota_exp_to_submit:当前用于提交的全球最优实验;uncommitted_experiments:已生成但尚未完成(未提交)的实验,键为 loop_id。
在此基础上,DSTrace提供了若干被所有策略共用的“查询原语”,例如:
sota_experiment()/last_successful_exp():按search_type(all或ancestors)检索最优/最近成功实验;next_incomplete_component():按COMPLETE_ORDER = ("DataLoadSpec", "FeatureEng", "Model", "Ensemble", "Workflow")(base.py)判断流水线还缺哪个组件;get_leaves():返回 DAG 的叶子节点,即当前所有可扩展的分支末端。
设计文档 exp_gen/README.md 开篇即点明了核心问题:“优化方案是一段漫长旅程(Optimization is a long journey),我们可能会在不同策略之间切换”。本文的其余部分正是围绕这一判断展开。
2. 三种优化策略与 router 元策略
设计文档将“优化解”的过程归纳为三种策略,外加一种用于在它们之间切换的元策略(router):
| 策略 | 文档定义 | 源码落地位置 |
|---|---|---|
draft | 创建一个新的(初始)方案 | draft/draft.py |
idea | 提出改进现有方案的假设 | proposal.py、idea_pool.py |
merge | 合并不同的方案 | merge.py |
router | 在策略之间路由的元策略,可有多实现 | router/init.py |
2.1 draft:从零构建首个可运行方案
“draft”策略的目标是当尚无有效基线时,先把流水线搭起来。源码中对应两个实现,均在 draft/draft.py:
DSDraftExpGen:面向“组件分解”模式。gen()按固定配置表依次为DataLoadSpec、FeatureEng、Model、Ensemble、Workflow生成任务(分别实例化DataLoaderTask、FeatureTask、ModelTask、EnsembleTask、WorkflowTask),并注入最近一次成功实验的工作区代码,使新实验站在已有代码基础上继续。它还特别处理了“连续失败”的情况:若上一次对同一组件的实现抛出异常,会把异常信息一并写入 prompt,提示模型“换一种方法,避免死循环”。DSDraftV2ExpGen:面向整条流水线的草案生成,流程为“生成领域 tag → 组装通用知识 → 基于知识生成假设 → 生成CodingSketch任务描述”,最终统一输出一个DSExperiment。
调用侧的入口是 proposal.py 中的draft_exp_in_decomposition():
def draft_exp_in_decomposition(scen: Scenario, trace: DSTrace) -> None | DSDraftExpGen: next_missing_component = trace.next_incomplete_component() if next_missing_component is not None: return DSDraftExpGen(scen=scen).gen(component=next_missing_component, trace=trace) else: return None即:只要DSTrace里还缺组件,就直接走 draft 路径补组件,而不进入“改进假设”流程。值得注意的是,proposal.py顶部保留了一条 TODO 注释(proposal.py):“DSDraftExpGen should be moved to router in the further”,这与设计文档“router 负责策略调度”的定位完全一致。
2.2 idea:提出并筛选改进假设
“idea”策略是 exp_gen 的主体。当前仓库的主实现是DSProposalV2ExpGen(proposal.py),其gen()方法(约 proposal.py#L1300-L1499)构成一个多步流水线:
- 问题识别(identify_problem):分为两类问题源:
SCENARIO_PROBLEM:从场景描述与 SOTA 描述中识别数据驱动/领域驱动的挑战(Pydantic 模型ScenarioChallenges约束“最多五个挑战、宁少而精”);FEEDBACK_PROBLEM:从历史实验反馈、代码实现审查与 trace 历史中提炼问题(TraceChallenges)。 两类问题按scen_prob_multiplier = max(0, 3 - weighted_exp_num // 4)的权重动态配比——实验积累越多,场景类问题权重越低,反馈类问题权重越高,避免 Agent 在已有充分反馈后仍泛泛地谈场景。
- 想法池采样(Step 1.5):若
DS_RD_SETTING.enable_knowledge_base开启,trace.knowledge_base.sample_ideas()会从知识库中采样历史想法注入问题集(数据结构见 idea_pool.py 的DSIdea)。 - 假设生成(hypothesis_gen):LLM 为每个问题产出一条对应假设,并自带五维自评(对齐度、影响力、新颖度、可行性、风险收益比,见
HypothesisEvaluation模型);可选的 RAG 检索(enable_research_rag)会补充社区讨论与公开代码知识。 - 假设批判与重写(可选):
enable_hypo_critique_rewrite开启时,先hypothesis_critique找缺陷,再hypothesis_rewrite产出改进版;失败时回退到原始假设,保证主流程鲁棒。 - 假设选择(Step 3):两条路径二选一:
- 加权打分:
compute_top_scores()采用固定权重(proposal.py#L859-L891):alignment 0.2、impact 0.4、novelty 0.2、feasibility 0.1、risk_reward_balance 0.1,取前五;随后select_hypothesis()对“来自想法池的假设 3 倍加权”“场景类/反馈类问题按 multiplier 线性衰减加权”,并用 MD5 哈希生成可复现的伪随机数从中挑选; - LLM 直接选择:
llm_select_hypothesis=True时改由hypothesis_select_with_llm()结合剩余时间、历史 SOTA 分数、按余弦相似度采样的历史假设(_prob_dis_torch)等上下文做决策。
- 加权打分:
- 任务生成(task_gen):将选定假设转成
CodingSketch(含current_state、modifications、structure、sketch、packages字段)并实例化为具体任务(如ModelTask、PipelineTask),同时按需追加WorkflowTask更新工作流;若任务声明了packages,还会通过get_packages()查询运行时环境并缓存。
此外还有一个更朴素的基线实现NaiveExpGen(naive.py):不区分组件,直接让 LLM 基于 SOTA 描述与完整 trace 描述生成一个PipelineTask,假设即任务描述本身,便于对照实验。
2.3 merge:合并不同分支的方案
设计文档将merge定义为“合并不同的方案”。merge.py 中提供两级实现:
MergeExpGen:最直接的合并——取 trace 前两棵子树的 SOTA(trace.get_leaves()得到叶子后分别取sota_experiment_fb),用模板渲染“当前最优解 + 其反馈 + 待合并解 + 其成功迭代过程”,生成一个PipelineTask,其假设固定为“合并两个版本的方案可兼得双方优点”(merge.py#L26-L96)。ExpGen2Hypothesis:继承DSProposalV2ExpGen的增强版。它会遍历除当前选中分支外的所有叶子,按max_sota_retrieved_num * 2 // 叶子数的配额从各分支收集 SOTA 实验(merge.py#L148-L200),再通过定制 prompt(merge.yaml)让 LLM 从多个分支的成功迭代中“提出合并假设”,而不是机械拼贴。若sota_exp_to_submit已存在,还会优先选择与其最终分数最接近的其他分支参与合并(get_exp_index)。
2.4 router:在 draft / idea / merge 之间路由
设计文档指出“router 是一个在策略间路由的元策略,可以有多实现”。当前仓库的核心 router 实现是 router/init.py 中的ParallelMultiTraceExpGen,它同时持有三个“子策略”:
self.exp_gen = DataScienceRDLoop.default_exp_gen(self.scen) # idea 策略(默认实验生成) self.draft_exp_gen = DSDraftV2ExpGen(self.scen) # draft 策略 self.merge_exp_gen = ExpGen2Hypothesis(self.scen) # merge 策略 self.trace_scheduler: TraceScheduler = import_class(DS_RD_SETTING.trace_scheduler)(...) self.planner = import_class(DS_RD_SETTING.planner)(self.scen)其async_gen()的路由决策(router/init.py#L104-L125)可以概括为:
- draft 分支:计时器未启动或剩余时间 ≥
merge_hours,且当前子 trace 还没有 SOTA 实验,且enable_draft_before_first_sota=True→ 走 draft; - merge 分支:计时器已启动、剩余时间 <
merge_hours(进入“收尾合并期”)、且叶子数 ≥ 2 → 走 merge; - 其余情况→ 走默认 idea 策略。
在进入收尾期前,router 还会用调度器选定的叶子把current_selection固化到 trace 上,并在返回前trace.register_uncommitted_exp(exp, loop.loop_idx)登记新实验。从源码结构看,这个“基于剩余时间 + SOTA 状态 + 分支数”的路由器,正是文档中“优化是长旅程、需要切换策略”的直接工程化。
3. select 工具:expand 与 submit 两种能力
文档将select定位为“供其他策略或步骤使用的能力”,并列出两种典型用法:
submit:结束优化前,必须选出一个方案提交;expand:可以选择一个点作为下一次扩展的起点。
这两点在 select/ 目录下各有一个对应文件。
3.1 expand:决定“下一次从哪个节点扩展”
select/expand.py 实现了四个CheckpointSelector,它们的返回值语义一致:(-1,)表示继续在最新试验上扩展;空元组trace.NEW_ROOT表示开一条新子 trace;(idx,)表示回跳到第 idx 个历史节点扩展:
LatestCKPSelector:始终返回(-1,),即线性迭代,是最简单的默认选择器;LimitTimeCKPSelector:按“总时长扣除merge_hours后除以max_trace_num”计算每条子 trace 的时间预算,超时就跳到新子 trace(达到max_trace_num上限则继续当前 trace);SOTAJumpCKPSelector:以sota_count_window为窗口统计最近试验中feedback.decision=True的次数,若低于sota_count_threshold判定“当前分支陷入瓶颈”,跳到新子 trace;BackJumpCKPSelector:在同样触发条件下,以 50% 概率开新 trace,50% 概率回跳到“倒数第二个 SOTA”节点(认为最新的 SOTA 未必是好的扩展点)。
与之配合的是 trace_scheduler.py 中的并发调度器(RoundRobinScheduler、SOTABasedScheduler、MCTSScheduler等),它们解决的是并行多 trace 场景下“下一个空闲执行槽该扩哪条分支”的问题;BaseScheduler.next()采用“先提交 pending 选择、再原子选择父节点,选不到就asyncio.sleep等待”的写法,保证多循环并发下的选择一致性。
3.2 submit:从整棵 trace 中挑出唯一提交方案
select/submit.py 实现了四个SOTAexpSelector,对应“结束前选出一个方案”这一能力:
GlobalSOTASelector:直接取全 trace 的sota_experiment(search_type="all"),即全局分数最优实验;AutoSOTAexpSelector:先从各分支叶子按配额收集 SOTA 候选(受max_sota_retrieved_num约束、按分数排序去重),再让 LLM 阅读每个候选的描述与最终分数,返回selected_SOTA_idx;LLM 输出非法时回退到“最新的 SOTA 实验”。候选数超过上下文时通过build_messages_and_calculate_token()提前截断;BestValidSelector:纯分数排序的 Top-N 选择器,支持use_decision(决策为正的实验优先)与each_trace(按分支各取 Top-k)两种模式;ValidationSelector:最重的“元选择器”。它在/tmp/mock/<competition>下用 LLM 生成(或复用已验证的)data.py(从原始数据采样、固定随机种子)与grade.py(本地评分),然后对每个候选实验多进程重跑main.py并按统一口径打分,最终按验证集分数排序选出最佳——从源码结构看,这是为缓解“离线分数与线上排名不一致”而设计的重新验证机制。
文件底部还提供了select_on_existing_trace()(fire.Fire入口)离线评估脚本:可对已有 trace 的 pickle 或日志目录回放上述四种选择器,统计“命中奖牌 loop”的命中率(check_hit),输出result_<selector>.json汇总。这为选择器策略的调优提供了可复现的评测手段。
4. 建议目录结构与源码对照
设计文档给出的目标结构如下(原文档“Suggest folder structure”):
- router/ - idea/ - samll_step(or refine?).py - normal.py - draft/ - merge/ - select/ - expand.py - submit.py对照当前仓库exp_gen/的实际布局,可以整理出如下映射:
| 文档建议 | 实际仓库状态 |
|---|---|
router/ | 已落地:router/init.py(ParallelMultiTraceExpGen路由器) |
idea/samll_step(or refine?).py、idea/normal.py | 从源码结构看,尚无独立的idea/子目录;idea 策略由 proposal.py(DSProposalV1ExpGen/DSProposalV2ExpGen)承载,“normal”(整轮改进)与“small step”(组件级微调)的区分体现在coder_on_whole_pipeline开关与组件分解模式上,文档中的设想尚未拆分为独立文件 |
draft/ | 已落地:draft/draft.py + draft/prompts_draft.yaml |
merge/ | 以单文件 merge.py + merge.yaml 形式落地,未拆为目录 |
select/expand.py | 已落地:select/expand.py |
select/submit.py | 已落地:select/submit.py |
目录之外,模块还包含若干支撑文件:base.py(DSTrace/DSHypothesis)、planner/init.py(实验计划)、trace_scheduler.py(并行调度)、idea_pool.py、diversity_strategy.py(跨 trace 多样性注入)、naive.py(朴素基线),以及提示词文件 prompts.yaml、prompts_v2.yaml、naive.yaml。总体上看,文档建议的“策略目录化”已实现约一半(router / draft / select 完全对应,idea 与 merge 待进一步拆分)。
5. planner:为每次实验生成附加计划
planner/init.py 定义了DSExperimentPlan与DSExpPlannerHandCraft。计划默认包含三个键:
exp_gen.draft:是否需要生成初始草案;exp_gen.suggest_model_architecture:是否建议新的模型架构;exp_gen.suggest_model_ensemble:是否建议模型集成(当前按时间比例触发的分支被注释掉,仅保留键)。
DSExpPlannerHandCraft.plan()的规则(planner/init.py#L27-L45):trace 中还没有 SOTA 时置draft=True;已有 SOTA 且剩余时间比例大于model_architecture_suggestion_time_percent时开启“模型架构建议”。路由器在enable_planner=True时才会调用它(否则使用空计划DSExperimentPlan()),计划随后通过exp_gen.gen(trace, plan=ds_plan)传入各策略,供 prompt 消费。
6. 关键配置项一览(DS_RD_SETTING)
上述策略的行为主要由 rdagent/app/data_science/conf.py 中的DS_RD_SETTING控制,与本文主题强相关的配置项及默认值如下(以当前仓库源码为准):
| 配置项 | 默认值 | 作用 |
|---|---|---|
coder_on_whole_pipeline | True | 组件分解 vs 整条流水线生成;True时任务统一走PipelineTask,否则按组件生成,且 draft 补组件逻辑被跳过 |
spec_enabled | True | 是否读取工作区内spec/*.md规格文件作为任务规格,否则用内置component_spec模板 |
max_trace_num | 1 | 并行子 trace 上限;expand 选择器与 trace 调度器均以此为界 |
sota_count_window | 5 | SOTAJump/BackJump 选择器统计 SOTA 密度的窗口长度 |
sota_count_threshold | 1 | 窗口内 SOTA 次数低于该值即触发“跳新 trace / 回跳” |
merge_hours | 0 | 收尾合并期时长;剩余时间低于该值时 router 切换到 merge 策略,expand 选择器也据此分配子 trace 时间预算 |
max_sota_retrieved_num | 10 | submit/merge 侧收集 SOTA 候选的总配额 |
enable_draft_before_first_sota | False | 是否在首个 SOTA 出现前允许走 draft 策略 |
enable_planner | False | 是否启用DSExpPlannerHandCraft生成实验计划 |
model_architecture_suggestion_time_percent | 0.75 | 剩余时间比例高于该值时开启模型架构建议 |
llm_select_hypothesis | False | 假设选择走 LLM 决策还是加权打分 |
trace_scheduler/planner/scheduler_temperature | 见 conf.py | 以字符串类路径方式被import_class动态加载,便于切换 RoundRobin / SOTA / MCTS 等调度实现 |
需要说明的适用前提:merge_hours、sota_count_window等参数依赖全局计时器RD_Agent_TIMER_wrapper.timer已启动(即任务设定了总时长限制);在max_trace_num=1的默认单 trace 配置下,多分支 expand/submit 逻辑不会触发,模块退化为“线性迭代 + 全局最优提交”。
7. 小结
exp_gen模块的设计文档虽然篇幅不长,但它给出的“draft / idea / merge 三策略 + router 元路由 + select(expand / submit)工具”分层,在源码中得到了相当完整的对应:
- draft负责冷启动补组件(
DSDraftExpGen/DSDraftV2ExpGen); - idea负责核心假设工程(问题识别 → 想法池 → 生成 → 批判重写 → 加权或 LLM 选择 →
CodingSketch任务); - merge负责收尾期跨分支融合(
MergeExpGen/ExpGen2Hypothesis); - router(
ParallelMultiTraceExpGen)按“剩余时间 / SOTA 状态 / 分支数”把执行流分发给上述策略,并借助 trace 调度器实现并行多分支; - select把“从哪扩展”(四个 CheckpointSelector + 调度器)与“最终交哪个”(四个 SOTAexpSelector + 离线评估脚本)拆成两个可独立替换的能力。
如果你想继续深入,建议按如下顺序阅读:先看 base.py 理解DSTrace的数据模型,再看 router/init.py 的路由决策,然后分别对照 select/expand.py 与 select/submit.py 的策略实现,最后用 conf.py 中的默认值核对各开关的实际生效条件。
【免费下载链接】RD-AgentResearch and development (R&D) is crucial for the enhancement of industrial productivity, especially in the AI era, where the core aspects of R&D are mainly focused on data and models. We are committed to automating these high-value generic R&D processes through R&D-Agent, which lets AI drive>项目地址: https://gitcode.com/GitHub_Trending/rd/RD-Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考