
阿里妹导读
本文是一份 LLM 微调 AutoResearch 落地实战。作者把过去几个月在电商场景(Query 改写 / 同款判定 / 重排打分)上做 Qwen3 微调时的完整流水线沉淀成一个由 Agent 驱动的三阶段框架:场景诊断 → 方案设计 → 自动化实验,同时把踩过的坑(PyTorch 2.6 vs DeepSpeed、Qwen3 thinking mode 让 BLEU 掉到个位数、多队列 OSS endpoint 差异)一并写成 SKILL.md 交给 Agent 执行。文章适合关注 AI Coding、Agent for ML、LLM 微调工程化的同学阅读。文中提到的部分平台/工具(如星云、TuningFactory、TAO/Pailitao-AutoResearch)为公司内部命名,思路和方法本身与平台无关,读者可类比到 PAI、SageMaker、Slurm 等外部环境。(文章内容基于作者个人技术实践与独立思考,旨在分享经验,仅代表个人观点。)
前言
两个月前写完《多模态检索 TBStars_VL_Emb 指令遵循微调》文章之后,结尾留了一句话:
可构建完整的 “请求 → 召回 → 曝光/点击/成交 → 样本反馈 → 模型训练 → 线上服务 → 请求” 优化迭代闭环。
当时只是有个朦胧的想法 —— “如果训练这一环也能自动化,整个闭环就能转起来”。但真正落地谁来推动?怎么落地?没想清楚。
Karpathy 把 AutoResearch 这个名字带火(他的思路是:让 Agent 按照一份 SKILL 文件驱动的实验清单,自主完成从假设到验证的循环),紧接着又陆续读到主搜团队的 TAO-AutoResearch 和拍立淘团队的 Pailitao-AutoResearch,发现 “Agent 接管训练优化迭代” 这件事已经被这两份工作在工业级跑通了——当时留的那句话,已经有人替我做完了一半。
但这两份工作都和它们所在的业务、训练平台耦合比较深。对于 1688 这边在星云平台上训 LLM 的同学,还需要一套接地气的版本:
-
底层换成团队里大量在用的 TuningFactory —— 它是内部对开源 LLaMA-Factory 的 Fork,把 ODPS 数据源、内部存储、多队列适配等都做了封装
-
训练平台换成星云(内部通用训练平台,支持多种 GPU 队列),全 API 拉日志,不用开浏览器
-
把过去几个月在数据接入 / 镜像 / 分布式训练上踩过的坑全部沉淀进 SKILL.md
本文 AutoResearch-LLM:
-
章节 1 讲来由和定位
-
章节 2 是流水线设计
-
章节 3 是工程实现
-
章节 4 是踩坑实录
-
章节 5 是和已有工作的横向对比
读者可自行查阅感兴趣的章节。给外部读者一个阅读建议:
-
只想看”Agent 怎么把 LLM 调参这件事跑起来”:直接看 §2 流水线设计
-
关心 LLM 微调工程化的踩坑:直接看 §4 踩坑实录(PyTorch 2.6 vs DeepSpeed、Qwen3 thinking mode 拉低 BLEU 是通用问题,与平台无关)
-
想在自己环境(K8s / Slurm / PAI / 云商方案)复用这套框架:§3 工程实现要点里的思路是可搬的,把星云 API 换成你们平台的 API 即可

