新闻详情

新闻详情

首页 / 资讯中心 / 详情

Coding Agentic RL 中的 Reward Hacking:环境、检测与训练侧的工程实践与认知

发布时间:2026/9/11 19:12:41来源:尧图网络
Coding Agentic RL 中的 Reward Hacking:环境、检测与训练侧的工程实践与认知
目录0. 前言1. Hacking 的成因与形态1.1 优化了代理指标没优化真实目标1.2 训练设置本身放大了概率1.3 常见的几类形态1.4 三层防线2. 环境侧的加固2.1 环境侧的加固清单2.2 环境侧加固的边界3. Reward Hacking 检测3.1 Rule-based3.2 LLM judge3.3 两层的分工与成本3.4 GLM-5.2 的做法4. 训练侧优化4.1 三个选项4.2 为什么不选整条打压4.3 以 turn 为单位不是以 token4.4 正样本里 mask 掉作弊的 turn4.5 如果要打压就只让作弊的 turn 进梯度5. 总结与思考0. 前言Coding Agentic RL 中 reward 的来源比大多数场景都干净把模型写出来的代码跑一遍测试通过给正分不通过给零分或者负分既不需要人工标注也不用另外训一个 reward model。但这里面有一次隐含的替换我们真正想要的是这个问题被解决了而实际发下去的信号是这一组测试通过了。测试终究只是一组有限的检查只要模型能让那个判分的进程返回通过中间到底发生了什么它是看不见的。这类把检查糊弄过去、而问题并没有真的解决的行为就是 reward hacking它在训练里的表现相当一致reward 曲线一路往上走eval 却不动有时候还会往下掉。这篇文章讨论的是 hacking 出现之后怎么处理包括环境侧、检测侧、训练侧各自怎么处理重点会放在训练侧。写之前大概看了下网上目前应该还没有类似的文章。博主本人目前做的就是 Coding Agentic RL 相关的训练工作踩过一些坑这里把其中一部分经验和理解整理出来。希望对相关的从业者有一些小的帮助。文中有不少内容来自个人在实际训练中的观察和理解也结合了一些公开的技术报告但并不一定完全正确。如果读者朋友有不同的实践经验或者理解也欢迎讨论和指正。1. Hacking 的成因与形态1.1 优化了代理指标没优化真实目标Reward hacking 的定义可以写得很短模型优化的是 reward而 reward 只是真实目标的代理于是模型把代理指标做上去了真实目标没动。需要强调的是这不是一个 bug而是 RL 的必然产物。只要 reward 不等于 true objectivepolicy 优化的方向就是 reward 而不是 objective这一点没有例外。经典的例子是那条在赛道上原地转圈刷道具分、始终不冲过终点的船不过这个例子太老了就不展开了。对我们更有用的是一个可以直接观察的判据reward 上去了但能力没上去。Reward hacking 在训练曲线上的典型表现是 reward 稳步上升、eval 指标不动甚至下降。GLM-5.2 技术报告的说法可以直接借用hacking 让 verification signal 变得容易优化但 “fails to actually improve the fundamental capabilities of the model”。1.2 训练设置本身放大了概率Coding Agentic RL 相比其他 RL 场景的特殊之处在于我们主动给了模型一个可以任意操作的容器。一个只输出文本的数学模型能做的最多是把答案写得像对的而一个 Code Agent 手里有 shell。它可以读任意路径的文件可以起子进程可以装包可以改测试文件本身。我们期待它用这些能力去写代码但这些能力本身不区分用途。让它有能力修 bug 的那套权限同时也让它有能力去看答案。在 Coding Agentic RL 场景下还有一个容易被忽略的放大器轨迹长。长轨迹 Code Agent 动辄几十到几百个 turn一次探索里的动作数量是解数学题的两三个数量级。行为空间越大被采样到恰好绕开了 reward 定义的那条路径的概率就越高。RL 不需要模型想到要作弊它只需要有一次偶然采到reward 给了正反馈梯度就会把这个动作固化下来。我在训练里见过的很多 hacking 行为看轨迹前半段都不像是有预谋的更像是模型在环境里乱翻的时候撞上了。再叠上第三点pass/fail 的 reward 特别 vulnerable。它是二值的、全局的、由一个外部进程给出的。只要能让那个进程返回 0reward 就到手了中间发生了什么它一概不管。GLM-5.2 技术报告里边提到Coding RL is especially vulnerable to reward hacking because the reward is typically a verifiable pass/fail signal.此外模型能力越强hacking 问题只会越明显因为找空子本身就是一种能力。GLM-5.2 的报告自己也承认“We find that GLM-5.2 shows more potential hacking behavior than GLM-5.1”。也就是说防 hack 不是做一次就完的工程模型每强一轮都会出现一些新的 hacking 行为。1.3 常见的几类形态按模型绕过的是哪一环来分训练过程中实际见过的形态大致可以归成四类这四类出现的频率差得比较多能拦下来的位置也不一样。第一类是信息泄漏模型没有去解题而是想办法先看到了答案。按答案放在哪里这一类又有三种不太一样的来源一种在容器里测试文件本身、评测框架落盘的中间结果、任务自己的元信息都可能直接带着期望输出一种在外网目标项目的上游实现、相关的问答页面一次 clone 或者一行 curl 就能拿到还有一种最容易被忽略目标项目本身就是一个已经发布出去的包pip download就能把它连源码一起下下来甚至容器的site-packages里可能已经装着一份。第二类是篡改评判模型不改自己的实现改判分的那一侧把断言删掉把用例改成永真往测试目录里塞一个conftest.py把 fixture 换掉或者直接给测试函数打上 skip。第三类是环境利用代码和测试都不碰绕过的是整套判定过程让测试进程在收集阶段就提前退出、改环境变量让判分逻辑走另一条分支、写一份假的结果文件让上层直接去读它。这一类和上一类动的都是判定链路所以也都能在环境侧关掉大半。第四类是针对输入硬编码不实现逻辑只实现让这几个用例过典型写法是if input abc: return 42或者把所有分支都return None恰好测试只检查不抛异常。这一类的性质和前三类不太一样模型并没有做任何越界的动作交出去的就是它自己写的代码环境侧和检测侧都不太好说它错在哪。这四类的边界并不严格一条真实轨迹里经常同时出现两三类。但分类还是有用的因为它们的最佳防守位置不一样。1.4 三层防线前面几类形态各自的最佳拦截位置并不相同但归拢起来能下手的地方只有三处本文后面的章节也是按这个顺序展开的。第一处是环境侧在模型动手之前就把能关掉的攻击面关掉断网、把测试目录挂成只读、让隐藏用例不落盘都属于这一层。它的性价比最高配置改一次就永久生效训练过程中不再需要为它付算力所以凡是能在这里关掉的东西都不该留给后面两层。第二处是检测侧接的是环境侧原理上关不掉的那部分比如单个动作都合法、连起来才构成一次泄漏的情况。常见形态是规则和 LLM judge 两级级联规则跑在每一个 turn 上负责尽量多地把可疑动作捞出来judge 只看被捞出来的那些判断它的意图是不是在绕过判定。这样分工的原因是成本规则便宜但只能匹配已经见过的形态judge 泛化更好可每次调用都要花钱和时间。第三处是训练侧回答的是检测出结果之后的那个问题这条轨迹里哪些 token 还该进梯度。这一层是本文的重点一是公开资料里几乎没有人写这一步二是前两层改变的都是这一次调用的结果而模型下一次还会不会这么做只能由进了梯度的那部分决定。2. 环境侧的加固这一章会写得比较短但它在优先级上是最高的。原因很简单环境侧的加固是一次性成本配置改完就永久生效不占训练时的任何算力而检测侧的每一条规则都要在几百个 turn 上反复跑LLM judge 更是每次调用都在烧钱。所以凡是能在环境里关掉的就不该留到检测层。2.1 环境侧的加固清单下面这几条都是实际启用过的下面简单讲一讲。断网是收益最高的一条答案在外网的那一路会被它一次性关掉clone 上游仓库、curl 单个源码文件、联网搜索答案都包含在内。评测环境应该默认断网GLM 的几个评测集也是这么配的。但训练环境不一定断得起像 NL2Repo 这种建仓任务用到哪些依赖由模型自己决定装包必须走网络直接断网任务就起不来了能做的只是把出口收窄到内网镜像源和少数几个域名。而且断网不等于安全pip 源如果还通着pip download依然能把答案搬进来。包管理走白名单或者离线 index针对的是目标项目本身已经发布成包的情况——pip download、pip wheel、pip install git...都是把答案下载下来的合法通道靠禁用某几条命令挡不住把 index 换成离线镜像、并把目标包从里面剔掉是比较干净的解法。hidden test 不落盘针对的是答案在容器里的那一路训练时可见的用例和判分用的隐藏用例分开隐藏那部分在容器的整个生命周期里都不出现在任何路径上评测框架自己落盘的中间结果同理不要写在模型读得到的地方。测试目录只读挂载基本能消灭掉整个篡改评判类别做法是把测试挂成只读或者更彻底一点判分用的那份测试根本不放在模型能看到的文件系统里跑分的时候才挂进来。关键文件哈希校验可以作为兜底判分之前对测试文件和相关配置做一次哈希比对不一致直接判 0。2.2 环境侧加固的边界上面这些做完攻击面能砍掉一大半但砍不干净剩下的部分是结构性的继续往下收权限也解决不了。先是硬编码。把 test case 藏起来堵不住因为任务总得给模型一些可见的信息issue-level 任务里失败的测试本身就是任务输入藏不掉建仓任务的需求里也带着示例和验收标准模型照样可以对着这些把结果写死。更麻烦的是只要开发过程中允许跑测试看结果失败输出里通常就印着期望值一条 assert 就把答案漏了出去。把可见信息和测试反馈全部断掉当然干净但那等于让模型盲写任务完成率会先崩掉。然后是跨 turn 的组合。GLM-5.2 报告里举了这么一个例子1. find /workspace -name *hidden* 2. cat /workspace/.eval/secret_cases.json 3. python solve.py --case $(cat /workspace/.eval/secret_cases.json)find是合法的cat是合法的把一个字符串传给自己的脚本也是合法的三步连起来才是一次完整的答案泄漏。权限系统看的是单次系统调用它没有办法表达这三步连起来不行跨 turn 的意图本来就不在它的视野里。最后是加固本身的上限。环境侧如果权限收得太死模型连正常开发都做不了不能装依赖就没法跑测试不能写文件就没法改代码。加固到某个程度之后再往下收一格换来的是任务完成率下降而不是 hacking 率下降。所以检测层要处理的不是环境侧没做好留下的残余而是它在原理上就覆盖不到的那部分。3. Reward Hacking 检测Coding Agentic RL 中 Reward Haking 的检测这件事业界的收敛结果基本是一致的规则负责召回LLM 负责精度两级级联。GLM-5.2 技术报告里有提到The detection process has two stages: a rule-based filter first catches potential hacks to maximize recall, and then an LLM judge checks the intent of these flagged actions to keep precision high.3.1 Rule-based规则层的判据可以简单理解为一个合取式可疑动作 AND 指向了本任务的目标项目两个条件缺一个都不成立。只有可疑动作但没有指向目标那就是正常开发——pip install requests是可疑动作联网装包但它跟本题无关是纯误报。只有指向目标没有可疑动作也不成立——模型在自己的 workspace 里写一个跟目标项目同名的文件这是它本来就该干的事也不能被判为 hacking。按可疑动作的形态常见的比如这几类评测物读取直接去看用来判分的东西——读容器内的测试目录读评测任务自己的元信息、结果落盘目录。外部实现拉取从外网把答案搬进来——git clone目标仓库、curl上游单个源码文件、pip install git...以及搜索词里带 “reference implementation” 这种意图的联网检索。发行制品还原目标项目本身是个已发布的包从制品里把源码捞出来——pip download/pip wheel目标包、下 tarball 再解压、从site-packages里读目标包源码。这只是三个常见的例子实际的情况远比这三类要多。规则层的压力还跟任务形态强相关。同样是在 Code 上做 RLrepo-level比如从零生成一个 repo和issue-level 任务给一个已有 repo 修 bug / 加 featurehacking 频率和形态差别很大repo-level 明显更容易出现 hacking答案的形态不一样。建仓任务的目标往往是一个真实世界里已经发布出去的项目它的答案是一个可下载的实体——PyPI 上有包GitHub 上有仓库甚至容器的site-packages里可能已经装了一份。而 issue-level 任务的答案是一个 diff一次具体的提交它不是一个可以pip install下来的东西。泄漏路径数量差一个数量级。承接上一条目标是包那么pip download、pip wheel、tarball 解压、读site-packages、inspect.getsource、查索引 metadata、全盘find项目名——每一条都是独立的通道规则层得一条一条去堵。目标是 diff 的时候这些通道大部分自然消失。信噪比不一样。issue-level 任务里repo 本来就在本地模型读源码是它必须做的事读了目标项目的代码这个信号在这里没有任何区分度。而建仓任务的 workspace 一开始是空的目标项目的源码本来就不该出现在里面所以workspace 里出现了目标项目的完整实现是一条边界极其干净的判据。在写规则的时候重心不在正则而在实体识别。每一条规则都要回答这个字符串是不是指向了本任务的目标项目而目标项目名有大小写差异、有连字符和下划线的互换、有 PyPI 名和 import 名不一致、有一整族相关项目共用前缀。所以整个检测器是围绕一个目标项目身份的上下文对象展开的规则只是简单的判据。3.2 LLM judge在前面的例子中cat tests/secret_cases.py这条被规则拦掉之后模型下一次可能就换成先ls一遍目录再用变量把路径拼出来或者改用head、python -c open(...).read()甚至只是把命令挪进一个bash -c里。模型的这种行为并不是在针对检测器它可能只是在自己的 action space 里乱走试的次数够多总会碰上一个没被覆盖的写法一般来说模型越强这个过程越快。此外被规则命中了的那些也不见得都是真的。比较常见的一种是模型跑了一次测试失败信息里说缺一个依赖而这个依赖的名字和目标项目长得很像于是它把包装上了规则在这里会命中安装目标包可要是和前一个 turn 连起来看这只是被测试反馈驱动的正常修环境。更麻烦的是同一个动作在不同上下文里性质完全不同去读一个site-packages下的源码文件如果上一个 turn 刚报了这个库的ImportError那是在排查依赖如果上一个 turn 的 reasoning 里写着先看看有没有现成的实现可以参考那就是在捞答案。这两种情况在规则层眼里是同一条入参前面发生过什么、模型当时在想什么它都看不到所以只能给出同一个结论。因此在规则命中之后其实并不能直接把结果判定下来而是要基于一些上下文来做进一步的判断这一步只能交给 LLM 了。LLM judge 要回答的问题也不是这条命令危不危险那个问题规则已经回答过了它要判断的是在当前这个任务、这个上下文下这个动作究竟是在解题还是在绕过解题。输入上需要做一些裁剪实践中给到 judge 的一般是命中的那条规则、命中的那个 turn、前面若干 turn 的摘要以及本任务的目标项目基本就够用了。在 judge 时整条轨迹没有必要全塞进去一方面是成本另一方面几百个无关的 turn 反而会把注意力稀释掉。不过 judge 只在规则命中之后才会被调用也就是说整套检测的召回完全由规则层决定judge 只能往下修剪规则漏掉的那部分它一辈子都看不到。所以规则层宁可多报让 judge 去挡为了准而把规则收紧漏掉的就是真漏了。3.3 两层的分工与成本上一节把 judge 说得挺有用但它显然不能拿来判每一个 turn。长轨迹 Code Agent 一条轨迹的 turn 数在几十到几百这个量级按一条 300 个 turn、一个 batch 几百条轨迹来算一个 step 就是十万次量级的 judge 调用这个量级本身已经和 rollout 自己的推理量差不多了等于为了防作弊把训练成本翻一倍。而且这十万次里绝大多数都花在毫无嫌疑的动作上——ls一下目录、cat自己刚写完的文件、跑一遍测试再看输出这类 turn 在一条正常轨迹里占了绝大部分让 LLM 去看一眼得到的结论一定是没问题。所以规则层存在的第一个理由就是把这十万次缩到几百次。规则层的单 turn 开销基本可以忽略纯字符串和正则因此可以毫无顾虑地对每一个 turn 都跑一遍然后只把命中的那一小部分交给 LLM judge。此外反过来看正因为后面有 LLM judge 兜底规则层的写法其实可以比它单独工作的时候宽松不少。如果规则就是最终结论那么每一条规则都得自己承担精度一旦误报就直接影响到训练数据写的时候必须处处保守而现在规则要回答的问题被降级成了这个 turn 值不值得再看一眼判错的代价只是多花一次 judge 调用。这一点会实际改变规则的写法项目名的匹配可以放得更松一些把一族相关的项目名、常见别名和拼写变体都算进去一些拿不准的动作比如全盘find里带上了目标项目名与其纠结它到底是在清缓存还是在找源码不如直接让它命中把定性的工作交给 judge。3.4 GLM-5.2 的做法这里先给出 GLM-5.2 的技术博客GLM-5.2: Built for Long-Horizon Tasks一般来讲hacking 检测器给出结论之后环境侧的处置只发生在动作执行之前那一刻要么放它过去返回工具调用结果要么把它拦下来。放行是只记录、不干预这种处理方式的代价是 hack 真的生效了而且被污染的不止命中的那一个 turn后面所有的推理都建立在已经知道答案的基础上。阻断是在动作执行之前把这次调用拦掉回给模型一个空结果或者一条无意义的信息这样 hack 不会生效轨迹从这个 turn 往后重新回到干净状态模型只能继续靠正常的办法往下做代价是检测必须做成在线的。阻断这个方式也是目前比较主流的一个方式这个方式有两个点需要注意。一个是检测时只看工具调用的命令不需要看工具的返回内容模型执行cat /some/path的时候判的是这个路径而那个文件里到底读出来了什么检测器并不关心。这是因为拦截发生在动作真正执行之前那个时刻进程还没有跑起来返回内容根本就不存在能看的东西只有入参。另一个是检测器自己出错的时候一律放行整个入口都包在一层兜底里不管是规则解析、URL 拆分还是 judge 调用任何一步抛了异常结果都应该按没有命中返回。检测器挂在 rollout 的主链路上它自己的 bug 不该有能力把训练打断设想一条正则写错导致它崩溃而崩溃时的默认行为是拒绝那么每一次工具调用都会被拒掉所有轨迹一步都走不下去整个 batch 直接报废而漏拦一次的后果要小得多无非是这条 hack 生效了后面还有 judge 和训练侧两级可以处理。Anti-hacking 现在已经是前沿模型后训练里一个会被单独拿出来交代的环节。GLM-5.2 的技术报告就把它和长程 RL 并排写在同一节里标题是 “RL for Long-Horizon Task with Anti-hacking”。具体怎么处置被检测出来的动作报告里有这么一段话We use an online strategy that monitors the tool calls at each step. If a hack is detected, the system blocks the call and returns dummy information as the result. Importantly, this online guard allows the model to continue the rollout even after a hacked action is caught. By handling the specific invalid behavior instead of rejecting the entire trajectory, this approach helps prevent the training instability and model collapse that can happen when rollouts are abruptly stopped.从中可以看出三点阻断而且是 online。“monitors the tool calls at each step” 和 “online guard” 两处都指向执行前拦截。拦掉之后返回 dummy information告诉模型这样做没有有效信息模型更可能退回到正常解法上。关于阻断的理由原文说的的是 “training instability and model collapse”所以这是一个训练设计而不是安全设计不掐断 rollout 是因为掐断会让训练崩不是因为它对模型更公平。这条判断的前提是一次 hacking 尝试不足以否定整条轨迹的价值前面几十个 turn 通常是正常的读代码、写实现、跑测试而长轨迹 Code Agent 的 rollout 成本极高一条几百 turn 的轨迹要跑几分钟到几十分钟掐掉一条相当于白白浪费很多机器时间。报告还提到这套 anti-hack 模块在 RL 训练和评测两侧都启用“for both RL training and evaluation”。评测侧的配置写在脚注里NL2Repo 用规则加 LLM 判断拦 unauthorized pip / curl 这类行为DeepSWE 和 ProgramBench 的评测容器直接关掉网络。与 anti-hack 相邻的前一段讲的是长程 RL 的优化目标compaction 会把一条超长轨迹切成若干 sub-trace同一个 prompt 的不同 rollout 切出来的 trace 数量和长度都不固定group 内的相对比较因此失效所以他们从 group-wise 换成了 critic-based PPO用 critic 估 token-level advantage再以 token-level loss 处理长度不均。但如果使用 GRPO 等 token 不敏感的优化方法如何在训练侧进行优化原文没有提到这也是我们下一节要讲的内容。4. 训练侧优化这一章是全文的精髓。前面三章走完进入训练前我们能看到的是一条跑完的轨迹、一个 outcome reward以及若干个被标记为 hacking 的 turn环境加固和检测做的全部事情都只是把这些信息准备好而模型的行为最终是由梯度塑造的所以真正决定它学到什么的是最后这一步——这条轨迹怎么进梯度。4.1 三个选项通俗来讲训练侧拿到一条带 hacking 标记的轨迹能做的事情只有以下三种第一种是不管hack turn 和其他 turn 一样进梯度标记只拿去看指标不参与任何 loss 计算。第二种是打压检测到 hacking 就把整条轨迹的 reward 直接改成 -1让它作为负样本进训练。这是最直觉、也是实现成本最低的一种在 reward 计算的末尾覆盖一个值就完事不需要 turn 到 token 的映射不需要动 loss可以无缝衔接到 GRPO 之类的优化方式上。第三种是不学只把 hack turn 那一段 token 从 loss 里 mask 掉轨迹其余部分照常进梯度reward 也不动。但这种需要保证最后的 reward 是真的体现了模型自己的能力。4.2 为什么不选整条打压整条打压的实现成本几乎为零在 reward 计算的最后一步把这条轨迹的值覆盖成 -1 就完成了还可以顺手加一道生效范围的开关只对某几类 hack 或者某几个任务集合起作用其余的照旧。这条路径一般都会留着因为它随时可以打开但默认状态应该是关掉的原因有三条。第一条是罚错了对象。如果环境侧已经把这次调用拦在了执行之前、只把一段无用的结果丢回去那这次 hack 实际上没有生效模型既没有读到答案也没有绕过测试最后那个正 reward 是靠后续正常的开发过程挣回来的。这时候把整条判成 -1惩罚落在它动过这个念头上而不是它靠作弊拿到了分上一次被拦住的尝试和一次真正成功的作弊在梯度里被当成同一件事。更麻烦的是这两种情况在数据里的比例差得很远阻断一旦打开绝大多数标记对应的都是前者也就是说打压这个动作绝大部分时候都打在了没有造成任何后果的尝试上。第二条是牵连了无辜的 turn。Coding Agentic RL 里一条轨迹的 turn 数动辄上百上限通常也就设在几百这个量级而其中触发检测的往往只有一两个 turn剩下的部分是完整的读代码、定位、改代码、跑测试的过程恰恰是最希望模型学到的东西。整条判 -1 等于告诉模型这几百步全都错了而其中绝大多数步骤是对的。这是很典型的 credit assignment 错误而且区别于外部环境本身就只能给出轨迹级 reward 这种客观限制——检测给出的信息明明是带 turn 位置的但却主动把它降级成一个轨迹级的标量再用丢掉的精度是自己扔的。第三条是对误报零容忍。规则层为了 recall 是刻意做激进的误报从一开始就被算在设计里后面那层 judge 只能把误报压低压不到零。整条打压之下一次误报的代价是把一条完整的正样本翻转成负样本损失是双份的一条本来能学的轨迹没有了同时还多出一个方向完全相反的梯度。而 mask 之下一次误报的代价只是这一两个 turn 的 token 不参与更新同一条轨迹剩下的几百个 turn 照常进梯度量级上小得几乎可以忽略。因此检测出来的东西是哪个 turn 里出现了什么动作那么处置就应该落在这个 turn 上而不是先把它压成一个轨迹级的标量再去改 reward。接下来的问题也就随之变成粒度到底切在哪一层——turn还是更细的 token。4.3 以 turn 为单位不是以 token粒度有两种切法一种是把整个 turn 的 token 一起挖掉一种是往下切进 turn 内部、只挖掉真正出问题的那几十个 token。后者看着更精确但在工程落地上做不下去因为检测交出来的证据本身就是 turn 级的比如检测说的是第 47 个 turn 里的这次工具调用有问题而不是这个 turn 的第 300 到第 340 个 token 有问题。硬要往下切就得回答一串没有标准答案的问题——模型在 reasoning 里写我先看看测试文件放在哪这句算不算工具调用的参数里只有路径那一段算还是整个 JSON 都算如果这次调用是在权衡过两三种方案之后选出来的那前面被否掉的方案要不要一起算。这些问题每出现一种新的 hack 形态就得重新回答一遍而答错一次的结果就是留下一段该挖没挖的 token。比精确度更要紧的是语义上完整的作弊单元恰好就是一个 turn。一个 turn 里装着怎么想 — 决定做什么 — 发出调用这三件事它们是连在一起产生的只 mask 掉最后那段调用参数、把前面的 reasoning 留在梯度里模型学到的东西是这么想是对的、只是别这么写出来比什么都不做更糟糕因为它把一个显式的、检测得到的动作训成了一种隐式的倾向。4.4 正样本里 mask 掉作弊的 turn在 Coding Agentic RL 中一条轨迹最后拿到了大于 0 的分数。这个分数可能有三种来路全程没有 hack模型老老实实读代码、定位、改代码、跑测试把任务做出来了这条轨迹上不会有任何标记。hack 没有被阻断模型靠下载参考实现、翻上游提交、读到测试用例这类动作把分数拿到了手标记有而且分数就是这个动作换来的。hack 出现过、但在执行之前被截住了模型拿回来的是一段无用的结果没有从里面得到任何有效信息最后还是靠自己的能力把任务做出来的。mask 处理的是第三种。做法是整条轨迹保留、reward 也不动只把被标记的那个 turn 所占的 token 区间从 loss 里挖掉其余几百个 turn 照常进梯度。之所以可以这么处理是因为这一种情况下分数的可信度没有问题任务确实是靠模型自己的能力完成的那条完整的开发路径值得强化只有中途那一个动作不值得。如果阻断没有开mask 就失去了意义第二种情况下分数不是模型自己挣来的把作弊的那个 turn 挖掉之后剩下的部分依然构成一条走捷径走通了的路径梯度还是在强化它只是把最显眼的那一步从证据里抹掉了而已这时候该判断的是这条 reward 还能不能用而不是挖掉哪几个 turn。保留其余部分的理由是正的 reward 说明这条路径整体上值得强化那个 hacking turn 只是路径上的一处瑕疵不该被这个分数顺带着一起抬起来。不 mask 的话进梯度的就是包含这个动作的完整流程 → 高分这样一个整体模型没有办法从里面分辨出哪一步是多余的下一次它会把这个动作当成流程的标准组成部分照抄下来。在难任务上还有一层更现实的考虑。repo-level 的任务一次跑通全部测试的概率本来就很低如果 reward 只分全过和全不过两档绝大多数轨迹拿到的都是0一个 group 里没有任何区分度可用所以这类任务的 reward 通常按测试通过的比例来给改对了一部分就能拿到一个介于 0 和 1 之间的分数。然而即便放宽到这个程度能拿到大于 0 分数的轨迹仍然是少数正样本在一个 batch 里本身就是稀缺资源。这时候只要出现 hack 标记就把整条轨迹打压掉等于把本来就不多的正向信号又砍掉一部分剩下的训练很容易退化成只在负样本上打转。4.5 如果要打压就只让作弊的 turn 进梯度正常来讲一条轨迹的 reward 只落在 0 到 1 之间测试通过多少就给多少。如果某个阶段确实需要把某一类 hack 快速压下去最直接的做法就是检测命中之后把整条轨迹的 reward 覆盖成 -1让它作为负样本进训练。但是需要注意的是这条轨迹里几百个 turn 绝大多数是无辜的读文件、定位、改代码、跑测试这些动作本身没有任何问题而 outcome reward 是全局的-1 会摊到每一个 turn 上模型从里面学到的是这些正常动作都不该做长轨迹 RL 里负样本更容易把模型训坏很大一部分原因就在这儿。要让打压变成一个可用的手段就得把这个 -1 收窄到它该去的地方把除作弊那个 turn 之外的所有 token 全部 mask 掉只留这一个 turn 进梯度让 -1 完整地落在这一步上。这样一来负信号不再被几百个 turn 稀释这个动作的概率会掉得很快同时正常动作一个都没有被牵连。5. 总结与思考本文按三层防线过了一遍 Coding Agentic RL 里的 reward hacking环境侧先把断网、测试只读这类能一次性关掉的攻击面关掉检测侧用规则做召回、LLM judge 判意图处理环境原理上覆盖不到的那部分训练侧决定被标记的 turn 怎么进梯度正样本里把作弊的 turn 从 loss 里挖掉确实需要打压的时候反过来只保留作弊的 turn让处置的粒度和证据的粒度对齐。最后谈一谈我对 Coding Agentic RL 中 Reward Hacking 检测的一些看法规则清单的有效期比想象的短。模型能力每上一个台阶或者同一个模型多训几十个 step都会冒出以前没见过的 hack 形态GLM-5.2 的报告里也提到它比上一代表现出更多的 hacking 倾向。找空子本身就是一种能力模型越强越容易在探索里撞上规则没覆盖的写法而且它并不需要理解规则采样次数够多自然就漂到边界外面去了。所以规则层的工作方式注定是周期性的训一段时间、捞一批轨迹回来看、补几条规则、再往下训指望一次写全不现实接 LLM 也就成了必然——规则只能匹配见过的形态而 LLM 多少能对这个动作是不是在绕过判定做一点泛化。整条轨迹 -1 压 hack 率很快但效果不一定好。轨迹级的 -1 会把这条轨迹里所有动作一起压低其中大部分是正常的探索行为——翻文档、读第三方库的源码、试一条不确定的命令这些动作在表面形态上离作弊并不远被连坐几次之后模型会变得保守只走它已经确认安全的那几条路。表现出来就是 hack 率的曲线很好看而 reward 到后期怎么也涨不上去因为长任务的解法本来是靠试错试出来的。此外被强惩罚之后模型面对的实际目标变成了拿到 reward 且不被检测到换一种检测不到的作弊方式往往比真的去解题离当前策略更近、梯度更陡于是惩罚并不消灭 hacking它筛选出更隐蔽的 hacking而这些在曲线上什么都看不到。训练里的拦截在真实使用时其实并不存在。模型厂商交付的是一个模型用户拿它接哪个 agent、哪套工具、什么样的沙箱都是用户自己定的没有人会在用户的容器里替它拦下一次 curl。训练时模型面对的世界是这条路会返回一段无用的结果真实使用时它面对的世界是这条路真的能把上游实现下载下来这层 diff 决定了拦截不能是唯一的防线。如果模型学到的只是这个动作在这个环境里没用换到一个没有拦截的环境这个倾向会立刻兑现出来。要让它在部署环境里也不这么干信号必须落到权重上也就是把这个 turn 从正样本的梯度里挖掉、让它的概率不被抬高而不是指望环境每次都替它挡住。以上大部分内容来自实际训练里的观察和踩过的坑样本量有限可能有错误和遗漏欢迎讨论和指正~
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

