IntraSkill / ShopSimRL · Qwen3.5-4B · Iter80 · 4 rounds
Original source document; content retains its source language. Markdown ↓

基于 slime 的训练链路

当前实现把 proposal 中的快、慢两层时间尺度分开:slime 负责同一 checkpoint 内的 GRPO rollout/update;Trace2Skill failure analysis 与 randomized validation 在一个有限训练 round 结束后运行。两条链路共享冻结、带哈希的 curriculum / candidate-pool / assignment 状态,通过 slime 的扩展点接入。

本次已发布实验为四轮、80 个 rollout step,最终 checkpoint 为 Iter80;结果与局限见 实验报告。以下操作面向新的训练运行,历史运行目录不应覆盖。

1. 数据与初始 curriculum

训练只读取项目冻结的 persona train task IDs:

python scripts\run_shopsimrl.py training prepare-data `
  ShopSimulator\shop_env\configs\persona_splits.v1.json `
  train data\shopsim_train.jsonl

本次运行使用已发布的 10-chunk S₀:artifacts/cold-start/selected_skillbank.json,与 artifacts/cold-start/curriculum.json 配套。默认 q=0.20、rho 范围 [0.20, 0.90];首轮离线检查可以直接使用这对冻结输入。S₀ 来自历史冻结上下文恢复,未使用测试 reward 重新选择,也没有伪装为新完成的 Gate A,见 来源说明

需要为新实验改变 q/rho 时,显式冻结新的 curriculum 并同步配置路径:

python scripts/run_shopsimrl.py training build-curriculum `
  artifacts/cold-start/selected_skillbank.json `
  runs/new-experiment/curriculum.json `
  --round-id round-000 --skill-free-probability 0.20 `
  --rho-min 0.20 --rho-max 0.90

默认 rho 映射为 clipped linear mapping;不传 --contribution-scale 时,本轮最大正贡献映射到 rho_max。本次 q 固定不变,没有运行自动 q controller。Gate 独占 chunk admission/retirement 权限,curriculum builder 不会重新接纳非正贡献内容。

每个 curriculum JSON 同时嵌入 active chunks、contribution、rho、q、来源 SkillBank hash 与映射参数。rollout worker 会校验 state_id,文件内容被修改但 hash 未更新时直接失败。

training prepare-data 生成 3726 个冻结 train task。data/runs/ 被 Git 忽略,换机器时需要重建任务数据,并配置模型权重与服务;发布的初始 SkillBank/curriculum 已随仓库提供。

2. slime rollout 与 GRPO 语义

训练入口是:

export HF_CHECKPOINT=/path/to/Qwen3.5-4B
export TRAIN_LOAD=/path/to/qwen35-4b-actor-torch-dist
export TRAIN_SAVE=/path/to/output
bash scripts/run_shopsimrl_slime.sh

训练 TP 由 TP_SIZE 控制(默认 2),推理引擎 TP 由 ROLLOUT_TP_SIZE 控制(默认 1)。 NUM_GPUS 必须同时被两者整除;TP_SIZE>1 时训练打开 sequence parallel。 当前默认 NUM_GPUS=4 TP_SIZE=2 ROLLOUT_TP_SIZE=1:训练切开词表 logits,rollout / val 仍是 4 个独立引擎。 训练打包 MAX_TOKENS_PER_GPU=16384,单条 Sample 由 ROLLOUT_MAX_CONTEXT_LEN=24576 封顶;二者不要当成同一件事,见 oom_qwen35_4b.mdROLLOUT_BATCH_SIZE 默认 16,即每步留下 16×8=128 条 episode。run_full_train.sh 另把 OVER_SAMPLING_BATCH_SIZE 设为 32、GLOBAL_BATCH_SIZE 设为 64:先多采再过滤,留下的 128 条切成两次优化器更新。波次规则见 dynamic_sampling_filter.md。 1 卡用 NUM_GPUS=1 TP_SIZE=1。 少于 8 卡时不能只修改 Ray 的可见 GPU 数而保留 slime 默认的 num_gpus_per_node=8

运行前按 ShopSimulator 文档启动环境服务,并按机器拓扑审核 GPU、batch、context 和 checkpoint 路径;脚本会在启动 Ray 前检查 checkpoint、task data、slime checkout 和 custom config,并执行以下无 GPU、无 API 的输入校验。自定义参数位于 configs/slime_shopsimrl.yaml;其中 shopsim_skillbank_pathshopsim_curriculum_path 必须对应同一版本:

