feat: add MemAgent multi-turn training example - #270
Draft
TabsPhasers wants to merge 17 commits into
Draft
Conversation
added 17 commits
August 4, 2026 13:26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
本 PR 为 ReLax 增加一套可运行的 MemAgent 训练与评测流程。
MemAgent 先按 token 将长文档切成多个 chunk。每轮模型根据
question + previous memory + current chunk生成新的 memory,并用新结果覆盖旧 memory。处理完全部 chunk 后,模型根据question + final memory生成最终答案。训练阶段会保存每次 memory 更新和最终回答,并将它们逐轮展开成独立训练数据。主要改动包括:
examples/mem_agent/实现多轮 memory rollout、最终答案 reward 和轨迹展开;tokens、response_length、loss_mask、rollout_log_probs;关联 #236 和 Contributor Program Task 36。
背景
ReLax 已支持替换 rollout 生成逻辑。MemAgent 还需要处理动态的 1:N 训练数据展开:一条 trajectory 会根据实际 chunk 数生成多条训练行。
TransferQueue 的容量和 Actor 的读取数量如果仍按 trajectory 数计算,可能丢失部分 memory turn、少读训练行,或因等待错误行数而卡住。GRPO normalization 的执行顺序也很重要;先展开 turn 会让 chunk 较多的问题在组内统计中占据更高权重。
本实现明确了两项约束:
实现
1. 多轮 memory rollout
每个 chunk 对应一次独立请求。下一轮重新构造 prompt,只传入截断后的 memory 文本。空的 memory 更新同样会覆盖旧值,确保每轮更新语义一致。
最新 Task 36 验收要求不限定模型规模。本次正式复现固定使用
Qwen/Qwen3-0.6B@c1899de289a04d12100db370d81485cdf75e47ca。ReLax 与 VIME先按已经完成验证的短配方进行控制变量训练,再用相同的长文档配置评测
RULER-HQA,检查从训练长度到更长文档的泛化效果。
控制变量训练配方如下:
Qwen/Qwen3-0.6B@c1899de289a04d12100db370d81485cdf75e47casplit1e-60.001长文档评测配方如下:
BytedTsinghua-SIA/hotpotqa@27275ff4fee67ac0acb6478e405e7ac07efbdc1a的eval_50/200/800最终 prompt 的输入范围固定为 question、最后一轮 memory 和答案格式说明。原始 context、当前 chunk 和此前各轮 memory 均在该阶段结束前释放。
训练和评测使用不同的文档长度是本次预先固定的泛化测试设计。VIME 对照训练必须采用与 ReLax 相同的条件,基础模型、VIME 和 ReLax 也必须使用相同的评测条件。现有 ReLax
iter_0000074使用短配方训练,因此不能将其描述为 VIME 默认长配方的训练结果。2. 训练数据展开
rollout 保存全部 memory turns 和 final turn。converter 先校验整条 trajectory,然后调用 ReLax 现有的 GRPO reward 后处理,最后逐 turn 展开。
每条轨迹必须满足以下条件:
memoryturn 后只能有一个finalturn;response_length为正,且处于对应 turn 的 token 上限内;response_length <= len(tokens);len(loss_mask) == response_length;len(rollout_log_probs) == response_length;默认信用分配方式为
split:一条 trajectory 的 advantage 除以 turn 数,每个 turn 获得其中一份。代码也实现并测试了share,该模式为每个 turn 分配完整 advantage。3. Reward 计算
中间 memory turn 不单独计分。reward function 从最终回答的
\boxed{}中抽取答案,再按照 HotpotQA normalized exact match 计算一次 0/1 reward。缺少合法 box、答案不匹配和生成失败的结果均记为 0。输出格式属于训练目标。训练与评测共用同一套答案抽取和规范化逻辑。
4. ReLax 接入方式
MemAgent 使用现有扩展点接入:
--custom-generate-function-path:多轮 memory rollout;--custom-convert-samples-to-train-data-path:trajectory converter;--custom-rm-path:最终答案 reward;--custom-config-path:chunk、memory、展开和信用分配配置。SGLang 核心保持原样,Controller 只处理通用的训练数据行数和队列分组信息。
框架侧增加了动态 1:N 展开需要的通用能力:
custom_train_sample_expansion_factor为 TransferQueue 预留最大容量;custom_train_data_group_size指定转换后数据在 TransferQueue 中的分组大小;RolloutFnTrainOutput.train_row_count返回当前训练分区实际写入的行数;rollout_id等待行数,生成或转换失败时同步结束等待;train_row_count专门承载训练行数,metrics继续用于监控指标。普通 1:1 rollout、Agentic 和 Forge 的非展开路径使用默认值train_row_count=None。启用custom_train_expanded_batch的高层 custom rollout 必须返回精确行数,缺失时会直接报错。主要文件
examples/mem_agent/rollout.py,prompts.pyexamples/mem_agent/convert.pyexamples/mem_agent/reward.py,metrics.pyprepare_data.py,prepare_data.shconfig*.yaml,run-*.sheval_ruler_hqa.py,compare_results.pytests/examples/mem_agent/及相关回归运行方式
准备固定版本的数据:
运行 Qwen3-0.6B 配方的 2-step 冒烟测试:
同一入口可通过
NUM_ROLLOUT、LOAD_PATH和START_ROLLOUT_ID运行完整训练或从 checkpoint 恢复。训练结束后使用convert-to-hf.sh转换选定 checkpoint,再通过统一评测入口运行 HotpotQA/RULER-HQA。训练日志、逐 rollout 结果、TensorBoard event、GPU 监控和 reward 汇总均保留在RUN_ROOT。在基础模型、同配方 VIME checkpoint 和 ReLax checkpoint 之间进行长文档同条件比较:
BASE_MODEL_PATH=/data/models/Qwen3-0.6B \ VIME_MODEL_PATH=/data/checkpoints/vime-hf \ RELAX_MODEL_PATH=/data/checkpoints/mem-agent-relax-hf \ TOKENIZER_PATH=/data/models/Qwen3-0.6B \ DATA_DIR=/data/mem-agent \ RESULTS_DIR=/data/results/vime-vs-relax \ LENGTHS="50 200 800" \ bash examples/mem_agent/run-paired-eval.sh评测产物包括逐样本 JSONL 和汇总 JSON。比较脚本会检查结果是否为空、请求是否成功,以及输入 SHA、评测格式和控制变量是否一致。
测试结果
本文对应的本地提交为
1f66ac8632d3b379116cfd73dcd34e3c60f7bc22,父提交为 merge commit467cfa1,其中已合入origin/main@682d474。本地工作树干净。/dev/shm用例)tests/test_s3_model_loader.py其余用例pre-commit run --all-files --show-diff-on-failurebash -n examples/mem_agent/*.shgit diff --check被排除的断言要求系统提供默认
/dev/shm,本机 macOS 不具备该目录。该文件的其余 50 个用例已经执行。本轮测试全部使用 CPU。测试覆盖:
split/share信用分配;Qwen3-0.6B 已完成实验
Qwen3-0.6B 是本次正式复现模型。以下结果已经覆盖真实训练控制流、reward 趋势、checkpoint 转换和 HotpotQA 留出集评测。剩余效果实验为 VIME 控制变量训练以及三个模型的 RULER-HQA 长文档对照评测。
实验配置
Qwen/Qwen3-0.6B@c1899de289a04d12100db370d81485cdf75e47cahotpotqa/hotpot_qa@1908d6afbbead072334abe2965f91bd2709910ab,distractor train先在 4,000 条 HotpotQA 候选上运行 Pass@8,共完成 32,000 条 trajectory,请求错误数为 0。随后固定 1,000 个唯一训练 ID。另取 500 条与训练集无交集的留出评测数据(held-out),选取过程与基础模型的答题结果无关。
2-step 冒烟测试
单张 RTX 4090 连续完成 2 个 rollout step。每一步的数据数量为:
Ray job 状态为
SUCCEEDED,运行期间没有 hang 或 OOM。第二步记录到非零 loss 和 gradient,并成功保存iter_0000001checkpoint。这组结果用于确认训练链路和行数契约可以连续运行。Reward 趋势
20-step 趋势检查中,前 5 个 rollout 的 reward 均值为
0.365625,后 5 个为0.734375,绝对增加0.36875。之后从
iter_0000019恢复训练,连续完成 rollout0..91,Actor 训练指标记录到 step 90。训练 reward 已形成明显提升趋势,归档中最新完整、可转换的 checkpoint 为iter_0000074,后续统一使用该 checkpoint 评测。按 trajectory 逐题汇总的 reward 如下:
训练归档保留 3 个原始 TensorBoard event 文件,共 77 个 scalar tags、6,777 个 scalar points。指标包括 reward、loss、grad norm、KL、entropy、learning rate、log-prob 差异、response length、truncation、throughput 和各阶段耗时。
同条件留出集评测
iter_0000074已转换为 Hugging Face 格式。它与原始 Qwen3-0.6B 在同一组 500 条留出数据上各评测一次。两组评测使用相同的数据、tokenizer、prompt、chunk/memory 配置、采样参数、停止条件、seed 和 evaluator,均完成 500/500 条请求,错误数为 0。iter_0000074同一组 500 题中,boxed EM 有 148 条从错变对、25 条从对变错、60 条两边都对、267 条两边都错。基础模型有 189 条输出缺少可解析的 box,checkpoint 中该数量为 0。checkpoint 仍有 292 条 boxed 答案不匹配,因此当前提升同时包含格式遵循和答题能力两方面变化。
验收进度
iter_0000074的长文档评测sub_EM绝对差不超过 3pp兼容范围与已知开销
train_row_count保持默认值None;train_row_count是RolloutFnTrainOutput的可选字段,现有非展开调用方保持原用法;剩余效果实验
官方验收要求中,流程可运行、行为正确和训练数据正确已有完整证据。效果验收中的 reward 提升和 HotpotQA 基础模型对照也已完成。剩余工作集中在一组控制变量实验:
512/128/4短配方、GRPO 参数、seed、训练步数和样本暴露量对齐 ReLaxiter_0000074的实际训练记录。iter_0000074运行同条件 RULER-HQA 评测。评测固定 chunk 上限 2048 tokens、memory 上限 1024 tokens、最大 chunk 数 64,并覆盖eval_50/200/800。abs(sub_EM_ReLax - sub_EM_VIME) <= 3pp。VIME 训练和正式评测将按现有 ReLax 实验的同一证据口径归档,包括 debug 日志、TensorBoard event、rollout、checkpoint、转换产物、配置、环境信息和 SHA-256 清单。PR 当前公开检查已通过;完整 CPU 回归结果和 macOS 上不适用的
/dev/shm用例已在上文分别说明。ReLax
iter_0000074使用512/128/4短配方训练;RULER-HQA 长文档配置用于检验其泛化能力。最终效果验收结论取决于 RULER-HQA 基础模型提升和 VIME/ReLax 3pp 对照结果。审核顺序
examples/mem_agent/rollout.py:检查每轮 memory 覆盖和 final prompt 的输入范围;examples/mem_agent/convert.py:检查 reward normalization 的执行顺序和全部 turn 的展开结果;relax/engine/rollout/base_types.py、relax/engine/rollout/sglang_rollout.py和relax/distributed/ray/rollout.py:检查实际行数的产生、传递和等待逻辑;relax/utils/utils.py、Controller 和 Actor:检查 converter、队列容量、数据分组和消费数量;tests/examples/mem_agent/及展开行数相关测试:检查异常输入和普通训练路径;custom_train_data_group_size表示 TransferQueue 一次交给训练端的行分组大小。默认 converter 继续按原有 group size 传输数据;MemAgent 在 trajectory 维度完成 GRPO normalization 后,将每个展开行作为一个可独立消费的分组。变更类型
检查清单
examples/mem_agent/目录git diff --check均已通过