Agentic RL入门4:Credit 错分与算法接口

本系列前两篇已经交代了 agentic RL 的基础知识、以及信用分配的介绍。本篇做的事是:先把 agentic RL 最容易错分 credit 的地方画清楚,再看 2026 年 4-6 月窗口里的工作分别改哪个接口

agentic RL 的核心难点是:长轨迹只拿到终点 0/1 reward,credit 很容易错分——成功轨迹里的坏工具会被奖励,失败轨迹里的好搜索也会被惩罚:

1. Agentic rollout:模型 action 和环境 observation 交错
$\tau_1$
读任务 好搜索 网页返回 多余工具 答对
$R=1$
$\tau_2$
读任务 好搜索 网页返回 证据误读 答错
$R=0$
$\tau_3$
读任务 错搜索 工具返回 无效工具 答错
$R=0$
实线框是模型生成的 decision tokens;虚线框是网页 / 工具 observation,只进入上下文,训练时通常 masked。
2. Trajectory-level RL 的错分:整条轨迹一起奖惩
标准广播
GRPO / PPO 先把每条轨迹的 reward 变成一个 trajectory-level advantage,再乘到该轨迹的模型 token 上。
$A(\tau)$ × all decision tokens
agentic 断点
成功轨迹里的"多余工具"也吃正 credit;失败轨迹里的"好搜索"也吃负 credit。最终分数知道这一局成没成,却不知道哪一步该负责。
3. Credit assignment 要补的一层:从轨迹分数到 step-level signal
$R(\tau_i)$
$\{A_{i,1}, A_{i,2}, \ldots, A_{i,T}\}$:每一步自己的 credit,再落到对应 action tokens。
$A_{\text{read}}$+读清任务约束
$A_{\text{search}}$+搜索命中方向
$A_{\text{obs}}$mask网页/工具返回不更新
$A_{\text{tool}}$-工具调用带偏
$A_{\text{answer}}$+答案整合有效
训练时真正更新的是 $A_{i,t}\times \log\pi_\theta(a_{i,t}\mid h_{i,t})$:credit 只乘到模型生成的 action tokens 上,环境 observation 保持 masked。

围绕这个 credit 错分问题,近期工作大致从五个接口切入:

01Intrinsic / Rollout 信号
动什么分支 / 截断 / 重采
修什么关键决策点被长轨迹淹没,坏 rollout 污染组内比较
APPO
T2PO
02信用分配 / 中间信号
动什么process reward / retrospective value
修什么末端结果无法定位哪一步贡献最大
Orchard
PiCA
03Reward 设计
动什么reward source / composition / density
修什么0/1 太稀疏,组级方差归零
SRL
ToolOmni
OpenWebRL
04Skill 信号 / 生命周期
动什么skill prompt、skill 蒸馏、skill 库增删
修什么高层经验只在外部说明书里,不能稳定内化或维护
SDAR
SLIM
05Loss 旁路监督
动什么PPO / GRPO 主项旁加 SFT-style aux loss
修什么主 reward 不教的局部行为学不到
AKBE

1. 信用分配:从终点 reward 到中间决策

按"信号来源"分两支:1.1 Intrinsic Signal 从 policy 自身的不确定性 / 未来策略升值里挖信号(无外部模型);1.2 Progress Reward 借助外部 step-PRM / 事后价值打分把"进展"显式注入。

1.1 Intrinsic Signal:用策略自身信号定位关键决策点

共性:不额外训练 PRM,而是从 policy 自身的不确定性、迟疑度或未来升值里找信号;这些信号可以用于截断、重采、branch,也可以辅助 advantage 加权。

[1] APPO — Agentic Procedural Policy Optimization ★ 2026/06 NEW USTC × Alibaba AMAP token 级 branching 未来感 advantage

一句话:同一个"未来升值量",既挑 token 分叉点又放大功劳。

动机前作 ARPO 只在 tool-call 边界 branching(即同一前缀派生多条 continuation 做 credit 局部化)。APPO 想把粒度推到每个 token,但 pilot 发现"光挑高熵 token"会大量命中 march/november 这类罕见词,而不是真正的决策点——需要再加一个信号来过滤。

