跳转到正文

20.3 轨迹信用分配

22.2 多轮 RL 形式化 把轨迹概率写成 ,并推出 action mask——只有 action token 参与 policy gradient。但这只回答了"该在哪些 token 上算梯度",没回答"每个 action token 该乘以多大的 advantage"。

策略梯度的形式是:

trajectory-level 的标量 对整条轨迹一视同仁——成功了所有动作都好,失败了所有动作都坏。这就是 Agentic RL 的核心难题:7 轮交互失败了,第 1 轮的正确搜索也要被惩罚吗?

回拆成每一步的 step-level advantage ,就是信用分配(Credit Assignment)。本节系统梳理这个问题:先从最简单的 ORM/PRM 二分讲起,再展开 2025-2026 年发展出的四类精细方法。

三层信号:从最终结果到 token 更新

一条 rollout 失败了,信号要经过三层才能落到 LLM 权重上:

第一层:Trajectory reward。 整条轨迹最后得到 ,只告诉我们"这一局成没成"。这是环境给的全部信号。

第二层:Step advantage。 把最终结果拆成 ,回答"第 轮该不该被奖励"。这是信用分配要做的核心工作。

第三层:Token gradient。 乘到这一轮 action 的 token log-prob 上:

其中 是第 条轨迹第 轮 LLM 生成的 token 集合, 是当时的 context。网页/工具返回内容被 masked,不参与这一步。

当前 GRPO-style Agentic RL 的很多工作,本质上都是在构造更好的

ORM 与 PRM:信用分配的基本二分

最简单的两种做法定义了信用分配的两个极端。

ORM:只看最终结果

ORM(Outcome Reward Model)——只在轨迹终点给奖励,中间步骤全部为 0:

ORM 的优势是简单且便宜——只需要知道最终结果对不对。对于可验证任务(代码是否通过测试、数学答案是否正确),连 reward model 都不用训,直接用 verifier。RLVR(Reinforcement Learning with Verifiable Rewards)就是 ORM 的极端形式,DeepSeek-R1 的成功证明了纯 ORM 可以激发强大的推理能力。

ORM 的劣势是信号稀疏。一条 10 步轨迹只有 1 个 reward 信号,模型很难从中学到"具体哪一步该改进"。如果失败时所有步骤都被惩罚——包括那些做得对的中间步骤——信用分配就严重失真。

PRM:每步独立评分

PRM(Process Reward Model) 对每一步独立打分:

PRM 看了从第 1 步到第 步的完整历史,判断第 步是否正确。OpenAI 的 "Let's Verify Step by Step"(Lightman et al., 2023)正式提出这一思路:第一步思路正确(+1)、第二步计算有误(-0.5)、第三步虽对但绕了路(+0.3)。模型能精确定位需要改进的步骤。

PRM 的劣势是标注成本极高——为每一步都标注"对/错"比只标注最终结果的工作量大了 倍。OpenAI 为此构建了 PRM800K 数据集。当前研究热点是自动化 PRM(如 Math-Shepherd 让模型自主判断每步质量)和领域特化 PRM(如 Web-Shepherd 专为网页导航设计)。

ORMPRM
信号密度稀疏(只有最终 reward)密集(每步都有 reward)
标注成本低(只看结果)高(每步都要标注)
学习速度慢(信号少,方差大)快(信号多,方差小)
适用场景可验证任务(代码/数学)复杂推理(需要精细指导)

SALT:从轨迹图里提取步骤级 advantage

ORM 太粗,PRM 太贵。SALTLi et al., EACL 2026)提供了第三条路——不训练 PRM,但比纯 ORM 精细得多。

核心思路:对同一个 prompt 采样多条轨迹,构建一个轨迹图——节点是每一步的动作,如果两条轨迹在某一步做了相同的动作,它们就共享同一个节点。通过分析图结构,可以量化每一步对最终结果的贡献。

直觉上:如果一个步骤被很多成功轨迹共享、但很少出现在失败轨迹中,那它大概率是个好步骤——应该得到正向 advantage。反之,如果某步骤只出现在失败轨迹中,它大概率拖了后腿。SALT 利用图结构计算每个步骤的 advantage,完全不需要额外奖励模型或人工标注——只需要最终结果的二元信号。

这让 SALT 在 GRPO 框架中特别好用:GRPO 已经在组内采样多条轨迹做比较,SALT 在此基础上进一步细化到步骤级别。