1. 背景
1.1 缘起
两个月前那篇 TBStars 文章里画了一张闭环图:
请求 → 召回 → 曝光/点击/成交 → 样本反馈 → 模型训练 → 线上服务 → 请求▲│这一环最难自动化
数据回流、特征生成、上线发布这几环 1688 都有相对成熟的工程基建。但模型训练这一环始终是手工活:算法同学盯着 eval_loss、改 LR、重提任务、看日志、再改 LR,一天下来真正的 “研究时间” 很少。
CV 分类领域我之前做过一版 auto_research_cv_cls_demo(下文简称 “前作 CV demo”),在 CIFAR-10 上跑通了 “Agent 按 SKILL 跑实验” 的小闭环,最终拿到 +1.22% 的提升。
不过 CV 分类还是太简单:单文件训练入口、本地数据集、单机训练。要把这套思路搬到 1688 的 LLM 微调场景,要解决的事情多得多:
|
维度 |
CV demo |
LLM 实战 |
|
训练框架 |
手写 |
TuningFactory(LLaMA-Factory 的内部 Fork) |
|
数据 |
CIFAR-10 本地 tar |
ODPS 表(百万行起步;ODPS 即 MaxCompute,阿里云的大数据仓) |
|
模型 |
ResNet18~50 |
Qwen3-1.7B ~ 35B-A3B(含 MoE) |
|
并行 |
单卡 |
DeepSpeed ZeRO-2/3 + Megatron Turbo |
|
训练时长 |
30~60 分钟 |
1~24 小时 |
|
评估 |
acc 直接打出来 |
还要再起一轮 generate + BLEU/ROUGE |
|
平台 |
星云 |
星云 |
1.2 参考的两份工作
写这个项目之前认真读了两份公司内部同学做过的 AutoResearch 落地:主搜团队的 TAO-AutoResearch 和拍立淘团队的 Pailitao-AutoResearch。这两份工作都是各自业务线上把 Agent 接进召回/排序模型训练迭代闭环的完整实践,思路很接近:
-
TAO-AutoResearch:4 个专职子 Agent(分析 / 训练 / 评测 / 压测)+ Git Worktree 实验隔离 + 三平台可插拔后端,跑了 20 轮迭代,hitrate@20 +3.05%;功能最完整,工程化程度也最高
-
Pailitao-AutoResearch:三阶段流水线(理解分析 / 改进方案 / 实验验证)+ Git 分支隔离,附带一个最小可跑的 demo 仓库;门槛低,适合作为新人理解 AutoResearch 思想的入口
auto_research_llm 的演化路径是这样的:先从 Pailitao demo 学到三阶段骨架 → 在 CIFAR-10 上做了一版前作 CV demo(CV 分类验证可行)→ 这次再把同一套三阶段、单 Skill、易上手的结构搬到 LLM 微调上,针对 1688 场景做了几处升级:
|
升级点 |
前作 CV demo | auto_research_llm |
| 任务领域 | CV 分类(CIFAR-10) | LLM 微调(1688 电商三类任务:Query 改写、同款判定、重排打分) |
| 训练框架 | 单文件main.py(PyTorch) |
TuningFactory(submodule 引入)+ ROLL(阿里开源的 RL 训练框架) |
| 数据源 | 本地 tar 数据集 | ODPS 三件套 + OSS JSON 备选 |
| 训练方式 | 单一训练循环 | SFT 全参 / SFT LoRA / SFT LoRA MoE / DPO LoRA + 离线数据蒸馏(DashScope 生成 + SFT,已验证)+ 在线 KD(--stage distill,配置就绪未验证)+ RL/GRPO(接入了 ROLL,未验证) |
| 并行 | 单卡 | DeepSpeed ZeRO-2/3 / Megatron Turbo + EP(MoE) |
| 平台对接 | 星云 API 拉日志 | 同样 API 化,新增 ODPS / MOS(内部对象存储,用于超大模型 ckpt)/ 多队列 endpoint 适配 |
| 评估 | acc 直接打出来 | Trainer 内置 predict_with_generate 自动出 BLEU/ROUGE |
| 调参清单 | CV 14 项 | LLM 调参清单按 6 级优先级分层(LR/LoRA → batch → 训练策略 → 数据 → 分布式 → 评估),外挂三类业务指标(BLEU / F1 / NDCG) |
所以四者的相对位置大致是:
入门 ─────────────────── 进阶 ─────────────────── 更完整Pailitao demo → 前作 CV demo → auto_research_llm → TAO-AutoResearch(最简骨架) (CV 验证可行) (1688 LLM 接地气版) (多 Agent + 多平台)
第一次接触 AutoResearch 思想,可以先看 Pailitao 的 demo 或前作 CV demo 跑一遍流程;刚好在 1688 用 TuningFactory 训 LLM,可以直接用本项目;如果你的研究规模更大(需要多平台调度、多 Agent 协作、长周期连续优化),那 TAO 是最合适的参考。
1.3 目前做到什么程度
|
维度 |
能力 |
|
训练方式 |
已端到端验证:SFT 全参 / SFT LoRA / SFT LoRA MoE / DPO LoRA / 离线数据蒸馏(大模型生成 → SFT);配置就绪未验证:在线 KD(TuningFactory |
|
场景 |
1688 电商 Query 改写 / 同款判定 / 重排打分 |
|
基座 |
Qwen3 系列(1.7B / 8B / 32B)+ Qwen3.6-35B-A3B(MoE) |
|
数据 |
ODPS(主)+ OSS JSON(小数据备选) |
|
存储 |
OSS(<14B)/ MOS(≥14B)/ NAS(产物) |
|
分布式 |
DeepSpeed ZeRO-2/3 / Megatron Turbo + EP(MoE) |
|
评估 |
Trainer 内置 |
1.4 仓库结构
auto_research_llm/├── SKILL.md ← Agent 主入口(Pipeline / 参数 / Debug 全在这)├── submit.sh / kill.sh ← 任务提交与终止├── user_info.json ← 个人凭证(已 gitignore)├── configs/ ← 训练/推理配置(一文件一方式)│ ├── sft_lora.sh 单卡 LoRA│ ├── sft_lora_multi.sh 多卡 LoRA + DeepSpeed│ ├── sft_full.sh 全参 + DeepSpeed│ ├── sft_lora_moe.sh MoE + Megatron Turbo│ ├── distill.sh KD Loss 蒸馏│ ├── dpo_lora.sh DPO│ ├── rl_roll.sh GRPO(基于 ROLL)│ ├── infer.sh 单独推理(zero-shot baseline / 评估别人 ckpt)│ ├── ds_zero2.json / ds_zero3.json│ └── cluster.json / cluster_2gpu.json / cluster_multigpu.json├── scripts/│ ├── sync_to_tuning_factory.sh submodule 实验分支自动 push│ ├── prepare_distill_data.py 蒸馏数据生成(走 DashScope,即阿里云百炼的大模型 API)│ └── convert_to_hf.sh ckpt 转 HF 格式├── scenarios/│ ├── query_rewrite/{README,odps_ddl.sql,sample_data.json,eval_metrics.py}│ ├── same_product/...│ └── reranking/...├── TuningFactory/ ← submodule(分支 openlm)├── experiment_logs/ ← 自动下载的训练完整日志└── experiment_results.csv ← 实验记录入口(自动追加)
2. 流水线设计
整体沿用 TAO / Pailitao 的三阶段写法,每个阶段都有明确的产出物,方便 Agent 在长上下文里 “接力” :
[用户输入] 研究目标 + 数据样例 + 预算│▼[阶段 1] 场景理解与基线分析├─ 7 维诊断(基座 / 数据质量 / Prompt / 训练方式 / Eval / Baseline 复盘 ...)├─ 跑 baseline → 拿到 eval_loss & 任务指标└─ 产出 analysis_report.md(同时锁定 MODEL_NAME)│▼[阶段 2] 实验方案设计├─ 从技术候选库(15+ 技术点)匹配候选├─ 按一级/二级/三级/四级/五级/六级参数清单展开└─ 产出 improve_guide.md│▼[阶段 3] 自动化实验验证├─ 单变量探索 → 正交组合 → 数据维度兜底├─ 每个实验:生成 config → push 实验分支 → submit → 监控 → 评估 → 入 csv└─ 产出 experiment_results.csv(实验级) + final_report.md(研究级)

2.1 第一阶段:场景理解与基线分析
不上来就调参,先做体检。Agent 会按七个维度把当前任务过一遍,前六维在 baseline 跑之前完成,第七维是 baseline 跑完之后的复盘:
|
# |
维度 |
何时检查 |
诊断问题 |
薄弱信号 |
|
1 |
基座模型 |
baseline 前 |
规模/能力是否匹配任务? |
简单分类用 72B → 浪费;长上下文任务用短窗口模型 → 切更大基座 |
|
2 |
数据质量 |
baseline 前 |
随机抽 20~50 条人审,明显错标比例多少? |
错标 >10% → 先清洗再训 |
|
3 |
数据规模 |
baseline 前 |
独立样本数对当前任务是否充分? |
生成 <5K / 分类 <1K → 先做离线蒸馏扩数据 |
|
4 |
Prompt 模板 |
baseline 前 |
instruction 是否表达清楚任务边界、给出格式约束? |
输出格式混乱 / 答非所问 → 改 instruction 通常 +5~10% |
|
5 |
训练方式选型 |
baseline 前 |
现有数据形态匹配 SFT / 蒸馏 / DPO / RL 哪一种? |
只有 (q, a) → SFT;有 teacher → 蒸馏;有偏好对 → DPO;有可编程 reward → RL |
|
6 |
Eval 配置 |
baseline 前 |
eval 集、eval_steps、任务指标 parse 都配好了吗? |
漏配 eval → 无法选最优 ckpt;漏 predict_with_generate → 没有 BLEU/ROUGE |
|
7 |
Baseline 复盘 |
baseline 后 |
LR 是否合理?是否过拟合?LoRA rank 是否到位? |
eval_loss 震荡 → LR 偏大;train_loss→0 但 eval 不降 → 过拟合;rank=8 不再下降 → 升 rank 或换全参 |
注意:基座模型在本阶段就要锁定。后续整个实验序列里,MODEL_NAME 不再是一个可调变量。Agent 不允许擅自换基座;如果出现 “LoRA 全跑过仍饱和” 等信号,会专门起一节 “需要决策” 提示用户,由用户拍板。
这块和 TAO 的 LOCKED 参数思路是一致的:把 “不该让 Agent 动” 的东西显式标出来,避免 Agent 越界探索。
2.2 第二阶段:实验方案设计
把 “我能做什么” 写成清单,把 “应该先做什么” 写成优先级。这里只贴一级参数,二到六级详见 SKILL.md:
一级(直接影响效果)
|
参数 |
取值范围 |
备注 |
|
|
LoRA: 1e-4 ~ 5e-4;全参: 5e-6 ~ 5e-5;DPO: 5e-7 ~ 5e-6 |
最敏感 |
|
|
8/16/32/64/128(alpha=2×rank) |
表达力 |
|
|
|
all-linear 参数 ×3 |
|
|
SFT 2 ~ 5 / DPO 1 ~ 3 / 蒸馏 1 ~ 3 |
真实训练用这个 |
|
Prompt 模板 |
instruction 文本改写 |
通常 +5~10% |
|
|
1024 / 2048 / 4096 / 8192 |
序列长度,吃显存 |
技术候选库(参考 TAO 的 46 项知识库,针对 LLM 微调精简到 15+)。需要说明的是,下表「业内经验区间」一列是业内常见报道值,不是本项目实测;本项目目前仅在 Query 改写 SFT LoRA 这一条路径上做过比较充分的实验,其它路径的数字仅供方向参考:
|
类别 |
技术 |
适用条件 |
业内经验区间 |
|
SFT |
LoRA rank 8/32/64 |
由低到高试 |
+1~5% |
|
SFT |
LoRA target=all-linear |
QKV 不够 |
+1~3% |
|
SFT |
全参 |
LoRA 到天花板 |
+2~5% |
|
蒸馏 |
大模型 hard label + SFT |
数据不足 |
视场景,常显著 |
|
蒸馏 |
在线 KD Loss |
有 teacher |
待验证(配置就绪) |
|
RL |
GRPO |
有可编程 reward |
待验证(已接入 ROLL) |
|
数据 |
Prompt 模板优化 |
输出乱 |
+2~10% |
|
数据 |
清洗/筛选 |
标注差 |
+3~8% |
2.3 第三阶段:自动化实验验证
2.3.1 实验分支管理
每个实验对应 TuningFactory 的一个分支,命名 experiment-{编号}-{场景}-{方式}-{变量},例如 experiment-03-qr-sft-lora32-prompt-v2。
scripts/sync_to_tuning_factory.sh 自动做三件事:
-
在 submodule 里基于
openlm拉一个新分支 -
把
configs/*复制到TuningFactory/scripts/ -
commit + push 到远端
实验隔离基于 submodule worktree 多分支,多个实验互不干扰,可并行可串行。
2.3.2 任务提交与监控
提交一行:
bash submit.sh experiment-03-qr-sft-lora32-prompt-v2# 输出 task_id(32 位 hex)
监控全走星云的 HTTP API,不用打开浏览器(下面 <meta-api> / <log-download-api> 是内部平台的域名占位符;这套调用结构本身跟平台无关,用 K8s / Slurm / PAI 的读者可以对照替换成自己平台的元信息 & 日志接口):
# 状态 + application_id + job_namecurl -s "https://<meta-api>/nebula/meta?task_id=${TASK_ID}"# 实时 stdout / stderrcurl -s "https://<meta-api>/nebula/log?task_id=${TASK_ID}&rank=0&stream=stdout&bufferSize=102400"curl -s "https://<meta-api>/nebula/log?task_id=${TASK_ID}&rank=0&stream=stderr&bufferSize=102400"# 训练完成后下载完整日志 zipAPP_ID=$(curl -s "https://<meta-api>/nebula/meta?task_id=${TASK_ID}" | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['application']['application_id'])")curl -L -o experiment_logs/${EXP}.zip "https://<log-download-api>/logview/logs/download/${APP_ID}"
不用 Agent 手搓 while 循环,直接用 Agent 自带的 /loop 能力做循环验证——例如 /loop 15m 检查 task_id ${TASK_ID} 状态,完成后提取 BLEU/ROUGE 写入 csv,到点自动唤起、自动收尾,省心也省 token。
2.3.3 自动指标提取
训练完之后 Agent 自动从日志里正则提取 eval_loss / predict_bleu-4 / predict_rouge-l,写入 experiment_results.csv:
curl -s "https://<meta-api>/nebula/log?task_id=${TASK_ID}&rank=0&stream=stdout&bufferSize=204800" | python3 -c "import sys,json,red=json.load(sys.stdin)for log in d.get('logs',[ ]):if log['role']=='worker':c=log['result']['data']['content']for m in re.finditer(r'predict_(w+)s+=s+([d.]+)', c):print(f' {m.group(1)}: {m.group(2)}')break"
补充说明阶段三的产出物粒度(沿用前作 CV demo 的规约):
-
experiment_results.csv:每个实验追加一行,列包括方案编号 / 场景 / 训练方式 / 基座模型 / 核心变量 / eval_loss / 任务指标 / 运行状态 / 任务ID / 备注 -
final_report.md:每轮研究跑完后由 Agent 汇总一次,对比各方案的相对增益、给出下一轮建议或终止结论
也就是说,csv 是实验级的明细,final_report.md 是研究级的总结,两者粒度不同。
3. 工程实现要点
3.1 数据接入
1688 业务侧的样本/日志大多沉淀在 ODPS(MaxCompute,阿里云的大数据仓)表里,所以训练数据源默认对接 ODPS。Alpaca 三字段 schema:
CREATE TABLE auto_research_llm_query_rewrite (instruction STRING,input STRING,output STRING) PARTITIONED BY (ds STRING) LIFECYCLE 90;
TuningFactory 的字段映射通过三个参数告诉框架:
--prompt=instruction # 哪个字段作为 prompt--query=input # 哪个字段作为 query--response=output # 哪个字段作为 label(SFT 必填)
提交到星云时,ODPS 三件套缺一不可:
--tables=${INPUT},${EVAL_INPUT}--odps_project=${ODPS_PROJECT}--access_id=... --access_key=...
3.2 训练配置脚本
|
配置 |
适用 |
|
|
单卡 LoRA,最快 |
|
|
多卡 LoRA + DeepSpeed ZeRO-2 |
|
|
全参 + DeepSpeed |
|
|
Qwen3.6-35B-A3B 等 MoE,Megatron Turbo + EP |
|
|
KD Loss 蒸馏 |
|
|
DPO |
|
|
GRPO(需单独 clone ROLL) |
|
|
单独推理(zero-shot baseline / 评估别人 ckpt) |
每个脚本头部都有一段统一格式的 “输入区”,要起新实验,复制一份改这块就够。以 sft_lora_multi.sh 为例:
# === 模型配置 ===MODEL_NAME="Qwen/Qwen3-8B"PROMPT_TEMPLATE="qwen3"# === 任务配置 ===SCENARIO="query_rewrite"EXP_NAME="lora16_ds_2gpu"JOB_NAME="auto_research_${SCENARIO}_${EXP_NAME}"# === 数据源 ===ODPS_PROJECT="<your_project>"INPUT="odps://${ODPS_PROJECT}/tables/auto_research_llm_${SCENARIO}/ds=20260624"EVAL_INPUT="odps://${ODPS_PROJECT}/tables/auto_research_llm_${SCENARIO}/ds=20260624"# === 训练超参 ===LR=2e-4
脚本下半部分是固定模板(拼出给 TuningFactory 的训练参数 + 给星云 nebulactl(即星云 CLI)的提交参数),起新实验只需要改顶部这十几行变量。
configs/*.sh 本身不是在本地执行的,它的完整生命周期是:
configs/sft_lora_multi.sh ← 主仓里改这一份↓ scripts/sync_to_tuning_factory.sh 拷贝TuningFactory/scripts/sft_lora_multi.sh (实验分支副本) ← submodule 上 push 到远端↓ submit.sh 把 "分支名 + 脚本路径" 告诉 llm_cli星云容器拉 git 仓库 → 执行 bash scripts/sft_lora_multi.sh ← 真正运行在训练节点↓ 脚本里调 nebulactl run mdl --user_params=...任务被调度到训练队列

所以 configs/*.sh 同时承担三个角色:本地是研究员动手改的那一份、submodule 实验分支里是被同步过去的提交入口、远端容器里是真正被执行的训练启动脚本。改一处就够,剩下都是脚本自动同步。
3.3 自动评估
最初的流程是训练完手动起一个推理任务,跑 generate → 写脚本算 BLEU。后来发现 TuningFactory 的 Trainer 自带 --predict_with_generate=True,训完直接打 BLEU/ROUGE:
--do_predict --predict_with_generate=True --enable_thinking=False
加了这三行,训练完日志里就有:
***** predict metrics *****predict_bleu-4 = 46.3219predict_rouge-1 = 70.2381predict_rouge-2 = 37.7778predict_rouge-l = 63.5714predict_runtime = 0:00:17.71predict_samples_per_second = 0.282
省掉一整轮推理脚本。--enable_thinking=False 是关 Qwen3 thinking mode 的开关,原因详见 4.2。
3.4 effective_batch 经验
eff_batch 公式:
eff_batch = world_size × per_device_train_batch_size × gradient_accumulation_steps
|
任务 |
推荐 eff_batch |
|
SFT 7B |
64~128 |
|
SFT 35B+ |
32~64 |
|
DPO/SimPO |
32~64 |
|
蒸馏 KD |
64~256 |
|
RL GRPO |
8~16 prompt × n rollouts |
调参顺序:先把 per_device_train_batch_size 调到显存极限 → 再用 gradient_accumulation_steps 凑 eff_batch → LR 按 √eff_batch 微调。
3.5 平台问题:星云 skill 兜底
这一节是本项目的一个方法论:平台级问题外挂给平台官方 skill,业务级问题留在项目 skill。放到任何有官方 skill 的训练平台上都成立,读者可以类比。
训练任务在星云上出错,绝大部分错误模式(OOM / NCCL / 镜像安装失败 / 队列额度 / OSS endpoint / pod 调度异常 ……)都在星云官方维护的 nebula-support skill 里覆盖了。本项目 SKILL.md 显式声明依赖它(a1 是内部 skill 包管理器,功能类似 pip):
a1 skill install nebula-support --agent claude --global
装上之后,Agent 遇到平台级报错会自动调起 nebula-support 来诊断、生成 nebula-cli 指令、检索官方文档;我们自己的 SKILL.md 只维护项目专属的 Debug 表(比如 4.1 的 DeepSpeed ckpt、4.2 的 Qwen3 thinking、4.3 的 oss_endpoint)。
平台问题交给平台 skill,业务问题留在项目 skill——下面第 4 章列的也都是这一层级的踩坑沉淀。
4. 踩坑实录
这部分是流水线最重要的 “沉淀物”,对应过去几次的失败任务。
4.1 PyTorch 2.6 +
DeepSpeed ZeRO + load_best 撞在一起
4.1.1 错误现象
stderr 关键栈:
WeightsUnpickler error: Unsupported global: GLOBAL deepspeed.runtime.fp16.loss_scaler.LossScalerwas not an allowed global by default. Please use `torch.serialization.add_safe_globals(...)`or `torch.serialization.safe_globals` to allowlist this global if you trust this class/function.Weights only load failed. Re-running `torch.load` with `weights_only=False` would likelysucceed, but it can result in arbitrary code execution.
4.1.2 根因
PyTorch 2.6 默认 weights_only=True,DeepSpeed 序列化的 LossScaler 对象不在白名单里,反序列化时直接拒绝。
更糟的是这个错有两个触发点:
-
Trainer auto-resume:output_dir 里有上一次的
checkpoint-N,新一轮训练默认会 “接着练”,加载时炸; -
训练结束
_load_best_model():开了--load_best_model_at_end=True之后,结束时会再次反序列化 best ckpt 的优化器状态。
4.1.3 修复
|
必做 |
禁用 |
|
|
不可用 |
|
全参再加 |
— |
需要 best ckpt 时,按 trainer_state.json 里的 best_model_checkpoint 字段手动定位:
cat /data/oss_bucket_0/.../checkpoint-100/trainer_state.json | python3 -c "import sys, jsonprint(json.load(sys.stdin)['best_model_checkpoint'])"
4.2 predict BLEU 异常低(<5)
4.2.1 错误现象
训练完打开日志:
***** predict metrics *****predict_bleu-4 = 1.8214predict_rouge-l = 5.1218eval_loss = 0.0042 ← 这俩居然能并存
eval_loss 明明只有 0.004,BLEU 怎么这么差?把 generated_predictions.jsonl 拉出来看了一眼:
{"label": "男士夏季透气运动鞋","predict": "<think>n用户输入是「夏天透气男鞋」,需要规范化为更标准的电商搜索词。考虑同义词替换......n</think>nn男士夏季透气运动鞋"}
4.2.2 根因
Qwen3 默认开了 thinking mode,generate 时会先吐一大段 <think>...</think> 再给答案。BLEU 拿这段思考内容跟 reference 算,分数当然惨。
训练时为什么不会出问题?因为 TuningFactory 的 qwen3 模板(ReasoningTemplate)会在训练阶段自动给 label 包一层 <think>nn</think>nn{output},loss 是基于这个完整序列算的——也就是说训练时模型学的是 “先输出空 think 再输出答案”。但推理时 enable_thinking 默认 True,模型就把空 think 填满了。
4.2.3 修复
所有 configs/*.sh 默认带上 --enable_thinking=False。实测:关闭前 predict_bleu-4 = 1.82,关闭后 predict_bleu-4 = 46.32,predict_rouge-l = 63.57。SKILL.md 的 Debug 表也补了一行:
| predict BLEU 异常低(<5) | predict 输出含 <think> 标签 | Qwen3 thinking mode 未关。加 --enable_thinking=False |
4.3 oss_endpoint 格式因队列而异
这一节的具体队列/endpoint 是我们内部环境的例子,读者关注结论即可:同一 region 的 OSS,在不同 GPU 队列(不同 IDC 线路)下白名单规则可能不同,切换队列时要顺手确认 endpoint 写法。
|
队列 |
正确格式 |
|
MI308X |
|
|
H20 / H200 |
|
MI308X 上写 oss-cn-* 直接被白名单卡掉,报 “域名白名单/域名不能是oss-cn-“。切换队列时同步修改 endpoint。
5. 同期工作横向对比
把同期看到的几个项目放在一张表里,按入门门槛由低到高排列,方便选型:

|
项目 |
入门门槛 |
适用领域 |
训练框架 |
平台 |
并行隔离 |
|
karpathy/autoresearch |
最低 |
单文件 train.py 实验 |
任意 |
本地 GPU |
— |
|
Pailitao-AutoResearch |
低 |
多模态排序 / 通用 demo |
自研 |
星云 |
Git 分支 |
|
前作 CV demo |
低 |
CV 分类 |
PyTorch |
星云 |
Git Worktree |
|
auto_research_llm(本文) |
中 |
1688 LLM 微调 |
TuningFactory + ROLL |
星云全队列 |
Submodule 分支 / Worktree |
|
TAO-AutoResearch |
高 |
搜索召回/排序 |
自研 + AOP(内部算法在线平台) |
多平台 |
Git Worktree(4 子 Agent) |
简单的选型建议:
-
想 5 分钟内跑通一个 demo 理解概念:先看 Karpathy 原版或 Pailitao 的 demo
-
CV 分类调参:前作 CV demo
-
1688 LLM 微调(SFT / DPO / 离线数据蒸馏已端到端验证;在线 KD / RL 已接入配置就绪,正在打通中)+ 星云平台:欢迎试用本项目
-
多平台 / 多 Agent / 长周期生产环境:直接用 TAO-AutoResearch
6. 还没做好的几块
写到这里,必须诚实承认还有几块工程没填上:
-
Agent 调参循环还不够智能:现在是按预设优先级跑(一级 → 二级 → 数据维度),没有根据中间结果剪枝。理想状态是类似 Hyperband / Successive Halving,或者借鉴 TAO 的 “压测子 Agent + 外部 LLM 独立审查” 机制
-
跨场景知识不流通:Query 改写学到的 Prompt 优化经验,目前没办法自动迁移给同款判定。打算搞一个
lessons.jsonl累计踩坑库(呼应 TAO 提到的 “团队共享调优知识资产”),但 schema 还没定 -
在线蒸馏未端到端验证:TuningFactory 原生支持
--stage distill,configs/distill.sh已经配好 student(Qwen3-8B)+ teacher(Qwen3-32B)+ forward KL 蒸馏目标,但还没真正提交任务跑通;teacher 在线推理的显存占用还需要实测 -
离线蒸馏(logit caching)TuningFactory 不支持:希望 “teacher 离线 dump top-k logits → student 训练时直接读 cache” 这套省显存方案。代码侦察发现 TuningFactory 只对 teacher 文本再 tokenize,不读 logits 字段,整条 data pipeline / collator / trainer / hparams 校验都要改。短期可以先用前一种离线数据蒸馏(DashScope hard label 生成 → SFT)兜底
-
RL 流水线没接进 submit.sh:当前 RL 走的是 ROLL 框架,按官方文档单独 clone(不是 submodule),独立配置 + 独立提交入口,没和 SFT/蒸馏的
submit.sh流程统一 -
本地评估缺失:训完只有 BLEU/ROUGE,业务真正关心的同款判定 F1、重排 NDCG@10 还需要离线再跑一遍 parse + 算分。
scenarios/{}/eval_metrics.py只是骨架 -
MOS 实测较少:14B 以下都用 OSS,MOS 路径只在文档里走过,还没真正跑通过 35B 模型的端到端 ckpt 加载
-
多 Agent 协作:当前还是单 Agent + Skill 的形式,没有像 TAO 那样拆出分析 / 训练 / 评测 / 压测四个子 Agent。下一版考虑改造
7. 写在最后
回到开头那张 “请求 → 召回 → … → 模型训练 → 线上服务” 的闭环图。
两个月前写下那句话的时候,我以为 “训练自动化” 是一个遥远的工程目标。现在回头看,主搜、拍立淘的同学已经把骨架搭起来了,前作 CV demo 在 CIFAR-10 上跑通过一次,本文的 LLM 实战版又把 1688 这边的细节填上。下一步是把 “模型训练” 这一环和 “样本反馈 / 线上服务” 接上——这部分还在 SKILL.md 的 TODO 列表里。
整个项目的初衷只是 “把自己干活的步骤写成脚本” ,做着做着发现 Agent 拉进来之后,重复但容易出错的部分(写配置、提交、监控、抓日志、算指标)确实可以让出来,自己专注于需要判断的部分(数据怎么洗、Prompt 怎么改、什么时候换基座)。
参考资料
[1] TAO-AutoResearch:Agent 接管模型训练优化全流程(内部技术分享)
[2] Pailitao-AutoResearch Skill(内部技术分享)
[3] auto_research_cv_cls_demo(前作 CV 版,内部仓库)
[4] 多模态检索 TBStars_VL_Emb 指令遵循微调(内部技术分享)
[5] 星云平台:离线训练 & 离线推理文档
[6] TuningFactory(LLaMA-Factory 的内部 Fork)
[7] ROLL 官方文档:
https://github.com/alibaba/ROLL
[8] Karpathy autoresearch:
https://github.com/karpathy/autoresearch
[9] LLaMA-Factory(TuningFactory 上游开源项目):
https://github.com/hiyouga/LlamaFactory

