现代基座大模型的 Agentic 能力,不是一个阶段训出来的,而是:
海量语料打底(Pre-Training)→ 长上下文+工程数据强化(Mid-Training)→ 行为先验建立(SFT)→ 推理能力拔高(Reasoning RL)→ 真实环境执行(Agentic RL)→ 通用偏好对齐(General RL)→ 跨阶段能力缝合(On-Policy Cross-Stage Distillation)
每个阶段解决一个特定问题,最后用蒸馏把所有能力统一收敛,防止遗忘。
GLM-5 的定位不是传统聊天模型,而是面向 ARC(Agentic + Reasoning + Coding) 的统一基座。因此从预训练阶段就优先注重代码和推理数据,不是平均对待所有数据。整条链路的逻辑是:不同能力分阶段建模 → 分阶段强化 → 最后统一收敛。现代大模型的训练,不再是"一个阶段解决所有问题",而是"不同能力分阶段建模、分阶段强化、最后统一收敛"。
GLM-5 的整体训练流程分为两大阶段:Base Model Training(基础模型训练)和 Post-Training(后训练)。
28.5T tokens
长上下文/SWE
行为先验
推理拔高
环境执行
通用对齐
能力缝合
- 先 Reasoning RL,再 Agentic RL:Agentic 能力建立在推理能力之上。代码执行、工具调用、长流程规划,本质都是推理。如果推理链条不稳定,后续 Agent 阶段即使加入更多工具也无法真正提升质量。
- 先 Agentic RL,再 General RL:Agentic RL 针对特定可验证环境做强化,目标清晰;General RL 针对更广泛的人类偏好,信号更模糊。先做前者可以在有清晰奖励信号的场景建立稳定策略,再泛化到更主观的场景。
- 最后用蒸馏缝合:多阶段 RL 每个阶段都可能削弱前面阶段学到的能力(灾难性遗忘)。最后用 On-Policy Cross-Stage Distillation 统一把所有能力融合,是整个 pipeline 的核心设计。
| 阶段 | 子阶段 | 主要作用 | 关键内容 |
|---|---|---|---|
| Base Model Training | Pre-Training | 学习通用语言、知识、代码与基础表征,构建统一基座 | Web / Code / Math&Science 语料,28.5T tokens,优化数据筛选/去重/质量控制 |
| Mid-Training | 面向目标能力定向增强,提升长上下文建模、Agent 场景适配、软件工程理解 | 分阶段扩展上下文至200K;引入长文档自然数据与合成数据;强化 repo-level code、issue、PR、commit diff 建模(160B tokens) | |
| Post-Training | SFT | 建立任务遵循、推理表达、工具使用与多轮交互行为先验 | General Chat / Reasoning / Coding&Agent 三大类;三种 thinking 特性;错误轨迹保留但 mask loss |
| Reasoning RL | 进一步提升复杂问题上的推理正确性与稳定性 | 数学/科学/代码/TIR 可验证任务;RLVR,0/1 reward | |
| Agentic RL | 提升模型在外部环境中的多步决策、执行与工具调用能力 | SWE / Search / Terminal 可验证环境;group-wise policy optimization(GRPO) | |
| General RL | 优化通用场景下的正确性、交互质量与人类偏好一致性 | 三大优化维度;混合奖励 rule+ORM+GRM;高质量人工答案锚点 | |
| On-Policy Cross-Stage Distillation | 融合不同阶段能力,缓解多阶段训练带来的能力遗忘 | 前序 checkpoint 为 teacher;on-policy 生成 + 跨阶段蒸馏;advantage 替换优化 |
整体训练总共使用了 28.5 万亿 tokens,相当于把人类历史上所有文本、所有知识灌进去。这还只是 Pre-Training + Mid-Training 阶段的数量,后续 SFT 和 RL 阶段的数据量虽然小很多,但质量要求极高。
GLM-5 的基础模型训练分成两个阶段:Pre-Training(预训练)和 Mid-Training(中期训练)。两个阶段加起来,总共使用了 28.5 万亿 tokens。
损失函数是标准的 next-token prediction(交叉熵),对全量通用文本做语言建模。
2.1 Pre-Training:建立通用语言、代码和知识能力
目标:通过大规模自监督学习,让模型掌握通用的语言模式、知识和表征能力,成为后续各种任务的基础。
GLM-5 的目标是面向 ARC(Agentic, Reasoning, Coding)的统一模型,所以在基础阶段就优先注重代码和 reasoning 能力,不是平均对待所有数据。预训练重点覆盖三类数据:
① Web 数据
在 GLM-4.5 的基础上,GLM-5 进一步优化了网页数据筛选流程:
- 通过新的质量分类器挖掘更多高质量网页
- 用专门的知识分类器保留长尾知识
- 从中等质量数据中提取高信息密度内容
- 减少低质量合成推理数据的引入(防止预训练阶段学到错误推理模式)
② Code 数据
思路:不是单纯增加代码量,而是提升代码语料的完整性、准确性和语言覆盖面,为后续 coding agent 打基础。
- 更新了主要代码平台(GitHub 等)的数据快照,扩充了含代码的网页
- 去重后的唯一 token 数提升了 28%(更少重复,信息密度更高)
- 修复了部分代码元数据问题(注释、文档字符串等)
- 提升了低资源编程语言(如 Rust、Julia、Kotlin 等)的数据质量
③ Math & Science 数据
- 来源包括网页、书籍和论文
- 优化文档解析流程(公式识别、表格理解等)
- 利用大模型对内容进行质量打分,保留更有"教育价值"的部分
- 严格过滤低质量或模板化内容(如机械刷题集、无推导过程的答案集)
预训练学到的是模型的"世界观"——对语言模式、知识体系和推理方式的基础认知。如果预训练阶段学了大量低质量数据,这些错误认知会深深嵌入模型参数,后续 SFT 和 RL 很难完全纠正。相反,如果预训练数据质量高,SFT 只需要调整行为格式,RL 只需要优化策略方向,整体效率更高,效果也更好。「Garbage in, garbage out」在预训练阶段尤为关键。
2.2 Mid-Training:定向语料强化
面向目标能力进行定向增强,提升长上下文建模、Agent 场景适配与软件工程理解能力。
- 模型在普通长度文本上的表现良好,并不意味着它在 128K 甚至 200K 的上下文下仍然稳定
- 具备代码生成能力,并不意味着它能理解跨文件、Issue、PR 和 commit 之间的复杂关联
- 能完成单轮问答,并不意味着它可以在长链路 Agent 任务中持续保持一致性
(1)上下文长度的逐步扩展
预训练只用了 4K 上下文。Mid-Training 采用分阶段扩展上下文长度的策略:
4K context
1T tokens
500B tokens
50B tokens
① 训练不稳定:attention 矩阵大小是 $O(L^2)$,突然放大会导致数值问题和梯度爆炸
② 性能退化:模型需要逐步学习如何利用远距离信息,跳跃式扩展会导致中间长度的信息利用能力不稳定
(2)软件工程数据的重点强化
训练样本会将以下内容组织到同一序列中,让模型看到的不是孤立代码,而是真实软件工程任务的上下文:
repo-level code files
完整代码仓库中多个相互引用的文件,理解跨文件的依赖关系
commit diffs
代码变更前后的对比,理解修改意图和代码演化逻辑
GitHub issues
真实 bug 报告和功能需求,从自然语言描述定位代码问题
Pull Requests
代码审查讨论和修改过程,理解软件工程中的协作与质量控制
这部分 Issue–PR 数据最终约为 160B tokens,为后续 Agentic coding 能力提供重要基础。
(3)长上下文数据:自然数据与合成数据并重
自然数据(Natural Data)
来自书籍、论文和长文档语料,这些数据天然具有长距离依赖
合成数据(Synthetic Data)
构造更强的长距离依赖和多轮记忆场景,专门训练模型的长程记忆能力
Post-Training 采用渐进式 alignment 策略:SFT 引入复杂 thinking 模式 → Reasoning/Agentic RL 分别强化推理和执行 → General RL 完成人类偏好对齐 → On-Policy Cross-Stage Distillation 防止遗忘。
3.1 SFT:建立行为先验
SFT 阶段:一方面完成基础行为对齐;另一方面为后续 Reasoning RL、Agentic RL 提前搭建行为模板。
SFT 数据覆盖三大类
General Chat
- 问答、写作、角色扮演、翻译
- 多轮对话和长上下文交互
- Role-playing 覆盖多语言和不同角色配置
- 评估:instruction following、语言表现力、创造性、逻辑连贯性、长对话一致性
Reasoning
- 数学推理、编程推理、科学推理
- 构造大量可验证问题,通过 rejection sampling 合成高质量数据
- 基于难度筛选,只保留真正有挑战性的问题
Coding & Agent
- 前端和后端工程代码、tool calling
- coding agents、search agents、通用型 agents
- 构建大量 execution environments,获取高质量 trajectories
- 错误轨迹保留但 mask loss(让模型观摩错误修正但不被错误信号强化)
对于 trajectory 中的错误片段,GLM-5 没有直接删除,而是保留内容但在 loss function 中 mask 掉。好处:模型可以学习错误修正的过程(看到"错误→发现→修正"的完整序列),但不会被错误动作的错误监督信号强化。
三种 Thinking 特性
Interleaved Thinking(交错式)
每次回复和 tool call 之前先思考,形成 Thought-Action-Observation 循环,相当于给后续 ReAct 流程打草稿。
Preserved Thinking(保留式)
Coding Agent 场景下,自动保留多轮对话中的 thinking blocks,复用已有推理过程,减少前后不一致。
Turn-level Thinking(轮次级别)
同一 session 中按轮次控制是否启用 reasoning。简单请求关闭降低延迟;复杂任务开启提升准确性。
3.2 Reasoning RL:先强化推理能力
在数学、科学、代码及 TIR 等可验证任务上进行强化学习;采用结果导向奖励信号优化推理性能
代码执行、工具调用、长流程规划,本质都建立在推理能力之上。如果推理链不稳定,后续 Agent 阶段即使加入更多工具也无法真正提升质量。推理能力是 Agent 能力的地基。先把地基打牢,再在上面盖房子。
训练数据涵盖四个方向
数学
竞赛数学题、高等数学等,答案可精确验证
科学
物理、化学、生物等有标准答案的推理题
代码
编程题,通过运行测试用例验证正确性
TIR
Tool-Integrated Reasoning,需要调用工具(如 Python 解释器)辅助推理,结果可验证
奖励设计:标准 RLVR
输出 0 和 1 的 reward,是标准的 RLVR(Reinforcement Learning with Verifiable Rewards) 任务。
{
"prompt": "如果 3x + 2 = 11,求 x。请逐步思考,并最终只输出答案。",
"ground_truth": "3",
"reward_rule": "模型最终答案等于 3 则奖励为 1,否则为 0"
}
reward 完全由程序自动判定,可以大规模并行训练,不需要人工打分。
- RLHF:奖励来自人类偏好判断,主观、慢、贵,且人类很难准确判断复杂数学推理链的对错
- RLVR:奖励来自程序自动判断(答案对/错),客观、快、准,完全可扩展
- DeepSeek-R1 用的是同一套思路,本质是把 LLM 的 RL 对齐做到了有明确验证标准的领域
3.3 Agentic RL:让模型真正学会"执行任务"
如果说 Reasoning RL 解决的是"模型能不能想清楚",那么 Agentic RL 解决的就是:模型能不能在真实环境里,把事情一步一步做出来。
Agentic 来自 Agent(智能体),形容词形式,意思是「具有自主行动能力的」「能在环境中主动完成任务的」。
简单说:普通 LLM 是「回答问题」的,Agentic 模型是「做事情」的。
| 对比维度 | 普通 LLM(非 Agentic) | Agentic 模型 |
|---|---|---|
| 任务形式 | 单轮问答,输入 → 输出 | 多轮交互,在真实环境里执行多步任务 |
| 有无工具 | 没有,只用语言推理 | 可以调用代码执行器、搜索引擎、终端命令 |
| 奖励时机 | 最终答案对不对 | 任务最终完成与否(测试通过?搜索结果正确?) |
| 典型任务 | 解数学题、解代码题 | 修复 GitHub Bug、多跳搜索、终端文件操作 |
❌ 普通 LLM(非 Agentic)
用户:帮我修复这个 Bug
模型:你可以在第 15 行加一个 if 判断……
(说说而已,不执行)
✅ Agentic 模型
用户:帮我修复这个 Bug
模型:
① 搜索代码库,找到相关文件 🔍
② 查看 issue 描述,理解问题 📄
③ 修改代码 ✏️
④ 运行测试,发现还有报错 ❌
⑤ 继续调整,再次运行,通过 ✅
(整个过程模型自己完成,无需人介入)
「Agentic 能力」= 工具调用 + 长流程规划 + 根据环境反馈调整 + 多轮自主执行 = 从「说」到「做」的能力。
训练目标场景
Coding Agent(SWE)
- 基于真实 GitHub issue / PR 构建任务
- 自动搭建 repo 环境
- test script 自动验证
Search Agent
- 构造多跳搜索问题
- 需要 search / open / extract / python 等多步操作
- 根据最终答案和证据是否正确给 reward
Terminal Agent
- 在 shell / Docker / 文件系统里操作
- 自动搭建 Docker 环境
- test script 验证执行结果
训练步骤(4步)
- 初始状态(initial state):任务起点,如"修复前的代码仓库快照"
- 可执行动作空间(action space):可用工具列表
- 观测(observation):每次动作后环境返回的信息
- 状态转移(state transition):动作执行后环境状态的变化
- 验证与反馈机制(verification):自动化测试脚本
每条轨迹:$y = (r_1, a_1, o_1, \; r_2, a_2, o_2, \; \ldots, \; r_n, a_n, o_n)$
SWE:F2P 测试是否通过;Search:答案和证据是否正确;Terminal:test script 是否通过。都是 verifiable environment,reward 由测试脚本自动打分。
对同一问题 $x$ 采样 $K$ 条轨迹,构造组内相对优势:
$A > 0$:鼓励;$A < 0$:压制。GRPO 不需要 Critic 网络,更轻量稳定,特别适合长轨迹场景。
3.4 General RL:通用场景对齐
优化通用场景下的正确性、交互质量与人类偏好一致性
三个维度:
① 基础正确性
instruction-following 错误、逻辑不一致、事实性错误、知识幻觉、语言不流畅
② 情绪智能
更好的共情能力、更自然的语气、更接近人类的表达方式,避免"AI味"(过度冗长、套路化)
③ 特定任务质量
写作、文本处理、主客观问答、角色扮演、翻译等细粒度优化
混合奖励系统(Hybrid Reward System)
| 奖励类型 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| Rule-based rewards | 规则直接判断(格式、长度等) | 明确可解释,不会被 hack | 覆盖范围有限 |
| ORM(Outcome Reward Model) | 只看最终输出结果打分 | 信号方差低,训练效率高 | 容易被 reward hacking(表面符合但实际有问题) |
| GRM(Generative Reward Model) | 语言模型生成评分和评价理由 | 不容易被 exploit,能评价细粒度质量 | 方差更高,稳定性较差 |
GLM-5 显式引入高质量的人类撰写答案作为风格和质量锚点。原因:完全依赖模型自我优化会产生"AI味"——过度冗长、套路化、缺少人类写作的自然细节。人工答案锚点把模型输出风格拉向更自然的方向。
3.5 On-Policy Cross-Stage Distillation 与 KL 散度方向性
由于内容较长,On-Policy Cross-Stage Distillation(§3.5)与 Forward KL vs Reverse KL(§3.6)已整合为一篇独立笔记,涵盖:
- §1 多阶段 RL 的灾难性遗忘问题与实证数据
- §1.2 RL 天然抗遗忘的三个视角(Mode-Seeking / On-Policy / RL's Razor)
- §1.3 OPD 设计:多 Teacher 分工、Token 级 Advantage、Group size=1
- §1.4–1.6 具体例子 / Off-Policy vs On-Policy / 三类方法横向对比
- §2 Forward KL vs Reverse KL:数学定义、双峰例子、SFT/RL 本质差异
notes-opd.html
· 位于同一 topics/ 目录下
Agentic 数据合成的关键不是"合成文本",而是:合成任务 + 合成环境 + 合成反馈 + 合成轨迹。不仅包括传统意义上的文本合成,更包括任务合成、环境合成、反馈合成与轨迹合成。
GLM-5 中 Agentic 数据主要围绕三类任务构建:
软件工程环境(SWE)
基于真实 Issue-PR 对构建可执行修复任务,目标是训练 coding agent
终端环境(Terminal)
构造需要在 shell / Docker / 文件系统里操作的任务,目标是训练 terminal agent
搜索任务(Search)
构造多跳检索、多网页证据聚合的问题,目标是训练 search agent
4.1 Agentic 数据合成的标准六步流水线
下面用一条贯穿始终的例子("修复注册接口不验证空用户名的 bug")来串讲六步,每步结合这个例子,让流程看得见摸得着。
种子任务是整个流水线的起点——没有种子就没有任务,没有任务就没有数据。种子的质量直接决定最终训练数据的多样性和覆盖范围。
- SWE 任务:GitHub 上真实的 Issue-PR 对(有 bug 描述,有人工修复 patch)
- Terminal 任务:Stack Overflow 的报错帖、技术博客的操作教程
- Search 任务:早期 search agent 在真实搜索中访问过的网页
原始的 Issue 只是一段文字描述,agent 没法直接对它"执行"。这一步把文字描述转化为一个结构化的任务对象,包含 agent 完成任务所需的一切信息。
| 字段 | 含义 | 示例内容 |
|---|---|---|
| 任务描述 | 用自然语言告诉 agent 要做什么 | "当 username 为空字符串时,接口应返回 HTTP 400 而非 500" |
| 输入上下文 | 任务开始时的初始状态 | Bug 发生前的完整代码仓库快照(见下方说明)、Issue 原文、依赖列表 |
| 可用工具 | agent 在这个任务里能用哪些工具 | view_file, search_code, edit_file, run_tests, bash |
| F2P 测试 | 修复前失败、修复后必须通过的用例(见下方说明) | test_empty_username_returns_400 / test_empty_username_error_message |
| P2P 测试 | 修复前已通过、修复后不能破坏的用例(见下方说明) | test_valid_username_registration / test_duplicate_username |
快照(Snapshot)= 某个时间点代码仓库的完整静态备份,就像手机的时间点备份——把那一刻所有文件的内容、路径都原样保存,随时可以还原到那个状态。
就是 PR 合并之前那个 commit 的所有代码。用
git checkout <commit-sha> 还原,然后打包进 Docker 容器里。
保证每次 agent 尝试这个任务都从同一个起点开始,不会因为上次 agent 改了什么文件而影响下一次实验(可复现性)。
测试直接从 GitHub PR 里自动提取,完全不需要人工编写。这是 SWE 数据合成能大规模进行的关键原因。
PR #456: Fix empty username handling 修改的文件: ├── validators/user_validator.py ← patch(修复逻辑) └── tests/test_user_validator.py ← PR 作者同时提交的测试!
正规开源项目(如 Django、NumPy、requests)有严格贡献规范:每个 bug fix 必须附带能复现 bug 的测试用例才能被合并(CI 不通过不让合并)。这些测试本来就是 PR 作者写好的,不是为训练数据专门写的。
对比 PR 前后的 diff,找出新增的 test 函数。这些测试在 bug 修复前运行会失败(bug 还在),修复后必须通过。
自动提取,零成本。
在 PR 合并前跑一遍全量测试套件,找出已经通过的用例。这些是回归测试——agent 修复 bug 时不能把它们搞坏。
也是自动提取,零成本。
随便的小项目可能没有测试、测试覆盖率很低、PR 不带测试——无法自动提取 F2P/P2P,就没法构造可验证的训练任务。SWE-bench 从 12 个知名 Python 开源库(Django、Flask、NumPy、SciPy 等)里筛选,保证每个 Issue 都有完整的测试覆盖。
有了任务描述还不够,agent 还需要一个能真正运行代码、执行命令、看到反馈的环境。这一步就是把任务描述"活化"成一个沙盒环境。
这是整个流水线中最核心的一步:让 agent 真正进入沙盒环境,完整地"走一遍"解决这个问题的过程,并把每一步的输入输出全部记录下来,形成轨迹(trajectory)。
传统 NLP 数据是"输入-输出"对:给一个问题,有一个答案。但 Agentic 任务是多步的——解决一个 bug 可能需要先搜索代码、再分析、再修改、再跑测试、发现不对、再改……这个过程本身就是训练数据,丢掉中间步骤就会丢掉大量"遇到问题怎么思考"的知识。
Thought: 先读一下 Issue,找到问题所在 Action: view_file("routes/auth.py") Observation: [代码内容] 第 62 行调用了 validate_username(username) Thought: 找到了 validate_username,进去看看是怎么实现的 Action: view_file("validators/user_validator.py") Observation: def validate_username(u): if u is None: raise ... → 只检查了 None! Thought: 问题找到了:空字符串 "" 不是 None,所以没被拦截。需要加一行 or u == "" Action: edit_file("validators/user_validator.py", old="if u is None", new="if not u") Observation: File saved successfully Thought: 改完了,跑一下测试确认 Action: run_tests("test_empty_username_returns_400") Observation: PASSED ✓ Action: run_tests("all") Observation: 12/12 PASSED ✓ → 提交
不是所有采集到的轨迹都能直接用——有的 agent 走了错误的路,有的轨迹里存在漏洞(exploit),有的任务本身就设计有问题。这一步是数据清洗,只留下高质量样本。
| 过滤项 | 检查什么 | 为什么要检查 |
|---|---|---|
| 结果正确性 | 验证脚本是否通过 | 最基本的门槛,没解决任务的轨迹不能用 |
| Exploit 检查 ⚠️ | 是否存在"取巧"通过测试的行为 | 最容易被忽略、危害最大的问题(见下方详述) |
| 环境稳定性 | 容器是否崩溃、工具是否报错 | 环境本身有问题的轨迹数据不可信 |
| 任务难度 | 是否在合理步数内完成 | 太简单(1步就完成)没有学习价值;太长(步数异常多)可能是轨迹质量差 |
| 一致性 | 同一任务多次运行结果是否一致 | 不稳定的任务说明验证机制有问题,不适合作为训练数据 |
Agent 有时会发现测试验证本身的"漏洞",找到一条不需要真正解决问题就能通过的捷径。这类轨迹表面上"验证通过",但实际是有害的训练数据——会教会模型走捷径而不是真正解决问题。
rm test_empty_username_returns_400.py测试文件不存在,pytest 直接通过。任务没有解决,但验证脚本说 PASSED ✓
assert status == 400 改成 assert status == 500,测试"通过了",但期望值已经被篡改。通过筛选的高质量轨迹,根据用途转化成不同格式的训练数据。
关键处理:mask 掉的是环境返回的 Observation token(bash 输出、文件内容等),不是"错误步骤"。SFT 阶段没有机制自动识别哪步是弯路——模型生成的所有 Thought + Action token 都参与 loss,包括走了弯路的那些步骤。
高质量 SFT 的做法是直接用"一步到位"的干净轨迹(由更强模型重新生成),而不是带弯路的探索轨迹——从源头避免弯路,而不是事后识别并 mask。
在 Agentic RL 阶段,agent 会在这个任务上重新探索、自己尝试、用验证结果作为 reward 信号来更新自己。同一个任务可以被使用无数次,每次 agent 都会走出不同的路径。
SFT 和 RL 不是二选一,而是串行的两个阶段,解决的问题完全不同:
用一个较强的模型(或早期版本)去刷任务,把探索成功的轨迹打包成训练样本。
目的:让模型至少知道"怎么用工具、怎么格式化输出、什么时候该提交"。
类比:先给新员工看一遍标准操作录像,让他知道流程。
让当前被训练的模型自己在任务上试,验证通过 reward=1,失败 reward=0。
目的:让模型在更难的任务上自己摸索策略,突破 SFT 数据覆盖不到的场景。
类比:让员工真正上手做项目,失败了自己总结经验。
传统 NLP 数据是人工写标注。Agentic 数据的难点在于:不能靠人工逐步标注一个 20 步的 agent 轨迹(太贵、太慢、规模化不了)。这六步流水线解决的核心问题是:怎么让机器自动判断一条轨迹是好是坏——答案就是可执行验证。只要最终结果可以被代码自动验证(测试通过 / 不通过),中间过程就可以被大规模自动采集和过滤,完全不需要人工逐步审核。
⚠️ 在看例子之前,先理解 Agent RL 的两个核心难点
难点一:Credit Assignment(功劳分配)
一条轨迹有 20-40 步,但只有最后"测试通过/失败"这一个 0/1 信号。中间每一步的功劳怎么分?
主流做法是 GRPO(组内对比 + leave-one-out):同一任务跑 G 条轨迹,成功 reward=1,失败=0,用其余 G-1 条的均值作 baseline,得到相对 Advantage,然后把 Advantage 广播给模型自己生成的所有 token(Thought + Action)。注意:环境返回的 Observation(bash 输出、文件内容等)被 mask 掉,不参与梯度。
难点二:上下文长度
一条 SWE 轨迹轻松达到 16k-64k tokens。主流做法(SkyRL-Agent 论文确认)是设硬上限(如 32K),超出就整条轨迹 mask 掉不参与梯度——不是"智能保留关键步骤",而是直接丢弃这条轨迹。研究还发现更短的历史 memory 反而效果更好(历史太长让模型找不到重点)。
任务设定
当 Session.get() 传入 timeout=0 时,
应该立即超时抛出 Timeout 异常,
但实际上会无限等待。
第一步:准备「任务 + 环境 + 验证」三件套
在训练开始前,把任务打包成可重复运行的格式。验证脚本从 PR diff 中自动提取,无需人工编写:
# F2P 测试(PR 新增的,修复前失败 → 修复后必须通过) def test_timeout_zero_raises(): s = Session() with pytest.raises(Timeout): s.get("http://httpbin.org/delay/5", timeout=0) # P2P 测试(已有的,修复后不能破坏) def test_timeout_positive_still_works(): s = Session() resp = s.get("http://httpbin.org/get", timeout=5) assert resp.status_code == 200
第二步:当前模型(被训练的那个)进 Docker 跑一遍(Rollout)
模型看到 Issue 描述,开始用工具一步步探索——这是 RL 的 rollout(采样) 阶段:
Thought: timeout=0 应该立即超时,先找 timeout 处理的代码在哪 Action: bash("grep -rn 'timeout' requests/adapters.py | head -20") Obs: 47: timeout=None 89: if timeout is not None: 91: conn.timeout = timeout.connect_timeout Thought: 第 89 行有判断,但 timeout=0 时 if 条件成立,进去看 connect_timeout 怎么处理 Action: view_file("requests/adapters.py", start=85, end=100) Obs: 89: if timeout is not None: 90: if isinstance(timeout, tuple): 91: connect, read = timeout 92: else: 93: connect = read = timeout ← timeout=0 会走这里 Thought: connect = read = 0,然后传给底层 urllib3。 问题可能在于 urllib3 把 0 当「无限等待」而不是「立即超时」 先验证一下 Action: bash("python3 -c \"import urllib3; h=urllib3.HTTPConnectionPool('httpbin.org',80); h.request('GET','/delay/5',timeout=urllib3.Timeout(connect=0,read=0))\" 2>&1 | head -3") Obs: (无限挂起...) ← 确认 0 被传给 urllib3 后不超时 Thought: 找到了。应该在接收 timeout 时,把 0 视为「立即超时」,在 adapters.py 加特判 Action: edit_file("requests/adapters.py", old="if timeout is not None:", new="if timeout == 0:\n raise Timeout('timeout=0 means immediate timeout')\nif timeout is not None:") Obs: File saved. Action: bash("python3 -m pytest tests/test_requests.py -x -q 2>&1 | tail -5") Obs: 48 passed, 0 failed ✓ → 验证通过,reward = 1
第三步:同一个 prompt 跑 G=8 条轨迹(GRPO 核心)
GRPO 的关键:同一个任务,让模型用不同的随机采样跑 8 遍,得到不同的探索路径:
| 轨迹 | 结果 | reward | 失败原因 |
|---|---|---|---|
| 轨迹 1 | ✅ 通过 | 1 | — |
| 轨迹 2 | ✅ 通过 | 1 | — |
| 轨迹 3 | ❌ 失败 | 0 | 走错方向,改了 cookies.py 没用 |
| 轨迹 4 | ✅ 通过 | 1 | — |
| 轨迹 5 | ❌ 失败 | 0 | 找到位置但修改有语法错误 |
| 轨迹 6 | ✅ 通过 | 1 | — |
| 轨迹 7 | ❌ 失败 | 0 | F2P 测试过了但破坏了 P2P 用例 |
| 轨迹 8 | ✅ 通过 | 1 | — |
第四步:计算 Advantage + 决定哪些 token 参与梯度计算
关键:不是所有 token 都参与,环境返回的 Observation 被 mask 掉,只有模型自己写的 Thought + Action token 才拿到梯度信号:
第五步:用这 8 条轨迹做一次 PPO/GRPO 参数更新
「看到 timeout 问题 → 先 grep adapters.py」在成功轨迹里,概率 ↑ 上升
「看到 timeout 问题 → 先去改 cookies.py」在失败轨迹里,概率 ↓ 下降
「写 Python 时有语法错误」在失败轨迹里,概率 ↓ 下降
「改完代码立刻跑测试验证」在成功轨迹里,概率 ↑ 上升
整个训练循环
采样一批任务(如 64 个 Issue)
→ 每个任务跑 G=8 条轨迹(并行)
→ 验证脚本跑出 reward(0 或 1)
→ leave-one-out 归一化算 Advantage
→ Observation mask 掉,只对 Thought+Action token 做梯度
→ 更新模型参数
→ 用更新后的模型再次采样(on-policy)
→ 循环 125 步...
最终结果(SkyRL-Agent 论文数据):
Qwen3-32B 基线 → SWE-Bench Verified 24.4%
纯 RL 训练 125步 → SWE-Bench Verified 39.4% (+15pp)
4.2 SWE 数据合成:从真实 Issue-PR 到可执行软件工程环境
SWE 是 Software Engineering 的缩写,是让模型像真实开发者一样去处理代码仓库中的问题。
SWE 数据合成的核心:不是把 issue 文本和最终 patch 直接拼成一条监督样本,而是把一次真实的软件修复过程,重建成一个模型可以亲自执行的任务环境。在 SWE 数据合成上,GLM-5 和 MiniMax 的做法是类似的。
① 原始素材(种子任务来源)
Issue: #123 Registration API returns 500 when username is empty string - 用户报告:注册时如果 username 字段为空字符串,接口会返回 HTTP 500 而不是错误提示 - 期望行为:应该返回 HTTP 400 和清晰的错误消息 PR: #456 Fix empty username handling in registration - 修改了 validators/user_validator.py 中的 validate_username 函数 - 添加了对空字符串的检查逻辑
② 转化为可执行任务
- 任务描述:"修复注册接口问题:当 username 参数为空字符串时,应返回 HTTP 400 错误和提示信息,而不是 HTTP 500。"
- 输入上下文:完整 Issue 文本 + 修复前代码库快照 + 可用工具列表 + 环境依赖
- 测试标准(F2P 测试):修复前失败的测试用例,修复后必须通过
test_empty_username_returns_400:期望 HTTP 400test_empty_username_error_message:期望返回 "Username cannot be empty"
- 测试标准(P2P 测试):修复前已通过的测试用例,修复后不能破坏
test_valid_username_registration:正常注册流程test_duplicate_username:重复用户名处理
③ 构建可验证环境
- 初始状态:容器中放入修复前的代码仓库快照
- 可执行动作空间:查看/搜索代码(
view_file,search_code)、编辑文件(edit_file)、运行测试(run_tests)、执行命令(bash) - 观测:代码内容、命令行输出、测试失败信息
- 验证机制:自动化测试脚本,F2P 和 P2P 两类用例全部通过才算成功
④ 执行任务并采集轨迹
Thought: 首先需要理解 Issue,然后找到注册接口的代码位置
Action: search_code("registration API", "username")
Observation: Found in routes/auth.py line 45-78, validators/user_validator.py line 12-28
Thought: 查看 validator 的实现,找到空字符串未检查的位置
Action: view_file("validators/user_validator.py")
Observation: [代码内容] validate_username 函数只检查了 None,没有检查空字符串
Thought: 找到了 bug 所在,需要在 validate_username 里加上对空字符串的检查
Action: edit_file("validators/user_validator.py", ...)
Observation: File updated successfully
Thought: 修改完成,运行测试验证
Action: run_tests("test_empty_username_returns_400")
Observation: PASSED ✓
Action: run_tests("all")
Observation: 12/12 tests PASSED ✓
4.3 Terminal 数据合成:怎么批量构造终端任务
训练的重点不是"记住某条命令怎么写",而是让模型学会在终端里连续执行任务:查看文件 → 修改内容 → 运行脚本 → 处理报错 → 根据结果继续调整,直到把任务真正完成。
SWE 任务本质上是"在代码仓库里修 bug"——任务边界清晰(就是这个 Issue),验证方式明确(跑单测)。
Terminal 任务更广泛:任何需要在 shell / Docker / 文件系统里完成的操作都算——部署服务、配置环境、数据处理、系统运维……验证方式也更多样(检查文件内容、检查进程状态、检查输出结果等)。
方式一:从已有任务种子扩展
准备一批真实工程场景作为种子,然后用模型批量"扩展"出更多类似任务,再让构造 agent 把这些任务落地成可执行的标准任务对象。
① 种子任务
来自真实工程场景:"配置 Nginx 把 /api 路径的请求反向代理到后端 Flask 服务的 5000 端口"
② 模型扩展出更多任务草稿
给模型看这个种子,让它生成 20 个类似但不同的任务:
任务草稿 1:配置 Nginx 开启 gzip 压缩,对 > 1KB 的响应启用 任务草稿 2:配置 Nginx 对 /static/ 路径设置 30 天浏览器缓存 任务草稿 3:把 Nginx 的访问日志改成 JSON 格式,字段包含时间戳、IP、方法、状态码 任务草稿 4:配置 Nginx 限流,对同一 IP 每秒最多允许 10 个请求 任务草稿 5:配置 SSL 证书,让 HTTP 请求自动跳转到 HTTPS ...
③ 构造 agent 落地成标准任务
以"任务草稿 3(日志 JSON 格式)"为例,构造 agent 生成:
| 任务描述 | "修改 /etc/nginx/nginx.conf,将访问日志的 log_format 改为 JSON 格式,字段包含:time_local、remote_addr、request_method、status、body_bytes_sent、request_time" |
| 初始状态 | Docker 容器,安装了 Nginx,使用默认配置,access_log 格式为标准的 combined 格式 |
| 可用工具 | bash(可执行任意 shell 命令)、view_file、edit_file |
| 验证脚本 | ① nginx -t 通过(配置语法正确);② 发一个 HTTP 请求;③ 读日志最新一行;④ 解析为 JSON,检查 6 个字段都存在 → 全部通过才算成功 |
④ 一条合格的 Terminal 轨迹长什么样
Thought: 先看看当前 nginx.conf 里的 log_format 是什么样的 Action: bash("grep -n 'log_format' /etc/nginx/nginx.conf") Observation: 14: log_format combined '$remote_addr...' Thought: 找到了,在第 14 行。需要把这行改成 JSON 格式,同时加上 request_time 字段 Action: edit_file("/etc/nginx/nginx.conf", old="log_format combined '$remote_addr...'", new='log_format json_log \'{"time":"$time_local","ip":"$remote_addr",' '"method":"$request_method","status":$status,' '"bytes":$body_bytes_sent,"rt":$request_time}\'') Observation: File saved Thought: 还需要把 access_log 指令里的格式名从 combined 改成 json_log Action: bash("grep -n 'access_log' /etc/nginx/nginx.conf") Observation: 18: access_log /var/log/nginx/access.log combined; Action: edit_file("/etc/nginx/nginx.conf", old="combined;", new="json_log;") Observation: File saved Thought: 验证一下语法对不对 Action: bash("nginx -t") Observation: nginx: configuration file test is successful ✓ Action: bash("service nginx reload && curl -s localhost/ > /dev/null && tail -1 /var/log/nginx/access.log | python3 -c 'import json,sys; d=json.load(sys.stdin); print(list(d.keys()))'") Observation: ['time', 'ip', 'method', 'status', 'bytes', 'rt'] ✓ → 全部字段存在
⑤ Refine agent 的三项检查
重复运行验证脚本 5 次,结果是否一致?(容器初始状态是否确定性)
验证脚本的期望值是否唯一、是否可以自动判定(不依赖人工检查)
是否存在"修改验证脚本本身"或"写死期望输出"等取巧行为
方式二:从技术网页自动构造
这条路线更自动化——直接从网络上爬取大量技术内容,从中提炼出可执行任务,不依赖人工写种子。
Terminal 任务的验证难点在于:不是所有操作的结果都能用"对/错"来二值判断。比如"配置好 Nginx 日志",Nginx 有 100 种合法的 JSON 格式写法,不可能枚举所有正确答案。
GLM-5 的解法是:从结果反推验证——不检查怎么做,只检查做完后系统的状态是否符合预期。验证脚本检查的是"日志能否被 JSON 解析"和"字段是否存在",而不是"配置文件是否和标准答案一模一样"。这样验证方式就灵活可靠了。
4.4 Search 数据合成:构造高难多跳检索任务
Search 数据合成的目标,不是生成普通问答数据,而是构造一批必须经过搜索、浏览和多跳推理才能完成的任务。
普通问答:问题 → 答案,一步到位。模型直接从自己的参数知识里找答案就行。
Search 任务:问题 → 搜索 → 读网页 → 发现需要再搜索 → 再读网页 → 整合多个证据 → 最终推理得出答案。
问题的关键是:如何构造一个"只靠参数知识答不上来、必须真正去检索"的好问题?这就是 Search 数据合成的核心挑战。
"2022 年带领阿根廷赢得世界杯冠军的队长,目前效力于哪家俱乐部?这家俱乐部所在城市的 NBA 球队主场球馆叫什么名字?"
这道题需要 4-5 跳推理,每一跳都需要从网页中获取信息:
| 跳次 | 需要查什么 | 答案 | 为什么要搜索而不是记忆 |
|---|---|---|---|
| 跳1 | 2022 年阿根廷世界杯队长是谁 | 梅西 | 大多数模型参数里有,但"带领夺冠的队长"措辞可能需要确认 |
| 跳2 | 梅西目前效力哪家俱乐部 | 迈阿密国际 | 时效性!转会信息随时变,模型训练后信息可能过期,必须查 |
| 跳3 | 迈阿密国际所在城市 | 迈阿密(佛罗里达州) | 俱乐部名字已经暗示,但严格来说需要确认 |
| 跳4 | 迈阿密的 NBA 球队 | 迈阿密热火 | NBA 常识,参数里有,但需要跟迈阿密关联 |
| 跳5 | 热火的主场球馆名称 | Kaseya Center | 球馆名字因赞助商经常更名,时效性强,必须查 |
"阿根廷 2022 年世界杯夺冠了吗?" → 模型参数直接能答,搜索没有价值
每一跳都涉及可能过时的信息(转会、球馆更名),模型必须真正搜索才能给出准确答案
方法一:GLM-5 的 WKG(网页知识图谱)构造方式
GLM-5 的核心创新是不直接让模型"想出多跳问题"(这样产出的问题容易偏向模型已知的知识),而是先从网页中建立结构化的知识图谱,再从图谱中"走路径"来生成问题,保证问题的多跳性是真实的而不是人为拼凑的。
先让早期 search agent 大量刷真实问答任务(从 Wikipedia 问题、新闻、知乎等来源),把它访问过的每一个网页都存下来,做去重和质量过滤。
直接爬互联网会得到大量无关页面(广告、导航页、重复内容)。让 agent 刷真实问题,它访问的页面天然是"回答实际问题时有用的页面",质量更高,后续构建知识图谱的信噪比也更好。
对每个网页,用 NLP 模型提取出实体-关系-实体的三元组,拼成一张大图:
核心技巧:选低频到中频实体作为起点,而不是高频实体(如"美国"、"中国"这种太常见、模型参数里全有)。
先定终点(低频答案),再往回推导问题,可以保证问题有唯一确定的答案。如果从起点正向出发,可能走到多个合法答案,验证时很麻烦。
生成的问题草稿中,有些可能太简单——模型不搜索就能答对。要把这些"假多跳题"过滤掉。
做法:直接问一个不能搜索的模型(无工具访问权限),如果它能答对,说明这道题在训练数据里出现过,不需要搜索就能答,过滤掉。
例:问"奥运会创始年份" → 模型直接回答 "1896年" → 无需搜索 → 过滤
做法:让 search agent 尝试用 ≤2 次搜索回答,如果成功,说明这题不够多跳,过滤掉。
例:问"梅西的国籍" → 一次搜索 "梅西 国籍" → 直接得到"阿根廷" → 只有 1 跳 → 过滤
最后一关:检查问题和答案在逻辑上是否严密。
多个 search agent 独立作答,结果是否一致?若有 agent 给出了不同答案,说明问题存在歧义,需要修改或丢弃
每一跳的信息是否都能在某个具体网页上找到?不能有"推理依赖无法验证的隐含知识"的跳
不同来源网页的信息是否冲突(如一个页面说球馆叫 A,另一个说叫 B)?有冲突的题需要更新到最新信息
方法二:MiniMax M2 的 WebExplorer + 迭代式 Query 进化
MiniMax M2 的路线与 GLM-5 不同——它不先建图再采路径,而是让 agent 先自由探索,形成"信息丰富的种子问题",再通过三步迭代操作把问题难度提升上去。
GLM-5 WKG vs MiniMax M2 WebExplorer:哪种更好?
| 维度 | GLM-5 WKG | MiniMax M2 WebExplorer |
|---|---|---|
| 多跳性保证 | ✅ 图结构天然定义跳数,可以精确控制 N 跳 | ✅ 通过进化操作逐步增加跳数,但不那么精确 |
| 多样性 | 中:图谱里的关系类型有限,问题结构可能单一 | ✅ 自由探索产出的问题更自然、多样,更接近真实用户提问 |
| 可扩展性 | ✅ 图构建一次,后续可批量采样,适合大规模 | 中:每次 WebExplorer 都需要真实的网络访问,成本较高 |
| 答案可验证性 | ✅ 图谱里有标准答案,可自动验证 | 中:需要人工或模型辅助验证进化后的问题是否合理 |
| 时效性 | 中:图谱需要定期更新,否则答案会过期 | ✅ 每次探索都访问实时网页,天然具备时效性 |
无论是 WKG 还是 WebExplorer,目标都是一样的:构造那些模型参数知识答不上来、必须真正搜索才能回答的问题。
• 验证多跳性:用"不给工具的模型"直接答,答对了就过滤
• 保证时效性:刻意选择时效性强的信息(转会、更名、选举结果等)
• 控制难度:双层过滤 / 迭代进化,确保不太简单也不过于模糊
与 SWE 和 Terminal 任务相比,Search 任务最难的不是"执行",而是搜索策略的规划——面对一个模糊的多跳问题,应该先搜哪个、怎么分解、在哪一页找到下一跳的线索,这才是 search agent 训练的核心目标。
这一章节讨论在实际训练中遇到的三大核心挑战,以及 GLM-5 和 Kimi K2.5 各自的解法。
挑战一
训推不一致(Training-Inference Inconsistency):训练时用的分布和推理时的分布不一样
挑战二
异步框架 off-policy 问题:为提升吞吐量用异步框架,但导致训练数据来自"旧版"模型
挑战三
长链路 Agent 延迟太高:串行执行多步任务,延迟随任务长度线性增长
5.1 训推不一致问题
🤔 训推不一致为什么会存在?
根本原因:训练和推理用的是同一套参数,但数值精度和计算方式不同
| 训练时 | 推理时 | |
|---|---|---|
| 参数精度 | bfloat16 / float32(保证梯度精度) | int8 / int4 量化(省显存、加速) |
| Attention 实现 | FlashAttention(训练也用) | FlashAttention(相同) ✅ 数学等价,不引入误差 |
| KV Cache | 训练也可以用 | 用(增量解码) ✅ 只是缓存,数学等价,不引入误差 |
| ⚠️ 量化精度(主要原因) | bfloat16 / float32(保证梯度精度) | int8 / int4 量化(省显存、加速) → 引入截断误差,π_train ≠ π_infer |
| ⚠️ 张量并行方式(次要) | 多卡 all-gather,某种切分方式 | 可能用不同并行切分,all-reduce 顺序不同 → 极小浮点舍入误差 |
具体例子:量化误差导致概率偏移
同一套参数,同一个 token 位置的 logits:
训练侧(bfloat16):[2.314, 1.892, 0.501, ...]
推理侧(int8 量化):[2.311, 1.895, 0.498, ...] ← 量化引入了微小误差
softmax 后:
π_train("搜索") = 0.52
π_infer("搜索") = 0.51 ← 只差 1%
训推不匹配比率 ρ = π_train / π_infer = 0.52 / 0.51 ≈ 1.02 ← 单步还好
但 Agentic 轨迹有 8k-32k tokens,每个 token 都有微小误差,
误差沿整条轨迹累积,最终 ρ 可能偏到 1.3、1.5 甚至更高。
为什么 Agentic RL 里特别严重?
普通数学/推理任务轨迹只有几十个 token,误差累积不明显。但 Agentic 任务(SWE、Terminal)的轨迹长达 8k-32k tokens,每个 token 都有一点点训推误差,误差沿整条轨迹累积,轨迹越长 ρ 偏差越大。PPO/GRPO 的重要性采样假设 ρ≈1,一旦打破,梯度方向就不可信了,会出现:梯度估计不稳定 / 奖励优化方向失真 / RL 越训越坏。
在 RL 训练大模型时,一个很常见但容易被忽视的问题:训练时,模型更新参数用的是一种分布,而采样生成时,轨迹却可能来自另一种分布。如果这个偏差太大,会带来:梯度估计不稳定 / 奖励优化方向失真 / RL 训练效果越训越坏。
5.1.1 GLM-5 的解法:IcePop
GLM-5 的 RL 算法在 GRPO 基础之上,结合了蚂蚁的 IcePop 思想,显式区分两种策略:
训练策略 π_train
用于梯度更新,包含了量化误差、推理时精度转换等差异
推理策略 π_infer
用于采样轨迹,可能因为推理优化(如算子融合、量化)而与训练时略有不同
定义训推不匹配比率:
pop(·) 函数会抑制那些不匹配比率偏离过大的样本:
过滤掉那些训练分布和推理分布偏差过大的 token,只保留更可信的部分参与更新。如果某个 token 在训练侧和推理侧的概率差异太大(梯度估计不可靠),就直接忽略它,不让它污染梯度。
5.1.2 DSA top-k 带来的额外不一致
除了策略层面的训推不一致,GLM-5 还特别提到了 DSA(Distributed Sparse Attention)中 top-k 检索带来的额外不一致问题。DSA 先通过 indexer 从长上下文中检索出最相关的 top-k 历史 KV,再对这些位置做稀疏注意力计算。
如果训练时和推理时 top-k 选出来的结果不一致(不同实现的 top-k 算子可能有细微差异),那么即使输入完全一样,模型实际利用的上下文子集也不同,后续注意力计算和 token 概率分布都会跟着变化。这类似 MoE 中的 Route Replay 问题,但 DSA 的 k=2048,显式 replay 成本太高。
① 使用确定性的 torch.topk(而非高性能但非确定性的 CUDA top-k 实现)
② 在 RL 阶段默认冻结 indexer 参数,保证 top-k 结果稳定
代价是 indexer 无法随 RL 训练继续优化,但换取了训推一致性和训练稳定性。
5.2 异步框架 Off-Policy 问题
在 Agentic RL 中,一个 agent 轨迹很长、rollout 耗时差异大。如果用同步 RL,GPU 会经常"等人",大量空转。所以主流基模训练都使用异步 RL 框架:推理端持续生成 agent 轨迹,训练端拿到足够轨迹后直接更新模型。
异步模式下,rollout 在持续生成数据的同时,训练端也在不断更新参数。早一点生成的轨迹来自旧模型,晚一点生成的来自新模型。甚至一条长 trajectory 内部,rollout engine 在中途同步了新权重,导致同一条轨迹的前后 token 可能来自不同 policy。
GLM-5 的三种解法
每隔 K 次梯度更新后,把最新权重推给推理端,并重置 optimizer 的一二阶动量(避免 Adam 动量记忆旧梯度方向引入错误偏差)。
双边裁剪:设定区间 [1-ε_l, 1+ε_h],落在区间内的 token 保留参与梯度,超出范围的直接 mask 掉。思路:能救的就救,太偏的直接丢掉。
记录每个 response 在 rollout 时经历的模型版本 (w_0, ..., w_k)。如果当前训练版本 w' 与 rollout 起始版本 w_0 相差过大(w' - w_0 > τ),说明样本离当前策略已经太远了,直接丢弃。
目标不是完全消灭 off-policy(异步框架下几乎不可能),而是在采样效率和梯度估计准确性之间取得实用平衡:
• 周期性同步:减少策略漂移
• 重要性采样 + 裁剪:校正轻微漂移
• 丢弃太旧轨迹:彻底过滤严重漂移
5.3 Kimi K2.5:用 Agent Swarm 解决延迟问题
很多现有的智能体模型依赖工具调用的串行执行:一步一步思考、一步一步调工具。任务一旦变复杂,推理时间随任务长度线性增长,延迟很快就会变得难以接受。
Kimi K2.5 的 Agent Swarm 核心思路:把复杂任务拆成异构的子问题并发执行。在 wide-search 场景下,相比单智能体基线,整体延迟最多降低 4.5×,而且任务效果继续提升。
核心架构:"a trainable orchestrator and frozen subagents"(可训练的调度者 + 冻结的子代理)
🎭 Orchestrator(可训练)
- 负责拆任务:把复杂任务分解为多个可并行执行的子任务
- 负责创建和分配子代理:为每个子任务分配一个 subagent 并告知工具
- 负责汇总结果:收到各 subagent 结果后整合成最终答案
- 在训练过程中持续优化调度策略(参数可更新)
🤖 Sub-agents(冻结)
- 来自固定的中间策略 checkpoint,参数不更新
- 每个 subagent 独立执行被分配的子任务,完成后返回结果
- 不参与 RL 训练过程
① 训练信号互相干扰:orchestrator 的好坏依赖 subagents 能力,两者耦合导致梯度方向混乱
② 系统整体不稳定:两端同时优化形成"moving target",难以收敛
③ 难以归因:任务失败时无法判断是 orchestrator 拆任务错了还是 subagent 执行不好
冻结 subagents 后,orchestrator 有了稳定的执行层,reward 信号可以清晰归因于调度决策。
总结:三大挑战的解法对比
| 挑战 | 根本原因 | 解法 | 代价 |
|---|---|---|---|
| 训推不一致 | 训练侧和推理侧计算实现有细微差异(量化、算子差异等) | IcePop:过滤不匹配比率偏离过大的 token;DSA:deterministic top-k + 冻结 indexer | 少量样本被过滤;indexer 无法继续优化 |
| 异步 off-policy | 推理和训练异步进行,轨迹生成时和使用时的模型版本不一致 | 周期性同步权重 + 重要性采样双边裁剪 + 丢弃太旧的轨迹 | 权重同步开销;部分轨迹被丢弃,数据利用率略低 |
| 长链路延迟高 | 串行执行多步任务,延迟随步数线性增长 | Agent Swarm:可训练 orchestrator + 冻结 subagents 并行执行 | 不做端到端联合训练;orchestrator credit assignment 有简化 |