别急着定"一次想几步":港大清华VAST让智能体按价值自适应挑时间尺度,Hard任务总分2.5倍于基线
别急着定"一次想几步":港大清华 VAST 让智能体按价值自适应挑时间尺度,Hard 任务总分 2.5 倍于基线
Part One:长程决策的病根,在"价值"这件事上
强化学习里有句老话:万物皆价值。智能体之所以知道"这一步该往哪走",靠的是价值函数对"当下选择会换来多少未来回报"的估计。价值估得准,策略才学得好。可一旦任务变长、阶段变多,这件事就开始失真。
2026 年 6 月,香港大学郑可欣、清华大学智能产业研究院(AIR)詹仙园、港大严鑫涛的论文 《Horizon Adaptive Offline Policy Learning via Value Stitching》(VAST) 挂上 arXiv(2606.21136),随后入选 NeurIPS 2026,代码在 GitHub(Whiterrrrr/value-stitching)开放。它瞄准的是离线强化学习(offline RL)里一个很根本、却常被绕过的问题:价值更新时,到底该往未来看多远?
先看病根。最经典的单步时序差分(TD)学习,用"即时奖励 + 下一状态价值的折扣估计"来更新当前价值:
V(s) ← r(s,a) + γ · V(s')
理论上,反复套用贝尔曼算子会收敛到最优价值函数。但那是在"样本无限、函数近似零误差"的理想国里。现实中我们只有有限数据,价值网络估出来的 V(s') 本身就不准;更麻烦的是,这个不准的估计会被当成"学习目标"再去教前一个状态,于是误差沿着决策链一路回传、层层放大。这就是自举偏差(bootstrapping bias)的累积——任务越长,链条越远,偏差滚得越大。
一个自然的补救是"多看几步":n-step TD 把未来 n 步的真实奖励都算进来再自举,Q-chunking 一类方法则围绕固定长度的动作片段建模。真实奖励用得越多,对"自己估自己"的依赖就越小,偏差理论上越轻。
但拉长跨度,不等于选对了跨度。 这正是 VAST 的切入口。
Part Two:固定步长的两难——为什么"多看几步"治标不治本
真实的长程任务有鲜明的阶段差异。想象你要指挥一个机械臂完成"把三个不同颜色的方块摆成指定图案":伸手、抓取、搬运、对位、放下,每个子阶段的节奏完全不同。而 n-step TD 或 Q-chunking 干的事,是预先钉死一个跨度 n(比如固定 5 步),所有状态、所有阶段都按这把统一的尺子去切。
这把尺子两头不讨好:
- 跨度定短了:关键转折还在视野之外,价值只能靠反复自举去"猜"远处,短期偏差依旧累积;
- 跨度定长了:把一个本应精细处理的关键动作(比如对位那一下)和无关紧要的过渡步骤混在同一段里打分,反而模糊了短期的关键选择。
更本质的矛盾是:任务的关键转折,绝不会恰好落在你预先画好的固定分段边界上。 用一把等距的尺子去量一个节奏不均的任务,边界系统性地对不齐。论文用一句话点破:现有方法采用的是"rigid, fixed-horizon recipe"(僵化的固定跨度配方),灵活性不足以刻画长程、多阶段任务里复杂的价值结构。
固定步长 n-step(一刀切) VAST 自适应跨度(按价值挑落点)
s0──s1──s2──s3──s4──s5 s0 s3 s6
|──── 5步 ────| |── G(s,s3,3) ─|
|──── 5步 ────| |── G(s3,s6,3) + γ³V(s6)
边界与真实阶段错位 ✗ 关键子目标在哪,就把自举落点钉在哪 ✓
所有状态同一把尺子 远目标用粗跨度,近关键用细跨度
这与人类的决策方式高度一致。论文特意打了个比方:规划远处目标时,人只会形成粗粒度的子目标加一个大概的跨度估计(“往前走约一百米再右转”);面对眼前的关键决策,则只盯住近端、可执行的后果(“减速、往左打一点躲开障碍”)。远粗近细、随状态而变——这是一种混合时间尺度(mixed-horizon)的建模需求。
经典的 option / 半马尔可夫框架、以及分层强化学习(hierarchical RL)其实都触碰过这个思想,但前者结构笨重,后者只能表达"高层 + 低层"两个固定尺度。真正能在任意 1~K 步之间自由、自适应地选跨度,并且好落地的方案,此前基本是空白。VAST 填的就是这个空。
Part Three:G-function——先学会给"任意一段"估价
要自由拼接不同长度的未来,得先能回答一个很具体的问题:从当前状态 s 出发,k 步后到达某个未来状态 s_k,这"一段"过程值多少?
VAST 为此引入一个辅助价值函数 G-function(horizon-based cumulative return):
G(s, s_k, k) = 从 s 出发、经过 k 步到达 s_k 这段过程的期望折扣累计回报
注意它和普通价值函数的区别:普通 V(s) 只看一个起点,目光一直延伸到回合结束;而 G 同时吃进起点 s、未来落点 s_k、跨度 k 三个输入,专门衡量"两个可达状态之间、指定步数内"的回报。它像是一把可以任意卡长度的卷尺——你想量 1 步到 K 步之间的哪一段,它都能给个数。
G-function 的训练很直接,可以用离线数据监督:从离线轨迹里抽出任意一段转移,这一段真实拿到的折扣奖励之和就是天然标签,不需要自举、不依赖网络自己的估计。这就从源头上避开了"用自己不准的输出教自己"的循环。
但只会给孤立片段打分还不够,模型可能只记住了训练数据里出现过的那些段。VAST 又加了一道组合一致性约束(compositional consistency):一段长回报,应当等于中间某点切开后的"前段 G + 折扣后的后段 G/价值"。这逼着模型在不同时间尺度之间保持自洽,从而能泛化到数据里没直接见过的 (s, s_k, k) 组合。
class GFunction:
"""辅助价值函数:估计从 s 走 k 步到达 s_k 这一段的折扣累计回报。
直接用离线轨迹里的真实分段回报监督,不靠自举。"""
def __init__(self, gamma=0.99):
self.gamma = gamma
self.table = {}
def label_from_segment(self, rewards):
# 一段真实轨迹的折扣回报:这就是 G 的监督标签
g = 0.0
for t, r in enumerate(rewards):
g += (self.gamma ** t) * r
return g
def fit(self, s, s_k, k, g):
self.table[(s, s_k, k)] = g # 直接监督
def __call__(self, s, s_k, k):
return self.table.get((s, s_k, k), 0.0)
def consistency_loss(self, s, s_m, s_k, m, k):
# 组合一致性:整段 ≈ 前 m 段 + γ^m · 后 (k-m) 段
whole = self(s, s_k, k)
split = self(s, s_m, m) + (self.gamma ** m) * self(s_m, s_k, k - m)
return (whole - split) ** 2
直接回报监督提供"数据依据",组合约束提供"跨尺度一致性",两者合起来把 G 训成一把刻度准确、还能量到没见过位置的卷尺。
Part Four:拼接贝尔曼算子——把自举落点从"下一步"放开到"任意子目标"
有了 G,VAST 就能改造价值更新规则本身。普通贝尔曼方程的自举落点被钉死在"下一状态 s′",跨度恒为 1。而既然 G 能给任意一段估价,我们完全可以把落点放开:
(拼接贝尔曼算子)
(T_stitch V)(s) = max_{k ∈ {1..K}, s_k} G(s, s_k, k) + γ^k · V(s_k)
读出来就是:在所有可能的跨度 k 和所有可达的未来状态 s_k 里,挑一个让"这一段回报 G + 落点之后的折扣价值 γ^k·V(s_k)“总和最大的组合。 选中的那个 s_k,就是当前最值得奔去的"中间子目标”;k 就是该用多远的尺度去看它。当 k=1 时,G(s,s₁,1) 退化为即时奖励 r,公式恰好还原成标准贝尔曼方程——所以传统单步 TD 只是 VAST 的一个特例。
这个算子漂亮在哪?它把"选多远"和"去哪"联合成了一个最大化问题,自举落点不再被动地等于环境给出的下一状态,而是主动选在价值结构最合理的那个子目标上。远的时候大步跨越、跳过无意义过渡;遇到关键节点时小步精细自举。
更重要的是它不是拍脑袋的启发式,论文给出了理论保证:拼接贝尔曼算子是一个 γ-收缩映射(γ-contraction),反复作用于任意有界价值函数,都会收敛到唯一不动点 V*。直观上,算子内部那个 max 不管选了哪个 k,对应的折扣系数 γ^k 都不超过 γ(k≥1),于是两次迭代之间价值函数的差距以至少 γ 的速率被压缩。有了收缩性,“这种自由选跨度的更新到底会不会发散、会不会不收敛"这个最容易被审稿人追问的问题,就有了干净的答案。
def stitch_backup(G, V, transitions, s, K=8, gamma=0.99):
"""VAST 拼接自举:在 1..K 步、所有可达落点中选价值最大的 (k, s_k)。"""
best = float("-inf")
best_choice = None
for k in range(1, K + 1):
for s_k, reachable in transitions.reachable(s, k):
if not reachable:
continue
candidate = G(s, s_k, k) + (gamma ** k) * V(s_k)
if candidate > best:
best, best_choice = candidate, (k, s_k)
return best, best_choice # 返回更新目标 + 最优(跨度,子目标)
def nstep_backup(rewards, V, s_n, n, gamma=0.99):
"""对照:固定 n-step,所有状态一刀切,落点被动等于第 n 步状态。"""
g = sum((gamma ** t) * r for t, r in enumerate(rewards[:n]))
return g + (gamma ** n) * V(s_n) # 无法挑落点,也无法按状态改 n
两段代码并排,差异一目了然:固定 n-step 只能被动接受"第 n 步到哪算哪”,而拼接自举主动在一张"跨度 × 未来状态"的表里搜最优。这正是 VAST 价值学习更准、尤其在长程任务上更稳的机制核心。
Part Five:两级策略——选子目标的脑,和走当下的手
光有准确的价值还不够,价值最终要变成动作。VAST 把策略干净地拆成职责不同的两层:
上层是拼接策略 π_stitch。 它读当前状态,输出"去哪个未来子目标 s_k、用多大跨度 k"。训练上采用优势加权(advantage-weighted):数据里价值更高的"状态—跨度"组合拿到更大的学习权重,让策略偏向那些被证明通向高回报的落点;实现上还用扩散/流模型(diffusion/flow model)来增强对子目标分布的表达能力。
下层是执行策略 π_exec。 它根据"当前状态 + 指定子目标",生成具体的原始动作,用基于目标的行为克隆(goal-conditioned BC)直接从离线轨迹学。测试阶段再叠加拒绝采样:围绕选定子目标采样多个候选动作,用 Q 函数打分、挑价值最高的那个执行。
当前状态 s
│
▼
┌─────────────────┐
│ π_stitch 拼接策略 │ 选 (k, s_k):去哪 + 看多远(优势加权/扩散)
└─────────────────┘
│ 子目标 s_k(注意:不传 k 给下层)
▼
┌─────────────────┐
│ π_exec 执行策略 │ goal-conditioned BC 生成原始动作
└─────────────────┘
│ 多个候选动作
▼
Q 函数拒绝采样,选价值最高 ──► 执行
这里藏着一个特别值得品的工程细节:执行策略的输入里刻意不含跨度 k。 为什么?因为训练数据是已有的、可能次优的行为策略产生的,那些轨迹里隐含的跨度是"旧策略的节奏";而推理时拼接策略给出的 k 是经过优化的"新节奏"。如果让 π_exec 在训练时依赖 k,就会造成训练分布(次优 k)和推理分布(优化后 k)的错配(train-test mismatch)。干脆不把 k 喂给执行策略——它只需要知道"眼下这个子目标是什么、我怎么走到它",至于这个子目标是几步之外的事,与手无关。脑管节奏,手只管走好眼前这一段,职责一劈开,错配就消失了。
整个闭环于是贯通:先用价值选出"值得前往的未来",再用价值筛选"眼前可执行的动作"。 同一套子目标信息同时服务于价值监督和动作生成,把"怎么评价未来"和"怎么走向未来"连成了一体。
把下层执行策略用几行伪代码写出来,两层分工和拒绝采样的位置会更清楚。注意训练阶段 π_exec 只吃"当前状态 + 子目标",完全看不到 k;k 只活在上层 π_stitch 的决策里:
class ExecPolicy:
"""下层执行策略:goal-conditioned BC 学动作,Q 拒绝采样挑最优候选。
关键:输入里没有 horizon k —— 手只需知道往哪个子目标走。"""
def __init__(self, q_net):
self.q = q_net
def bc_loss(self, s, s_k, a_expert):
a_pred = self.policy(s, s_k) # 只给 (s, 子目标),不给 k
return ((a_pred - a_expert) ** 2).mean() # 行为克隆:模仿离线轨迹动作
def act(self, s, s_k, n_candidates=8):
cands = [self.policy.sample(s, s_k) for _ in range(n_candidates)]
return max(cands, key=lambda a: self.q(s, a, s_k)) # 用 Q 给候选打分,选最高
这里其实藏着一个值得反复体会的工程判断:信息并不是给得越多越好。 直觉上,把"还要走几步"k 也告诉执行层,似乎能帮它更好地规划动作;但训练数据来自可能次优的旧行为策略,其隐含的时间尺度与上层优化后的新节奏并不一致。一旦执行层在训练时依赖 k,部署时拿到的却是另一套 k 分布,模型就会在一个它没见过的输入条件上做外推。VAST 选择干脆不传 k,本质上是用"接口层面的信息裁剪"来换取训练与部署的一致性——这是一种很典型的、用结构性约束消灭分布错配的做法,值得做 Agent 系统的人借鉴。
Part Six:OGBench 50 个任务——优势集中在多阶段硬任务
实验落在 OGBench,一个面向目标条件离线 RL 的基准,全是有挑战的机器人运动与操作任务:全身人形控制、迷宫导航、方块操作等。具体配置:
- 50 个状态输入任务、10 个领域:Basic 组含 6 个常用领域的 30 个任务,Hard 组含 4 个高规划复杂度领域的 20 个任务;
- 8 个基线,覆盖现有几条主流路线:单步价值学习(IQL、FQL、IFQL)、固定 5 步学习(FQL-n、IFQL-n)、分块价值(QC)、生成式价值(VALUE-FLOWS、FLOQ);
- 每个方法离线训练 100 万步,主表用 8 个随机种子、每个任务测 50 个 episode,规格相当扎实。
结果上,VAST 在领域均分和总分上拿到最佳或并列最佳,而增益最明显的地方,恰恰是 scene、cube、puzzle 这类需要多阶段操作与复杂规划的机械臂任务——也就是固定跨度最容易切歪的场景。最有说服力的一个数字:在全部 Hard 领域上 VAST 都是最强方法,整体总分达到最佳基线的 2.5 倍。 学习曲线上它在 Hard 设置里保持持续上扬,说明这种 horizon-aware 的建模范式确实能随任务复杂度"scale",而不是只在简单任务上占便宜。
论文还做了很直观的可视化:在 cube-triple-play-task3(三方块摆放)上,把真实轨迹和 VAST 预测的未来状态叠在一起,能看到模型选出的跨度不是机械地等距伸缩,而是贴合任务里一个个阶段性的"原子意图"——该跨就跨、该停就停,给出一条阶段定义清晰的中间目标路径,把遥远的最终目标逐级翻译成当下可执行的动作。自适应跨度改变的不只是"看多远",更是"下一段该走向哪里"。
Part Seven:启示与边界——它是离线 RL,不是现成的 Agent 外壳
最后把边界讲清楚,也把它和当下火热的 Agent 时序规划接起来。
第一,VAST 是离线 RL 方法,不是在线 Agent harness。 它的全部训练只依赖预先收集好的静态数据集,训练中不与环境交互;价值拼接、两级策略都是在离线范式下证明和评测的。把它直接等同于"让 ChatGPT 类 Agent 自适应决定一次执行几步"是过度外推——后者面对的是在线、高维、语言驱动、强随机的环境,挑战并不相同。
第二,实验基于状态输入而非原始像素。 OGBench 这 50 个任务用的是结构化状态,进入视觉/触觉等真实传感场景还需要表征学习的配合。论文结尾也坦承,VAST 的内核未来可以自然地与扩散策略、基于模型的方法、表征学习结合——这是开放方向而非已完成项。
第三,但它的核心洞察对 Agent 设计是通的。 本周我们刚讨论过"承诺深度(commitment depth)“问题:一个长程系统每次"规划一串动作后开环执行、再重新观察”,这串动作该多长?定短了,反复重规划、徒增前向计算;定长了,想象状态与真实状态逐渐背离、误差累积。另一项研究(arXiv:2605.09860)从在线 VLM 侧独立得到了几乎相同的结论——当最优深度随状态变化时,把深度当成状态的函数去学,严格优于任何固定标量。 VAST 从离线价值学习这一端,用 G-function 和可证明收敛的拼接算子,给出了同一个哲学的严谨版本:时间尺度不该是一个手工超参,而应是一个随状态、随价值动态决定的控制变量。
这对做 Agent 系统的人是个直接提醒:如果你现在还在用一个写死的"每次规划 N 步 / 每 N 步重规划一次",那无论 N 调成几,在节奏不均的真实任务里都是次优的。更值得投入的方向,是让模型学会判断"眼下这件事值不值得我一次承诺得更远,还是该赶紧停下来重新看一眼世界"——把"何时重新承诺"本身,交还给价值去决定。
参考资料:Zheng, K., Zhan, X., Yan, X.《Horizon Adaptive Offline Policy Learning via Value Stitching》,NeurIPS 2026,arXiv:2606.21136,代码 github.com/Whiterrrrr/value-stitching;清华大学 AIR、香港大学;新浪财经/AIR 学术解读。文中 G-function、拼接贝尔曼算子及 OGBench 50 任务、Hard 总分 2.5× 等均出自原论文;Python 代码为帮助理解的原理简化复现,非官方实现。