OpenClaw与OPD蒸馏技术:Top-K反向KL实现对比

项目背景与核心概述
本系列文章将以智能体强化学习领域的OPD(On-Policy Distillation,在线策略蒸馏)技术为核心,以OpenClaw-RL与Hermes Agent的源码作为切入点,系统梳理相关概念与实践细节。过程中会涵盖基础理论、扩展思路与完整的工程实现,所有内容作为一个整体呈现,部分概念会在不同章节中交叉出现以保证理解的完整性。
OpenClaw-RL是专为智能体工具使用场景设计的在线强化学习框架,通过从环境反馈中提取过程奖励信号训练语言模型,支持三种核心工作模式:
- openclaw-rl:基于二元奖励的强化学习,即Binary RL / GRPO
- openclaw-opd:基于后见之明提示的在线策略蒸馏
- openclaw-combine:联合训练模式,在单次PPO更新中同时结合RL奖励信号与OPD的教师信号
Hermes Agent中的OPD实现本质是OpenClaw OPD的Atropos平台移植版,二者共享同一套OPD算法逻辑,但在工程架构层面存在显著差异:Hermes适配了Atropos的解耦架构,实现环境与训练器分离;而OpenClaw则采用SLIME的一体化架构,环境与训练器紧耦合。
OPD核心原理与工作流程
OPD完整工作流
OPD的完整工作流程包含七个核心环节:
- 学生模型执行rollout,生成包含工具调用与结果的智能体对话
- 从对话历史中提取`(assistant回复,下一状态)`的配对,例如助手生成了存在bug的代码,后续工具返回了pytest失败的信息
- 由LLM Judge从下一状态中提取后见之明的提示信息,比如“应该使用dict而非list来存储映射关系”
- 将原始上下文与提取到的hint结合,构建增强后的prompt
- 通过VLLM的`get_logprobs`接口完成计算:输入为增强后的prompt与学生原始回复的token序列,输出为模型在知晓hint的条件下,对学生每个token的Top-K logprobs
- 得到蒸馏所需的`distill_token_ids`与`distill_logprobs`,也就是带有后验知识的教师概率分布
- 训练器计算逐token优势:`A_t = teacher_logprob(token_t) - student_logprob(token_t)`,正值代表教师认可该token,需要上调权重,负值则代表教师不认可,需要下调权重
VLLM在OPD中的应用
OPD流程依赖VLLM(Managed Server)完成推理与logprobs计算,完整的技术链路如下:
OPD 环境调用 self.server.get_logprobs(input_ids=enhanced_ids, top_k=50)
↓
ManagedServer.get_logprobs():如果传入的是消息格式,则先通过tokenizer.apply_chat_template转换为prompt,再委托给底层服务
↓
VLLMServer._get_logprobs_wrapper():构建请求参数,包括精确的prompt_token_ids、请求返回的prompt_logprobs数量,通过HTTP POST发送到VLLM的/generate端点,最后解析返回的结果并归一化为统一格式
↓
VLLM服务进程:接收请求后,对每个位置计算条件概率 P(token_i | token_0, ..., token_{i-1}),返回每个位置的Top-K token ID与对应的logprobs
特别说明:VLLM的请求参数中`max_tokens=1`,这个设计非常关键——该请求不会生成任何新文本,仅对输入的prompt token序列完成一次完整的前向传播,获取每个位置的prompt_logprobs,也就是条件概率。
Teacher Scoring与Teacher Forcing的区别
在深入技术细节前,需要厘清两个容易混淆的概念:Teacher Scoring与Teacher Forcing,尽管名称相似,但二者的核心目的与实现逻辑完全不同。
- Teacher Forcing:经典的NLP训练技术,训练时输入为真实的ground truth token序列,模型预测下一个token,核心特点是每一步输入的都是真实的ground truth,而非模型自身上一步的输出,能够避免错误滚雪球的问题,所有的SFT与预训练任务都使用该技术。
- Teacher Scoring:OPD流程中使用的核心技术,并非“强制训练”,而是“评估打分”:向教师模型传入完整的序列(prompt+hint+学生的回复),让模型完成一次前向传播,输出每个token的log-prob,整个过程不会生成任何新的token,仅完成打分评估。
两者的核心区别可以通过下表清晰对比:
| 维度 | Teacher Forcing | Teacher Scoring |
|---|---|---|
| 核心目的 | 提供ground truth输入以完成模型训练 | 获取概率分布以完成评估与蒸馏 |
| 使用的token | 真实的ground truth数据 | 学生已经生成的回复内容 |
| 最终产出 | 模型的预测结果与对应的loss | 每个token的log-probability |
| 是否生成新token | 不生成,但会计算loss | 不生成,仅完成概率计算 |
| 并行性 | 支持全序列并行 | 支持全序列并行 |
| 典型用途 | SFT/预训练任务 | OPD知识蒸馏 |
OPD对推理基础设施的特殊要求
OPD技术必须依赖VLLM或类似的工具(如SGLang),而非普通的LLM API,原因在于OPD需要三个核心能力:
- Tokenizer对齐:将增强后的prompt与学生回复准确编码为token IDs
- Prompt Logprobs计算:获取增强分布下每个response token的概率
- 精确Token对齐:将教师的logprobs准确映射回学生序列的对应位置
普通的OpenAI API仅能返回模型生成的token的概率,无法满足OPD的需求。而Token对齐的要求更为关键:OPD需要明确知道“位置37的教师logprob对应学生token序列的位置37”,这要求输入必须是精确的prompt_token_ids,而非原始文本,因为文本会被不同版本的tokenizer重新分词,产生不同的token序列。因此VLLM是OPD的硬依赖,整个teacher scoring pipeline都建立在VLLM独有的prompt_logprobs能力之上,需要对token进行精确到级别控制。
OPD管线与论文定义的关键机制
OPD的完整蒸馏管线包含五个核心步骤:
- 标准Rollout:继承自HermesAgentBaseEnv的`collect_trajectories`方法,先执行标准的Phase 2 rollout,得到包含tokens/masks/scores的ScoredDataGroup,OPD是在此基础上的后处理增强。
- 提取Turn Pairs:遍历对话历史,找到所有`(assistant_turn, next_state)`的配对,每个配对包含上下文消息、助手回复内容、后续的工具/用户回复、以及回复的角色类型。长工具输出会按照配置的`hint_max_next_state_chars`进行截断。
- LLM Judge提取Hint:从下一状态中提取后见之明的提示信息,Judge会被明确告知角色的语义,例如role='tool'代表这是助手调用工具后产生的结果,不应被视为助手事先应该知晓的信息。投票策略为并发发送多次judge查询,默认3次,每次temperature=0.7,最终选择最长的hint作为最终结果。
- 构建增强Prompt与VLLM Scoring:将hint附加到原始上下文后构建增强消息,对增强后的prompt与学生回复进行tokenize,通过VLLM的get_logprobs接口获取每个token位置的Top-K预测结果,最后提取回复部分的教师分布,并映射回学生的完整token序列。
- 打包训练信号:在ScoredDataGroup中添加`distill_token_ids`与`distill_logprobs`两个新字段,失败的序列会用零填充以保证形状一致,最终将打包后的数据交给Atropos Trainer处理。
OPD的论文将上述管线抽象为四个关键机制:
- Hint提取:通过PRM Judge从next-state中提取文本形式的hint
- Teacher scoring:使用hint增强prompt,计算token-level的logprob gap
- Overlap-guided hint selection:当教师与学生分布差异过大时,选择与学生Top-K重叠度最高的hint
- Log-prob-difference clip:裁剪逐token的advantage估计,防止教师与学生分布严重不一致时导致训练不稳定
两种工程实现的对比:同一算法,两套架构
Hermes的OPD实现本质上是OpenClaw-RL官方代码的Atropos平台移植版,二者共享核心的算法逻辑,但在系统架构层面存在显著差异。
共享的核心逻辑细节
两者的核心实现细节完全一致,包括:
- Judge Prompt的文本内容完全相同,包含`\boxed{1} / \boxed{-1}`的决策格式、`[HINT_START]/[HINT_END]`标记,以及对role='tool'的特殊说明
- 投票策略一致:并发多次查询后选择最长的hint
- Hint的插入方式相同:深拷贝上下文消息,在最后一条用户消息后追加hint内容
- Teacher scoring的核心逻辑一致:将增强后的prompt与学生回复拼接后tokenize,发送给推理引擎获取回复部分的logprobs
- 均支持Top-K蒸馏,默认K=50
- 优势计算公式相同:`A_t = teacher_lp - student_lp`
两种架构的具体实现
在线异步Proxy架构(OpenClaw-RL)
OpenClaw-RL采用Proxy模式,实时拦截用户的对话请求,核心设计如下:
User/Environment <--> OpenClaw Proxy (FastAPI) <--> SGLang Policy Server | | (intercepts live chat) | v PRM/Judge Server (hint + teacher lp) SLIME Trainer (PPO updates)
- 用户通过OpenAI-compatible API使用模型,Proxy透明转发请求到SGLang服务,同时获取response的logprobs
- 对对话turn进行分类,区分可训练的main turn与需要跳过的side turn
- 延迟触发OPD:当下一条消息到达时,才对上一条回复执行OPD,因为此时才能获取到next_state
- 实时提交采样结果,训练器在后台持续更新模型权重
- 使用`submission_enabled`事件控制权重更新时的提交暂停
离线Batch环境架构(Hermes)
Hermes采用先完成rollout,再批量执行OPD的模式,核心设计如下:
Atropos Trainer --> HermesAgentBaseEnv.collect_trajectories() | v group_size 个并行 rollout | | (完整 rollout 结束后) v AgenticOPDEnv._apply_opd_pipeline() | v per-sequence OPD 处理 | v ScoredDataGroup + distill fields → Trainer
- 由Atropos Trainer触发`HermesAgentBaseEnv.collect_trajectories`,生成多个并行的rollout
- 整个rollout完成后,一次性遍历整个对话历史,提取所有的turn pairs
- 批量执行OPD流程,为每个turn pair完成hint提取与teacher scoring
- 将结果打包为ScoredDataGroup,返回给Atropos Trainer
关键差异对比
| 维度 | OpenClaw-RL(在线Proxy) | Hermes(离线Batch) |
|---|---|---|
| 运行模式 | 在线持续学习,边用边练 | 离线批量训练,先收集再训练 |
| 数据来源 | 真实用户交互对话 | 合成的编程任务rollout |
| OPD触发时机 | 实时,下一条消息到达时触发上一轮的OPD | 延迟,整个rollout结束后批量处理 |
| 延迟特性 | 秒级,依赖对话间隔 | 分钟级,依赖rollout周期 |
| 并发模型 | 每个会话独立的asyncio Task | 多组并行rollout+顺序OPD处理 |
| 状态管理 | 基于session_id的pending字典,需要清理会话状态 | 无状态,每次重新遍历消息历史 |
| Reward信号 | 固定为1.0,仅作为占位符 | 多信号加权的连续值,包括正确性、效率与工具使用 |
| 权重更新方式 | 连续异步更新,训练器后台运行 | 同步批次更新,批量提交后完成训练 |
| 策略新鲜度 | 高,使用最新的模型权重生成对话 | 中,一批rollout共享同一模型权重 |
训练端的核心分歧:传单值vs传递分布
训练端存在一个核心分歧:OpenClaw-RL采用传单值的方式,而Hermes则传递完整的分布信息。
两种方式的通俗类比
可以用厨师学徒的场景来类比两种模式:
- 传单值(OpenClaw-RL):大厨品尝完学徒做的菜后,只给出一个分数,比如7.2分,学徒只能知道大厨觉得菜品还行,但无法得知具体的调整方向,比如盐放多了还是少了,火候是否合适。对应到OPD中,教师仅为学生生成的每个token提供一个单独的logprob值,训练器只能计算出优势值,无法得知教师的偏好排序。
- 传分布(Hermes):大厨不仅给出分数,还会告知心目中的Top-50选择排名,比如“最好的选择是加白胡椒,概率35%,其次是黑胡椒,概率25%”,学徒不仅知道自己的表现如何,还能了解到各个替代方案的优劣,以及离最优选择的距离。对应到OPD中,教师为每个token位置提供完整的Top-K分布,训练器可以获取更丰富的信号。
Reward与Advantage的概念澄清
需要明确两个不同层级的概念:
- Reward函数:由环境端计算,返回一个标量分数,代表本轮对话的整体质量
- Advantage函数:由训练器端计算,决定每个token的梯度方向与大小,二者属于不同的抽象层级。
在OpenClaw-RL中,环境端会计算teacher_log_probs(每个token一个值)与rollout_log_probs(学生的每个token的logprob),将两者放入Sample对象后提交给SLIME训练器,训练器内部计算`A_t=teacher_lp[t]-rollout_lp[t]`,再使用PPO clipped loss完成训练。
而Hermes不会传递student的logprobs给训练器,仅传递`distill_token_ids`与`distill_logprobs`,也就是每个位置的Top-K token ID与对应的logprobs。训练器可以选择两种方式处理:
- Token-level OPD:与OpenClaw-RL的方式一致,从Top-K中找到学生生成的token对应的教师logprob,计算优势值
- Top-K Reverse KL:使用完整的Top-K分布计算KL散度,让学生的分布整体靠近教师的分布
KL散度蒸馏的实际实现
在实际实现中,通常会选择Reverse KL(学生→教师)而非Forward KL(教师→学生),因为Reverse KL更适合智能体训练:我们希望学生专注学习教师最强的行为模式,而非试图模仿所有可能性,这样可以避免分散注意力。
Tail Trick技术
当教师提供了Top-50的token概率,但词表包含约15万个token时,剩余的149950个token需要通过Tail Trick进行处理:将词表从150K维压缩为K+1=51维,前50个bin对应教师选择的Top-K token,第51个bin(tail bin)代表所有剩余token的总概率之和。
具体的计算过程在log空间中完成,以保证数值稳定性:
# 1. 计算Top-K概率之和 log_sum_topk = logsumexp(topk_logprobs) # 2. 对结果进行clamp处理,防止log_sum_topk接近0导致下溢 log_sum_topk = clamp(log_sum_topk, max=-1e-7) # 3. 使用expm1计算tail的logprob,避免log(1-x)的灾难性抵消 tail_logprob = log(-expm1(log_sum_topk)) # 4. 拼接得到K+1维的分布 distribution = [topk_logprobs..., tail_logprob]
Reverse KL的计算
在获取学生与教师的K+1维分布后,计算Reverse KL散度:
per_token_kl = F.kl_div(
input=teacher_with_tail, # 教师的log-prob
target=student_with_tail, # 学生的log-prob
log_target=True,
reduction="none"
).sum(dim=-1)
loss = sum_of_sample_mean(per_token_kl)
根据PyTorch的约定,`F.kl_div(input, target, log_target=True)`计算的是`D_KL(target || input)`,因此上述代码计算的是`D_KL(student || teacher)`,也就是反向KL。
Top-K蒸馏技术详解
单值的OPD存在一个根本的信息瓶颈:教师对每个位置仅传递一个标量值,无法传递偏好排序的信息,比如教师知道“第5个token应该是print”,但也知道“如果不用print,write也比echo好得多”,这类信息在单值传输中会完全丢失。
Top-K蒸馏正是为了解决这个问题,将“教师告诉你哪个token对”升级为“教师提供一份参考答案册”,不仅告知标准答案,还说明每个备选答案的得分。该思路源自SDFT与SDPO两篇工作,核心是使用教师的Top-K logits加上tail bin完成KL散度蒸馏,而非传统的全词表KL或单token交叉熵。
单值OPD的局限
| 维度 | 单token OPD | Top-K OPD |
|---|---|---|
| 信息量 | 每个位置仅1个标量值 | 每个位置K+1维的分布 |
| 教师的第2到K个选择 | 完全丢失 | 保留完整的偏好排序 |
| 尾部token处理 | 完全未知 | 通过tail bin保底 |
| 存储开销 | [T]维度 | [T,K]维度,K=50时会增加50倍的存储量 |
| 学生过拟合风险 | 仅关注教师的最强token,容易过拟合 | K+1维的KL分布更温和,降低过拟合可能 |
Top-K蒸馏的实现与优势
Top-K蒸馏不是替代标准的OPD,而是作为可选的增强功能,二者可以共存。
在OpenClaw中,两种模式通过不同的shell脚本区分:
| 维度 | 标准OPD(token-level) | Top-K OPD |
|---|---|---|
| 启动脚本 | `run_qwen3_4b_openclaw_opd.sh` | `run_qwen3_4b_openclaw_opd_topk.sh` |
| 传输字段 | `teacher_log_probs [T]` | `teacher_topk_log_probs [T,K] + teacher_topk_indices [T,K]` |
| 损失计算方式 | 使用内置的`on_policy_distillation`分支,融入PPO clip | 自定义`topk_distillation_loss.py`,计算Reverse KL |
| 数据量 | 约300MB/sample,全vocab的logprobs | 约200KB/sample,仅K+1维的分布 |
Teacher Top-K vs Student Top-K的设计选择
OpenClaw对原版SDFT/SDPO进行了修改,使用Teacher Top-K而非Student Top-K:
- Teacher Top-K:在API端完成教师的前向传播,获取概率最高的K个token,再在训练端查询学生在这些位置上的logprob
- Student Top-K:原版方法,先获取学生的Top-K token,再查询教师在这些位置上的logprob
选择Teacher Top-K的核心原因包括:
- 架构约束:仅需要一次HTTP往返,SGLang原生支持`top_logprobs_num=K`参数,一次请求即可获取Top-K的logprobs与token ID,而Student Top-K需要两次往返,实现成本更高。
- 场景适配:在teacher远强于student的场景下,Teacher Top-K更合理,因为学生的Top-K可能指向低质量的模式,而Teacher Top-K直接告知应该学习的内容。
- Tail Bin区分度:Teacher Top-K的tail bin能够反映学生在教师重视的位置上的概率分布,而Student Top-K的tail bin通常很小,信息量较低。
数值风险与常见问题
在训练后期,当学生在教师的Top-K位置上的概率之和接近1时,`1 - exp(log_sum_topk)`会趋近于0,导致tail的logprob趋近于负无穷。代码中通过`clamp(log_sum_topk, max=-1e-7)`进行了上界保护,但并未对tail的下界进行限制,极端情况下可能产生NaN值,这是已知的工程取舍,通常在训练后期会停止Top-K蒸馏。
常见的问题与解决方案:
- 反向KL的潜在问题:反向KL是mode-seeking的,一旦学生放弃教师的某个模式,梯度会趋近于0,无法再学习回来。在训练后期可以切换为JSD对称散度或混合KL,平衡学习的专注度与覆盖范围。
- 信息损失评估:Tail Trick将大量尾部token聚合为一个bin,会丢失尾部的分布结构。当教师分布足够尖锐(Top-K捕获了超过95%的概率质量)且K足够大时,该损失可以忽略;如果教师分布平坦或K过小,则需要调整K的大小或使用全词表的KL。
- Reverse KL的定义:代码中`F.kl_div(input=teacher, target=student, log_target=True)`计算的是`D_KL(student || teacher)`,也就是反向KL,因为PyTorch的约定是`input`代表教师分布,`target`代表学生分布。
设计哲学与架构选择
Hermes-agent在整个RL训练流水线中扮演环境(Environment)的角色,负责生成任务、运行智能体对话、计算奖励、准备蒸馏数据,最终将打包好的数据交给外部训练器完成训练。Hermes本身不包含任何训练代码,没有loss计算、梯度更新与权重修改,训练由独立的组件完成,比如tinker-atropos或SLIME。
选择分离式架构的核心原因包括:
- 解耦架构的必然结果:OpenClaw的融合式架构需要同时传递teacher_log_probs与student_log_probs,但Hermes采用的解耦架构要求环境与训练器通过标准数据格式通信,任何一方都可以独立替换。Hermes的OPD是在rollout之后执行的,而非在线流程,因此可以自然实现解耦。
- Trainer可替换性:如果OPD信号已经融入advantage中,那么训练器必须理解该advantage包含蒸馏成分,破坏了标准GRPO/PPO的接口契约,导致更换训练器时需要重新实现融合逻辑。而分离式架构中,OPD作为独立的loss项,训练器可以选择性实现,甚至可以忽略蒸馏字段,仅使用标准的GRPO训练。
- 数据格式约束:Hermes传递的ScoredDataGroup是通用的数据格式,可以服务于多种环境,比如仅包含reward的纯GRPO环境、包含OPD数据的环境,以及未来可能的其他辅助信号环境,分离式架构可以灵活适配这些场景。
- 工程简洁性:分离式架构中,环境仅负责产出数据,训练器各自计算loss并相加,调试时可以分别关闭GRPO或OPD,观察各自的效果;而融合式架构中,两个信号耦合在一起,难以单独调试。
OpenClaw选择融合式架构的优势在于仅使用一个目标函数,不存在loss量级冲突的问题,更适合自研的一体化系统。

