# PPO训练设计与验收手册 > 状态:2026-08-30停止追加PPO训练并完成项目归档。P0的SFT与父checkpoint step20统一64K评测均已完成:test38均为6/38,Verified27按全部attempt均为9/27,配对检验均无差异。parent的Sphinx 8621单Pod 8Gi OOM作为基础设施异常保留,不再为单题恢复整套环境。腾讯云CVM、云硬盘及相关计费资源已释放;本文命令仅作为历史验收与未来重建手册,当前不能直接执行。 ## 1. 范围 PPO历史上与GRPO共用冻结数据、split、任务镜像、ACK兼容sandbox、Agent、rollout、Harbor verifier、terminal reward和运行日志。模型及Canary验证过64K能力;PPO smoke/soak为控制80GB trainer上的反向激活峰值,独立使用40K训练轨迹边界。共同链路不在本文重复;以[`README.md`](PROJECT_README.md)、[`AUTODL_SETUP.md`](AUTODL_SETUP.md)和现有GRPO入口为准。由于运行面已经释放,任何复测都必须先完成基础设施重建和全套preflight。 PPO仅增加以下训练语义: 1. critic/value模型为response token估计value; 2. terminal verifier reward与value共同生成GAE advantage和return; 3. actor执行PPO clipped update,critic执行value update; 4. checkpoint只在末步保存actor PEFT adapter与critic BF16模型;不保存actor基础模型、optimizer或extra状态。 这是一项独立PPO对照实验,不是Klear-AgentForge论文所述revised GRPO的复现。 ## 2. 实现边界 现有GRPO入口保持默认行为不变。PPO只暴露三个入口: ```bash make ppo-smoke make ppo-soak make ppo ``` - 通用资源、网络、数据和镜像检查复用现有preflight,不新增 `ppo-preflight`。PPO入口设置`PREFLIGHT_REQUIRE_CANARY_MODEL=false`,只跳过必须已停止的独立Canary vLLM存活探测;Python、Kubernetes、RBAC、Registry和BuildKit门禁仍全部执行。独立Canary链路继续由默认`make preflight`和`make network-smoke`严格验收。 - checkpoint延续训练通过actor `lora_adapter_path`与critic FSDP model-only shards warm-start;optimizer、scheduler、RNG、dataloader和run global step重新初始化,不属于严格resume。 - `ppo-smoke`固定执行1个update;`ppo-soak`只允许2–3个update,`PPO_SOAK_TOTAL_BATCHES>3`会在preflight前失败。soak只验证训练链路,不承担数据遍历或收敛判断。 - `make ppo`是formal入口:强制`TRAIN_TASK_LIMIT=0`、空`TRAIN_TASK_IDS`、`TRAIN_TOTAL_BATCHES=-1`、完整187题train split和确定性尾批补齐。默认batch 8时每epoch执行24个update,187题至少各暴露一次,尾批重复5题;每个epoch记录任务、仓库和重复暴露统计。 - formal训练期validation从冻结42题中按固定seed选择21题,约占train+validation使用量的10%;step 0/10/20/24各运行一次。checkpoint保存step 10/20/24。训练结束后再用单卡评测入口对选中checkpoint运行完整validation42。 - 第一轮smoke设置 `TRAIN_KL_BETA=0`;后续通过同一入口设置非零KL复测,不新增 `ppo-kl-smoke`。 - `make train-smoke` 和 `make train` 继续只表示已验证的GRPO链路。 已实现、待GPU验收的PPO特有配置包括: | 配置 | 计划值/语义 | | ------------------------------------ | ------------------------------------------------------------ | | advantage estimator | GAE | | `PPO_BATCH_SIZE` | 首轮 `4`,须能被trainer GPU数整除 | | `PPO_ROLLOUTS_PER_TASK` | 首轮 `1` | | `TRAIN_GAMMA` | `1.0` | | `TRAIN_GAE_LAMBDA` | `0.95` | | `TRAIN_PPO_CLIP_RATIO` | `0.2` | | `TRAIN_PPO_VALUE_CLIP` | `0.2` | | `TRAIN_PPO_EPOCHS` | 首轮 `1` | | `TRAIN_CRITIC_MODEL_PATH` | 默认与actor使用同一8B SFT checkpoint | | `TRAIN_CRITIC_LEARNING_RATE` | 首轮计划 `1e-5`,以实际稳定性为准 | | `TRAIN_CRITIC_MINI_BATCH_SIZE` | 4卡首轮 `4` | | `PPO_SMOKE_CONTEXT_WINDOW` | smoke/soak默认`40960`;同时约束Plus runtime与vLLM/训练边界 | | `TRAIN_CRITIC_MAX_TOKEN_LEN_PER_GPU` | smoke/soak自动与40K训练边界对齐 | | `PYTORCH_CUDA_ALLOC_CONF` | 分离smoke/soak固定`expandable_segments:False`以兼容CUDA IPC | | `TRAIN_CRITIC_OPTIMIZER_OFFLOAD` | 4卡smoke首轮启用 | | `TRAIN_KL_BETA` | 首轮 `0`;基础链路通过后再测试非零值 | | `TRAIN_PPO_ACTOR_LORA_ONLY` | 默认`true`;actor只保留PEFT safetensors adapter | | `TRAIN_WARM_START_CHECKPOINT` | 可选`global_step_*`目录;加载actor adapter与critic model-only shards | | `PPO_SOAK_MAX_TIMEOUT_RATIO` | 默认`0.35`;900秒对照运行显式设为`0.20` | | checkpoint上限 | actor 2048MiB、critic 24GiB、合计26GiB | PPO smoke/soak默认总长度40960、prompt 32768、response 8192;actor train及critic train/infer预算均为40960。该限制不修改64K模型配置、Canary或GRPO入口。 ## 3. 资源计划 | 阶段 | 资源 | 目的 | | ---------------- | ------------------------------ | ---------------------------------------------------- | | PPO micro smoke | 单机4×80GB | critic初始化、GAE、actor/critic backward及一次optimizer step | | PPO 3-step soak | 单机4×80GB历史结果 | 已通过连续更新、权重同步、有限值漂移与末步model-only checkpoint | | PPO 20-step soak | 单机5×80GB:4 trainer + 1 rollout | 验证单vLLM分离拓扑并观察critic、reward、solve rate和显存趋势 | | checkpoint延续验收 | 同一5×80GB环境 | 验证actor adapter和critic模型可加载;optimizer重新初始化 | | 非零KL验收 | 同一4×80GB环境,显存允许时执行 | 验证reference-policy路径 | | 正式PPO | 单机5×80GB当前候选 | 4卡actor/critic FSDP,1卡TP=1 vLLM;更多卡仅作未来余量选项 | 5卡分离拓扑固定为4个trainer rank加1个独立rollout GPU;每步batch、任务数和workflow实际并发默认均为8。单个vLLM保留最多10条并发序列的容量,超出即时KV容量时由scheduler排队;PPO训练请求边界为40K。 ## 4. 实现任务 1. 已完成:将GRPO与PPO配置解耦,`TRAIN_ALGORITHM`默认仍为`grpo`。 2. 已完成:固定rLLM版本的unified Verl路径原先硬编码`use_critic=False`;现以`patches/rllm-verl-native-ppo.patch`增加native critic、GAE和critic checkpoint,不使用group-relative advantage冒充PPO。 3. 已完成:critic已纳入colocated FSDP、gradient checkpoint和独立optimizer;四卡micro smoke已证明`Klear-AgentForge-8B-SFT`的value forward、backward和update可运行。 4. 已完成:PPO使用独立experiment、episode和checkpoint目录,并写出解析后的`config-summary.json`。当前末步策略只保存actor LoRA与critic BF16模型,不保存optimizer/extra。 5. 已完成:batch、mini-batch、epochs、clip、GAE、critic token budget、offload、KL和恢复参数均已展开;项目级非法组合在创建运行目录前失败。 6. 已完成:FSDP2 smoke支持参数化rank数;`make ppo-smoke`固定4 rank,`make ppo-soak`默认4个trainer rank并另占1张rollout GPU。 7. 已完成:增加PPO静态配置测试、shell解析检查和GRPO默认值回归检查。 8. 2026-08-17首轮四卡执行:公共preflight与四rank FSDP2 forward/backward/optimizer门禁通过;PPO在创建训练进程前因旧式`critic.model.fsdp_config.*`覆盖路径被Verl 0.8.0 Hydra struct拒绝。实现已改用`critic.fsdp.*`并补充critic dtype断言,下一轮仍从无KL micro smoke开始,不把本次配置失败记作训练结果。 9. Hydra路径修正后的第二轮执行已完成数据选择和配置摘要写入,但在colocated Ray worker构造时发现`TrainingWorker`被错误传入`role`。Verl 0.8.0要求先将`CriticConfig`转换为`TrainingWorkerConfig`,只传`config`,随后执行`reset()`并注册`value_loss`;value forward和critic update也必须分别走`infer_batch`与`train_mini_batch`。实现已按该完整契约修正。actor/critic模型、rollout、value/GAE和optimizer仍未完成本轮GPU验收。 10. Worker契约修正后的第三轮已证明`gpu_memory_utilization=0.35`可初始化四个rollout服务并完成4/4任务调度,但3题在Plus runtime静默复合自检中退出,另1题因缺少base-image rewrite而回退Docker Hub并超时。所有episode均为`error`、`steps_used=0`,原流程仍以0退出并保存actor/critic收尾checkpoint。根因之一是`.env.example`仍把curation指向历史`swesmith-20260803-v1/poc`(84 available/3 skipped),而训练数据已切换到`swesmith-curated-grpo-267-v1`,其权威profile为`swesmith-grpo-700-v1/grpo700`(103 available/2 skipped)。默认值现已对齐;实现同时强制Plus命令使用`/opt/mini-swe-agent-plus/bin`、逐项报告revision/schema/CLI失败,训练启动前逐题检查Dockerfile基础镜像存在私有rewrite。PPO smoke仅在values/returns/advantages存在、critic和actor均完成update、存在有效trajectory且双checkpoint分片存在时输出`PPO_SMOKE_PASSED`,否则非零退出。 11. 第四轮的逐项诊断确认4/4 episode均注入了schema v2 runtime,而训练契约固定为v3;这不是模型失败。空batch随后仍进入checkpoint保存,历史30GiB无效checkpoint叠加使71GiB数据盘达到100%,PyTorch最终产生128-byte损坏分片和`unexpected pos`。已清理无效checkpoint与回收站,恢复47GiB可用空间。训练入口现强制schema必须为v3且runtime镜像tag与revision/schema一致,并要求PPO smoke checkpoint文件系统至少有40GiB可用空间;backend在真实update marker不存在时先失败,禁止保存空checkpoint。 12. 2026-08-18四卡无KL micro smoke通过:4/4 rollout完成、183个有效agent step、平均reward 0.5;value、return、advantage、actor loss、critic loss及grad norm均为有限值,actor/critic update marker为true,model-only checkpoint约30GiB。初始critic explained variance较差属于单步未拟合状态,不作为链路失败。 13. 已增加`make ppo-soak`:默认对一个固定task batch执行3个连续update,且现已硬限制为2–3步;逐步累积`smoke-update.json.updates`并校验有限值。该门禁通过后必须转入完整数据formal训练,禁止扩展soak步数。 14. 首次3-step soak的4/4 rollout与公网请求均正常,但step 1在actor old-log-prob计算前失败:official 64K profile产生36322-token序列,而actor与critic动态batch预算仍为32768。该运行没有value/GAE、optimizer update或有效checkpoint。PPO smoke/soak现将两个预算自动对齐`TRAIN_MAX_MODEL_LEN`并在rollout前输出`PPO_SMOKE_TOKEN_BUDGET_GATE_PASSED`;正式训练仍允许显式配置预算。 15. token预算修正后的运行完成logprob、value/GAE和critic update,但actor backward申请8.06GiB时每卡仅余7.05GiB;训练Ray进程占43.78GiB,同卡vLLM仍占28.38GiB。该运行可能已更新critic但actor未更新,未写合格marker或checkpoint,必须整体丢弃。随后尝试通过`enable_sleep_mode=true`与`free_cache_engine=true`释放vLLM权重/KV显存;该尝试的兼容性结果见下一项。 16. 当前AutoDL vLLM 0.22.1报告sleep mode可用,但CUDA platform没有可调用的`is_cumem_allocator_available`接口;启用sleep在vLLM ModelConfig阶段以`cumem allocator is not supported`失败。非cuMem fallback关闭sleep/cache release并启用actor与critic参数/optimizer CPU offload。`gpu_memory_utilization=0.30`只有7.26GiB KV cache,无法承载64K所需的9.0GiB;colocated验证值因此使用0.35。历史入口曾用`PPO_SOAK_N_GPUS`整体扩容,后续6卡实测证明该方式会同步增加vLLM副本,现已由4+1分离拓扑取代。 17. 2026-08-19四卡3-step soak通过:12个episode、513个agent step、errors/timeouts为0,最终step最大序列55167 token,无截断或aborted;actor/critic连续更新并保存`global_step_3`。峰值reserved显存49.74GiB,单step约634秒。critic最终`vf_explained_var=-7.96`。当时曾据此安排20-step中等soak;该方案现已废弃,因为它重复同一小批任务,不能替代完整train遍历。 18. 首轮20-step中等soak在step 1–5完成有效actor/critic更新,step 6完成4个episode后于critic value inference停止。训练覆盖已将`critic.ppo_max_token_len_per_gpu`和`critic.forward_max_token_len_per_gpu`设为65536,但native `TrainingWorkerConfig.engine_config.infer_max_token_len_per_gpu`误从未展开的`ppo_infer_max_token_len_per_gpu`读取默认32768;首个34713-token序列因此触发`max_token_len >= max_seq_len`断言。修复将engine inference预算映射到`forward_max_token_len_per_gpu`,并在worker构造前输出`PPO_CRITIC_TOKEN_BUDGET_GATE_PASSED required=65536 infer=65536 train=65536`。由于本次`save_freq=20`,step 1–5只有指标和episode证据、没有可恢复checkpoint;重跑必须使用新run ID。 19. critic预算修复后,4卡及6卡colocated重跑仍会在长序列actor/critic更新阶段OOM。扩为6卡时Verl继续按trainer world size启动6个vLLM副本,所以每个rank仍承担训练状态和约28GiB rollout常驻开销;卡数增加不能解决单rank显存竞争。当前补丁把`separated_rollout`与`async_training`拆开:同步trainer使用4卡FSDP并保留critic/GAE,独立1卡只启动一个TP=1 vLLM,`max_num_seqs=10`,每个policy update结束后再同步权重。该路径完成静态验收,GPU结果待测。 20. 五卡分离路径随后完成单步更新、actor/critic checkpoint及trainer到单vLLM权重同步。并发8、20-step运行在step 3完成8个episode后进入actor backward,GPU 0已有67.19GiB进程占用、仅余12.00GiB,而单次反向仍需申请13.04GiB,触发CUDA OOM;PyTorch仅2.47GiB为reserved未分配,碎片不是主因。由于`save_freq=20`,本轮没有可恢复checkpoint。当时将PPO边界从64K降至49152;后续第23项记录900秒warm-start再次触发的48K峰值及40K调整。 21. Verl 0.8.0的`save_contents=[model]`仍会把LoRA actor的完整FSDP state写盘,不能满足adapter-only要求。训练入口现对固定0.8.0做fail-closed backport:actor保存前只保留`lora_`/adapter keys,训练结束后将小型FSDP LoRA shards合并成`lora_adapter/adapter_model.safetensors`与`adapter_config.json`并删除actor shards;导出配置显式保留训练使用的rank 32、alpha 16,critic继续保存BF16 model shards。末步门禁禁止optimizer/extra和actor分布式model文件,要求actor不超过2048MiB、critic不超过24GiB、合计不超过26GiB,并输出`PPO_COMPACT_CHECKPOINT_PASSED`。20-step AutoDL运行已验证该格式与大小门禁。 22. 2026-08-21五卡48K、并发8的20-step运行完整结束:160个episode、errors=0、timeouts=48、成功21个;后10步成功率16.25%,高于前10步10%,但样本不足以形成能力结论。critic VF loss从2.973降到0.091,explained variance仍为负;actor KL、entropy和grad norm稳定。末步导出actor 186086224 bytes、critic 15149181794 bytes、合计15335269261 bytes并通过`PPO_COMPACT_CHECKPOINT_PASSED`。Make退出2仅因旧门禁要求零timeout。现增加`TRAIN_WARM_START_CHECKPOINT`:严格核对base model、rank/alpha、四个critic shards及无optimizer/extra后,actor通过PEFT adapter初始化,critic仅加载model shards,随后输出`PPO_WARM_START_LOAD_PASSED`并从新run step 0训练;checkpoint summary记录父step与optimizer reset。 23. 从step 20以900秒启动的首轮warm-start完成8个rollout,但在actor `loss.backward()`时GPU 0已有76.61GiB进程占用、仅余2.59GiB,继续申请13.59GiB而OOM,未完成step 1或生成新checkpoint。同时确认driver-local monkey patch不会进入Ray `VerlTaskRunner`,所以只有actor adapter加载、没有critic加载标记。现改为在配置中携带`warm_start_model_only`契约,由Ray actor内的`load_checkpoint`加载critic并在rollout前输出门禁;PPO默认边界降至40960。 24. 修复后的40K启动已输出`PPO_WARM_START_LOAD_PASSED`,证明四个critic shard在Ray actor内加载成功,但首次trainer到独立vLLM的CUDA IPC同步因宿主机内核缺少expandable segments所需的`pidfd_open`而失败。与此同时PEFT对meta LoRA参数产生1008次copy no-op警告。分离拓扑现固定`expandable_segments:False`。首次仅向`PeftModel.from_pretrained`传`low_cpu_mem_usage=True`仍留下每rank 504个meta adapter tensor,fail-closed门禁在Actor初始化阶段正确终止。现针对PEFT 0.20.0改为显式两阶段路径:`get_peft_model`创建meta adapter,`load_peft_weights`读取CPU state,再由`set_peft_model_state_dict(..., low_cpu_mem_usage=True)`以assign materialize;随后核对checkpoint key/value、非meta、finite和非零并输出`PPO_ACTOR_LORA_LOAD_PASSED`。 25. 显式meta materialize路径在AutoDL四个FSDP rank上表现不一致:仅一个rank成功,另外三个rank仍各留下504个meta LoRA参数。V3加载器因此禁用PEFT的low-memory meta初始化,在每个rank以`low_cpu_mem_usage=False`创建真实CPU adapter并复制checkpoint state,再执行相同的key/value、非meta、finite和非零门禁。rank 32 adapter每个worker约增加333MiB临时CPU内存,四rank约1.3GiB,不增加GPU常驻参数量。 26. V3复测仍只有rank 0成功:非主rank的基础层由Verl保留在meta device,PEFT加载结束时会把adapter移动到基础层设备,导致已分配权重再次回到meta。V4不再调用该PEFT加载路径;它仅为标准LoRA checkpoint key插入`default` adapter名,核对504个目标参数全部存在后直接调用`module.load_state_dict(..., assign=True)`,避免自动设备迁移,再执行checkpoint key/value、非meta、finite和非零门禁。 27. V4已在四个FSDP rank上全部输出`PPO_ACTOR_LORA_LOAD_PASSED`,critic warm-start也成功。随后第二个batch中某个agent在40K上下文耗尽后仍以`max_tokens=0`发起40961-token请求;vLLM错误响应被gateway记录为空token trace,最终令Verl no-padding转换得到`prompt_len=0`。现保持40K不变:gateway在`max_tokens<=0`时返回`context_length_exceeded`且不转发、不记录非2xx或空token trace;PPO转换层再过滤空prompt/response step并输出丢弃计数,避免错误终止调用进入梯度batch。 28. 第二轮40K、900秒warm-start完成20步:160个episode、errors=0、timeouts=12、成功32个,actor adapter为186086224 bytes、critic为15149181794 bytes。前10步成功18/80,后10步14/80;Pygments单题贡献16/32次成功,另两题始终未成功,不能把稳定更新等同于整体能力提升。critic VF loss从0.0894降至0.0078,但explained variance仍为-1.689。训练期间仍偶发40961-token请求,因此PPO训练和test38评估覆盖official公开配置并强制启用256-token Plus context guard;Verified27保持独立32K历史协议。能力门禁只使用完整test38与Verified27;历史SWE-Smith30全部进入最终267且含train12,不作为held-out指标。 29. 父checkpoint `state/checkpoints/swesmith-curated-mini-swe-agent-plus-ppo-smoke-20260820T154406Z/global_step_20`以run `20260822-parent-full-184437Z`完成单卡并发8基线。test38使用40K、256-token guard,38/38有效、成功6题、pass@1 15.79%、Wilson 95%为7.44%–30.42%,0 exceptions/timeouts,耗时1h0m45s;成功题来自Parso 1、Click 1、python-docx 3和grafanalib 1。Verified27使用32K,27 attempts中26个有效、成功6题,scoreable pass@1 23.08%、Wilson 95%为11.03%–42.05%,全部attempt口径为22.22%;成功题为Django 10973/12741、Flask 5014、Requests 5414、Pytest 6202和scikit-learn 14710,`pylint-dev__pylint-4661`有1个`RuntimeError`,耗时51m46s。两个Harbor job分别为`ppo-ppo-parent-test38-20260822-parent-full-184437Z-r1`和`ppo-ppo-parent-verified27-20260822-parent-full-184437Z-r1`,聚合报告为`state/checkpoint-evals/ppo-parent-20260822-parent-full-184437Z/report.json`。vLLM全程无OOM、400/500或上下文错误。后续current在test38为4/38、Verified27按全部attempt仍为6/27,未形成随step单调提升;历史SFT单次结果的波动也禁止把差异直接归因于PPO。 30. 原始`Klear-AgentForge-8B-SFT`以base模式、run `20260822-sft-full-204740Z`完成相同seed `20260822`、单卡、并发8、retry 0正式基线。test38使用40K、256-token guard,38/38有效、成功4题、pass@1 10.53%、Wilson 95%为4.17%–24.13%,0 exceptions/timeouts,耗时31m31s;成功题为Conan `pr_12775`、Parso `d16kevtr`、python-docx `atty59fl`和grafanalib `7971608v`,平均input/output token与agent step分别为729925.1/9857.5/51.9。Verified27使用32K,27/27有效、成功4题、pass@1 14.81%、Wilson 95%为5.92%–32.48%,0 exceptions/timeouts,耗时24m50s;成功题为Django 12741、Requests 5414、Pytest 6202和scikit-learn 10297,平均input/output token与agent step分别为959324.6/11286.9/60.4。两个Harbor job分别为`ppo-sft-base-test38-20260822-sft-full-204740Z-r1`和`ppo-sft-base-verified27-20260822-sft-full-204740Z-r1`,聚合报告为`state/checkpoint-evals/sft-base-20260822-sft-full-204740Z/report.json`。父checkpoint相对本次SFT在test38为6/38对4/38(+5.26个百分点),Verified27按全部attempt为6/27对4/27(+7.41个百分点);置信区间高度重叠、每题仅一次采样,且历史SFT同一Verified27曾为8/27,因此该点估计不足以宣称PPO提升。 31. Effective step 40 checkpoint `state/checkpoints/swesmith-curated-mini-swe-agent-plus-ppo-smoke-20260821T114029Z/global_step_20`以run `20260822-current-full-214727Z`完成同口径LoRA评测。test38为38/38有效、成功4题、pass@1 10.53%、Wilson 95%为4.17%–24.13%,0 exceptions/timeouts,耗时1h3m31s;成功题为ExceptionGroup `1085phdz`/`xcemkw8b`、django-money `ddzxlx5n`和python-docx `rm3gxhmv`,平均input/output token与agent step为802617.2/9358.2/55.3。Verified27为27/27有效、成功6题、pass@1 22.22%、Wilson 95%为10.61%–40.76%,0 exceptions/timeouts,耗时46m56s;成功题为Django 12741、xarray 4094、Pytest 6202及scikit-learn 10297/14710/25973,平均input/output token与agent step为966376.6/10198.6/61.4。两个Harbor job分别为`ppo-ppo-current-test38-20260822-current-full-214727Z-r1`和`ppo-ppo-current-verified27-20260822-current-full-214727Z-r1`,聚合报告为`state/checkpoint-evals/ppo-current-20260822-current-full-214727Z/report.json`。current相对SFT在test38为0题/0个百分点、Verified27为+2题/+7.41个百分点;相对parent在test38为-2题/-5.26个百分点,Verified27按全部attempt为0题/0个百分点。current与SFT的test38成功集合无交集;Verified27与SFT及parent均只共享3个成功任务,不能把相同总分解释为稳定的相同能力,也不能从这次单样本宣布step 40优于step 20。 32. P0.1增加PPO critic观测:所有value/return/advantage与EV分解统一使用action-token `response_mask`;记录population std、EV的return variance与residual variance、有效action token和trajectory数。return variance为0时显式记录`critic/observability/ev_defined=0`且不伪造EV。训练batch同时保留可逆编码的task ID,并按任务记录episode/success/timeout/error、action token、agent step、reward及critic return residual。空mask、shape错配和masked非有限值均fail closed。critic worker的`vf_loss`、`vf_clipfrac`和`vpred_mean`另提供无重复前缀的canonical名称。 33. P0.2把冻结validation接入可审计的checkpoint选择闭环。完整PPO强制`val_before_train=true`、正数`test_freq/save_freq`,且每个validation step必须存在checkpoint;默认保留5个actor/critic checkpoint。validation默认greedy(temperature 0、top-p 1、每题1次),精确任务顺序与SHA-256写入`validation-contract.json`,逐次总体/逐任务结果追加到`validation-metrics.jsonl`。train/validation空ID、重复ID、任务重叠、镜像rewrite缺失均在rollout前失败。`select_ppo_checkpoint`以`val/success_rate_all`选最高分、同分选更早step,并报告相邻checkpoint成功任务Jaccard与patience建议;它不删除checkpoint,也不使用critic指标代替能力指标。 ## 5. 验收顺序 ### 5.1 静态与分布式门禁 - 现有测试与GRPO配置快照全部通过; - PPO配置展开后明确包含critic与GAE; - 6张GPU可见且没有独立Canary vLLM; - 五卡collective、FSDP forward/backward和optimizer step通过; - freeze manifest、267个任务镜像、训练Gateway配置和ACK容量门禁通过;PPO启动前不要求已经停止的独立Canary vLLM返回`/v1/models`。 - 当前选中train任务的每个Dockerfile基础镜像必须命中私有rewrite;存在skipped镜像或profile/dataset错配时在创建Ray worker前失败,禁止回退Docker Hub。 - Plus训练必须先输出`PLUS_RUNTIME_IMAGE_GATE_PASSED schema=v3`;5卡soak还必须输出`PPO_SEPARATED_ROLLOUT_GATE_PASSED trainer_gpus=4 rollout_gpus=1 total_gpus=5 replicas=1 max_num_seqs=10 async=false`。shell token门禁及native critic门禁都必须显示40960,显存门禁必须显示`allocator=expandable_segments:False`;warm-start还必须在rollout前依次显示`PPO_ACTOR_LORA_LOAD_PASSED`和`PPO_WARM_START_LOAD_PASSED`。 - 40K PPO还必须输出`PPO_CONTEXT_GUARD_PASSED window=40960 reserve=256`;vLLM日志不得再出现40961-token或`max_tokens=0`请求。 ### 5.2 无KL micro smoke 首轮只运行一个极小batch,关闭reference KL。验收必须同时满足: - rollout与verifier reward完整; - value、return和GAE advantage均为有限值; - actor policy loss、critic value loss和两个grad norm均为有限值; - `critic/observability/ev_return_variance`、`ev_residual_variance`、action token数和trajectory数存在且有限;若`ev_defined=1`,则必须同时存在有限的`critic/observability/ev`,若为0则该batch只标记EV不可定义,不得填0冒充EV; - `critic/vf_loss`、`critic/vf_clipfrac`和`critic/vpred_mean`三个canonical指标存在且有限; - 每个rollout任务都有`train/task//rollout/*`,进入critic的任务还有对应`critic/*`统计; - actor与critic各完成一次optimizer step; - actor权重同步到rollout后仍能产生合法动作; - 无OOM、NCCL timeout、Ray worker崩溃或ACK遗留Pod。 - 终端出现`PPO_SMOKE_UPDATE_PASSED`和最终`PPO_SMOKE_PASSED`;checkpoint根目录包含`smoke-update.json`,其中actor/critic update均为true。 - checkpoint必须包含actor的`lora_adapter/adapter_model.safetensors`、`adapter_config.json`、critic BF16 model shards和`checkpoint-summary.json`;不得保留actor完整model shards、optimizer或extra。 仅出现`global_step=1`、进程退出码0或actor/critic checkpoint不足以判定通过。 ### 5.3 恢复与KL验收 无KL smoke通过后先运行: ```bash make ppo-soak ``` soak必须输出`PPO_SOAK_PASSED`,`smoke-update.json.updates`必须依次包含全部预期step且每步指标有限;episode error必须为0,timeout比例不得超过配置门限。checkpoint只能包含末步目录,其中actor为标准PEFT adapter、critic为BF16 model shards,不得包含optimizer或extra状态。 soak通过后,使用actor adapter与critic模型重新启动一个batch,确认两侧权重可加载且optimizer按设计重新初始化。该紧凑checkpoint不支持optimizer、RNG、dataloader位置和global step的无损续接。加载通过且4卡仍有显存余量后,再设置小的非零 `TRAIN_KL_BETA` 验证reference logprob和KL指标。任一步失败都不影响现有GRPO入口。 3-step通过后直接进入formal训练,不再执行“20-step中等soak”。历史20-step run复用了同一批8题,其step 1–20不是对train split的遍历;这些结果仅保留为显存、checkpoint和critic观测历史,不能代表20个不同训练批次。当前入口会拒绝`PPO_SOAK_TOTAL_BATCHES=20`。 正式训练使用: ```bash export TRAIN_WARM_START_CHECKPOINT="$PWD/state/checkpoints//global_step_20" export PPO_FORMAL_BATCH_SIZE=8 export PPO_FORMAL_PARALLEL_TASKS=8 export PPO_FORMAL_TOTAL_EPOCHS=1 export PPO_FORMAL_EXPECTED_TRAIN_TASKS=187 export PPO_FORMAL_VALIDATION_TASK_LIMIT=21 export PPO_FORMAL_TEST_FREQ=10 export PPO_FORMAL_SAVE_FREQ=10 make ppo ``` 启动必须输出`PPO_FORMAL_PHASE_GATE_PASSED ... validation_tasks=21`、`TRAIN_VALIDATION_SUBSET_GATE_PASSED tasks=21 full_validation=42 train=187`和`TRAIN_FULL_DATASET_GATE_PASSED tasks=187 batch_size=8 batches_per_epoch=24 padded_exposures=5`。epoch结束必须输出`TRAIN_EPOCH_COVERAGE exposures=192 unique_tasks=187 repeated_exposures=5`。默认save/test间隔为10,保存step 10/20;末步24通过`save_final_checkpoint`补存,因此validation与checkpoint最终时点为step 0/10/20/24和step 10/20/24。warm-start仍须在首个rollout前输出`PPO_WARM_START_CONFIG_PASSED`和`PPO_WARM_START_LOAD_PASSED`。 ### 5.4 P0:SFT与parent step20的Klear 64K对齐评测 P0不启动训练,只比较原始SFT与父checkpoint `20260820T154406Z/global_step_20`。两个候选均依次运行冻结test38和既有SWE-bench Verified27;两面板统一使用`official-public-repro`、65536 context、200 agent steps、temperature 1.0、provider默认top-p、未显式设置max_tokens、seed `20260822`、retry 0和每题1次。Verified27保持既有27题,不扩大到完整500题。每个候选产生65个attempt,两个面板严格分开报告: ```bash PPO_EVAL_MODE=base \ PPO_EVAL_CHECKPOINT= \ PPO_EVAL_CANDIDATE=sft-base \ PPO_EVAL_RUN_ID=p0-klear64k-sft- \ PPO_EVAL_PROTOCOL=klear-64k-p0 \ PPO_EVAL_CONTEXT_WINDOW=65536 \ PPO_EVAL_CONTEXT_RESERVE_TOKENS=0 \ PPO_EVAL_SEED=20260822 \ PPO_EVAL_REPEATS=1 \ PPO_EVAL_PANELS=test38,verified27 \ PPO_EVAL_TEST_EXPECTED_COUNT=38 \ PPO_EVAL_CONCURRENCY=8 \ make ppo-eval-checkpoint PPO_EVAL_MODE=lora \ PPO_EVAL_CHECKPOINT="$PWD/state/checkpoints/swesmith-curated-mini-swe-agent-plus-ppo-smoke-20260820T154406Z/global_step_20" \ PPO_EVAL_CANDIDATE=ppo-parent \ PPO_EVAL_RUN_ID=p0-klear64k-parent- \ PPO_EVAL_PROTOCOL=klear-64k-p0 \ PPO_EVAL_CONTEXT_WINDOW=65536 \ PPO_EVAL_CONTEXT_RESERVE_TOKENS=0 \ PPO_EVAL_SEED=20260822 \ PPO_EVAL_REPEATS=1 \ PPO_EVAL_PANELS=test38,verified27 \ PPO_EVAL_TEST_EXPECTED_COUNT=38 \ PPO_EVAL_CONCURRENCY=8 \ make ppo-eval-checkpoint ``` 入口在执行任务前强制验证P0协议、官方采样配置、test38题数、Verified27 manifest/offline-v3任务与镜像rewrite,并对本地vLLM发送约64512-token真实请求,确认服务端没有退回32K/40K。该本机探针只临时使用`127.0.0.1`;Harbor任务必须继续使用`.env`中ACK Pod可达的公网/隧道`CANARY_MODEL_BASE_URL`,loopback地址会在启动前被拒绝。正式任务前还会创建临时ACK Pod访问该公网模型端点;不可达时不会进入38题评测。每个面板结束后若出现`NetworkConnectionError`立即终止,不继续下一面板。每次运行在`state/checkpoint-evals/-/jobs.json`记录完整协议和配置SHA-256,并生成分面板`report.json`。两个候选合并比较: ```bash PYTHONPATH="$PWD/src" python -m harbor_qwen_grpo_experiment.ppo_checkpoint_eval \ --run sft=state/checkpoint-evals/sft-base- \ --run parent=state/checkpoint-evals/ppo-parent- \ --json-output state/checkpoint-evals/p0-klear64k-sft-vs-parent.json ``` 每个面板分别给出benchmark Pass@1(全部attempt为分母,主指标)、Wilson 95%区间、无异常scoreable Pass@1、timeout/exception、token/step均值、逐任务胜/平/负和exact McNemar p值,不生成65题混合分数。基础设施异常在benchmark口径计为未通过,同时保留scoreable诊断口径;协议对象不完全相同的run会拒绝合并。 SFT侧正式run `p0-klear64k-sft-20260828T145119Z`已经完成。test38为6/38(15.79%,Wilson 95% 7.44%–30.42%,平均input/output tokens 614526.3/8537.6,平均46.8 agent steps,耗时34m59s);成功题为Parso `d16kevtr`/`xp4gz0nc`、Click `dcme2i6c`、jsonschema `3hn9c0ns`、python-docx `atty59fl`/`rm3gxhmv`。Verified27为9/27(33.33%,Wilson 95% 18.64%–52.18%,平均input/output tokens 749155.3/8726.9,平均51.4 agent steps,耗时31m33s);成功题为Django 10973/12741/13449、Flask 5014、Requests 5414、Pytest 6202、scikit-learn 10297、Sphinx 8621和SymPy 12481。两个面板均0 exceptions、0 timeouts;协议配置SHA-256为`681736ee9aaaa1a68d3dfe80fd437663236abebc1f6a2d69f6b5db2204109b22`。报告为`state/checkpoint-evals/sft-base-p0-klear64k-sft-20260828T145119Z/report.json`。 Parent step20正式run `p0-klear64k-parent-20260828T160138Z`也已完成。test38为6/38(15.79%,Wilson 95% 7.44%–30.42%,平均input/output tokens 907401.1/9905.0,平均55.8 agent steps,耗时1h21m41s);成功题为Conan 12775、Parso `d16kevtr`、django-money `ddzxlx5n`、Click `dcme2i6c`、jsonschema `3hn9c0ns`和grafanalib `7971608v`。Verified27按全部attempt为9/27(33.33%,Wilson 95% 18.64%–52.18%),scoreable为9/26(34.62%,Wilson 95% 19.41%–53.78%),平均input/output tokens 1099618.6/9923.3、平均65.3 agent steps、耗时1h10m34s;成功题为Django 10973/12741、Flask 5014、Requests 1724/5414、Pytest 6202、scikit-learn 10297/14710和SymPy 12481。Sphinx 8621的agent主容器在8Gi limit下OOMKilled(exit 137),Harbor记录1个`NonZeroAgentExitCodeError`;原始attempt必须保留,若恢复则单独标记infrastructure recovery。 严格配对比较中,test38为parent 3胜32平3负、benchmark delta 0、exact McNemar p=1.0;Verified27 benchmark为2胜23平2负、delta 0、exact McNemar p=1.0。scoreable Verified27因Sphinx没有有效parent值,为2胜23平1负,不能替代全部attempt主口径。parent相对SFT的平均input tokens在test38/Verified27分别增加约47.7%/46.8%,平均agent steps增加约19.2%/27.0%,运行时间约为2.33/2.24倍,但两个面板成功数均未提高。P0结论是不支持继续追加PPO训练;Sphinx单题16Gi恢复只用于补齐审计,不会改变该停止判断。 ### 5.5 单卡冻结validation42评测 仅评测actor泛化时不使用`make ppo`:该入口即使设置`TRAIN_VAL_ONLY=true`,仍会按PPO/GAE初始化4-way FSDP critic与完整trainer拓扑。改用单卡checkpoint评测入口,只加载基础模型与actor LoRA,不恢复critic、optimizer或trainer worker: ```bash PPO_EVAL_MODE=lora \ PPO_EVAL_PROTOCOL=validation42 \ PPO_EVAL_CHECKPOINT="$PWD/state/checkpoints/swesmith-curated-mini-swe-agent-plus-ppo-smoke-20260821T114029Z/global_step_20" \ PPO_EVAL_CANDIDATE=ppo-current-val42 \ PPO_EVAL_RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)" \ PPO_EVAL_SEED=20260822 \ PPO_EVAL_REPEATS=1 \ PPO_EVAL_PANELS=validation42 \ PPO_EVAL_VALIDATION_EXPECTED_COUNT=42 \ PPO_EVAL_CONTEXT_WINDOW=65536 \ PPO_EVAL_CONCURRENCY=1 \ PPO_EVAL_GPU=0 \ make ppo-eval-checkpoint ``` 入口在启动vLLM前强制验证冻结validation目录恰好42题及64K context,并在run目录保存由公开配置派生的确定性Agent配置(temperature 0.0、top-p 1.0);运行协议固定为retry 0,并将整体与逐题结果写入`state/checkpoint-evals/ppo-current-val42-/report.json`。该路径评测actor成功率,不产生critic EV或VF loss。 ### 5.6 P0结论边界 P0是固定65题上的配对小样本实验,不等价于Klear完整SWE-bench Verified 500题复现。只有当两面板方向一致、基础设施异常可接受且任务级翻转支持总体差异时,才能把结果记录为“在P0诊断集上优于SFT”;即使如此也不外推为完整榜单提升。若两面板方向冲突或McNemar证据不足,保留点估计并停止因单次结果追加PPO训练。 ## 6. 正式训练放行条件(历史标准,当前未放行) - 4卡历史无KL门禁及5卡分离拓扑、紧凑checkpoint加载和计划中的KL策略均有明确结论; - 20-step soak的每一步actor/critic及critic监控指标均为有限值、episode无error、timeout比例不超过门限,且只生成末步model-only checkpoint; - 至少一个batch出现有效reward与非零actor/critic更新; - actor adapter与critic模型重新加载后能够继续更新;optimizer重新初始化必须被明确记录; - checkpoint文件系统至少有30GiB可用空间,末步总checkpoint不得超过26GiB; - 每卡峰值显存、主机内存、磁盘增长和单batch耗时均已测量; - 已确认5卡拓扑只产生一个vLLM server,4个trainer rank不再承载rollout常驻显存; - validation/test仍保持冻结且不参与训练。 - P0的SFT与parent step20统一64K test38/Verified27报告完成前,不追加训练step;P0不以训练reward或critic指标替代能力评测。 最终P0没有显示parent相对SFT的总体增益,因此上述条件没有转化为新的正式训练授权。项目归档后,即使未来重建基础设施,也应先提出新的实验假设、预算和held-out判据,再决定是否恢复训练。 ## 附录A:故障记录要求 OOM、NCCL、Ray或ACK故障的详细排查命令放在后续运行记录或本附录,不占用主流程。每次故障至少记录:run ID、代码版本、完整配置、GPU拓扑、各rank峰值显存、异常类型、残留进程/Pod、是否产生checkpoint,以及恢复动作。不得把基础设施异常计作模型reward 0,也不得用删除整类错误记录的方式恢复作业。