Step-Level Advantage 的三类来源

ORM/PRM/SALT 之外,2025-2026 年发展出了一大批更精细的方法。XiaoRed5 的入门资料 和 2026 年 4 月的综述 From Reasoning to Agentic: Credit Assignment in RL for LLMs 把这些方法按信号来源分成了三类。它们的区别不在于最后怎么更新 policy,而在于 step-level 信号 从哪里来。

方向一:State-anchored stepwise——同状态下比较动作

核心思想:同一个状态下,不同动作的相对好坏,可以从它们各自的后续回报里看出来。不需要 PRM,只需要利用 group 内多条 rollout 的天然结构。

GiGPO:同 state group 内相对回报

GiGPO(Group-in-Group Policy Optimization)的想法是:同一个任务采多条 rollout,如果这些 rollout 中间遇到了相同的环境状态 ,那就可以比较"同样局面下,不同动作谁更好"。

先把所有在 state 下发生的动作聚成一个组:

这里的 不是整条轨迹 reward,而是从这一步开始往后的折扣回报:

如果环境只有最终成功奖励,可以直觉理解成:这一步之后最终成功就有正回报,成功来得越晚、折扣后回报越小,最终失败则回报接近 0。

然后在同一个 state group 里做相对优势:

一个动作的 step 分数 = 它后续带来的回报 − 同 state 下其他动作的平均后续回报。

WebShop 例子:多个 rollout 都来到同一个商品搜索结果页:

动作后续结果
点正确商品很快成功1.00
点错误商品,但后来返回并买对晚一点成功0.73
点下一页最终失败0.00

组内平均值 。三个动作的相对分数:

点正确商品:  1.00 - 0.58 = +0.42
点错误商品:  0.73 - 0.58 = +0.15
点下一页:    0.00 - 0.58 = -0.58

模型学到的不是"成功轨迹里所有动作都好",而是更细的局部偏好:在同一局面下,点正确商品最好;点错误商品虽然不理想,但还有补救机会;点下一页最差,因为后续失败了。

最后 GiGPO 把整条轨迹的 macro advantage 和这一步的 micro advantage 合起来:

其中 是 trajectory-level 的相对好坏, 是 step-level 在同 state 下的相对好坏。

HGPO:还要看历史上下文

HGPO(Hierarchical Group Policy Optimization)是对 GiGPO 的一个关键修正。GiGPO 在同一个 state 下比较动作,但 HGPO 指出:同一个当前 state,不一定代表同一个决策上下文

最容易出错的场景是网页任务:两个 rollout 可能当前页面完全一样,但它们是怎么走到这个页面的并不一样。一个 agent 可能带着完整约束("黑色、防水、低价的雨靴")来到这里,另一个 agent 前面已经漏掉了"防水"约束(只搜了"black boots")。此时同一个动作("加入购物车")的好坏含义就会不同——前者大概率是好动作,后者可能是坏动作(因为漏掉了关键约束)。HGPO 把这个问题叫 historical context inconsistency

HGPO 定义一个 -step context operator:

表示"当前 state 加上前面 个历史 state"。 时只看当前 state; 越大,要求共享的历史越长,上下文越一致。

对每个 step 构造多个层级 group:

这些 group 之间是嵌套关系: 是"只看当前页面"的大组(样本多但容易混), 要求更长历史一致(比较公平但样本少)。

在每一层 group 里算组内相对 advantage:

最后 HGPO 把不同层级的 advantage 加权融合:

控制更深层 group 的权重——本质上是 bias-variance trade-off:深层 group 比较公平但样本少、方差高。

一句话说,GiGPO 问"同一个 state 下哪个动作更好",HGPO 进一步问"在同一个 state 且历史上下文相近时,哪个动作更好"。

Group-Graph PO:把轨迹建模成 DAG

Group-Graph Policy Optimization(2026-06)走得更远。它指出,现有 step-level 框架仍然把 agent 探索看作"孤立的线性轨迹"——这是过度简化的视角。实际上多条 rollout 之间经常共享前缀、共享中间状态,结构上更像一棵**有向无环图(DAG)**而非一组平行线。

把轨迹建模成图后,可以在图上做更精细的信用分配:识别关键分叉点、追踪某条子路径的独立贡献、避免重复计算共享前缀的 advantage。这对长程任务特别有价值——当反馈延迟到几十步之后才出现时,DAG 结构能更准确地归因到具体决策。

