Repository navigation
【No.9】新增 Agentic Rollout 停止原因分布与轮数分位数指标 - #325
ldemon2333 wants to merge 1 commit into
Conversation
Nyanpasu 审查看板审查状态: ✅ 已通过 审查版本: 已复审 PR #325 修订 22f0a9f。新增 rollout 停止原因计数/占比与 turn 分位数指标,并修复空输入与 metadata=None 两处健壮性问题。作者的修复以 13 类白名单归一化停止原因:env_error:<明细> 收敛为 env_error,未登记值归入 other,每批次固定输出 26 个键并补零;已独立验证该逻辑及白名单对全部真实生产者的覆盖。证据更正:我此前称 deepeyes_v2_agentic 的 env_error:<明细> 链路可复现,经按 AST 复核该示例全部 12 处 ToolObs 构造均为 done=False,agent.py:230 的该分支不可达,故撤回“仓库内可复现”的表述;防御性归一化仍然有效,自定义 Agent 写入动态 metadata.stop_reason 的通用风险依然成立。无遗留问题,批准合并。
Powered by Nyanpasu with deepseek-v4.1-flash-ali medium, please check the suggestions carefully.
|
rai-studio-bot
left a comment
There was a problem hiding this comment.
整体评估:本次改动为纯增量,且附带两处合理的健壮性修复——空样本提前返回 {}(两个调用点 _log_rollout_data/_log_eval_rollout_data 均已做保护),以及 partial-rollout staleness 计算容忍 metadata=None。stop-reason 优先级、turn 归一化与分位数逻辑清晰且测试充分(48 项新指标测试覆盖空输入、归一化、分母语义、只读性与训练/评估发布)。zip(strict=True) 与 int | None 均满足仓库 >=3.10 要求,无兼容性问题。
一项非阻塞建议见行内 P3:stop_reason 以原始字符串直接作为指标 key,建议归一化以避免无界的指标命名空间。不阻塞合并。
| for reason, count in sorted(reason_counts.items()): | ||
| metrics[f"stop_reason/{reason}/count"] = float(count) | ||
| metrics[f"stop_reason/{reason}/ratio"] = count / len(samples) |
There was a problem hiding this comment.
stop_reason 直接以原始字符串作为指标 key(stop_reason/{reason}/count|ratio)。当前 Agentic 路径的 stop_reason 是有界枚举(end_turn/max_tokens/tool_use),但该函数对任意字符串来者不拒——测试用例中的 env_error:detail/part 即说明 reason 可能携带明细。若某些环境把可变明细(错误信息、ID 等)写入 stop_reason,会在 wandb 等后端产生无界增长的指标 key,且含 / 时还会被解析成嵌套路径。
建议对 reason 做归一化/白名单(例如仅保留已知枚举、其余归入 other,或截断到首个分隔符),以避免指标命名空间膨胀。非阻塞。
There was a problem hiding this comment.
补充证据:该风险在仓库内已有具体触发链,特此从"潜在"升级为"可复现"。
examples/deepeyes_v2_agentic/app/env_deepeyes_v2.py:288在搜索异常时写入error=str(exc)(任意异常消息,非枚举)。examples/deepeyes_v2_agentic/app/agent.py:230将其插值为stop_reason = f"env_error:{obs.error}"。- 同文件
agent.py:239通过metadata.stop_reason返回,经 session 导出合入样本元数据(即本 PR 描述中引用的relax/agentic/session/service.py导出链路)。
因此本 PR 会生成形如 stop_reason/env_error:<异常原文>/count 的指标 key:异常消息长度与内容不可控,且常含空格、:、URL 等字符,在 wandb 等后端会产生无界且难以阅读的嵌套指标名。该路径在搜索/网络类异常下即可触发。
依据本仓库既有惯例,建议按白名单归一化——examples/on_policy_distillation/agentic_opd/alfworld/reward_alfworld.py:33 的 STOP_REASONS = ("env_done", "max_turns", "finish_length", "context_exhausted") 即为同类做法,且它对白名单内所有原因每步都输出(缺失为 0),时间序列不会出现缺口。可考虑保留 env_error 类别本身、丢弃其后可变明细,其余未知值归入 other。
优先级调整为 P2(可复现的可操作边界问题),仍不阻塞合并。
There was a problem hiding this comment.
已修复,针对 deepeyes_v2_agentic 例子,env_error: 统一为 env_error
There was a problem hiding this comment.
核对确认:你的判断是对的,我此前"该路径在搜索/网络类异常下即可触发"的表述不成立,特此更正。
我按 AST 逐一检查了 examples/deepeyes_v2_agentic/app/env_deepeyes_v2.py 中全部 12 处 ToolObs(...) 构造:全部为 done=False,没有通过位置参数传入 done,也不存在对 obs.error 的后续赋值;agent.py 自身不构造 ToolObs。因此在 agent.py:230 的 if obs.done: 分支内 obs.error 恒为 None,f"env_error:{obs.error}" 当前是不可达分支——搜索超时等异常(返回 done=False)不会产生 env_error:<明细>。
结论修正如下:
- 上述具体链路不可复现,我 P2 证据中"仓库内可复现"的部分予以撤回,优先级依据(P2)相应失效。
- 但"外部 Agent 自行写入动态
metadata.stop_reason会使指标键无界"这一前提仍然成立(正如你在 PR 描述中所说明的)。因此本次白名单归一化仍是有效的防御性修复,我对此修复的认可不变。
感谢补充这一细节,更正后的证据记录不影响 PR 结论(已批准)。
# ⭐ Feature - Aggregate stop-reason counts and ratios over all input samples. - Add turn-count P50/P90/P95/P99 while retaining min/mean/max. - Handle empty batches and missing metadata without changing rollout control flow. --- # 🐛 Bug Fix - Bound stop-reason metrics to 13 categories and emit zeros for absent categories. - Fold env_error details into env_error and route unrecognized reasons to other. - Preserve raw sample metadata and primary-field precedence during normalization. --- # ✅ Tests - Cover long dynamic reasons, bounded keys, zero filling, SessionOutput exports, and logging. - Validate 125 CPU tests; one existing Megatron-dependent test is skipped.
6891f0f to
22f0a9f
Compare
|
@SigureMo 任务已完成,请求 review。 |
|
没有成功报名第 9 题哦,目前成功报名的是 3、4 两题 |
哦哦,没事,就先放在这里好了,给其他同学参考 |
rai-studio-bot
left a comment
There was a problem hiding this comment.
本次修订(22f0a9fb)已解决此前的 P2 问题(作者亦在讨论串中确认修复),未发现遗留问题。
已验证:
- 白名单生效:13 个类别每批次固定输出 26 个
stop_reason/*指标,缺失类别补 0,时间序列不再出现缺口。 env_error:<明细>收敛为env_error,其余未登记值归入other——原始字符串不再进入指标 key,此前的无界指标名问题已消除。- 白名单覆盖全部真实生产者:遍历
relax/与examples/中所有写入顶层stop_reason/rollout_stop_reason的位置(含字典字面量、三元表达式、f-string 与**info展开),实际取值集合为budget_exhausted、completed、context_exhausted、discarded_by_pipeline、env_done、env_error:*、finish_abort、finish_length、format_error、llm_unavailable、max_turns,均已在白名单内。测试中归入other的end_turn/max_tokens/tool_use仅用于 Anthropic 兼容 API 的出站响应构造(relax/agentic/session/service.py),no_tool_call仅存在于env_infos列表,二者都不会写入顶层字段,因此排除是正确的。 - 新增的
test_rollout_metadata_dynamic_reasons_have_bounded_keys用 100 条env_error:<明细>与 100 条超长自定义 reason 断言 key 集合恰为 26 个,直接锁定了该问题;test_rollout_metadata_known_reasons_preserved对每个类别做恒等断言,可防止白名单笔误。
验证方式说明: 我将 _compute_rollout_metadata_stats 的真实源码按 AST 抽取后配合 NumPy 1.26.4 执行,覆盖类别恒等映射、env_error 收敛、other 归并、26 键上界、补零、ratio 求和为 1、空输入与不改写入参等语义,全部通过。此沙箱内 pytest 无法运行(对平凡用例亦 core dump,属环境问题),故未执行完整测试文件,建议以 CI 结果为准。
|
本赛题已经由 #330 合入并锁定,感谢参与~ |
关联 issue
关联 issue:【No.9】Agentic Rollout 停止原因分布与轮数分位数指标。
变更内容
为 rollout 样本指标新增停止原因分布和轮数分位数,并保留现有
num_turn/min、num_turn/mean、num_turn/max指标。聚合时把动态错误明细归入固定类别,避免异常原文、URL、请求 ID 不断生成新的监控指标键。停止原因按类别分别统计
count和ratio。聚合函数返回的键为:stop_reason/{reason}/count:当前批次中该原因的样本数。stop_reason/{reason}/ratio:该原因的样本数除以当前批次全部有效样本数;非空批次的所有原因占比之和为 1。例如一个批次共有 100 条样本,其中
env_done72 条、max_turns18 条、缺少有效停止原因 10 条,则聚合结果包含:经现有训练日志入口上报后,键会增加
rollout/前缀,例如rollout/stop_reason/env_done/count;评估入口则增加eval/{dataset}/前缀。样本元数据中的停止原因仍由各 rollout 或 Agent 应用写入;指标出口统一归一化为以下 13 个固定类别。每个非空批次输出全部类别的
count和ratio,未出现类别为 0,因此停止原因指标始终为 26 个键。空批次返回{}。env_donemax_turnsfinish_lengthbudget_exhaustedfinish_abortcompletedcontext_exhaustedllm_unavailablediscarded_by_pipelineformat_errorenv_errorenv_error或以env_error:开头;只保留类别,错误明细仍留在原始元数据中。otherEnv_Done。unknownrollout_stop_reason和stop_reason均缺失、为空或不是有效字符串。同时新增
num_turn/p50、num_turn/p90、num_turn/p95、num_turn/p99,用于观察轨迹轮数分布和长尾。实现方式
rollout_stop_reason,兼容现有 Agentic 的stop_reason字段;缺失或空原因归入unknown。rollout_turns读取轮数,采用线性插值计算分位数;缺失或非法值沿用单轮默认值 1。metadata=None。rollout/与eval/{dataset}/上报路径,保持 rollout 调度和停止条件不变。停止原因字段的兼容差异
rollout_stop_reason和rollout_turnsrollout_stop_reasonmetadata.stop_reason和metadata.num_turnstop_reason;轮数仍读rollout_turnsrollout_turns,没有统一生成rollout_stop_reasonunknownAgent 进程输出的元数据经 session/service.py 第 2745 行的导出入口 和 第 2832 行的构样本逻辑 合入样本。因此只读取任务建议的
rollout_stop_reason会漏掉现有 Agentic 示例的停止原因。聚合端兼容读取已有字段即可补齐。DeepEyes Agentic 的
metadata.num_turn来自env.turn,与导出轨迹中已提交的模型响应节点数可能不同。例如生成因长度限制结束时,应用会在调用env.step前退出。因此本 PR 不将metadata.num_turn作为轮数回退来源,统一使用 SessionForest 导出的rollout_turns;该字段缺失或无效时沿用默认值 1。测试与验收
验收场景与测试函数对应
以下 15 个场景对应 tests/distributed/ray/test_rollout_metadata_metrics.py 中的实际断言。部分场景共用同一个测试函数;指标测试文件现有 12 个函数,经参数化展开为 80 个用例。
{};将RuntimeWarning视为错误时仍通过。空批次下总入口允许args=None。None、{}和字段缺失均得到unknown/ratio = 1,轮数统计为 1;num_turn和轨迹记录中的turn_count不替代rollout_turns,并验证总入口。stop_reason;去除首尾空白,非字符串或缺失值使用有效回退值或unknown。显式unknown阻止回退,未知有效主字段归入other;精确验证env_error:合并及相似前缀、协议原因值归入other。COMPLETED、PENDING、TRUNCATED、ABORTED状态;直接聚合和总入口均对每条输入计数一次。rollout_id=7时,metadata=None对应版本差 0;另一条起始版本为 5 的样本使版本差均值/最小值/最大值为 1/0/2,批量大小为 2,输入不变。SessionOutput.from_payload读入原因后,一轮和三轮 SessionForest 导出分别覆盖已知、动态错误及未知原因,指标归一化且原文保留;既有用例验证轮数与元数据透传。turn_count=1/3);test_session_forest_build_sample_and_session_spec验证结果与范围
使用 uv 虚拟环境执行 CPU 验证,环境为 Python 3.12、NumPy 1.26.4、PyTorch 2.10.0+cpu、Ray 2.58.0:
125 项通过。
同一个有界键回归测试在修复前提交
6891f0f上失败,输出含 URL 和动态错误原文;在修复后通过。全部 pre-commit 检查在与提交内容一致的隔离工作树中通过。pre-commit run --all-files,全部检查通过。变更类型
补充:DeepEyes V2 动态停止原因的指标键问题
Bug 内容
DeepEyes V2 示例的 agent.py:229 有一条错误结束分支,会把错误原文拼进停止原因:
该值通过
metadata.stop_reason返回,经 Session 导出合入样本。修复前,公共聚合函数把原因原文直接拼成stop_reason/{reason}/count|ratio,于是同一类错误的不同消息、URL、请求 ID 都变成不同指标。以两条终止错误样本为例:
在什么情况下触发
DeepEyes V2 上述分支需要同时满足:
obs.done=True、obs.error含错误信息,并且该轨迹成功导出样本、进入指标汇总。错误明细随请求变化时,修复前便会持续新增指标名称。当前普通搜索异常不会直接触发该分支。 env_deepeyes_v2.py:283 虽然写入
error=str(exc),但返回done=False;当前工具处理函数也均返回done=False。因此,不能声称现有示例发生一次搜索超时就会污染停止原因指标。该示例的动态拼接分支在环境返回“带错误的终止观察”时才会执行;其他自定义 Agent 直接返回动态metadata.stop_reason也能触发同一问题。这里由 DeepEyes V2 的错误原因拼接方式暴露出公共聚合入口缺少限制。已复现的是“动态原因进入指标键”的公共路径,尚无 DeepEyes V2 搜索异常贯通到监控服务的线上复现记录。
影响范围与规模
/混入分组路径,异常原文可能随指标名称传到监控后端。如何修改
修复位于公共聚合函数
_compute_rollout_metadata_stats,不修改 DeepEyes V2 的环境执行或停止条件:env_error、env_error:<任意明细>env_errorotherunknown保留
rollout_stop_reason优先、stop_reason回退的字段选择顺序,再执行类别归一化。每个非空批次为 13 个固定类别输出共 26 个计数/占比键,缺席类别补零;空输入仍返回{}。原始元数据不变,错误明细仍可用于排查。