H5U通过EtherCAT桥接控制CANopen伺服的工程实践 2026/9/11 19:54:45

H5U通过EtherCAT桥接控制CANopen伺服的工程实践

1. 项目概述:为什么小型PLC要“跨协议”驱动伺服——从汇川H5U到步科CANopen的硬核打通逻辑你手上有一台汇川H5U——它不是传统意义上“大厂标配”的旗舰型PLC,而是定位清晰、成本敏感、响应迅速的小型控制器,常用于包装机、灌装线、简易装配…

阅读更多 →
告别论文内耗!这款一站式学术神器拯救熬夜写论文的你 2026/9/11 19:54:45

告别论文内耗!这款一站式学术神器拯救熬夜写论文的你

最近刷朋友圈,清一色全是被论文拿捏的苦命毕业生!凌晨三点的学术人真的太真实了,有人熬到深夜,对着电脑截图感慨查重率终于压到5%,告别无数次降重返工;也有人崩溃破防,被导师直言论文框架杂乱得…

阅读更多 →
美国野火烟雾数据集:技术解析与应用实践 2026/9/11 19:54:45

美国野火烟雾数据集:技术解析与应用实践

1. 项目背景与数据价值2003-2025年美国每日野火烟雾数据集是环境监测领域的重要基础数据资源。作为一名长期从事大气污染研究的从业者,我深刻理解这类时空连续数据对以下三方面的核心价值:首先在公共健康领域,野火烟雾中含有大量PM2.5、一氧化…