方向二:Process / Progress Reward——训练额外的步级 scorer

第二类方法不依赖 rollout 之间出现同 state 碰撞,而是额外训练一个能给步骤打分的模型。

SPA-RL:把最终 reward 分摊成每步 progress

SPA-RL(Step-level reward attribution via Path analysis)走的是 reward redistribution 路线:额外训练一个 progress estimator,让它学会判断"这一步让任务向完成目标推进了多少"。

基本假设:长程任务最终完成,是每一步 incremental progress 累积出来的。所以不直接给每步打"绝对好坏分",而是让模型预测每步贡献 ,并要求这些贡献加起来能还原最终 reward:

progress estimator 的形式:

用一个 LLM 编码当前 state-action,再接轻量 MLP 输出标量贡献。训练时用多条探索轨迹的最终 reward 做监督:

意义非常直接:如果某条轨迹最终成功,所有步骤贡献的和应该接近 1;如果最终失败,贡献的和应该接近 0。模型为了拟合这个目标,会学着把正贡献分给真正推动任务成功的步骤,把低贡献或负贡献分给无效步骤。

直觉例子:一个搜索 agent 最终答对,轨迹里可能有 5 步:

步骤行为可能的 progress
1生成一个模糊 query0.05
2打开无关网页0.00
3改写 query 命中关键网页0.30
4抽取关键证据0.45
5组织成最终答案0.20

这些分数加起来约等于最终成功 reward。RL 训练时不再只有最后一步有信号,而是每一步都有 dense reward。

SPA-RL 还加了一个 grounding signal (动作是否真的能在环境中执行),最终中间奖励是 ——一个动作既要"有用",也要"能执行"。

AgentPRM:把 PRM 当成 agent 的 Q(s,a)

AgentPRM 的思路更接近经典 RL 里的 actor-critic。它把 process reward model 看成一个状态-动作价值函数:

PRM 输出的不是"这一小步贡献了多少",而是"如果在当前 state 做这个 action,后面按当前 policy 继续走,预期能拿多少总回报"。

AgentPRM 的训练是一个三阶段循环:

  1. Rollout and Compute Target:用当前 policy 在环境中采样大量轨迹,用 MC return 给每个 估计目标值。
  2. Train PRM:把这些自动标注的 当 soft label,用 soft BCE 训练 PRM。
  3. Train Policy:用训练好的 PRM 作为 dense reward,更新 policy,同时用 KL 约束不要离上一轮 policy 太远:

注意 KL 约束到上一轮 policy 而不是 SFT policy——因为 PRM 是用上一轮 policy 的 rollout 训练的,新 policy 飘得太远 PRM 估计就会失真。

AgentPRM 在推理时还可以做 Best-of-N:每一步从 policy 采样 个候选动作,用 PRM 选 最高的那个。最大风险是 reward hacking——policy 学会让 PRM 打高分而不是让真实任务成功。常见缓解方式包括迭代重训 PRM、增加 rollout 数据、加 KL 约束。

PAIR:prefix-aware internal reward

PAIR(Prefix-Aware Internal Reward Model,2026-05)针对 AgentPRM 这类方法的训练成本问题——三阶段循环里 PRM 重训很贵。PAIR 训练一个 internal reward model,它在评估某一步时显式感知前缀:同一个动作在不同前缀下应该得到不同分数。

Web-Shepherd:领域特化 PRM

Web-Shepherd(NeurIPS 2025 Spotlight)是首个专为网页导航设计的步骤级 PRM,能自动评估 Agent 在每一步的操作是否正确。实验表明,用 Web-Shepherd 提供步骤级 reward,GPT-4o-mini 的性能提升了 10.9%,而成本仅为使用 LLM 做判官的 1/10。这说明 PRM 不只是理论上的美好愿景——在特定领域,领域特化的 PRM 可以低成本、高效率地提供密集的步骤级信号。

方向三:Intrinsic Signal——从 policy 自身找信号

第三类方法不额外训练 PRM,而是从 policy 自己的行为分布里找 step-level 信号。

ARPO:把 rollout 预算花在 entropy 飙升的位置

