AgentEvolver 源码解读 · 第 3 章:经验导航(Self-Navigating / ExpManager + ReMe)
> 三大机制之二 Self-Navigating:把败仗变避坑的肌肉记忆。人靠经验少走弯路,agent 也该如此。代码核心在 agentevolver/module/exp_manager/,外加一个外部服务 ReMe(Reflective Memory Engine)。
---
一、Why:从瞎撞到借力
传统 RL 靠 trial-and-error,轨迹冗余、收敛慢。Self-Navigating 让 agent 把过往轨迹提炼成「经验」,rollout 时注入、训练时按需剥离——把探索从「无引导乱撞」扭成「有知识的自进化」。
官方分两类:ExperienceManager(调度分配)+ ExperienceWorker(上下文注入/剥离)。另有一座 EMClient 桥接 ReMe 服务(默认 http://127.0.0.1:8001)。
---
二、ExperienceManager:两级动态分配
1. 任务级分配 allocate_train_mode
mode_to_ratio = {"allkeep": 1.0, "alldiscard": 0.0, "hybrid": self.train_sample_keepratio}
keep_ratio = mode_to_ratio.get(self.train_sample_mode, self.train_sample_keepratio)
keep_count = int(len(tasks) * keep_ratio)
exp_modes = ['keep'] * keep_count + ['discard'] * (len(tasks) - keep_count)
random.shuffle(exp_modes)
allkeep 全留经验、alldiscard 全剥(让模型学原始推理)、hybrid 按 train_sample_keepratio 抽比例。结果写入 TaskExpConfig(train_mode=...)。2. Rollout 级分配 allocate_add_exp
rollout_n = self.rollout_config.n # 或 val_kwargs.n
add_exp_choices = {
"woexp": [False] * rollout_n, # 纯探索
"mixed": sorted([i < round(rollout_n*self.rollout_ratio)
for i in range(rollout_n)], key=lambda _: random.random()),
"all": [True] * rollout_n, # 全引导
}[exp_mode]
woexp/mixed/all 决定一组 rollout 里多少条带经验;mixed 按 rollout_ratio 取前若干条标 True,再打乱——探索与利用动态平衡。get_complete_exp_configs 把两级拼成每任务每轨迹的 TrajExpConfig。---
三、ExperienceWorker:注入与剥离
1. rollout 注入 manage_rollout_context
trajectory = Trajectory(data_id=..., steps=init_messages, query=traj_exp_config.query)
history_experience = self.em_client.call_context_generator(
trajectory=trajectory, retrieve_top_k=reme_config.retrieve_top_k, workspace_id=...)
formatted_experience = self.experience_template.format(history_experience)
new_content = formatted_experience + trajectory.steps[-1]["content"]
trajectory.steps[-1]["content"] = new_content
前置条件:add_exp and enable_context_generator。从 ReMe 拉 top-k 相关经验,套进 experience_template(默认 \n\nSome Related Experience...{} \n\n),拼到末条 user 消息之前。任务本身不动,只加上下文。2. 训练剥离 manage_training_context
if metadata_config.get("task_train_mode", "discard") == "discard":
pattern = re.escape(self.experience_template).replace(r'\{\}', '(.*?)')
match = re.search(pattern, message, re.DOTALL)
if match:
experience = match.group(1)
cleaned_message = re.sub(pattern, '', message, flags=re.DOTALL)
train_mode=="discard" 时用正则把经验块抠掉——训练数据保持干净,模型学的是原始推理链,不被经验污染。这正是「注入但不训练」的精妙处。3. 接驳点
AgentFlow.execute(第 5 章)一开头就调 manage_rollout_context,把经验织进 init_messages。所以经验导航不是旁路,而是嵌入执行流的第一道工序。---
四、EMClient ↔ ReMe:异步总结
ExperienceManager 持 EMClient(base_url=...),两套投递:
def summarize_in_batch(self, trajectories):
grouped = groupby(sorted(trajectories, key=lambda t: t.task_id), ...)
futures = [self.thread_pool.submit(self.em_client.call_summarizer,
trajectories=b, workspace_id=...) for b in batches]
# as_completed 收结果
def _should_submit_summary(self, global_steps):
return (self.reme_config.enable_summarizer and self.reme_config.updated_freq
and global_steps % self.reme_config.updated_freq == 0)
ThreadPoolExecutor 异步把原始轨迹蒸馏成经验片段;updated_freq 把关——0 即关更新,非 0 每 updated_freq 步灌一次。经验池随训练并行增长。
---
五、异策略损失:on/off 一念之间
最硬核在 module/exp_manager/het_core_algos.py 的 het_compute_token_on_off_policy_loss(官方 docs/guidelines/exp_manager.md 直引):
negative_approx_kl = log_prob - old_log_prob
ratio = torch.exp(negative_approx_kl)
# 1️⃣ on-policy(exp_mask=0)
on_pg_losses, _, _ = compute_pg_losses(cliprange_low, cliprange_high)
on_pg_loss = masked_mean(on_pg_losses, (1.0 - exp_mask) * response_mask)
# 3️⃣ off-policy(exp_mask=1)
off_pg_losses, _, _ = compute_pg_losses(off_cliprange_low, off_cliprange_high)
off_pg_loss = masked_mean(off_pg_losses, exp_mask * response_mask)
# 4️⃣ 合并
pg_losses = off_pg_losses * exp_mask + on_pg_losses * (1.0 - exp_mask)
pg_loss = agg_loss(loss_mat=pg_losses, loss_mask=response_mask, loss_agg_mode=loss_agg_mode)
exp_mask 是定海神针:把「新鲜 rollout」与「经验回放」干净分开。off 分支用更紧的 off_cliprange_high(默认 0.6)当信任域,防止旧经验把更新带飞。loss_agg_mode 默认 token-mean。这就把「复用过去」与「不稳定学习」解耦——借力而不翻车。
---
六、六模式配置矩阵
经验池支持六种用法,矩阵记牢(updated_freq / init_exp_only / init_exp_before_training / enable_summarizer / enable_context_generator / workspace_id):
| 模式 | updated_freq | init_exp_only | init_before | summarizer | ctx_gen | 说明 |
|---|---|---|---|---|---|---|
| No Pool | 0 | F | F | F | F | 基线,无经验 |
| Init Only | 0 | T | T | T | F | 只建池不训练用 |
| Init+Train | 0 | F | T | F | T | 推荐起点 |
| Init+Train+Update | k≠0 | F | T | T | T | 持续更新池 |
| Exist+Train | 0 | F | F | F | T | 复用旧池 |
| Exist+Train+Update | k≠0 | F | F | T | T | 复用旧池且更新 |
basic.yaml 里 enable_summarizer: false / enable_context_generator: false / updated_freq: 0 → 即 No Pool,正是最小化训练关经验管理之由。---
七、小结
Self-Navigating 的代码骨架:Manager 做两级分配(任务级 keep/discard、rollout 级 woexp/mixed/all)→ Worker 在 rollout 注入经验、训练时正则剥离 → EMClient 异步把轨迹喂给 ReMe → 异策略损失用 exp_mask 把 on/off 策略分开、off 分支加信任域。整条线回答一个问题:如何让 agent 借经验之力,又不让旧经验劫持新学习。
下一章,看最锋利的一环——Self-Attributing 之 ADCA-GRPO,如何把「步级信用」算到 token。