python scripts\run_shopsimrl.py training check configs\slime_shopsimrl.yaml

检查覆盖 curriculum 内容 hash、source bank、chunk 文本/系数、完整且不重复的 frozen train IDs、persona 和并发配置。ready 只表示离线输入通过,不表示 GPU、SGLang 或环境服务已验证。脚本使用 slime 原生扩展点:

  • shopsimrl.slime_runtime.generate:多轮 function-tool agent rollout;
  • shopsimrl.slime_runtime.normalize_grpo_by_prompt_and_rollout:按 prompt group、unique rollout 做 GRPO centering;
  • Sample.tokens / rollout_log_probs 来自 SGLang 实际采样,不从文本重新分词恢复;
  • 环境 observation、tool result 和 prompt token 的 loss mask 为 0,模型生成 token 的 loss mask 为 1;
  • 动态工具 schema 造成 token-contiguous trajectory 分段时,siblings 共享 rollout_id。reward normalization 先按 unique rollout 计算,再向 siblings 广播,不会让长轨迹在组均值中被重复计数。
  • 适配器明确使用 fork_threshold_tokens=0:slime 默认会在短 assistant reasoning 回显或 token drift 时丢弃先前 response 的训练信号;本实现保留每一轮实际生成的 token/logprob,必要时独立分段。
  • 任一 rollout 因环境、协议运行时或空 trajectory 而不可评分时,动态采样过滤器丢弃整个 prompt group 并补采;技术失败不会作为 reward=0 样本污染 GRPO。连续失败达到配置阈值时直接终止,避免环境服务宕机后无限补采。zero-std(全对/全错)带 keep_when_insufficient,波次与凑批规则见 dynamic_sampling_filter.md
  • SGLang 的 abort 及 rollout 停止状态属于技术中止;停止后不再启动新的 turn/retry 请求。达到生成长度上限仍是可评分的 policy failure。当前页面未提供的工具与 evaluator 一样返回 unavailable_tool feedback,不执行环境动作。

脚本给 slime dataset loader 开启 --apply-chat-template,兼容 Qwen3.5 checkpoint 附带 processor 时必须使用 message-list 的输入要求;实际购物 prompt 仍由 runtime 根据 metadata.task_id 从环境生成。run_shopsimrl_slime.shGLOBAL_BATCH_SIZE 默认等于 ROLLOUT_BATCH_SIZE * N_SAMPLES_PER_PROMPTrun_full_train.sh 覆盖为 64,必须整除一轮留下的 episode 数,且是 N_SAMPLES_PER_PROMPT 的倍数,避免训练侧丢弃尾部 rollouts 或把同一个 prompt group 拆进两次更新。累计 NUM_ROLLOUT 变长时必须 --override-opt-param-scheduler,否则 Megatron 会因 checkpoint 里的 lr_decay_steps 与新 job 不一致而拒绝 load(constant lr 下这个总步数本来就不影响学习率)。

当前入口固定 top_p=1.0, top_k=-1。这是 runtime contract,而不是随意的解码偏好:slime 的普通 rollout 能在 top_p<1 时携带截断分布 replay 元数据,但当前多轮 TrajectoryManager 只保存 exact token/logprob,不能把该 ragged 元数据跨 turn/fan-out 无损传到训练侧。验证配置使用同一采样分布;若未来扩展 manager 的 replay contract,再联合修改训练和 validation,不能只改脚本参数。

shopsim_episode_concurrency(正整数,默认 16)限制每个 rollout worker 中同时执行的完整 episode,普通 rollout 与 full-skill retry 共用名额。超额任务在异步 semaphore 上等待,不进入阻塞 reset 的线程池;名额覆盖 reset、交互和 close,在 group 协调前释放。该值不能超过分配给此 worker 的环境槽位数;多个 worker 或评测进程共享服务时,需要分别预算,不能各自按服务总槽位数配置。

Group-consistent 双层 mask

同一个 prompt 的 n_samples_per_prompt 个样本共享 slime group_index。runtime 先用 (curriculum state_id, group_index) 对 q 做一次确定性采样:

  • 命中 skill-free:整个 group 不注入任何 chunk;
  • 否则进入 assisted,再按每个 chunk 的 rho 采样一次联合 mask。