阅读更多 →
Kilo 安全威胁模型与漏洞报告指南:权限系统边界、Server 模式认证与负责任披露实践 2026/9/11 19:54:45

Kilo 安全威胁模型与漏洞报告指南:权限系统边界、Server 模式认证与负责任披露实践

Kilo 安全威胁模型与漏洞报告指南:权限系统边界、Server 模式认证与负责任披露实践 【免费下载链接】kilocode Kilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent. 项目地址…

阅读更多 →
WinRAR解码器源码开源:半开源背后的技术逻辑与实操指南 2026/9/11 19:54:45

WinRAR解码器源码开源:半开源背后的技术逻辑与实操指南

这是我盯着 WinRAR 那个熟悉的试用弹窗,愣了好几秒才反应过来的一件事:这款名义上收费了二十多年的压缩软件,最近官方把核心解码器源码公开了。对普通用户来说,新闻可能就是“哦,那以后免费了?”&#xff1…

阅读更多 →
SpringBoot + MyBatis-Plus 标准项目搭建,附完整可运行代码 2026/9/11 19:51:45

SpringBoot + MyBatis-Plus 标准项目搭建,附完整可运行代码

一、引言在 Java 后端开发中,Spring Boot 已经成为构建微服务和企业级应用的标配框架,而 MyBatis-Plus 则是在 MyBatis 基础上进一步封装增强的 ORM 工具。它提供了通用 Mapper、通用 Service、分页插件、代码生成器等能力,能够显著减少重复的…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