在这里插入图片描述

0. 前言

  今年天池AFAC的赛题三是Auto Research问题,要求实现一个Agent,让这个Agent自动的写代码、做实验、根据准确率反馈自己调整,包括2个数据集,对于每个数据集任务,有在1张4090显卡上总共2个小时运行的时间跑出测试集结果提交。

两个具体子任务:

(1)产品分类任务:以图节点分类的形式进行评测;

(2)产品推荐任务:在用户-产品的二部图上的图链接预测问题。

  真的很想吐槽组委会…居然选用的是公开数据集,虽然B榜做了置换等操作,还是有人用codex把测试集标签完整复原了测试集ACC=100%,题目也很有歧义,不同人看理解不一样,是只允许LLM coding,还是可以直接走AutoGL这样的贝叶斯优化调参,最后阶段才看明白它的稀疏的意思是线上测试集的acc也可以当成反馈来过拟合线上测试集…
  或许现在是AICoding的时代了,看群里有高中生用CodeX冲到了靠前的位置,不能再古法编程了,最后时刻发现bug也来不及提升了。
  代码仓库:AFAC_AutoResearch

1. Auto Research

1.1 Auto Research定义

  • 以顶层研究目标作为输入,在低人工干预下自主形成闭环:猜想提出 → 方案落地 → 实验执行 → 结果解读 → 迭代探索。
  • 基础形态:自主修改代码、搭建实验、评估结论、调整探索方向
  • 理想高阶形态:除完成科研探索之外,还能够基于海量历史实验轨迹反思自身工作缺陷,自动优化智能体提示词、执行流程、决策策略,实现系统自进化。

1.2 Auto Research 演进历程

完整演进链路:从封闭空间参数搜索 → 半开放模板搜索 → LLM 驱动开放科研 → 双层闭环自进化系统

  • 网格搜索:【纯调参】无先验暴力枚举,全量遍历离散参数空间寻找最优解;不复用历史信息,高维场景遭遇维度灾难。
  • TPE / Optuna:【纯调参】放弃盲目穷举,利用历史实验样本构建概率代理模型,平衡探索与利用,指导下一轮参数采样;参数空间必须人工预先定义固定。
  • NAS(神经架构搜索):【模板内架构搜索 + 调参】在人工给定算子库内组合网络结构,同步优化超参;代表论文《Neural Architecture Search with Reinforcement Learning》。自动化模型剪枝(《HYDRA: Pruning Adversarially Robust Neural Networks》)也可以属于NAS范畴,在固定网络内寻找有效子网络。
  • AutoGL:【领域专用 AutoML,图 NAS + 调参】面向图学习场景,自动挑选图编码器、聚合方式、超参数;论文 AutoGL: An AutoML Framework for Automated Graph Representation Learning。
  • Sortify:【LLM 引导封闭空间调参】LLM 分析实验指标,输出高层优化约束与参数范围,搭配 TPE 贝叶斯优化完成权重搜索;排序策略骨架固定,无法修改业务代码、新增特征、创造全新策略结构。论文《Let the Agent Steer:Closed-Loop Ranking Optimization via Influence Exchange》。
  • Data Agent:例如MetaGPT、QwenAgent等,原始的版本能完成一轮轮实验迭代,不能自动修改自身角色 Prompt、优化整套 Agent 工作范式。
  • Arbor:【LLM 驱动开放空间自主科研】依靠假设树持久存储全部猜想、代码产物、成功 / 失败实验结论;Coordinator 推进新方向、修剪无效分枝、合并分枝,Executor 执行代码修改与实验;但缺少对智能体自身的优化。论文《Toward Generalist Autonomous Research via Hypothesis-Tree Refinement》。
  • AgentX:【LLM 自主科研 + 元自进化双层闭环】快手声称用在了线上推荐,人工只在idea生成阶段参与,其余全部由LLM完成,自动化的coding+AB实验+线上重排等规则+根据AB实验结果coding。LLM还会根据线上结果,来反馈修改当前各个子模块的prompt,完成系统自进化。论文《AgentX: Towards Agent-Driven Self-Iteration of Industrial Recommender Systems》。