ARPO(Asymmetric Rollout Policy Optimization)关注工具调用场景。它的关键观察是:LLM 在收到工具返回结果之后,接下来生成的前 10 到 50 个 token 的 entropy 往往会明显升高

token entropy 的定义:

entropy 高表示模型此刻不确定:可能有多种解释、多种下一步工具调用、多种推理路径。ARPO 的想法是:既然不确定性最高的位置最可能是关键决策点,那 rollout 预算就不应该平均花,而应该集中在那里

ARPO 的 rollout 分成两类:

  • Global rollout:从头到尾采完整轨迹,保证整体探索
  • Partial rollout:在高 entropy 工具调用节点分叉,重点探索局部决策

具体流程:总 rollout budget 是 ,先采 条完整轨迹,剩下 留给局部分叉。每次工具调用返回后生成 个 token,计算 entropy ;与初始 entropy 比较,得到归一化 entropy 变化 ;如果超过阈值,就从当前节点分出 条 partial rollout。

例如搜索工具返回了几段互相冲突的证据,模型 entropy 飙升,ARPO 就在这里多分几条分支:一条追证据 A、一条追证据 B、一条换 query 验证。分叉后的差异用来更新 individual tokens;共享前缀则吃多个分支 advantage 的平均。

IGPO:用正确答案概率的增量当 turn-level reward

IGPO(Information Gain Policy Optimization)的核心想法是:每一轮搜索、读证据、工具调用,本质上都应该让模型更接近正确答案。那就直接衡量这一轮之后,模型对 ground-truth answer 的概率有没有上升。

设 ground-truth answer token 序列为 。第 条 rollout 到第 轮为止的历史是 。IGPO 用 teacher forcing 计算模型生成正确答案的平均 log probability:

除以 是为了避免长答案天然 log probability 更低。

然后把连续两轮之间的 log probability 增量定义成 information gain reward:

直觉上:

  • 有用交互:log-prob 从 -4.0 升到 -1.5 → (搜索找到了关键证据,模型现在更确信正确答案)
  • 带偏交互:log-prob 从 -2.0 降到 -3.2 → (搜索带回了无关内容,模型反而偏离了正确答案)

IGPO 还保留最终 outcome reward,并把两类 reward 分开做 group-wise z-normalization:

然后像普通 RL 一样向后累积折扣回报 ,分配给第 轮产生的 decision tokens;工具返回内容本身不更新。

IGPO 的优点是便宜、稠密、ground-truth-aware,不需要额外 PRM,也不需要 Monte Carlo 估值。缺点是它依赖高质量 ground truth——如果问题本身有多个合理答案但数据集只认其中一个,IGPO 可能会惩罚事实正确但不匹配标注的推理路径。

AEM:自适应熵调制

AEM(Adaptive Entropy Modulation,2026-05)走了另一条 intrinsic 路线。它指出 sparse outcome-only reward 提供的指导有限,但额外加 PRM 或 self-supervised signal 又会增加监督和调参复杂度,可能限制跨任务泛化。

AEM 不引入任何额外监督,而是动态调节 policy 的 entropy bonus——在探索不足时增大 entropy 鼓励,在策略开始坍缩时减小。具体地,它根据 trajectory-level reward variance 自适应调整 entropy 系数,避免 ARPO/RAGEN 等工作中常见的 "echo trap"(模型陷入自生成 reasoning 模板的死循环)和 entropy collapse。

RefGRPO:反思鸿沟与校准 bonus

RefGRPO(Closing the Reflection Gap,2026-06)发现了一个反直觉现象:LLM agent 在看到环境反馈后,倾向于错误评估自己的输出——即使对它本已答对的问题。作者称这是 "reflection gap",并指出标准 RL 由于 "credit-assignment mismatch" 几乎无法修复它。

RefGRPO 的修复很简单:在 GRPO 基础上加一个 calibration bonus——当模型在看到环境反馈后仍然正确评估自己的输出时给予额外奖励。这个 bonus 是"免费"的(不需要额外标注),但能显著缩小 reflection gap,让 agent 学会真正利用环境反馈而非机械响应。

Step-Aligned 范式:解决 granularity mismatch

前三类方法都在"如何更精细地分配 advantage"上做文章。还有一类工作从更根本的地方入手:改变优化的基本单元