group 内每个 rollout 都校验 state_id 和完整 skill_ids 相同。worker 调度、并发完成顺序和断点恢复不会改变 treatment。assisted_empty 被显式记录,表示进入 assisted 分支后所有 Bernoulli chunk 均未命中;它不会被伪装成另一次 q 命中。

3. all-wrong full-skill retry 与 error analysis

每个训练 rollout 都写入:

runs/slime-training/<round-id>/group-XXXXXXXXX/
├── rollout-<sample-index>.json
├── triage.json
└── full-skill-retry.json      # 只在 all-wrong 时存在

只有当 group 中所有 rollout 都是正常、可评分终局且 r_success=0 时,runtime 才使用完整 current skill 额外生成一条诊断 trajectory。该 retry:

  • 不返回给 slime trainer;
  • 不参与原 group 的 GRPO advantage;
  • 成功时标为 model_internalization_deficit
  • 仍失败时只标为 full_skill_failure_pending_analysis,不会自动宣判 skill deficit;
  • 原 group 含环境/runtime 技术错误时不触发 skill 分析。

round 结束后复制并修改在线分析示例配置。外循环在 slime 训练期间启动 training watch-analyze,对着同一台 ShopSimulator 并发调用百炼 Failure Analyst,把 card 追加到 failure_cards.jsonl;训练 round 结束后再跑 training analyze,补上剩余 retry 并由 compiler 一次汇总:

Copy-Item configs\archive\trace2skill_online_analysis.example.yaml `
  configs\trace2skill_online_analysis.yaml
python scripts\run_shopsimrl.py training watch-analyze `
  configs\trace2skill_online_analysis.yaml
# slime 结束后:
python scripts\run_shopsimrl.py training analyze `
  configs\trace2skill_online_analysis.yaml

Analyst 只消费失败的 full-skill retry trajectory。ADD 必须有真实成功 counterfactual trial;REWRITE 的 target 必须是 current active chunk;gold firewall 与 cold start 相同;Analyst 仍可返回 NO_PROPOSAL。Compiler 在 round 结束时对所有 eligible cards 做一次 many-to-one 合并,近义机制去重后每轮最多输出 6 条 ADD/REWRITE candidatesmax_candidates 默认值及当前配置均为 6)。单条 trajectory card 不会直接成为 validation factor。第二轮起可在配置中指定上一轮 proposal_ledger.jsonl;compiler 只把它作为审计/去重状态,不注入 policy prompt,gate 输出会累计历史并更新本轮候选结果。watch-analyze 与训练共享环境服务,并发沿用 Trace2Skill 配置(当前为 2),不要把 analyst 塞进 slime 进程。

主要产物:

  • failure_cards.jsonl:privileged audit 与 deployable abstraction 分离;
  • candidate_pool.json:current chunks + validation-ready ADD/REWRITE;
  • proposal_ledger.jsonl:等待 validation 的候选;
  • analysis_summary.json:覆盖和候选统计。

4. 当前 checkpoint 的 randomized validation

在线 gate 使用固定 val 400 tasks,每个 task 一条 bare 加一条 masked rollout,统一拟合 \(R_i-B_i=C_i^\top\beta+\epsilon_i\)。先将当前 checkpoint 以 OpenAI-compatible endpoint 暴露;训练循环里用 4 卡 tp=1 dp=4 起 sglang,configs/qwen35_4b_val_slime.yaml 仍连 http://127.0.0.1:30000/v1,并发 32。每轮更新 experiment.namemodel.checkpoint_id 为实际冻结权重的唯一标识,并保持服务在 bare/masked/补测期间不换权重。

先用原始 evaluator 取得 bare validation score;已有相同 checkpoint、同协议的完整 run 时直接复用这批 traces,不重复调用模型。online_gate.bare_run_dir 指向该 run,experiment_config 引用同一份 bare YAML:

python scripts\run_shopsimrl.py run configs\qwen35_4b_val_slime.yaml
# 已有匹配的完整 bare run 时跳过上一条,并设置 bare_run_dir。
Copy-Item configs\archive\trace2skill_online_gate.example.yaml `
  configs\trace2skill_online_gate.yaml
python scripts\run_shopsimrl.py training online-gate-plan `
  configs\trace2skill_online_gate.yaml
python scripts\run_shopsimrl.py training online-gate `
  configs\trace2skill_online_gate.yaml