2. Auto Research——Arbor

  Arbor 是一个自主科研智能体,它把一个长周期目标转化为持续累积的搜索过程。 给它一个基准 (benchmark)和一个目标,它会提出假设、修改代码、运行真实实验、从结果中学习,并保留那些在 留出(held-out)数据上确实站得住脚的改进作为当前最佳,不断迭代。
  不同于“一次性尝试、过后即弃”的做法,Arbor 会生长出一棵假设树:每个想法都是一根分支—— 失败则剪枝,成功则保留——而洞见会沿树反向传播,让后续想法从更可靠的起点出发。
在这里插入图片描述
Arbor系统包含2个独立角色
在这里插入图片描述

  • Coordinator(协调者): 维护想法树,通过 arbor cycle 驱动搜索,并派发实验。
  • Executor(执行者):给定一个想法,它实现代码改动,在隔离的 git worktree 中运行实验,并汇报证据。

  这种角色分工把策略(哪些假设值得验证)与执行(把一个假设变为现实)分开,让每个智能体各司其职。主体循环为:

Coordinator 以一个不断重复的循环驱动搜索。从概念上:

1. 构思(Ideate)。 在一个有潜力的节点下起草候选假设——每个都是真正的机制,而非参数微调。 (由一个 Skill 负责守住质量底线。)
2. 选择(Select)。 挑出下一个最有潜力的想法去验证,在利用当前最优路线与探索新方向之间取得平衡。
3. 实验(Experiment)。 派一个 Executor 去隔离地实现并运行这个想法。
4. 评估(Evaluate)。 读取产生的证据,并对照 dev 信号打分。
5. 反向传播(Backpropagate)。 抽象出学到的东西,把洞见上推到树中,让未来的想法继承它。
6. 合并或剪枝(Merge or prune)。 保留跨过留出闸门的改动;剪掉死分支。
循环持续进行,直到满足停止条件——预算耗尽、搜索收敛,或达成目标。

树的结构——想法树(the Idea Tree)
  想法树是 Arbor 的持久记忆。每个节点是一个假设,附带其状态、它产生的证据以及从中提炼的洞见:

root: maximize held-out accuracy
├── stronger augmentation pipeline        [merged]   +1.4
│   ├── mixup + cutmix                     [pruned]   overfits dev
│   └── test-time augmentation             [merged]   +0.6
└── swap backbone to ConvNeXt             [running]

  由于树是共享状态,智能体从不从聊天记录里重建上下文。Coordinator 读取树来决定下一步;Executor 只收到一份简洁、相关的摘要,而不是把先前的每条消息全部传给它。正是这一点,让 Arbor 能在一项长期 研究中持续推进,而不被自身历史淹没。
  经验的反向传播:每次实验后,由 LLM 抽象出它为何成功或失败——“折叠构造中的数据泄漏”、“增益来自校准而非新层” ——并把那条洞见写回树的上层。同辈与后代想法于是从该知识出发,而不必重新发现它。

效果超过CodeX和CC
在这里插入图片描述

局限性
  只有内层coding循环,不存在外层元优化循环,Arbor会学习怎么解决当前科研任务,但是不会持续优化自己这套Agent系统本身的提示词、工作流程、执行规则。 没有类似AgentX的机制反思Agent自身行为缺陷。

3. Auto Research——AgentX

在这里插入图片描述
  快手的AgentX是近乎完全的Auto Research,应用于线上,人只在头脑风暴阶段参与,提升推荐系统迭代的效率,之后人只负责优化Agent和基础模型,让agent进行scaling。Agent的loop流程包括:头脑风暴(人说出需求,产生和筛选下一步迭代的idea)–>代码生成(生成线上规则,或开发训练模型)–>线上AB实验–>结果观测评估–>迭代优化生成的代码 + Agent系统的prompt自进化,包含2层自动化:

第一层:内层科研执行闭环(对应Arbor的能力域)
	- Brainstorm Agent:自由生成全新方案提案
	- Validation & Handoff:提案准入校验,生成标准化开发契约
	- Developing Agent: policy → code↔verify→ exec → final review 多路并发专家、代码迭代自修复,支持线上策略/离线模型;
	- Evaluation Agent 评估AB实验,收集完整执行轨迹。
第二层:外层元进化闭环 —— SGPO(Semantic-Gradient Prompt Optimization)
	采集海量完整执行轨迹 → Judge分析失败案例 → 产出语义梯度(自然语言诊断) → 自动更新整套Agent的Prompt、约束、工作规范。

3.1 Brainstorm Agent