StepPO(2026-04)指出:现有 LLM RL 算法继承了 RLHF/RLVR 的 token-centric paradigm——token 是建模和优化的基本单元。但在 agentic RL 中,LLM 通过"环境观测 ↔ 动作"的循环做 step-level 决策,token-level 优化与 step-level 决策之间存在 granularity mismatch。StepPO 提出以 step 为核心的范式:每个 step 是一个完整的决策单元,policy gradient 在 step 粒度上计算,而不是 token 粒度。

Turn-PPO(2025-12)从另一个角度到达类似结论。它发现直接把 GRPO 套到多轮任务上、特别是长程推理场景下表现很差——原因是 GRPO 的 group-relative advantage 在 trajectory level 估算时方差太大。Turn-PPO 改用 PPO 并设计 turn-level advantage estimation:每个 turn(不是每个 token、也不是整条 trajectory)有自己的 advantage,advantage 在 turn 内部的 token 间共享。

AT²PO(2026-01)进一步把 tree search 引入 turn-level optimization。它针对多轮 agentic RL 的三个核心挑战——exploration diversity 不足、credit assignment 稀疏、policy optimization 错位——提出统一的 turn-based + tree search 框架。Tree search 在关键 turn 上展开多条假设路径,比较它们的最终回报,从而为该 turn 提供更精确的 advantage。

这一组工作的共同洞察:Agentic RL 不应该硬套 RLHF 的 token-centric 框架。Agent 的决策天然是 step / turn 粒度的,优化框架应该尊重这个粒度。

Turn-Level Discounting:越早犯错,责任越大

无论用哪种信用分配方法,多轮 RL 都需要处理一个时间维度的问题:早期步骤的错误影响更大。直觉上很好理解——第 1 步就走错了方向,后面每一步都在错误的基础上展开;而第 6 步犯的小错,第 7 步还有机会纠正。

为了建模这个直觉,研究者引入了 Turn-Level Discounting

注意这里的 不是对"未来"打折,而是对"过去"的步骤赋予不同的权重。在实际实现中,更常见的做法是反向折扣——从最终结果往回推,越早的步骤折扣越大:

python
def compute_turn_rewards(turn_rewards, gamma=0.9):
    """计算多轮 RL 的折扣累积回报"""
    T = len(turn_rewards)
    returns = []
    G = 0
    # 从最后一轮往前累计
    for t in reversed(range(T)):
        G = turn_rewards[t] + gamma * G
        returns.insert(0, G)
    return returns

# 7 轮交互,只有最后一轮有即时 reward
# turn_rewards = [0, 0, 0, 0, 0, 0, 1.0]
# discount gamma = 0.9
# 返回: [0.531, 0.590, 0.656, 0.729, 0.810, 0.900, 1.000]
# 越早的步骤,折扣越大 → 对最终结果的"责任"被稀释

这个实现和第 6 章 REINFORCE 中的 计算完全一样——只是现在每一步是一个完整的"轮次"(包括文本生成和工具调用),而不是单个 token。

代表性框架对比

框架信号来源主要贡献适用场景
ORM (RLVR)最终结果简单、便宜、可验证短程任务(≤ 5 步)、有客观答案
PRM (PRM800K)人工标注每步信号密集、定位精准高标注预算、复杂推理
SALT轨迹图结构不需额外标注GRPO 框架、长程任务
GiGPO同 state groupstate-anchored 比较状态可枚举(WebShop)
HGPOk-step context解决历史上下文不一致网页导航、上下文敏感任务
Group-Graph PO轨迹 DAG长程 + 共享前缀长程任务(10+ 步)
SPA-RLprogress estimatorreward redistribution任意多步任务
AgentPRMMC return + soft BCEPRM as Q(s,a)需要 Best-of-N 推理
ARPOentropy spike局部分叉采样工具调用密集任务
IGPO正确答案 log-probinformation gain有 ground truth 的任务
AEM自适应 entropy无需额外监督通用、易坍缩场景
RefGRPO校准 bonus缩小反思鸿沟需要自评估能力的任务
StepPOstep-centricgranularity 对齐任意 agentic RL
Turn-PPOturn-level PPO方差低于 GRPO长程多轮任务
AT²POturn + tree search探索 + 精细归因高计算预算的长程任务

综合选型建议