普通 current/ADD slot 使用 absent/present 两个等概率状态。存在 rewrite family 时,一个逻辑 slot 使用 absent/old/new1/... 外生互斥状态;old 和 new 不会同时出现在 prompt。所有版本 dummy columns 与其他 slots 一起进入 paired-delta 无截距 main-effect OLS,因变量是同任务的 masked reward 减 bare reward,各 r_* 分量同样逐项相减。绝不减全局 bare 均值、不另外估计截距、不复用上一 checkpoint 的基线。

共享数据契约、完整性/provenance 校验和解释边界见 paired_validation.md。训练中的 skill-free group 或 full-skill retry 不能替代固定 val 的 bare run。run_full_train.sh 每 20 个 slime step 停下来跑同一 checkpoint 的 bare val 与 masked gate,这不是 slime --eval-interval

选择顺序严格固定:

  1. 每个 rewrite family 内按 reward coefficient 选唯一 winner;相等时保留 old;
  2. loser 在全局排名前废弃;
  3. unchanged、ADD 与 rewrite winner 合成 survivor pool;
  4. 只对 coefficient > 0 的 survivors 做全局 top-K;
  5. current chunk 的非正或 budget-excluded 结果记为 retired;
  6. new rewrite winner 进入 active bank 时沿用 target 的逻辑 skill_id,避免每轮改写破坏 slot 身份。

原始 bare run 的 summary.json 继续作为训练汇报的 skill-free validation score;gate 的 summary.json 则是 masked 原始分数。contributions.json 单独保存差值、系数和 baseline 来源/hash,不覆盖两组绝对指标。符号用于当前选择规则,不单独证明正面/负面因果作用。

Gate 完成后输出 mask_assignments.json、通用 evaluator traces/summary、contributions.json、带最终 validation result 的 proposal_ledger.jsonlselected_skillbank.jsonselected_skill.mdonline_gate_manifest.json。任何 coverage 缺失都会保留 incomplete,不会冻结 selection。

5. 下一轮

用新 gate 的 selected_skillbank.json 显式选择下一轮 q/rho 参数并构建新的 curriculum,然后让 slime 从对应 checkpoint 继续一个有限 round。

注意 slime 的 NUM_ROLLOUT 是累计结束位置,不是本轮增量。当前默认及本次完整主实验为 4 段、每段 20 step:NUM_ROLLOUT 依次为 21、41、61、81(转换后的 ckpt 从 start_rollout_id=1 起)。早期脚本默认五轮、100 step;若要重用这一计划可显式设置 NUM_ROUNDS=5。每段结束后从已保存 checkpoint 继续,并更新 SkillBank / curriculum / round_id 与评测的 checkpoint identity。仍填上一段的结束值会使循环为空。run_shopsimrl_slime.sh 打开 --override-opt-param-scheduler,允许后一段用更大的 NUM_ROLLOUT 覆盖 checkpoint 里的 scheduler 总步数。

完整循环为:

validated SkillBank + explicit q/rho
        → frozen curriculum state
        → slime GRPO round
        → all-wrong full-skill retry queue
        → gold-aware online analysis + many-to-one candidates
        → same-checkpoint bare val score + reusable task traces
        → frozen-checkpoint paired-delta randomized val gate
        → replacement winner + positive top-K
        → next validated SkillBank

Test split 不参与上述任何更新。最终 checkpoint 只在 test 上分别报告 full-skill paired performance 与 skill-free performance,二者差值作为 internalization gap。

6. 无 GPU 链路回归

python -m pytest tests/test_slime_runtime_integration.py tests/test_training.py tests/test_episode_admission.py tests/test_training_preflight.py tests/test_paired_validation.py -q

7. 训练监控

W&B 的登录、run/group 组织、完整指标口径、Analyst/gate Table 与 Artifact、 offline sync、建议面板和异常排查见 training_monitoring.md

集成测试直接使用当前 slime 的 OpenAIAdapter、TrajectoryManager 和 Sample,以本地替身提供模型输出与环境,验证多轮 token/logprob/loss mask、工具修复、中止、group reward、diagnostic retry 不进入 trainer、失败 retry 到 online analysis/candidate pool、在线 gate 到下一轮 curriculum。未启动 GPU 更新或真实 Analyst/Compiler API。真实 SGLang parser/服务、环境联调和 Megatron forward/backward 留给少量卡 smoke。