#JitRL:不用梯度更新,让 LLM Agent 在测试时做“即时强化学习”
论文:Just-In-Time Reinforcement Learning: Continual Learning in LLM Agents Without Gradient Updates
作者:Yibo Li, Zijie Lin, Ailin Deng, Xuan Zhang, Yufei He, Shuo Ji, Tri Cao, Bryan Hooi
时间:v1 2026-01-26;v3 2026-06-08
arXiv:<https://arxiv.org/abs/2601.18510>
代码:<https://github.com/liushiliushi/JitRL>
#0. 一句话先讲清楚
JitRL 想解决的是:LLM Agent 部署之后权重被冻结,怎么在不做梯度更新、不 fine-tune、不跑 PPO/GRPO 的情况下,仍然能从过去任务轨迹中学习,并在当前状态下改变行为策略。
它的做法可以概括成一句话:
把过去 episode 里的
(state, action, return)存进非参数记忆;当前推理时检索相似状态,估计每个候选动作的 advantage,然后把这个 advantage 直接加到 LLM 输出 logits 上。
形式上就是:
其中:
- :base LLM 对候选动作 的原始 logit;
- :从历史相似轨迹里估计出的 advantage;
- :控制记忆修正力度的温度参数;
- :修正后的 logit,再 softmax 采样动作。
所以 JitRL 不是“把经验写进 prompt 里让模型读”,而是更接近:
用外部记忆构造一个非参数 critic,在测试时对 policy logits 做 closed-form policy improvement。
这点很有意思,因为它把三个方向接到一起了:
- RL 的 advantage-based policy improvement;
- RAG / memory-based agent 的非参数经验检索;
- test-time adaptation / continual learning 的无梯度更新版本。
#1. 背景:为什么 LLM Agent 需要“部署后继续学习”?
LLM Agent 和普通聊天模型不一样。Agent 要长期和环境交互:
- 在 WebArena 里点击网页、填表、导航;
- 在 Jericho 这种文字游戏里输入命令;
- 在真实软件环境里调用工具、处理错误、完成长程任务。
这类任务天然有一个问题:同一个 agent 会反复遇到类似状态、类似失败、类似局部决策。
比如:
- 第一次找商品评价时,它可能直觉点击
Catalog,但后来发现评价入口其实在Marketing; - 第一次查 subreddit 帖子时,它可能用全局搜索,结果噪声很大;后来发现走
Forums更稳定; - 第一次打开产品库存时,它可能点击
Products,但后来学到 hover 才会直接展开子菜单。
人类员工会从这些失败中学会“这个系统的门道”。但当前 LLM Agent 部署后通常是 frozen model:
- 权重不变;
- prompt 可以变;
- memory 可以增加;
- 但 policy 本身没有真正被优化。
传统 RL 可以优化 policy,但代价很高:
- 要收集大量 trajectories;
- 要训练 value model 或 reward model;
- 要反复做梯度更新;
- 容易 catastrophic forgetting;
- 对线上 agent 来说更新频率和成本都很麻烦。
于是论文问了一个很直接的问题:
能不能保留 RL 的“根据 reward 改变策略”的能力,但不更新模型参数?
JitRL 的答案是:可以。不要把学习写进权重里,而是写进外部记忆和 logits 修正里。
#2. 和普通 memory / Reflexion 方法有什么区别?
很多 Agent 方法已经用了 memory:
- Memory baseline:把过去完整轨迹塞进上下文;
- Reflexion:任务结束后生成文字反思,下次放进 prompt;
- AWM / Voyager 类方法:从成功轨迹里抽取 reusable workflow 或 skill;
- EvoTest:用进化式策略优化 prompts / tests。
这些方法大多还是 in-context learning:把经验转成文本,让 LLM 自己读懂,然后希望它改变行为。
JitRL 的关键区别是:
| 方法 | 经验怎么用 | 本质 |
|---|---|---|
| Memory | 把历史 transcript 放进 prompt | 文本上下文增强 |
| Reflexion | 把失败总结成自然语言反思 | prompt-level self-improvement |
| AWM | 存储成功 workflow | 程序/流程记忆 |
| JitRL | 存储 (state, action, return),检索后估计 advantage,直接改 logits | 非参数 RL / test-time policy improvement |
直白说:
- Reflexion 告诉模型:“上次你错在这里,下次注意”;
- JitRL 则在动作层面说:“在这个状态附近,
click(MARKETING)历史回报高,click(CATALOG)回报低,所以现在直接提高前者 logit、压低后者 logit。”
这也是为什么论文强调:JitRL 不是简单检索文本,而是把 memory 当成一个 non-parametric policy distribution / value estimator。
#3. 方法总览:JitRL 的循环
JitRL 的整体循环分成两部分。
#3.1 推理时:即时检索、估值、改 logits
每一步 agent 面对当前 observation:
- 把原始 observation 抽象成紧凑 state ;
- 从记忆库 中检索 top-k 个相似状态;
- 用这些历史 transition 的 return 估计当前 state value ;
- 对每个候选动作估计 action value ;
- 得到 advantage:
- 用 advantage 修正 logits:
- 从修正后的 softmax 分布里采样动作。
#3.2 episode 结束后:反思打分、更新记忆
一次任务结束后,JitRL 会让一个 LLM evaluator 对完整 trajectory 做 step-wise reward assignment。
假设 trajectory 是:
Evaluator 输出每一步 reward:
然后计算每一步的 discounted return:
最终把每一步 transition 存成:
加入动态记忆库 。
这就形成了一个持续循环:
做任务 → 得到轨迹 → evaluator 做信用分配 → 存入 memory → 下次相似状态即时检索 → 改变 logits → 得到更好轨迹。
注意这里没有任何 weight update。
#4. 关键细节一:state 怎么表示?
原始 agent 状态通常很脏:
- WebArena 里可能是巨大的 HTML DOM;
- Jericho 里可能是冗长的游戏文本;
- 真实软件环境里可能有大量无关 UI 元素和历史记录。
如果直接拿这些原始文本做检索,相似度会很差。JitRL 的原则是:
不要追求完整复现 observation,而要让“功能等价”的状态映射到相近表示。
例如 WebArena 中,“找商品评论”任务下不同页面 DOM 可能差异很大,但它们的关键状态可能都是:当前在商品管理界面,需要找到 review 入口。
所以 JitRL 会构造 compact structured state,保留任务相关语义,丢掉无关噪声。这个设计非常重要,因为整个方法的 critic 是 retrieval-based:检索质量基本决定 value estimate 质量。
如果 state abstraction 做不好,就会出现两类问题:
- 相似状态检索不到,memory 不能复用;
- 不相似状态被误检索,错误经验污染当前决策。
这也是 JitRL 最像“非参数 RL”的地方:它不是把世界规律压进神经网络 value function,而是依赖 state representation + nearest-neighbor retrieval。
#5. 关键细节二:怎么估计 、、?
记忆库里存的是:
当前状态 来了以后,先检索 top-k 邻居:
#5.1 state value:邻居平均回报
意思是:当前状态附近,历史上平均能拿多少回报。
#5.2 known action 的 Q:同动作邻居平均回报
对某个候选动作 ,找到邻居里也做过这个动作的 subset:
如果这个集合非空:
意思是:在相似状态下,历史上执行这个动作平均效果怎么样。
#5.3 unseen action:乐观探索
如果相似状态里没人做过这个动作,JitRL 不能简单认为它不好,否则 agent 会过早锁死在旧动作里。
所以论文引入 optimism under uncertainty:以概率 给 unseen action 一个探索 bonus:
这里 的含义是:
- 当前附近经验越少,越应该探索;
- 经验越多,bonus 越小,逐渐从 exploration 转向 exploitation。
以概率 ,则给 unseen action 中性值 0,避免过度探索。
#5.4 advantage:相对当前状态平均水平的收益
最后:
这一步很关键。JitRL 不直接用 return,而用 advantage,因为它想知道:
在当前状态下,这个动作比“平均动作”好多少?
这正是 policy gradient 里 advantage 的作用。
#6. 关键细节三:为什么“加 logits”是合理的?
这是论文最核心的理论点。
JitRL 把当前推理时的 policy update 写成一个 KL-constrained optimization:
这是什么意思?
- 第一项:希望新 policy 多选 advantage 高的动作;
- 第二项:希望新 policy 不要偏离 base LLM 太远,保留语言能力和先验;
- :控制“相信 memory”还是“相信 base model”。
这个目标的 closed-form 解是:
如果 base LLM 的概率来自 logits:
那么上式等价于:
所以 JitRL 的 logit update 不是拍脑袋 heuristic,而是这个 KL 约束策略优化目标的闭式解。
这点可以理解成:
PPO / GRPO 通过梯度更新参数,间接改变 logits;JitRL 直接在当前候选动作层面,用检索到的 advantage 对 logits 做一次解析形式的 policy improvement。
当然,它优化的是局部候选动作集合,不是整个 token vocabulary 或完整 policy 参数空间。但对 agent action generation 来说,候选动作通常本来就是有限集合,这个近似是实用的。
#7. 算法伪代码:把 JitRL 写成可实现流程
下面是一个简化版:
memory = [] # stores (state, action, return)
for episode in episodes:
traj = []
while not done:
obs = env.observe()
s = abstract_state(obs)
# 1. retrieve similar transitions
neighbors = top_k(memory, key=lambda x: sim(s, x.state))
# 2. estimate V(s)
V = mean([x.return_ for x in neighbors])
# 3. get LLM candidate actions and logits
candidates, logits = llm.propose_actions(obs)
# optional: add actions appearing in memory neighbors
candidates = union(candidates, [x.action for x in neighbors])
advantages = {}
for a in candidates:
same_action = [x for x in neighbors if x.action == a]
if len(same_action) > 0:
Q = mean([x.return_ for x in same_action])
else:
if random() < lambda_explore:
Q = V + alpha / max(len(neighbors), 1)
else:
Q = 0
advantages[a] = Q - V
# 4. normalize advantage in practice
advantages = normalize(advantages)
# 5. closed-form policy update in logit space
new_logits = {
a: logits.get(a, 0.0) + beta * advantages[a]
for a in candidates
}
action = sample_softmax(new_logits)
traj.append((s, action))
env.step(action)
# 6. after episode: evaluator assigns step-wise reward
rewards = evaluator(traj)
returns = discounted_returns(rewards, gamma)
for (s, a), G in zip(traj, returns):
memory.append((s, a, G))
实现上有几个工程点:
- 候选动作可以来自 LLM,也可以把 memory 中出现过的 action 加进来;
- memory-only action 没有原始 LLM logit,论文算法里可初始化为 0;
- advantage 实际会做归一化,避免 logit 修正过大;
- 检索相似度论文里使用了针对 state 的相似函数,例如 Jaccard 类方法;
- evaluator 的质量会影响 step-wise reward,从而影响后续 memory。
#8. 实验:WebArena 和 Jericho 上效果如何?
论文主要在两个环境上验证:
- WebArena:真实网页导航任务,涉及 Admin、GitLab、Map、Reddit、Shopping 等站点;
- Jericho:文字冒险游戏,测试长程文本交互,包括 Library、Zork1、Zork3。
#8.1 WebArena:training-free 方法里明显最强
论文报告 WebArena 平均 success rate:
| Method | Avg | Final |
|---|---|---|
| Static | 35.63 | 36.30 |
| Memory | 41.36 | 43.00 |
| Reflexion | 41.08 | 42.12 |
| AWM | 39.37 | 40.32 |
| EvoTest | 39.24 | 42.49 |
| JitRL | 46.98 | 51.35 |
这里 Avg 表示所有 episode 的平均成功率,Final 表示最终阶段成功率。Final 高于 Avg,说明方法确实在持续交互中变强。
按站点看,JitRL 在五类站点上 final success 都是最高:
- Admin:56.59
- GitLab:45.00
- Map:42.19
- Reddit:61.98
- Shopping:45.83
特别是 Shopping 上,JitRL 从 Memory 的 33.16、Reflexion 的 31.25 提到 45.83,说明它对复杂 UI 操作和站点经验复用有帮助。
#8.2 和 weight-update 方法 WebRL 比:更便宜,还更高
论文还在 WebArena-Lite held-out test set 上和 SFT、WebRL 比较 final success:
| Method | Average Final |
|---|---|
| SFT | 23.00 |
| WebRL | 46.06 |
| JitRL | 60.00 |
这个结果是论文最强的 claim 之一:JitRL 不更新参数,却超过了计算代价更高的 WebRL。
需要谨慎看待的是:不同方法的训练数据、模型配置、评测设置细节会影响公平性。论文附录也做了 identical settings 和 same-backbone controlled comparisons 来补充。但即便如此,JitRL 至少说明一件事:
对很多 agent adaptation 场景,把经验变成可检索的 action-level value signal,可能比直接微调权重更经济、更有效。
#8.3 Jericho:长程文字游戏也有效
Jericho 结果:
| Method | Library Final | Zork1 Final | Zork3 Final |
|---|---|---|---|
| Static | 10 | 10 | 0 |
| Memory | 14 | 25 | 1 |
| Reflexion | 18 | 35 | 1 |
| AWM | 10 | 44 | 2 |
| EvoTest | 26 | 54 | 4 |
| GRPO | 11 | 10 | 2 |
| JitRL | 30 | 69 | 5 |
这里比较有意思的是 GRPO 表现不强。原因可能不是 GRPO 本身弱,而是这类长程、稀疏、环境交互任务上,在线或小规模 RL 很容易遇到:
- rollout 成本高;
- credit assignment 难;
- reward 稀疏;
- 训练不稳定;
- 泛化不一定好。
JitRL 用 evaluator 做 step-wise credit assignment,再通过 memory 检索复用局部经验,反而更适配这类“重复尝试、逐渐摸清环境规则”的任务。
#9. 泛化:JitRL 是不是只记住了训练任务?
论文做了两个方向的泛化实验。
#9.1 不同 backbone 上有效
在 Gemini-2.5-flash、GPT-5-mini、DeepSeek-V3.2 上,JitRL 基本都能超过其他 training-free baselines。
这说明它不是依赖某个特定模型内部机制,而是更像一个外接 policy improvement module:只要模型能给候选动作和 logits,就可以接上。
#9.2 cross-task memory 也能迁移
论文模拟 cold-start:当前任务不能检索同任务 memory,只能检索 disjoint tasks 的经验。
WebArena 上 JitRL 仍然优于 baselines:
| Method | Admin | GitLab | Map | Shopping | |
|---|---|---|---|---|---|
| Static | 36.96 | 37.25 | 31.19 | 36.43 | 23.44 |
| Memory | 47.83 | 37.75 | 33.03 | 50.39 | 26.04 |
| Reflexion | 46.74 | 34.80 | 31.19 | 48.84 | 31.77 |
| AWM | 47.28 | 36.27 | 34.86 | 53.49 | 23.44 |
| EvoTest | 43.48 | 35.78 | 34.86 | 49.61 | 18.75 |
| JitRL | 48.37 | 38.73 | 35.78 | 55.04 | 36.98 |
论文还统计了 cross-task memory utilization,平均约 47.03%。这说明 memory 不是只在复用具体答案,而是在复用一些抽象过程知识,例如:
- 某类站点入口常在特定菜单;
- 全局搜索不如局部导航稳定;
- hover / dropdown / filter 等 UI 操作模式;
- 长程游戏里某类动作顺序的价值。
这点对 Agent 很重要,因为真实世界任务很少完全重复,但局部结构会重复。
#10. 机制案例:JitRL 怎么纠正 base model 的错误先验?
论文给了几个 qualitative cases。
#10.1 找 customer reviews
Base model 觉得 Catalog 看起来很像入口:
click(CATALOG)base logit 0.90;click(MARKETING)base logit 0.70。
但 memory 发现历史上 review 在 Marketing 下,所以修正后:
click(CATALOG)降到 0.40;click(MARKETING)升到 1.40。
这就是典型的“语义直觉错了,环境经验纠正直觉”。
#10.2 找 subreddit posts
Base model 倾向全局搜索:
fill(Search, "..")base logit 0.95;click(Forums)base logit 0.80。
但 memory 发现全局搜索噪声大,走 Forums 更稳定:
- 搜索动作降到 0.45;
- Forums 升到 1.80。
#10.3 产品库存页面
Base model 认为应该点击 Products:
click(Products)base logit 0.95;hover(Products)base logit 0.40。
但 memory 发现 hover 可以直接展开子类:
- click 降到 -0.28;
- hover 升到 0.90。
这些例子很好地说明了 JitRL 的定位:
LLM 的语义先验负责提出合理动作;环境 memory 负责纠正那些“看起来合理但在这个系统里不对”的动作。
这对真实 agent 很关键,因为很多错误不是语言理解错误,而是环境 affordance 错误。
#11. 成本:为什么它比 RL 便宜?
传统 RL 的成本主要来自:
- 大量 rollout;
- reward / value 估计;
- 梯度反传;
- 多轮参数更新;
- 可能还要保存、部署新 checkpoint。
JitRL 避免了参数训练,只增加:
- episode 后 evaluator 的调用成本;
- memory 存储和检索成本;
- 推理时候选动作 logits 修正成本。
论文声称相对 expensive fine-tuning methods 成本降低超过 30 倍。
更本质地说,JitRL 把“学习成本”从 parameter update 转移到了 memory update + retrieval-time computation:
| 路径 | 学到哪里 | 成本形态 | 风险 |
|---|---|---|---|
| PPO/GRPO/WebRL | 模型权重 | 训练成本高 | 遗忘、部署复杂 |
| Reflexion | prompt 文字 | token 成本随上下文增长 | 经验利用不稳定 |
| JitRL | 外部 transition memory | 检索 + evaluator 成本 | 检索质量和 reward 质量依赖强 |
这也是它对长期 agent 系统有吸引力的原因:外部记忆可以持续增长、可删除、可审计、可跨模型迁移,而权重更新更重、更不可控。
#12. 和 wenjun 关心的 model-based RL / 长轨迹 Agent RL 的关系
这篇文章和“直接在超长 agent 轨迹上做 RL 是否可持续”这个问题非常相关。
它隐含的判断是:
对 deployed LLM Agent 来说,很多 adaptation 不一定要通过端到端梯度 RL 写入权重;可以通过外部经验、局部价值估计和测试时策略修正完成。
这和 model-based / memory-based agent learning 有相通处:
- 都不满足于纯 prompt engineering:它们都希望 agent 真正利用 reward/return 改变行为;
- 都不想每次都重训大模型:学习应该更多发生在外部结构、世界模型、记忆、规划器、critic 上;
- 都强调长程轨迹的信用分配:JitRL 用 evaluator 做 step-wise reward,model-based RL 可能用 learned dynamics / value 来做更系统的 credit assignment;
- 都适合持续部署场景:agent 在环境中不断积累经验,而不是离线训练一次就结束。
但 JitRL 不是 model-based RL。它没有显式学习环境 dynamics,也不 roll out future states。它更像:
retrieval-based non-parametric actor improvement。
如果放进更大的 Agent RL 图谱里,我会把它放在这个位置:
Prompt / ICL memory
↓
Reflexion / workflow memory
↓
JitRL: memory as non-parametric critic, logit-level policy improvement
↓
Learned value / world model / model-based planning
↓
Gradient-based continual RL / self-evolving agent
JitRL 的价值在于:它给了一个很轻的中间层。它比 prompt memory 更 RL,比 full RL 更便宜。
#13. 局限:哪些地方可能会卡住?
#13.1 它高度依赖 state abstraction
如果 state 表示不稳定,检索会失效。真实环境里 UI、DOM、任务描述、用户偏好变化都可能导致相似状态难以匹配。
未来更强的版本可能需要:
- learned state embedding;
- task-aware abstraction;
- hierarchical memory;
- 把状态拆成 goal、subgoal、UI affordance、history summary 等多粒度表示。
#13.2 evaluator 的 step-wise reward 可能有偏
JitRL 用 LLM evaluator 给每一步打分。这比只用最终 success/fail 更细,但也引入了 evaluator bias。
如果 evaluator 错把某个无关动作评成关键动作,memory 里就会存入错误 return,后续 logits 会被错误修正。
这在长程任务里尤其危险,因为 credit assignment 本来就是难点。
#13.3 它主要优化候选动作,不是生成任意新策略
JitRL 很依赖候选动作集合。如果正确动作从来不在候选集合里,或者 memory-only action 没被检索出来,它就很难凭空创造新行为。
所以它更像“在已有候选动作中重排序和校正”,而不是完整策略搜索。
#13.4 memory 增长后的检索和污染问题
长期运行后,memory 会越来越大,可能出现:
- 过时经验;
- 任务分布变化;
- 相互矛盾的经验;
- 用户偏好变化;
- 检索延迟上升;
- bad trajectory 污染好策略。
因此真实部署需要 memory management:
- aging / decay;
- confidence weighting;
- source tracking;
- task clustering;
- failure memory 和 success memory 分开;
- 对高影响动作设置安全约束。
#13.5 logit access 不是所有 API 都方便提供
JitRL 需要候选动作 logits。很多闭源聊天 API 不直接暴露完整 logits,或者只暴露 top-logprobs。
实际落地可能要做近似:
- 让模型先生成候选动作列表;
- 用 logprob API 估计每个候选动作概率;
- 或者在本地 open-weight model 上实现更完整的 logits modulation。
#14. 我对这篇文章的判断
JitRL 的核心贡献不是“又一个 memory agent baseline”,而是给出了一个很干净的连接:
外部经验记忆 → advantage estimate → KL-constrained policy improvement → logit additive update。
这个链条让 memory-based agent 从“把经验写进 prompt”升级为“把经验作为即时 critic”。
我觉得它最值得关注的点有三个。
#14.1 它提供了一个轻量级 continual learning 接口
不更新权重也能持续改策略,这对部署型 agent 很重要。尤其是在个人助理、浏览器 agent、代码 agent、企业流程 agent 中,很多知识是局部、环境相关、长期积累的,不一定值得写进基础模型权重。
#14.2 它把 RL 的一部分能力拆出来了
传统 RL 里,policy improvement、value estimation、credit assignment、parameter update 常常绑在一起。
JitRL 把它们拆开:
- credit assignment:LLM evaluator;
- value estimation:retrieval over memory;
- policy improvement:closed-form logit update;
- parameter update:不要。
这套拆法很适合 LLM Agent,因为 agent 的可解释轨迹、文本状态和动作候选本来就比较结构化。
#14.3 它可能是未来“agent 外脑”的一个组件
如果未来 agent 系统有一个外部长期学习层,里面可能包括:
- episodic memory;
- semantic memory;
- procedural memory;
- value memory;
- learned world model;
- user preference model;
- tool affordance model。
JitRL 可以看作 value memory / procedural memory 的一种简单实现:它记录“在类似状态下,哪个动作带来更高回报”,并在测试时直接改变动作分布。
#15. 总结
JitRL 解决的问题很明确:让 frozen LLM Agent 在部署后持续学习,但不做梯度更新。
它的核心机制也很明确:
- episode 结束后,用 evaluator 给 trajectory 做 step-wise reward;
- 把每一步存成
(state, action, return); - 当前推理时检索相似状态;
- 用历史 return 估计 、、;
- 根据 KL-constrained policy optimization 的闭式解,把 advantage 加到 logits 上;
- 得到一个被历史经验即时修正后的动作分布。
最关键的公式是:
它让 JitRL 成为一种很轻量的 test-time RL:没有反传,没有 checkpoint,没有 catastrophic forgetting,但能利用 reward signal 改变 policy。
如果用一句更偏研究判断的话说:
JitRL 的意义在于,它把 Agent 的持续学习从“更新基础模型权重”转向“维护一个可检索、可解释、可增长的非参数价值记忆”,并用一个有 RL 理论解释的 logit update 把这份记忆接回行动策略。
这未必是长期 agent RL 的终局,但很可能是一个实用中间形态:比 Reflexion 更结构化,比 PPO/GRPO 更轻,比纯 RAG 更像真正的 policy improvement。