在这里插入图片描述
  Brainstorm Agent要完成和用户的交互,输入包括人的模糊需求、历史实验经验,以及数据分析结果(Data Agent)、论文调研信息(Web Agent),结合可选的人工review和检查等,根据人输入的需求,产生出若干idea,最后筛选出一批最合理的idea出来,缩小后续筛选空间,告诉后续代码生成的agent要具体执行的代码生成任务。
  这里的核心是前置的这些知识库和Agent的构建,例如System KB要负责把不符合线上pipeline和代码接口逻辑的idea拒绝掉,Model Research Agent要负责调研论文,整合成结构化的知识存储。
  在获得一批idea后下发给编码环节之前,由一个validator来验证idea。校验器自动核查一整套条件:是否和顶层核心业务目标对齐、业务语义逻辑无误、满足所有外部约束、模型打分逻辑合规、在代码仓库中具备可落地性、和历史已尝试方案重复度(避免重复造轮子)、A/B 实验参数合法、方案成熟度统一。

3.2 Developing Agent

Developing Agent负责将审批通过的idea,转化为可核验的代码产物,包括两条推荐系统的并行工作流:

  • Online strategy track 产出物 = 可合入主干的生产代码变更
    面向新增特征、排序策略改动,安全承接真实线上流量,杜绝悄无声息的稳定性故障。
  • Offline model track 产出物 = 一套完整训练实验
    面向模型结构、网络架构创新提案,产出足够可信的实验结论,沉淀进入长期模型探索知识库。

重点硬核规则: 一次实验结论想要被认定「可信」,四条条件必须同时全部满足

  • 最终代码实现,和提案声明的因果逻辑、预期观测现象保持一致【避免偶然随机提升】
  • 独立专家组 Agent 评审,形成绝对多数共识
  • 指标从原始日志确定性解析,禁止大模型口头解读、脑补指标,规避 LLM 幻觉
  • 上报的指标提升(例如 AUC 上涨)必须附带可验证的因果链路归因。单纯数字上涨不算有效成果;找不到归因的指标提升,视作风险警报,不能存入知识库

3.2.1 模型代码开发

  无论是online还是offline都可以用并发的方式实现一个LOOP提升效率,以Offline model track为例,每次开发包括下面的步骤:
在这里插入图片描述

- policy:依据idea进行代码生成,policy里面需要包含具体的实现和要观测的指标,可观测变量必须命名到可以直接嵌入 tf.print / tf.summary 的程度,并且明确定义什么是“健康”vs“病态”行为(例如:“step 1000 后 gate activation > 0.5 = 正常;≈ 0 = 坍塌”)。
- code与verify交互:在指定文件边界内实现 policy,Verify 模块检查代码实现是否和policy说的一样,可观测变量是否也都在代码里面,这个交互最多三次。
- 专家隔离盲审:专家互相隔离,避免观点传染;投票由程序硬规则执行
- exec:运行代码
- final_review:核心的验证,验证实验是否真正有效

3.2.2 模型效果评价 Final review

  最终评审不只是简单看指标涨没涨;逐条核验 Policy 预先声明的整条因果链路。只有整条因果链条全部被观测证据证实,提升才被采信;AI 不能只看结果指标(AUC),必须验证“因果链条”上的每一环是否真的发生了。如果指标涨了但因果链断了,系统会拒绝记录这次成功,将其视为“刹车信号”。
  Final review 汇总三类信息:指标数值、专家投票意见、训练日志,产出实验结论,针对 Policy 里提前写好的完整因果链,一环一环单独判定,结果三选一:
(1)verified 得到观测证实
(2) broken 和观测矛盾
(3)unclear 现有证据无法确认
之后由 Python 程序做确定性聚合规则:
(1)整条链条全部 verified → 归因状态 = CLEAR
(2)只要存在任意 broken /unclear → 归因状态 = UNCLEAR

文中有一个很具体的例子,避免随机巧合等被复用沉淀为有效经验:

Round 1:复现论文原始门控 x · tanh(Vₒx)
	代码实现无误,离线 AUC 小幅上涨 +0.0003(如果没有因果归因校验,系统会直接标记为成功方案)。
	Policy 提前定义的观测指标:门控激活值全程趋近于 0。
	复盘根因:Glorot 初始化导致Vₒx≈0,tanh(0)0。门控持续输出 0、梯度直接被阻断,机制完全没有正常工作。
	归因状态:UNCLEAR。本次指标提升不予记录、不采纳。
	现象:分数涨了;机制和设想不一致 → 纯粹数值巧合,不可复用,不能沉淀为有效经验。
	