按任务复杂度选择合适的策略:

  • 3-5 轮简单任务:纯 ORM / GRPO 就够了——episode 短,信号稀疏性不严重。
  • 5-15 轮中等任务:里程碑式奖励塑形,或 SALT/GiGPO 这类不依赖额外标注的方法。
  • 15+ 轮复杂任务:必须用 PRM 或 progress reward(SPA-RL/AgentPRM)+ MCTS 探索(AT²PO)。
  • 环境不可复现 / 高方差:AEM 自适应熵调制,或 STO-RL 两阶段(先离线预热,再在线精修)。
  • 需要 Best-of-N 推理:AgentPRM 提供步骤级 Q 值,推理时挑 最高的动作。
  • Granularity mismatch 严重:换 step-aligned 范式(StepPO / Turn-PPO)。

关键原则:先确认 reward 信号的密度是否足以支撑学习,再决定用什么 RL 算法。如果 reward 太稀疏,再好的算法也学不动。

从信用分配到规划能力

信用分配回答了"每步做得好不好"的问题。但一个更深层的问题是:模型能否在行动之前就制定出好的多步计划? 这是规划(Planning)能力的核心。

到目前为止讨论的 Agent 主要是反应式的——根据当前观察做下一步决策。但真正的智能体需要前瞻式规划——在行动前推演多种路径,评估预期结果,选择最优路径。

规划能力可以从 RL 训练中涌现。DeepResearcher 等工作的实验揭示:模型自发产生了预搜索规划(先列出关键词列表)、信息分层(先搜概览再深入)和交叉验证等行为——这些都没被 reward 显式鼓励,纯粹是 RL 优化的副产品。

实用启示:在投入复杂的树搜索和分层 RL 之前,先试试简单的 GRPO + outcome reward——模型可能自己就能学会规划。只有简单方法无法涌现规划能力时,才需要引入 TreeRL(ACL 2025)、PGTS(ICML 2025)这类显式的树搜索训练方法。

Mini Agent Loop——ORM vs PRM 信用分配对比

前面的章节里,RL 训练都是"单轮"的:模型生成一段文本,奖励函数打一个分,更新策略。但真正的智能体不是这样工作的——它需要在多轮交互中搜索信息、执行代码、观察结果,最后才给出最终答案。7 轮交互之后只有一个"成功/失败"信号,你怎么把这个信号分摊到 7 个步骤上?

这就是 Agentic RL 的核心挑战:信用分配(Credit Assignment)。这一节我们亲手建一个轻量的工具环境,用 Python 模拟多轮 Agent 交互,然后对比两种信用分配策略——ORM(只看最终结果)和 PRM(每步都评估)——看看它们的差异有多大。

搭建一个 Mini Tool Environment

我们用纯 Python 搭建一个模拟的"研究助手"环境。Agent 可以调用三种工具:

工具功能返回
search(query)模拟搜索信息搜索结果文本
calculate(expr)执行数学计算计算结果
verify(fact)验证某个事实True / False
python
# ==========================================
# 1. Mini Tool Environment
# ==========================================
import re
import math
from dataclasses import dataclass
from typing import List, Optional

@dataclass
class ToolResult:
    """工具调用的返回结果"""
    tool: str          # 工具名称
    input: str         # 调用输入
    output: str        # 返回内容
    success: bool      # 是否成功

class MiniToolEnv:
    """模拟的轻量工具环境"""

    # 预设的"知识库"——搜索工具会从这里查
    KNOWLEDGE = {
        "earth_radius": "6371",
        "pi": "3.14159265",
        "speed_of_light": "299792458",
        "gravity": "9.8",
        "moon_distance": "384400",
        "population_china": "1400000000",
        "python_release": "1991",
        "gpt_release": "2020",
        "transformer_paper": "2017",
    }

    def search(self, query: str) -> ToolResult:
        """模拟搜索:在预设知识库中查找"""
        query_lower = query.lower()
        for key, value in self.KNOWLEDGE.items():
            if key in query_lower or any(w in key for w in query_lower.split("_")):
                return ToolResult("search", query, f"找到:{key} = {value}", True)
        return ToolResult("search", query, f"未找到与'{query}'相关的信息", False)

    def calculate(self, expression: str) -> ToolResult:
        """模拟计算器:安全的数学表达式求值"""
        try:
            safe_expr = re.sub(r'[^0-9+\-*/().]', '', expression)
            result = eval(safe_expr)
            return ToolResult("calculate", expression, str(result), True)
        except:
            return ToolResult("calculate", expression, "计算错误", False)

    def verify(self, fact: str) -> ToolResult:
        """模拟事实核查"""
        for key, value in self.KNOWLEDGE.items():
            if key in fact.lower() and value in fact:
                return ToolResult("verify", fact, "正确", True)
        return ToolResult("verify", fact, "无法验证", False)