$\Omega$ 升值对 rollout 第 $i$ 个 token,把之后所有 token 的新旧策略概率比 $\rho_{i'}$ 按距离衰减地累乘起来。$\Omega$ 大就意味着沿这条路走下去,新模型相对旧模型显著更看好——它在量化"这个位置的选择对后续轨迹有多重要"。

$$\Omega_{n,i} = \exp\Big(\sum_{i' \ge i} \gamma^{i'-i} \log \rho_{i'}\Big)$$

BS 选位置Branching Score = 熵 × 未来升值——两个信号分别 z-score 归一再相乘,任一项接近 0 都会被压掉。所以只有"现在不确定 + 未来真的会改变模型偏好"的 token 才能拿高分,自动过滤掉高熵罕见词。$\Omega$ 先做不对称 clip 防止离群 token 主宰排名:

$$\mathrm{BS}_{n,i} = Z\big(\mathrm{clip}(\Omega_{n,i})\big) \cdot Z(H_{n,i})$$

$\Omega$ 复用每条 rollout 取 BS top-B 的 token 用当前策略续写 branch(init 和 branch 因采样分布不同需分组各算 advantage)。然后 base advantage 再被 $\Omega$ 派生的项放大一次——同一个未来升值量,前面用来选位置,后面用来分功劳。这是 APPO 比同期 step-PRM 注入、固定 workflow 模板等方案"轻"的核心原因。

结果GAIA Qwen3-14B 46.6 新 SOTA;消融显示去掉 $\Omega$ 派生的 advantage 放大项就掉 3.4 点(Qwen2.5-7B),说明"选位置"和"分功劳"两处复用都有贡献。→ 深读

[2] T2PO — Two-level Trajectory Policy Optimization UCLA ICML Spotlight token 不确定性 turn 重采

一句话:loss 和 reward 都不动,靠一个迟疑度信号在 rollout 当下截断卡壳段、重采重复轮。

承接前几篇都在动 reward / advantage / loss 三件套,T2PO 反过来——这三件全不改,只改 rollout 本身。理由是:多轮 agentic RL 的崩塌有相当一部分来自坏 rollout 污染了组内对比(一条轨迹在 think 里磨蹭半天、或者这一轮跟上一轮在重复同一套推理),把这些样本砍掉,组内方差自然就稳了。

迟疑度两个钩子共用一个 token 级标量 $M_t$:把每步的 Shannon 熵 $H_t$(分布平不平)和 top-$j$ 负对数概率均值 $C_t$(头部确不确定)各自沿轨迹 min-max 归一化后线性融合,落在 $[0,1]$:

$$M_t = \alpha \cdot \tilde{H}_t + (1-\alpha) \cdot \tilde{C}_t$$

token 层 TTI判 over-thinking 不看 $M_t$ 绝对值(商品名等关键词本就高熵),而是看相邻步差 $\Delta_t = |M_t - M_{t-1}|$ 在滑动窗口 $N$ 步内的均值是否塌到阈值以下——塌了就说明思考在原地踏步,直接覆写下一步 logits 强制收尾 </think> 进入动作段。每条轨迹只允许触发一次,防止模型学到"先吐两个废 token 就被切"的退化解。

turn 层 TDS把这一轮所有 token 的 $M_t$ 做几何平均得到整轮迟疑度 $\Phi^k$,再看跨轮差 $\Gamma^k = |\Phi^k - \Phi^{k-1}|$。若 $\Gamma^k$ 小于阈值,说明这一轮和上一轮的"迟疑 profile"几乎一模一样(典型如反复 search 同款商品却又选错颜色),同 state 重采本轮。与 SimpleTIR 那种"reward=0 / 无 tool call 就过滤"不同,TDS 判的是跨轮不确定性变化,而非单轮内的合法性。

收益两个钩子都跑在采样路径上,零反向传播、零额外模型,可直接叠到 GRPO / GiGPO / DAPO 之上。WebShop 上 Qwen3-4B 把 SR 推到 81.64,三 seed 方差从 ±4.11 压到 ±0.39——稳定性才是真正的护城河。→ 深读

这条线的判别标志:所有改动都发生在 generate 调用内部或两次 rollout 之间,从不进 loss 公式。Orchard 的 BAR 自适应采样思路类似(动态调 $N$),但 Orchard 主贡献在 §1.2 CA-SFT,故放在那里。

1.2 Progress Reward:给中间步骤打进展分

共性:借助一个事后价值 $V(s_t)$ 或 step-PRM 给中间步打"进展分",再按方法差异把信号落到 SFT 监督、reward tensor 或 GAE 回流路径上。

[3] Orchard — CA-SFT + Balanced Adaptive Rollout Microsoft Research CA-SFT 自适应采样 SWE+GUI

一句话:失败轨迹里仍有"做对的子段",事后给每步打价值,把上升段捞出来当 SFT 监督。

动机SWE 任务里约 30% 的轨迹最终测试不过,常规做法是整条丢掉。但失败轨迹里往往有几步确实在朝对的方向走,全扔等于把这部分免费监督也浪费了。

事后价值关键铺垫是 $V(s_t)$ 不是要训的 critic,而是 teacher 已经知道最终结果之后回头打的事后分数,含义是"站在这一步往后看,通过测试的概率有多少"。它只在数据准备阶段算一次,不进训练 loop。

上升段 SFT由 $V(s_t)$ 派生出每步的功劳信号 $c_t = V(s_{t+1}) - V(s_t)$,找出连续若干步满足 $c_t \ge 0.05$ 的子段 $S(\tau)$(rise segment),只在这些 token 上算 SFT loss:

$$\mathcal{L} = -\sum_{t \in S(\tau)} \log \pi_\theta(a_t \mid h_t)$$

注意 $c_t$ 只用来定位高功劳子段,定位完之后做的是 SFT,不是把它当 advantage 喂给 PPO——所以也不需要外挂 step-PRM 这种步级奖励模型,是纯 trajectory-internal 的信用分配。

BAR 采样组内全对或全错时 advantage 归零、整组算力浪费,是采样工程线的老问题。BAR 改成边采边判:每次增量采 $s$ 条,一旦累计正例比例 $\rho$ 落进中间区间 $[0.375, 0.625]$ 就立刻早停凑足一组;持续全对/全错则采到上限再 fallback。本文重心仍在 CA-SFT,BAR 是配套。

结果这套配方让一个 4B VLM 在 SWE-bench Verified 上反超 235B 的 distill teacher,说明"从失败轨迹捞高功劳子段"在小模型上尤其划算。→ 深读

[4] PiCA — Pivot Credit Assignment HKUST(GZ) step-PRM GAE 兼容 无 value head

一句话:用单调 PRM 把每次检索的信息增益写成中间 reward,让 GAE 自己把功劳回流到 pivot 步。

动机Search-R1 这类多跳检索代理只在末端拿一个 outcome reward(EM/F1),中间哪一次检索才是 pivot(让模型从"不会"跳到"会"的关键步)、哪一步只是凑数,advantage 一路均摊之后完全分不清。PiCA 的解法是外挂一个过程打分器,把信用分配的活儿还给 PPO。

单调 PRM训一个 step-PRM 吃截至第 $t$ 步的部分轨迹,输出"还没答对"的概率 $h(t)$,越接近答案越小。关键是加一道单调性约束(hinge 罚 $h(t)$ 在好轨迹上回升、在坏轨迹上反悔),强制"信息只增不减"——这样 $h(t)$ 的下降量才能当成"这一步真的有进展"的可信信号。

注入 rewardrollout 时每碰到一次 </search>,就把相对一个固定基线的增益写进该 token 的 reward:

$$r_t^{\text{step}} = 1 - h(t) - r_{\text{base}}$$

后面再乘 $\alpha\gamma^{(i-2)}$ 的指数衰减抑制无脑刷工具调用。注意这是直接落到 token 级 reward 张量上,而不是新增一路 head。

GAE 回流因为中间 reward 已经在轨迹内,GAE 自然把它沿 $\gamma\lambda$ 衰减回流到造成那次 pivot 的所有 token 上,原来那一路 outcome critic 完全够用——不需要新增 value head,这是 PiCA 相对 step-level RM 路线最便宜的地方。→ 深读

这一组的本质冲突:Orchard / PiCA 各用一种"自上而下"的信号 — retrospective value、step-PRM — 把端点 reward 分摊到中间。两者其实可叠,例如把 PiCA 的过程打分器接到 Orchard 的失败轨迹回溯链路里。
方法credit 来源关键设计结果亮点
APPO未来感 $\Omega$(intrinsic)BS 选位置 + advantage 放大GAIA 14B 46.6 SOTA
T2POtoken 迟疑度(intrinsic)TTI 截 think + TDS 重采 turnWebShop 81.64 / 方差 ±0.39
Orchard (CA-SFT)retrospective value $V(s_t)$失败 → 监督信号SWE-V 64.3 → 67.5%
PiCAstep-PRM注入 reward_tensor 让 GAE 接手不动 trainer,~80 行

2. Skill 信号:把外部经验内化或维护起来

[5] SDAR — Self-Distilled Agentic RL ZJU-REAL aux loss 蒸馏门控 verl 兼容

一句话:同模型双前向自蒸馏,只在"看了攻略才会"的 token 上拉 student。

动机agentic 任务 reward 稀疏,想让模型学一些高层 skill(怎么 clean、怎么 pick_and_place),但又不愿意推理时还在 prompt 里挂一份 skill 说明书。SDAR 想把"看攻略才会做"的能力直接压进权重,推理时把攻略撤掉。

双前向对当前 student $\pi_\theta$,第一遍只喂原 prompt $x$,第二遍把人工写的 skill markdown $r$ 拼在 $x$ 前面再 forward 一次。两次共享同一份权重,第二次充当"看了攻略的 teacher"——这就是 self-distillation 的来源:老师就是带 hint 的自己。

$\delta_t$ 门控对每个生成 token $y_t$,算 teacher 比 student 多出来的 log-prob $\delta_t$,再过 sigmoid 压成 0~1 的软门控 $g_t$,$\beta$ 控制切换陡峭程度:

$$\delta_t = \log \pi_\theta(y_t \mid r, x) - \log \pi_\theta(y_t \mid x),\quad g_t = \sigma(\beta \cdot \delta_t)$$

$g_t \to 1$ 意味着"看了攻略才会",该蒸馏;$g_t \to 0$ 说明 student 已经反超,别瞎纠正。

加权 NLLaux loss 形式是 student 自身负对数似然,再被 $g_t$ 加权——可以理解为把 teacher 近似成 one-hot 后退化出来的 token-level CE/KL,只是用 $g_t$ 决定每个 token 学多少。它跟主 policy gradient 并行相加,GRPO 那一套 advantage、rollout、reward 没动:

$$\mathcal{L}_{\text{SDAR}} = -\sum_t g_t \cdot \log \pi_\theta(y_t \mid x)$$

推理时撤掉 $r$,student 必须把"看攻略才学得到"的那部分技巧内化到权重里。→ 深读

[6] SLIM — Skill Lifecycle Management CUHK skill 生命周期 边际贡献估计

一句话:把 skill 库本身当成可学变量,用 leave-one-out 决定每张小抄的去留。

动机skill(一段自然语言小抄,写成 SKILL.md 挂在外部库里供 agent 现查现用)该怎么管,前人各执一端——Voyager 把跑出来的技能一股脑塞进库、只增不减;SkillRL 则把 skill 当成 RL 训练时的脚手架,训完就丢。SLIM 把活跃 skill 集 $\mathcal{S}=\{s_1,...,s_K\}$ 和策略并列为训练变量:策略在学,库也在学。

边际贡献判断某张小抄值不值得留很直接——同一批 validation 任务跑两遍,一遍带它、一遍抠掉它,成功率差就是它的贡献:

$$\Delta_k = \mathrm{Eval}(\mathcal{S}) - \mathrm{Eval}(\mathcal{S} \setminus \{s_k\})$$

保守退役单次 audit 噪声大,$\Delta_k$ 走 EMA 平滑后做三档调度:高于上界留下,落入中间带继续观望,只有跌破下界(极保守,几乎打平才退)且曝光数与连续低分轮次都达标才正式退役。门槛刻意压低,是为了避免把"暂时没用上但关键时刻顶事"的 skill 误杀。

扩库当某类任务带着现有 skill 仍频繁失败,说明库里缺角,触发 expand——把失败轨迹喂给外部 LLM,让它写一张新 SKILL.md 补进库。于是 skill 集大小是一条非单调曲线:先扩后缩,库随着策略一起被裁剪和补完。→ 深读

这条线的共性:两篇都把"skill 说明书"作为一阶变量处理——SDAR 通过双前向蒸馏把攻略压进权重、推理时撤掉;SLIM 直接把 skill 库本身作为可学集合、按边际贡献增删。与 Voyager-style skill library 的根本区别:skill 信号参与训练 epoch 更新,而非只读 prompt 拼接。

3. Reward 设计:让终点信号变密、变可分

共性:advantage 公式、PPO loss、采样策略都不动,只换 reward 的"来源"或"形状"。解法是把 reward 变连续、变可分解、或换一个更密的判别源。

[7] SRL — Step-wise Reward Learning Google 稠密 reward SWE-Bench

一句话:稀疏奖励全挂时,用字符相似度救活 GRPO 梯度。

动机难题上 7B 模型一组 rollout 经常全答错,组内 reward 全是 0,GRPO 标准化后 advantage 也整组归零——模型完全没梯度可学。这是 agentic RL 训练最常见的"全 fail 组无信号"困境,题越难越无解。

动作切分专家轨迹生成:数学用 R1 在 s1K-1.1 上生成的长 CoT,SWE 用 Claude 3.7 Sonnet 跑出来且 patch 通过校验的 5000 条 agent 轨迹(展开成 134k 个 step item)。切分完全沿用 teacher 已有的结构化分段:一步就是一次 <function=bash> 工具调用或一个"1. Title"编号子标题,不引入新算法。模型先在 <think> 段自由思考、再在 <solution> 段吐出 action。

稠密 rewarddifflib 算模型 action 与专家 action 的字符重合率 $r\in[0,1]$ 作为 reward(Ratcliff-Obershelp 算法:递归匹配两个字符串的最长公共子串、占总长度的比例,能对"部分对"给出连续分数)。

救活梯度把这个连续 $r$ 喂进 GRPO 的组内归一化。即便整组都不像专家,字符重合率也会散开、$\sigma>0$,advantage 天然不再塌零。

结果7B 模型在 SWE-Bench Verified 端到端从 3.2% → 8.6%(oracle file edit 设置下 5.8 → 14.8%,相对增益 74%)。论文最强配方是两阶段:先用 SRL 的 dense reward 把 policy 拉进专家行为流形,再切到 RLVR 用稀疏 0/1 做 outcome 校准。→ 深读

[8] ToolOmni — 多目标 GRPO 乘性组合 ACL 2026 多目标 GRPO 乘性 reward

一句话:工具使用拆成两段独立 GRPO,乘性 reward 让模型没法靠格式分凑数。

两段 RL开放工具使用其实是两件事——先翻到对的资料,再基于资料拼出答案。ToolOmni 把它拆成两段独立训练:检索段只考"召回到没",回答段只考"答得对不对",互不干扰。

乘性 reward检索段把"召回率"和"工具顺序命中率"两个分量相,再加一小项 format 分。乘的意义是任意一项归零则主分归零——光 search 召回拉满但 final_tools 乱排照样零分;想只靠 format 分撑场面也撑不起来:

$$R_{\text{ret}} = \text{InfoRecall} \times \text{AnswerTran} + 0.2 \cdot \text{format}$$

段间隔离两段 reward 量纲完全不同(一个是 0~1 比率,一个是 RM 整数分),所以各自做组内归一化避免互相挤压;再用一道 $R_{\text{ret}}\ge 0.95$ 的门槛把"资料没找全"的烂样本挡在回答段之外,防止检索失败把噪声传到答题环节。→ 深读

[9] OpenWebRL — MM-GRPO + 自蒸 judge Microsoft Research MM-GRPO 自蒸 judge 多模态

一句话:把闭源 judge 蒸成会讲理由的小模型抗 hack,再用整轨广播 + 不对称 clip 的 MM-GRPO 续命视觉 agent 的探索。

动机视觉 web agent 在活体浏览器里跑长轨迹(看截图、点 click/scroll/type 等原子动作),结尾只拿一个 0/1 reward。这条 reward 默认走闭源 API,又贵又把 pipeline 卡死,必须先把 judge 拿回自己手里。

蒸 judge作者让闭源大模型给一批 rollout 打"(理由, 0/1)"双标签,再蒸出一个小 judge 替代 API。关键不是省钱,而是让 student 同时学"为什么判 1"——只学 bit 的 judge 容易把判断折叠成几个表面信号,policy 把这些信号刷出来就能骗高分;逼 judge 输出理由能把判断顶到更鲁棒的语义层,让 reward hacking 难得多。

MM-GRPO不做 step 级拆解(那是 §1 那组的活),整条轨迹共享同一个 advantage,再广播给该轨迹里每一个 assistant token。文章押的赌注是 judge 的语义信号足够 dense,整轨广播就能区分轨迹好坏,不需要再造 process reward。

不对称 clip稀疏正奖励一旦命中,会把那条 token 概率快速推向 1,policy 分布过早集中、探索消失(熵塌缩)。MM-GRPO 把 importance ratio 的上探空间故意做得比下压空间大,给"提概率"方向多留 headroom——反直觉地放大上行 clip 反而是在保留探索:

$$\rho \in [\,1 - \epsilon_{\text{low}},\ 1 + \epsilon_{\text{high}}\,],\quad \epsilon_{\text{high}} > \epsilon_{\text{low}}$$

另外两 trick(1) dynamic sampling——组内 reward 方差为 0 的整组当场丢,不在零梯度样本上浪费算力;(2) K=1 截图——上下文里只留最近一张网页截图,旧图全砍,靠文字 reasoning trace 续历史,省显存让长轨迹跑得动。最终 4B 视觉 agent 在 Online-Mind2Web 上反超自家 235B Thinking 基线。→ 深读

方法改 reward 的方式关键设计代表结果
SRL形态:0/1 → 连续相似度Ratcliff-Obershelp + format gateSWE-V 3.2 → 8.6%
ToolOmni组合:加性 → 乘性Recall × Tran,分段 GRPO双段独立 GRPO
OpenWebRL判别源:规则 → 自蒸 Judge-8B$545 蒸 43.2K rollout4B → Mind2Web 67%
这条线的本质:稀疏二元 reward 让 group 经常零方差,三篇分别从"形状"(SRL)、"组合"(ToolOmni)、"来源"(OpenWebRL)三个角度把 reward 信号变密、变可分解。

4. Auxiliary Loss:用旁路监督补主 reward

共性:advantage 与 PPO 主 loss 都不动,只在总目标里加一条 SFT-style 或 distill-style 的 aux loss,引导模型学到主 reward 之外的特定行为。

[10] AKBE — Active Knowledge Boundary Exploration 腾讯 双路 rollout aux loss 工具成本

一句话:把"该不该查工具"从 reward 拆出来,靠双路 rollout 投出 2-bit 标签,挂一条便宜的辅助 CE。

动机Search-R1 这类 agentic RL 越训越爱调搜索,因为"该不该查"本质是 per-instance 的决策,塞进全局 reward 必然 mismatch——模型学不到"这题我自己会"和"这题我真不会"的区别。

双路 rollout同一道题采两组轨迹:允许调工具的 with-tool 组、屏蔽工具的 no-tool 组。每组只问一句"有没有至少一条答对",得到两个 0/1 指示符 $WT, NT$,拼成 2-bit 标签——这就把"模型自己会不会、工具帮不帮得上"两个维度物理分开了。

四象限$(1,1)$ 自己就能答 → 模仿 no-tool 正解,教它别多查;$(0,1)$ 工具反把答案带歪 → 同样回拉到 no-tool;$(0,0)$ 都不会 → 不动梯度,留给 GRPO 自己挣扎;$(1,0)$ 才是真知识盲区,需要专门挑模范。

挑模范$(1,0)$ 象限里同时可能有"调 3 次工具才答对"和"调 1 次就答对"的轨迹,AKBE 选后者作为模范——用最便宜的成功轨迹隐式传递 step 级节制信号,免去单训 PRM。

辅助 CE把挑出的模范轨迹 $y_q^*$ 当 gold,做一项 token-CE 挂到 GRPO 旁边,reward 和 advantage 全程不动,所以跟 GRPO / DAPO / GSPO 即插即用:

$$\mathcal{L}_{\text{AKBE}} = \lambda \cdot \mathcal{L}_{\text{SFT}}(\pi_\theta \mid y_q^*), \quad \lambda = 0.05$$

最终工具调用 −18%,且因为 no-tool 那组省了检索 I/O,每步还顺带快一截。→ 深读

这一组的共性:aux loss 是"局部手术刀"。AKBE 教模型"何时该调工具"。aux loss 形态都是 SFT 风格 ($-\log \pi_\theta$),门控来自双路 rollout 对比。