Round 2:一行代码改造 x · (1+tanh(Vₒx))
	当Vₒ≈0时表达式退化为恒等映射,保证梯度通畅;
	最终 ΔAUC=+0.0022,整条因果链路全部被观测验证,状态 CLEAR,成果正式收录。

3.4 Evaluation Agent

Evaluation Agent 负责合上 AgentX 整条工业闭环:接收线上 A/B 实验结果,不只是简单输出指标,把嘈杂、滞后的真实流量数据清洗、加工成可靠奖励信号;同时产出可复用约束,指导后续所有智能体迭代。Evaluation Agent 裁决本次代码变更三种去向:(1) 正式保留上线 (2)回滚代码(3)作为负面经验回流迭代
在这里插入图片描述

3.5 AgentX Harness Evolution

AgentX还包含了一个prompt自进化的模块,读取历史轨迹判断迭代方向,然后修改了prompt之后,和修改之前的,对同一份数据进行推理(不涉及模型训练的稀疏反馈,仅用LLM对这2份prompt输出的结果 h h h h ′ h' h进行打分,LLM-as-judge的方式),如果打分有提升,就保留修改后的prompt。在自进化时,每次只选一个模块的来迭代,其他的固定住。
在这里插入图片描述
论文中提到用这种方式,对例如Model developing agent进行自进化,可以有效提升输出提升:
在这里插入图片描述

4. AFAC 2026赛题三实践

  赛题三包括图结点分类和推荐系统2个数据集,图结点分类评估测试集ACC,推荐系统评估测试集的NDCG@10。
  难点和挑战主要有:(1)仅2个小时运行时间,禁止并行实验,需要尽可能多让GPU训模型(2)要让实验朝着正向方向迭代收敛,节约每次实验机会
  我的实现整体上包含3个模块,数据分析Agent、代码生成Agent和冒烟测试Agent:
在这里插入图片描述

每个模块负责的任务包括:

  • 数据分析Agent:负责根据任务+数据集描述和当前训练情况,针对性生成数据探查代码,探查数据集分布,给出数据层面的模型建议
  • 代码生成Agent:负责根据数据集分布、历史训练经验,总结本次最有希望的迭代方向,然后根据该方向生成代码。如果输出的代码被冒烟测试驳回,根据报错信息对代码进行修改,报错次数超过阈值则重新生成。
  • 冒烟测试Agent:负责将epoch数修改为10,训练样本数修改为1000,运行冒烟测试,判断训练是否正常、是否报错,通过则正式运行,不通过则把结果返回给代码生成Agent:
  • 预测结果后处理:将每次运行的模型结果进行集成和后处理,例如对图结点分类任务,执行GES进行N模型集成、运行LP或者C&S等图分类后处理算法,生成最终提交文件。

4.1 实现细节

4.1.1 迭代方向生成

  这个对于结果的影响最大,这个写的好,在实验次数少的情况下模型的冷启动效果就好。可以约束好在实验次数已经较多的情况下小幅度迭代、逐渐收敛。

train_history_analyse_template = """你是图神经网络模型训练与调优专家,专注图节点分类,目标持续拉高验证集ACC。

### 当前状态信息
数据集探查信息:
{data_inspect_result}

所有训练尝试:
{history_train_summary}

### 探索方向和思路
1. **优先排查特征预处理瓶颈**:原始稀疏高维特征易造成注意力失效、收敛困难。ACC低于0.6优先校验标准化+PCA/SVD降维;若预处理后效果下跌,可尝试关闭标准化、减弱特征正则。
2. **分层管控正则强度(区分特征/结构/路径正则)**
    特征:普通Dropout;图结构:DropEdge、DropNode;深度正则:DropPath。
    中小稠密数据集禁止三类正则高强度叠加,极易欠拟合压制注意力表达;仅保留单一种类轻量正则,推理禁用拓扑扰动TTA,仅可使用Dropout TTA。
3. **架构迭代优先级**:实验不足5轮优先横向对比模型(GATv2/MixHop/JKNet);5轮以上聚焦细粒度结构与超参调优。
4. **图结构校验**:核对边无向化、自环配置、邻接归一化(对称归一/仅自环两种方案对照),拓扑是本数据集核心判别特征,不可随意扰动。
5. **模型容量与训练轮次匹配**:没有严重过拟合时可以提升层数、隐藏维度、注意力头数;epoch上限450,搭配早停。
6. **归一化、深度架构专项调优**
    归一化层:对比BatchNorm(BN)、LayerNorm(LN)效果,深层GATv2优先LN,浅层可尝试BN;
    深度连接:启用残差块缓解梯度消失,可堆叠3~4层深层GAT,搭配JK跳跃连接(Jumping Knowledge)聚合各层特征;
    深度正则:DropPath随机丢弃层路径做轻量化正则,需调低丢弃概率,DropPath丢弃率0.1以内,不和高强度DropEdge叠加。
7. **损失与优化器超参**:可选CE/标签平滑,严控学习率与权重衰减组合,高lr+大weight_decay易训练震荡;如果类别极度不平衡,逆频加权后导致验证集ACC下滑,优先保持无类别加权
8. **连续多轮精度下滑策略**:回溯历史最优实验,对比预处理、归一化、JK、DropPath、正则、lr等差异,回退有效配置再小幅迭代。

### 任务要求
1. 定位验证集ACC瓶颈,覆盖特征预处理、正则组合、归一化(BN/LN)、JK跳跃、DropPath正则、模型容量、图结构、超参全维度;
2. 输出落地调优方案,明确架构、层数、归一化选择、JK开关、DropPath丢弃率、预处理、正则、超参细节;
3. 全文400字以内,纯文本简洁输出。

请开始分析并直接输出文本:
"""

4.1.2 代码生成

  整体异步并行的方式,代码生成的输入包括当前全局最佳结果。在每次实验,首次生成时进行全量生成,如果没通过冒烟测试,则基于编辑的方式快速修改。全量生成,以图结点分类为例:

train_code_gen_template = """
你需要完成的任务为:
{task_description}

数据集信息为:
{dataset_description}

为此需要帮我完成一份完整的划分训练集验证集、构建模型、使用训练集训练模型,使用验证集验证ACC、对测试集进行预测并输出B1.csv到文件夹为{model_save_dir}下的代码

特别的,训练集/验证集划分方式为:
1. 加载 B1.npz 中预定义的 train_idx 和 test_idx 作为固定的训练集与测试集节点索引;
2. 验证集的构建方式:从 train_idx 中随机抽取 20% 的节点作为验证集(val_idx),剩余 80% 作为实际训练集(actual_train_idx)。抽取时必须使用种子 666 确保可复现;

可以参考的逻辑为:
def _reproduce_val_split(train_idx: np.ndarray) -> tuple:
    rng = np.random.RandomState(666)
    shuffled_idx = rng.permutation(train_idx)
    n_actual_train = math.ceil(len(shuffled_idx) * 0.8)
    actual_train_idx = shuffled_idx[:n_actual_train]
    val_idx = shuffled_idx[n_actual_train:]
    return actual_train_idx, val_idx

测试集输出文件命名为B1.csv,必须包含表头,且仅允许包含两列,例如:
test_idx,label
18,3
19,7
其中:test_idx:测试节点编号,必须与数据集保持一致。label:选手预测的类别编号,必须为合法整数类别编号。

为了便于调试,你可以比如每隔10个epoch打印一次验证集ACC,便于后续排查是否过拟合、欠拟合等等。
训练过程中,你需要print验证集上准确率,并且在每次新出现效果最好的模型时,立即保存效果最好的模型(该模型命名为best_model.pth,模型保存路径为{model_save_dir}),然后使用这个模型完成测试集推理,输出B1.csv到文件夹为{model_save_dir}下(如果这个文件存在,直接覆盖)。
同时每次新出现效果最好的模型时,还必须输出测试集每个类别的类别的softmax分数候选文件 B1-softmax.csv 和验证集候选文件 validation-softmax.csv以便于后续集成使用。

validation-softmax.csv和B1-softmax.csv格式要求:
- 两个文件表头统一为:test_idx,class_0,class_1,class_2,class_3,class_4,class_5,class_6,class_7
- validation-softmax.csv 的 test_idx 列存放验证集节点编号,B1-softmax.csv 的 test_idx 列存放测试集节点编号
- 每行包括idx以及对应的8个类别的softmax分数,用英文逗号连接,test_idx 必须为整数类型,softmax分数必须是浮点数,保留4位小数,类别列按编号升序排列

【索引对齐铁律 · 必须严格遵守】
保存CSV时,必须直接用 val_idx / test_idx 整数数组索引模型输出,保证行顺序与ID顺序一一对应;
禁止用布尔掩码(val_mask/test_mask)提取输出后再拼接打乱的索引数组,否则ID与概率会错位。
参考写法(核心只看这两行):
    val_probs = F.softmax(out[val_idx], dim=1).cpu().numpy()    # 验证集:用 val_idx 直接取
    test_probs = F.softmax(out[test_idx], dim=1).cpu().numpy()  # 测试集:用 test_idx 直接取
训练时计算ACC可以继续使用布尔掩码,不影响准确率。


【测试集TTA增强 · 默认启用】
1. 仅测试集最终输出启用TTA(测试时增强),基于Dropout多次推理取平均,免费提升泛化性能;验证集始终使用纯eval模式,保证与训练过程口径一致。
2. 实现方式:训练全部结束、加载最优模型后,开启模型train模式启用Dropout,重复推理40-50次,对softmax概率取平均后生成最终测试集文件。
3. validation-softmax.csv 不做TTA,保持与训练验证ACC同口径,用于后续集成评估。

为了提升训练效率和效果,你只能使用一种模型(不考虑多模型融合),并且注意准确率和效率(显存20GB以内)。你可以使用torch_geometric库构建模型(例如GAT、GATv2、MixHop和各种变体等)。
在模型准确率持续上升的情况下,即使过拟合程度有略微提升,也可以尝试加大一些epoch和early stopping的patience。如果都还没有发生过拟合,甚至比较欠拟合,更可以大胆的上调epoch和模型复杂度。初期epoch可以在100-200左右,后期可以epoch上调到300-500。patience可以设置大一些,比如30-50。

之前你做了一些尝试,有前景的方向为:
{optimize_direction}

当前代码【重点参考格式】:
{reference_code}

上一轮报错信息(如果有):
{last_error}

# Constraint
要完成这个任务,你需要分析瓶颈和提升方向,完成对当前代码的调整和优化,直接输出可执行的python代码,不要解释。尤其注意数据集导入和最后预测的代码的逻辑准确性,如果之前出错,注意不要重犯错误。
请开始输出:
"""

4.1.3 冒烟测试

  冒烟测试负责短时间快速运行,可代码是否报错,并用脚本验证运行结果是否包含所需的输出文件、输出文件格式是否准确。冒烟测试前,需要将代码基于编辑的方式进行修改,修改epoch和训练样本数:

smoking_test_edit_template = """
# Role
你是一个代码冒烟测试专家。你的任务是修改给定的代码片段,使其能以最小代价快速跑通全流程,用于验证代码逻辑和输出格式是否正确。

# Goal
请对以下代码进行冒烟测试级别的修改,具体目标:
1. 将训练轮数 epochs 修改为 10,确保能快速跑完)
2. 在划分完训练集和验证集之后的代码里面,将训练集规模缩小(使训练集仅使用 1000 条数据,注意有可能前半部分和后半代码都要修改,防止取数报索引越界和device不一致错误;禁止缩小测试集和验证集大小,禁止缩小会输出的csv这部分大小,后续有校验)
3. 保持其他核心逻辑(模型结构、损失函数、输出格式、测试集规模和顺序等)完全不变
4. 确保修改后代码语法正确,且能正常产出验证集 loss 和完整的测试集输出文件
5. 尤其关注如果你修改或者引入了index变量,要注意变量tensor的device,确保不会出现cpu和gpu混用导致的报错

# 特别注意
* 千万不要在训练集/验证集划分结束前动训练集大小,否则划分出的验证集将会受到影响,会导致验证报错

# Constraint
修改将通过 llm_diff_edit_tool 执行,该工具要求:
- 每次替换的 original_str 必须在文件中【唯一存在】
- 支持多对替换,按顺序依次执行
- original_str 必须确保唯一,避免匹配到多处相同代码,必须保证和要替换的原文一模一样否则匹配不上。如果是修改某些参数,往往仅当前行局部即可锁定唯一,不要包含过多上下文导致上下文过长
- 对于original_str,建议小型的修改例如就参数的修改,不要把参数前面的空格或者tab内容包含进去,防止匹配不上字符串;长段的修改则需要严格保证特殊字符(缩进换行等)完全一样防止匹配不上,原始代码文件是4个空格缩进而非tab的

# Output Format
你必须且只能输出一个 JSON 数组,每个元素是一对替换,可以有多对替换,格式如下:
[
  {{
    "original_str": "要替换的原始代码字符串(确保唯一)",
    "new_str": "替换后的新代码字符串"
  }}
]

⚠️ 禁止输出任何解释、分析、markdown 代码块标记或其他文字,仅输出纯 JSON 数组。

# Last round error
下面是上次编辑时的报错信息(如果有),清注意避免:
{last_edit_error}

# Code to Edit
{code_piece}
"""


def llm_diff_edit_tool(file_path, original_str, new_str, save_path)->bool: 
    try:
        # 判断文件是否存在
        if not os.path.exists(file_path):
            dual_print(f"⚠️ 文件不存在: {file_path}")
            return False,f"⚠️ 文件不存在: {file_path}"

        # 读取文件内容
        with open(file_path, "r", encoding="utf-8") as f:
            content = f.read()

        # 判断原始字符串是否唯一存在
        if content.count(original_str) == 0:
            dual_print(f"⚠️ 原始字符串不存在: {original_str}")
            return False,f"⚠️ 原始字符串不存在: {original_str}"
        elif content.count(original_str) != 1:
            dual_print(f"⚠️ 原始字符串不唯一: {original_str}")
            return False,f"⚠️ 原始字符串不唯一存在: {original_str}"

        # 替换字符串
        content = content.replace(original_str, new_str)

        # 保存文件
        with open(save_path, "w", encoding="utf-8") as f:
            f.write(content)

        dual_print(f"✅ 替换成功: {original_str} -> {new_str}")
        return True,""
    except Exception as e:
        dual_print(f"❌ 替换失败: {e}")
        return False,"❌ 替换失败: {e}"


def apply_code_edits(file_path: str, edits: List[Dict[str, str]], save_path: str = None) -> bool:
    """
    串行执行多对 diff 编辑,每对编辑基于上一次编辑后的文件内容。
    """
    if save_path is None:
        save_path = file_path

    current_file = file_path
    for i, edit in enumerate(edits):
        # 首尾去掉空白,后面看看是否需要
        original = edit["original_str"]#.strip()
        new = edit["new_str"]#

        dual_print(f"🔧 执行第 {i+1}/{len(edits)} 对替换...")
        success,error_info = llm_diff_edit_tool(current_file, original, new, save_path)

        if not success:
            dual_print(f"❌ 第 {i+1} 对替换失败,中止后续编辑")
            return False,f"❌ 第 {i+1} 对替换失败,中止后续编辑。"+error_info

        # 后续替换基于已保存的文件
        current_file = save_path

    dual_print(f"✅ 全部 {len(edits)} 对替换执行完毕")
    return True,""

  同时,冒烟测试还要检查训练过程是否有loss异常的情况,但是注意,冒烟测试样本很少,不能对acc/ndcg要求太高:

smoking_test_judge_template = """
# Role
你是一个冒烟测试结果判定器。请根据提供的训练日志,严格按照以下规则判断代码是否跑通且逻辑基本正常。

# Judgment Rules
满足以下【全部】条件则判定为"正常",否则判定为"异常":
1. 日志中无任何 Error / Exception / Traceback 报错
2. Loss 值全程不为 NaN / Inf
3. 验证集 ACC > 0.1(冒烟测试数据量极小,仅为排除模型完全失效)
4. 训练流程完整执行到了结束或 EarlyStopping,未中途崩溃

# Output Constraint
⚠️ 你必须且只能输出两个字:"正常" 或 "异常"
⚠️ 禁止输出任何分析过程、标点符号、换行符或其他文字

# Training Log
{train_log}
"""

4.2 结果观察

  • 好的冷启动很重要,一开始acc很低的后面也很难震荡上去
  • 虽然3个code worker异步生成,中间还是有不少时间GPU在等待任务
  • 在图结点分类任务上,除了初期的抖动,平均的acc是一直在稳步提升的
    在这里插入图片描述
  • B榜图结点分类的验证集acc(包括带后处理)曲线,B榜里面测试集疑似操纵过,A榜的验证集和测试集acc是比较接近的,B榜线下0.56是线上0.41
    在这里插入图片描述
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