# 测试环境
env = MiniToolEnv()
print(env.search("earth_radius"))
print(env.calculate("2 * 3.14159 * 6371"))
print(env.verify("earth_radius is 6371"))

定义多轮交互的 Agent Loop

python
# ==========================================
# 2. Agent Turn 与 Episode 定义
# ==========================================
@dataclass
class Turn:
    """一个交互轮次"""
    action: str          # "search" | "calculate" | "verify" | "answer"
    input: str           # 工具输入或最终答案
    observation: str     # 环境返回
    success: bool        # 工具调用是否成功

@dataclass
class Episode:
    """一个完整的 Agent 交互过程"""
    task: str
    ground_truth: str
    turns: List[Turn]

def run_agent_loop(env, task, action_plan, ground_truth):
    """执行一次 Agent 交互循环。"""
    turns = []
    for step in action_plan:
        tool = step["tool"]
        inp = step["input"]

        if tool == "search":
            result = env.search(inp)
        elif tool == "calculate":
            result = env.calculate(inp)
        elif tool == "verify":
            result = env.verify(inp)
        elif tool == "answer":
            correct = inp.strip() == ground_truth.strip()
            turns.append(Turn("answer", inp,
                              "正确!" if correct else "错误", correct))
            return Episode(task, ground_truth, turns)
        else:
            result = ToolResult(tool, inp, f"未知工具: {tool}", False)

        turns.append(Turn(tool, inp, result.output, result.success))

    return Episode(task, ground_truth, turns)

设计一个多步任务

任务:"地球的赤道周长是多少公里?" 正确路径是 search → calculate → verify → answer。

python
# 正确的工具调用序列
good_plan = [
    {"tool": "search", "input": "earth_radius"},
    {"tool": "calculate", "input": "2 * 3.14159 * 6371"},
    {"tool": "verify", "input": "earth_radius is 6371"},
    {"tool": "answer", "input": "40030"},
]

# 第 2 步算错了(π 取成了 3)
bad_plan = [
    {"tool": "search", "input": "earth_radius"},
    {"tool": "calculate", "input": "2 * 3 * 6371"},
    {"tool": "verify", "input": "earth_radius is 6371"},
    {"tool": "answer", "input": "38226"},
]

good_episode = run_agent_loop(env, task, good_plan, ground_truth)
bad_episode = run_agent_loop(env, task, bad_plan, ground_truth)

注意差策略的关键特点:只有第 2 步犯了错(π 取成了 3),但第 1、3 步其实都做对了。 最终结果错误(第 4 步),但错误根源在第 2 步。

对比 ORM 和 PRM 的信用分配

python
# ==========================================
# 4. ORM vs PRM 信用分配
# ==========================================
import numpy as np

def orm_credit(episode: Episode, gamma: float = 0.95) -> List[float]:
    """ORM:只有最终结果给 reward,中间步骤全部为 0。"""
    T = len(episode.turns)
    final_success = episode.turns[-1].success
    immediate = [0.0] * (T - 1) + [1.0 if final_success else 0.0]

    returns = np.zeros(T)
    G = 0
    for t in reversed(range(T)):
        G = immediate[t] + gamma * G
        returns[t] = G
    return returns.tolist()

def prm_credit(episode: Episode, gamma: float = 0.95) -> List[float]:
    """PRM:每一步根据工具调用是否成功给即时 reward。"""
    T = len(episode.turns)
    immediate = []
    for turn in episode.turns:
        if turn.action == "answer":
            immediate.append(1.0 if turn.success else -0.5)
        else:
            immediate.append(0.3 if turn.success else -0.3)

    returns = np.zeros(T)
    G = 0
    for t in reversed(range(T)):
        G = immediate[t] + gamma * G
        returns[t] = G
    return returns.tolist()

orm_bad = orm_credit(bad_episode)
prm_bad = prm_credit(bad_episode)

print(f"\n{'轮次':<6} {'动作':<12} {'结果':<8} {'ORM Credit':<14} {'PRM Credit':<14}")
for i, turn in enumerate(bad_episode.turns):
    status = "✓" if turn.success else "✗"
    print(f"第{i+1}{turn.action:<12} {status:<8} {orm_bad[i]:<14.3f} {prm_bad[i]:<14.3f}")

输出:

轮次   动作          结果      ORM Credit     PRM Credit
第1轮   search       ✓        0.000          0.656
第2轮   calculate    ✓        0.000          0.376
第3轮   verify       ✓        0.000          0.170
第4轮   answer       ✗        0.000          -0.500

ORM 模式下,差策略的所有步骤 credit 都是 0——包括第 2 步(calculate)。这是因为 ORM 只看最终答案对不对(第 4 步 answer 错了 → reward = 0),然后通过折扣把这个零信号反向传播。由于 ,所有步骤的 credit 都是 0。

如果改成"失败给负 reward",更严重的问题暴露出来——第 1 步正确的搜索也会被惩罚

python
def orm_negative(episode, gamma=0.95):
    """ORM 变体:失败时所有步骤都被惩罚"""
    T = len(episode.turns)
    final_success = episode.turns[-1].success
    immediate = [0.0] * (T - 1) + [1.0 if final_success else -1.0]
    returns = np.zeros(T)
    G = 0
    for t in reversed(range(T)):
        G = immediate[t] + gamma * G
        returns[t] = G
    return returns.tolist()

# 输出:第 1 步正确的搜索 credit = -0.857,被错误地惩罚

第 1 步搜索完全正确,却得到了 -0.857 的惩罚。 这就是 ORM 的核心问题:信号太粗糙,无法区分"正确的步骤"和"导致失败的步骤"。

PRM 的区分度是 ORM 的 19 倍。ORM 几乎无法区分正确步骤和错误步骤(区分度仅 0.045),而 PRM 能清晰地告诉模型"哪些步骤做对了,哪些做错了"(区分度 0.856)。在同样的训练步数下,PRM 的任务成功率比 ORM 高出约 30 个百分点。

实验总结

这个实验用纯 Python 模拟了一个多轮 Agent 环境,让你亲手感受到了 Agentic RL 的核心挑战:

  • ORM 信号太稀疏:失败时所有步骤 credit 都接近 0,模型不知道该改哪里。
  • ORM 错怪好人:失败时连正确的搜索步骤都被惩罚。
  • PRM 精确归因:正确步骤得正分,错误步骤得负分,区分度是 ORM 的 19 倍。
  • PRM 的代价:每步都需要评估——在真实场景中需要标注成本或训练 PRM。

核心洞察:多轮 Agent 的关键难题不是"用什么 RL 算法",而是"中间步骤怎么给 reward"。ORM 简单但粗糙,PRM 精确但昂贵,2025-2026 年的所有精细方法(GiGPO / SPA-RL / ARPO / IGPO / StepPO...)本质上都在 ORM 和 PRM 之间寻找更好的折中。

这个实验是模拟的

真实场景中,Agent 不会使用预定义的 action_plan,而是由模型动态决定每一步调用什么工具。模型策略的"好坏"取决于 RL 训练的效果,而 RL 训练的效果又取决于信用分配的质量——这是一个闭环。本实验跳过了策略学习,专注于理解信用分配本身。

与前面章节的联系

多轮 RL 的信用分配问题和第 6 章的策略梯度定理一脉相承。REINFORCE 用蒙特卡洛采样来估计 ——从当前步到结束的累积回报。多轮 RL 做的是同样的事,只不过"步"从单个 token 变成了一个完整的轮次。第 8 章的 PPO 通过引入价值函数(Critic)来降低方差——同样的思路在多轮 RL 中依然适用,只是 Critic 需要评估的不是"当前 token 的价值",而是"当前轮次的价值"。Turn-PPO 就是这条思路的现代版本。

规划能力则是多轮 RL 的进阶形态——信用分配解决"每步做得好不好",规划解决"整体走哪条路径最优"。两者共同构成了 Agentic RL 的决策核心。

下一节我们来拆解 Agentic RL 的工程核心——22.4 工具调用 RL,看看训练数据从哪里来、工具策略怎么学、系统怎么跑起来。

参考资料

现代强化学习实战课程

个人非商业离线镜像 · 内容版权归 WalkingLabs 所有,依 CC BY-NC-SA 4.0 授权 · 查看原文