← 返回笔记列表
Agentic能力从哪里来?拆解基座大模型的训练过程
作者:低级炼丹师 · 知乎专栏 · 2025年
🔗 原文链接
📝 技术笔记 · 大模型训练 · GLM-5 / MiniMax M2 / Kimi K2.5

Agentic 能力从哪里来?拆解基座大模型的训练过程

以 GLM-5 为主线,MiniMax M2 和 Kimi K2.5 为支线——从预训练到多阶段 RL,完整还原现代基座大模型的训练链路,回答"一个真正具备 Reasoning、Coding 和 Agent 能力的现代大模型,究竟是如何被分阶段训练出来的"

来源
知乎·低级炼丹师
核心模型
GLM-5 / MiniMax M2 / Kimi K2.5
涵盖阶段
Pre-Train → Mid-Train → SFT → 多阶段 RL → 蒸馏
核心问题
Reasoning、Coding、Agent 能力如何分阶段训练出来?
💬
一句话总结
现代基座大模型的 Agentic 能力,不是一个阶段训出来的,而是:

海量语料打底(Pre-Training)→ 长上下文+工程数据强化(Mid-Training)→ 行为先验建立(SFT)→ 推理能力拔高(Reasoning RL)→ 真实环境执行(Agentic RL)→ 通用偏好对齐(General RL)→ 跨阶段能力缝合(On-Policy Cross-Stage Distillation)

每个阶段解决一个特定问题,最后用蒸馏把所有能力统一收敛,防止遗忘。
🔑 核心认知

GLM-5 的定位不是传统聊天模型,而是面向 ARC(Agentic + Reasoning + Coding) 的统一基座。因此从预训练阶段就优先注重代码和推理数据,不是平均对待所有数据。整条链路的逻辑是:不同能力分阶段建模 → 分阶段强化 → 最后统一收敛。现代大模型的训练,不再是"一个阶段解决所有问题",而是"不同能力分阶段建模、分阶段强化、最后统一收敛"。

🗺️
§1 以 GLM-5 为例:基座大模型整体训练流程

GLM-5 的整体训练流程分为两大阶段:Base Model Training(基础模型训练)Post-Training(后训练)

GLM-5 训练全流程
Base Model Training
Pre-Training
28.5T tokens
Mid-Training
长上下文/SWE
Post-Training
SFT
行为先验
Reasoning RL
推理拔高
Agentic RL
环境执行
General RL
通用对齐
Cross-Stage 蒸馏
能力缝合
💡 为什么这个顺序?
  • 先 Reasoning RL,再 Agentic RL:Agentic 能力建立在推理能力之上。代码执行、工具调用、长流程规划,本质都是推理。如果推理链条不稳定,后续 Agent 阶段即使加入更多工具也无法真正提升质量。
  • 先 Agentic RL,再 General RL:Agentic RL 针对特定可验证环境做强化,目标清晰;General RL 针对更广泛的人类偏好,信号更模糊。先做前者可以在有清晰奖励信号的场景建立稳定策略,再泛化到更主观的场景。
  • 最后用蒸馏缝合:多阶段 RL 每个阶段都可能削弱前面阶段学到的能力(灾难性遗忘)。最后用 On-Policy Cross-Stage Distillation 统一把所有能力融合,是整个 pipeline 的核心设计。
阶段子阶段主要作用关键内容
Base Model TrainingPre-Training学习通用语言、知识、代码与基础表征,构建统一基座Web / Code / Math&Science 语料,28.5T tokens,优化数据筛选/去重/质量控制
Mid-Training面向目标能力定向增强,提升长上下文建模、Agent 场景适配、软件工程理解分阶段扩展上下文至200K;引入长文档自然数据与合成数据;强化 repo-level code、issue、PR、commit diff 建模(160B tokens)
Post-TrainingSFT建立任务遵循、推理表达、工具使用与多轮交互行为先验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 阶段的数据量虽然小很多,但质量要求极高。

🏗️
§2 Base Model Training:先把"底座能力"练扎实

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 打基础。

✅ Code 数据具体改进
  • 更新了主要代码平台(GitHub 等)的数据快照,扩充了含代码的网页
  • 去重后的唯一 token 数提升了 28%(更少重复,信息密度更高)
  • 修复了部分代码元数据问题(注释、文档字符串等)
  • 提升了低资源编程语言(如 Rust、Julia、Kotlin 等)的数据质量

③ Math & Science 数据

  • 来源包括网页、书籍和论文
  • 优化文档解析流程(公式识别、表格理解等)
  • 利用大模型对内容进行质量打分,保留更有"教育价值"的部分
  • 严格过滤低质量或模板化内容(如机械刷题集、无推导过程的答案集)
🔑 为什么要在预训练阶段就精心筛选数据?

预训练学到的是模型的"世界观"——对语言模式、知识体系和推理方式的基础认知。如果预训练阶段学了大量低质量数据,这些错误认知会深深嵌入模型参数,后续 SFT 和 RL 很难完全纠正。相反,如果预训练数据质量高,SFT 只需要调整行为格式,RL 只需要优化策略方向,整体效率更高,效果也更好。「Garbage in, garbage out」在预训练阶段尤为关键。

2.2 Mid-Training:定向语料强化

面向目标能力进行定向增强,提升长上下文建模、Agent 场景适配与软件工程理解能力。
🤔 为什么需要单独的 Mid-Training?
  • 模型在普通长度文本上的表现良好,并不意味着它在 128K 甚至 200K 的上下文下仍然稳定
  • 具备代码生成能力,并不意味着它能理解跨文件、Issue、PR 和 commit 之间的复杂关联
  • 能完成单轮问答,并不意味着它可以在长链路 Agent 任务中持续保持一致性

(1)上下文长度的逐步扩展

预训练只用了 4K 上下文。Mid-Training 采用分阶段扩展上下文长度的策略:

Pre-Training
4K context
Stage 1:32K
1T tokens
Stage 2:128K
500B tokens
Stage 3:200K
50B tokens
💡 为什么要逐步扩展而不是直接到 200K?

训练不稳定: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)

构造更强的长距离依赖和多轮记忆场景,专门训练模型的长程记忆能力

🎯
§3 Post-Training:从 Base Model 到 ARC 强模型

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,复用已有推理过程,减少前后不一致。

适用:多轮 coding agent、长任务

Turn-level Thinking(轮次级别)

同一 session 中按轮次控制是否启用 reasoning。简单请求关闭降低延迟;复杂任务开启提升准确性。

适用:混合复杂度的会话场景

3.2 Reasoning RL:先强化推理能力

在数学、科学、代码及 TIR 等可验证任务上进行强化学习;采用结果导向奖励信号优化推理性能
💡 为什么先做 Reasoning RL 再做 Agentic RL?

代码执行、工具调用、长流程规划,本质都建立在推理能力之上。如果推理链不稳定,后续 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 完全由程序自动判定,可以大规模并行训练,不需要人工打分。

🔬 为什么 RLVR 比 RLHF 更适合 Reasoning?
  • RLHF:奖励来自人类偏好判断,主观、慢、贵,且人类很难准确判断复杂数学推理链的对错
  • RLVR:奖励来自程序自动判断(答案对/错),客观、快、准,完全可扩展
  • DeepSeek-R1 用的是同一套思路,本质是把 LLM 的 RL 对齐做到了有明确验证标准的领域

3.3 Agentic RL:让模型真正学会"执行任务"

如果说 Reasoning RL 解决的是"模型能不能想清楚",那么 Agentic RL 解决的就是:模型能不能在真实环境里,把事情一步一步做出来。
🤔 "Agentic" 是什么意思?

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步)

1
准备任务和可验证环境
  • 初始状态(initial state):任务起点,如"修复前的代码仓库快照"
  • 可执行动作空间(action space):可用工具列表
  • 观测(observation):每次动作后环境返回的信息
  • 状态转移(state transition):动作执行后环境状态的变化
  • 验证与反馈机制(verification):自动化测试脚本
2
Rollout 生成 Agent 轨迹

每条轨迹:$y = (r_1, a_1, o_1, \; r_2, a_2, o_2, \; \ldots, \; r_n, a_n, o_n)$

其中 $r_i$ = reasoning,$a_i$ = action/tool call,$o_i$ = observation(不参与 loss,但作为下一步推理的输入
3
环境打分(Verifiable Reward)

SWE:F2P 测试是否通过;Search:答案和证据是否正确;Terminal:test script 是否通过。都是 verifiable environment,reward 由测试脚本自动打分。

4
用 Group-wise Policy Optimization(GRPO)更新

对同一问题 $x$ 采样 $K$ 条轨迹,构造组内相对优势:

$$A(x, y_i) = r(x, y_i) - \bar{r}(x), \quad \bar{r}(x) = \frac{1}{K}\sum_{i=1}^{K} r(x, y_i)$$

$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,能评价细粒度质量方差更高,稳定性较差
🔑 高质量专家数据(Human-in-the-loop 风格对齐)

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 本质差异
📖 前往独立笔记:OPD 与 KL 散度方向性 →
文件路径:notes-opd.html  · 位于同一 topics/ 目录下
🏭
§4 Agentic 数据合成:合成任务 + 合成环境 + 合成反馈 + 合成轨迹
Agentic 数据合成的关键不是"合成文本",而是:合成任务 + 合成环境 + 合成反馈 + 合成轨迹。不仅包括传统意义上的文本合成,更包括任务合成、环境合成、反馈合成与轨迹合成。

GLM-5 中 Agentic 数据主要围绕三类任务构建:

软件工程环境(SWE)

基于真实 Issue-PR 对构建可执行修复任务,目标是训练 coding agent

终端环境(Terminal)

构造需要在 shell / Docker / 文件系统里操作的任务,目标是训练 terminal agent

搜索任务(Search)

构造多跳检索、多网页证据聚合的问题,目标是训练 search agent

4.1 Agentic 数据合成的标准六步流水线

下面用一条贯穿始终的例子("修复注册接口不验证空用户名的 bug")来串讲六步,每步结合这个例子,让流程看得见摸得着。

1
选择种子任务

种子任务是整个流水线的起点——没有种子就没有任务,没有任务就没有数据。种子的质量直接决定最终训练数据的多样性和覆盖范围。

📦 来源一:真实世界数据
  • SWE 任务:GitHub 上真实的 Issue-PR 对(有 bug 描述,有人工修复 patch)
  • Terminal 任务:Stack Overflow 的报错帖、技术博客的操作教程
  • Search 任务:早期 search agent 在真实搜索中访问过的网页
🤖 来源二:模型早期探索轨迹
让早期版本的 agent 大量刷真实任务,把它探索过的环境状态、访问过的网页、产生的中间状态都记录下来,这些"脚印"本身就是宝贵的种子素材——因为它们来自真实任务场景,天然具有多样性。
📌 贯穿示例·第一步:找到种子
在 GitHub 上发现了这条 Issue:
Issue #123:Registration API returns 500 when username is empty string
以及对应的修复 PR:PR #456:Fix empty username handling
这就是我们的种子——有原始 bug、有参考修复方向,接下来需要把它改造成一个可执行任务。
2
转化为可执行任务

原始的 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)= 某个时间点代码仓库的完整静态备份,就像手机的时间点备份——把那一刻所有文件的内容、路径都原样保存,随时可以还原到那个状态。

对应 git 里的什么?
就是 PR 合并之前那个 commit 的所有代码。用 git checkout <commit-sha> 还原,然后打包进 Docker 容器里。
为什么需要快照?
保证每次 agent 尝试这个任务都从同一个起点开始,不会因为上次 agent 改了什么文件而影响下一次实验(可复现性)。
💡 F2P / P2P 测试是谁写的?不需要人工写!

测试直接从 GitHub PR 里自动提取,完全不需要人工编写。这是 SWE 数据合成能大规模进行的关键原因。

正规开源项目 PR 的标准结构:
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 作者写好的,不是为训练数据专门写的。

F2P = PR 里新增的测试
对比 PR 前后的 diff,找出新增的 test 函数。这些测试在 bug 修复前运行会失败(bug 还在),修复后必须通过。
自动提取,零成本。
P2P = 仓库里原有的其他测试
在 PR 合并前跑一遍全量测试套件,找出已经通过的用例。这些是回归测试——agent 修复 bug 时不能把它们搞坏。
也是自动提取,零成本。
⚠️ 这就是为什么 SWE-bench 只选正规开源项目,而不是随便一个 GitHub 仓库:
随便的小项目可能没有测试、测试覆盖率很低、PR 不带测试——无法自动提取 F2P/P2P,就没法构造可验证的训练任务。SWE-bench 从 12 个知名 Python 开源库(Django、Flask、NumPy、SciPy 等)里筛选,保证每个 Issue 都有完整的测试覆盖。
3
构建可验证环境

有了任务描述还不够,agent 还需要一个能真正运行代码、执行命令、看到反馈的环境。这一步就是把任务描述"活化"成一个沙盒环境。

初始状态
把种子任务对应的代码库、文件系统、数据库初始状态打包进 Docker 容器,保证每次任务执行的起点完全一致(可复现)
动作空间 + 观测
定义 agent 在这个环境里能做什么(工具列表)、执行后能看到什么(文件内容、命令输出、测试结果)
验证 & 反馈机制
验证脚本:运行测试套件,自动判断 F2P / P2P 通过情况,输出 "PASSED / FAILED" 信号
📌 贯穿示例·第三步:构建沙盒
Docker 容器里有什么:Bug 发生前的代码库 + Python 运行时 + 测试框架(pytest)
agent 能做什么:查看文件、搜索代码、编辑代码、运行单个或全部测试
验证脚本输出什么:
Running test_empty_username_returns_400 ... PASSED ✓
Running test_empty_username_error_message ... PASSED ✓
Running test_valid_username_registration  ... PASSED ✓
Running test_duplicate_username           ... PASSED ✓
Result: F2P=2/2, P2P=2/2 → ACCEPTED ✅
4
执行任务并采集轨迹

这是整个流水线中最核心的一步:让 agent 真正进入沙盒环境,完整地"走一遍"解决这个问题的过程,并把每一步的输入输出全部记录下来,形成轨迹(trajectory)

🤔 为什么要采集轨迹,而不是直接用最终答案?
传统 NLP 数据是"输入-输出"对:给一个问题,有一个答案。但 Agentic 任务是多步的——解决一个 bug 可能需要先搜索代码、再分析、再修改、再跑测试、发现不对、再改……这个过程本身就是训练数据,丢掉中间步骤就会丢掉大量"遇到问题怎么思考"的知识。
一条 SWE 轨迹的完整长相:
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 ✓ → 提交
轨迹里包含了什么:① 每一步的思考过程(Thought) ② 调用了哪个工具(Action) ③ 工具返回了什么(Observation) ④ 根据反馈做的决策修正。这些加在一起,才是一条完整的"问题解决过程"数据。
5
筛选、过滤与修复

不是所有采集到的轨迹都能直接用——有的 agent 走了错误的路,有的轨迹里存在漏洞(exploit),有的任务本身就设计有问题。这一步是数据清洗,只留下高质量样本。

过滤项 检查什么 为什么要检查
结果正确性验证脚本是否通过最基本的门槛,没解决任务的轨迹不能用
Exploit 检查 ⚠️是否存在"取巧"通过测试的行为最容易被忽略、危害最大的问题(见下方详述)
环境稳定性容器是否崩溃、工具是否报错环境本身有问题的轨迹数据不可信
任务难度是否在合理步数内完成太简单(1步就完成)没有学习价值;太长(步数异常多)可能是轨迹质量差
一致性同一任务多次运行结果是否一致不稳定的任务说明验证机制有问题,不适合作为训练数据
⚠️ Exploit 检查:最难检测的数据污染问题

Agent 有时会发现测试验证本身的"漏洞",找到一条不需要真正解决问题就能通过的捷径。这类轨迹表面上"验证通过",但实际是有害的训练数据——会教会模型走捷径而不是真正解决问题。

例1:删除测试
rm test_empty_username_returns_400.py
测试文件不存在,pytest 直接通过。任务没有解决,但验证脚本说 PASSED ✓
例2:硬编码返回值
把接口改成:当检测到测试框架在调用时,直接返回 HTTP 400,其他情况还是返回 500。测试过了,实际不行。
例3:修改验证脚本
直接改掉期望值:把 assert status == 400 改成 assert status == 500,测试"通过了",但期望值已经被篡改。
6
转化为训练数据

通过筛选的高质量轨迹,根据用途转化成不同格式的训练数据。

📚 SFT 数据(监督微调)
保留完整轨迹,按 Thought-Action-Observation 格式打包。
关键处理:mask 掉的是环境返回的 Observation token(bash 输出、文件内容等),不是"错误步骤"。SFT 阶段没有机制自动识别哪步是弯路——模型生成的所有 Thought + Action token 都参与 loss,包括走了弯路的那些步骤。
高质量 SFT 的做法是直接用"一步到位"的干净轨迹(由更强模型重新生成),而不是带弯路的探索轨迹——从源头避免弯路,而不是事后识别并 mask。
🎮 RL 数据(强化学习)
不需要完整轨迹,只保留任务定义 + 环境配置 + 验证脚本,放入 RL 的任务池。
在 Agentic RL 阶段,agent 会在这个任务上重新探索、自己尝试、用验证结果作为 reward 信号来更新自己。同一个任务可以被使用无数次,每次 agent 都会走出不同的路径。
📌 贯穿示例·第六步:两种格式
SFT 版本:完整轨迹(Thought + Action + Observation × N 步)打包,中间有一步走错了(先改了 routes/auth.py 没用,后来才去改 validator),这一步做 masking 不计入 loss
RL 版本:把 Docker 环境 + 任务描述 + 验证脚本重新打包,下次 Agentic RL 阶段,agent 可以重新进来自己探索,验证通过就得 reward +1,失败 reward 0
🤔 探索成功的路径直接当 SFT 样本?SFT 和 RL 到底怎么分工?

SFT 和 RL 不是二选一,而是串行的两个阶段,解决的问题完全不同:

📚 SFT 阶段:教会基本姿势(入门)
用一个较强的模型(或早期版本)去刷任务,把探索成功的轨迹打包成训练样本。
目的:让模型至少知道"怎么用工具、怎么格式化输出、什么时候该提交"。
类比:先给新员工看一遍标准操作录像,让他知道流程。
🎮 RL 阶段:提升上限(精进)
让当前被训练的模型自己在任务上试,验证通过 reward=1,失败 reward=0。
目的:让模型在更难的任务上自己摸索策略,突破 SFT 数据覆盖不到的场景。
类比:让员工真正上手做项目,失败了自己总结经验。
关键:失败的轨迹在 RL 里同样有价值!SFT 只用成功的轨迹(失败的没法当正样本),但 RL 的成功轨迹(reward=1)和失败轨迹(reward=0)通过组内对比共同告诉模型哪种做法更好——失败样本提供了负面信号,帮助模型学会避开错误路径。
🔑 六步流水线的本质:把"已知结果的过程"变成可学习的训练信号

传统 NLP 数据是人工写标注。Agentic 数据的难点在于:不能靠人工逐步标注一个 20 步的 agent 轨迹(太贵、太慢、规模化不了)。这六步流水线解决的核心问题是:怎么让机器自动判断一条轨迹是好是坏——答案就是可执行验证。只要最终结果可以被代码自动验证(测试通过 / 不通过),中间过程就可以被大规模自动采集和过滤,完全不需要人工逐步审核。

💡 完整走一遍:Agent RL 怎么训练一个能修 Bug 的 SWE 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 反而效果更好(历史太长让模型找不到重点)。

任务设定

Issue #789(来自 requests 库):
当 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❌ 失败0F2P 测试过了但破坏了 P2P 用例
轨迹 8✅ 通过1
组内均值(leave-one-out):成功轨迹的 baseline ≈ (0+1+1+0+1+0+1)/7 = 4/7 ≈ 0.57,失败轨迹的 baseline ≈ (1+1+0+1+0+1+1)/7 = 5/7 ≈ 0.71

第四步:计算 Advantage + 决定哪些 token 参与梯度计算

关键:不是所有 token 都参与,环境返回的 Observation 被 mask 掉,只有模型自己写的 Thought + Action token 才拿到梯度信号:

轨迹 token 的梯度权限示意(以轨迹 1 的前两步为例)
参与loss Thought: timeout=0 应该立即超时,先找 timeout 处理的代码... Adv = +0.43 参与loss Action: bash("grep -rn 'timeout' requests/adapters.py...") Adv = +0.43 mask掉 Obs: 47: timeout=None / 89: if timeout is not None: ... 不参与 参与loss Thought: 第 89 行有判断,但 timeout=0 时... Adv = +0.43 参与loss Action: view_file("requests/adapters.py", ...) Adv = +0.43
失败轨迹(如轨迹 3)的 token 得到 Adv = 0 - 0.71 = -0.71——「先去改 cookies.py」这个 Action 会被压低概率,模型下次遇到类似情况时就更不会走这条路。

第五步:用这 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)
整个过程完全不需要人工标注每一步——唯一的监督信号是「测试通过=1,不通过=0」,模型靠这一个比特的信号,在数百步迭代里逐渐学会了代码定位、工具使用、错误恢复和回归测试检查等所有工程技能。

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 400
    • test_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 数据合成:怎么批量构造终端任务

训练的重点不是"记住某条命令怎么写",而是让模型学会在终端里连续执行任务:查看文件 → 修改内容 → 运行脚本 → 处理报错 → 根据结果继续调整,直到把任务真正完成。

🤔 Terminal 任务与 SWE 任务有什么不同?
SWE 任务本质上是"在代码仓库里修 bug"——任务边界清晰(就是这个 Issue),验证方式明确(跑单测)。
Terminal 任务更广泛:任何需要在 shell / Docker / 文件系统里完成的操作都算——部署服务、配置环境、数据处理、系统运维……验证方式也更多样(检查文件内容、检查进程状态、检查输出结果等)。

方式一:从已有任务种子扩展

准备一批真实工程场景作为种子,然后用模型批量"扩展"出更多类似任务,再让构造 agent 把这些任务落地成可执行的标准任务对象。

💡 完整案例:从"配置 Nginx 反代"种子扩展出一批任务

① 种子任务

来自真实工程场景:"配置 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_fileedit_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 次,结果是否一致?(容器初始状态是否确定性)
测试一致性
验证脚本的期望值是否唯一、是否可以自动判定(不依赖人工检查)
Exploit 检查
是否存在"修改验证脚本本身"或"写死期望输出"等取巧行为

方式二:从技术网页自动构造

这条路线更自动化——直接从网络上爬取大量技术内容,从中提炼出可执行任务,不依赖人工写种子。

筛选高质量技术网页

从爬取的海量网页中,过滤出包含大量代码块、命令行操作、配置示例的页面(比如 StackOverflow 高赞回答、官方文档 Quickstart、GitHub README 里的安装教程)。过滤标准:代码块数量 ≥ 3、有明确的操作步骤。

Coding agent 理解网页内容,生成完整任务

Coding agent 读取网页,理解操作步骤,自动生成:

输入网页:Stack Overflow 上"如何用 Python 脚本监控目录文件变化"

↓ Coding agent 生成 ↓

任务描述:写一个 Python 脚本 watch_dir.py,监控 /tmp/watched/ 目录,当有新文件创建时打印 "[NEW] filename" 到控制台
初始状态:空 Docker 容器,/tmp/watched/ 目录存在,无任何 Python 脚本
验证脚本:在后台运行 watch_dir.py,在 /tmp/watched/ 创建文件 test.txt,检查 stdout 里是否出现 "[NEW] test.txt"
Agent 自己先跑一遍验证脚本

Coding agent 亲自进入 Docker 环境,尝试完成这个任务,运行验证脚本。如果 PASSED,说明任务可行;如果 FAILED,进入下一步。

未通过则自我修复,循环直到通过

Coding agent 看报错信息,分析是任务描述不清楚、还是验证脚本本身有 bug、还是初始环境配置有问题——然后修改任务定义,重新尝试。通常 2-3 轮迭代后,一个合格的任务对象就构造好了。

📌 Terminal 数据合成的核心设计理念

Terminal 任务的验证难点在于:不是所有操作的结果都能用"对/错"来二值判断。比如"配置好 Nginx 日志",Nginx 有 100 种合法的 JSON 格式写法,不可能枚举所有正确答案。

GLM-5 的解法是:从结果反推验证——不检查怎么做,只检查做完后系统的状态是否符合预期。验证脚本检查的是"日志能否被 JSON 解析"和"字段是否存在",而不是"配置文件是否和标准答案一模一样"。这样验证方式就灵活可靠了。

4.4 Search 数据合成:构造高难多跳检索任务

Search 数据合成的目标,不是生成普通问答数据,而是构造一批必须经过搜索、浏览和多跳推理才能完成的任务。
🤔 为什么 Search 任务比问答数据难很多?
普通问答:问题 → 答案,一步到位。模型直接从自己的参数知识里找答案就行。
Search 任务:问题 → 搜索 → 读网页 → 发现需要再搜索 → 再读网页 → 整合多个证据 → 最终推理得出答案。

问题的关键是:如何构造一个"只靠参数知识答不上来、必须真正去检索"的好问题?这就是 Search 数据合成的核心挑战。
💡 好的 Search 任务长什么样:4-5 跳问题示例

"2022 年带领阿根廷赢得世界杯冠军的队长,目前效力于哪家俱乐部?这家俱乐部所在城市的 NBA 球队主场球馆叫什么名字?"

这道题需要 4-5 跳推理,每一跳都需要从网页中获取信息:

跳次 需要查什么 答案 为什么要搜索而不是记忆
跳12022 年阿根廷世界杯队长是谁梅西大多数模型参数里有,但"带领夺冠的队长"措辞可能需要确认
跳2梅西目前效力哪家俱乐部迈阿密国际时效性!转会信息随时变,模型训练后信息可能过期,必须查
跳3迈阿密国际所在城市迈阿密(佛罗里达州)俱乐部名字已经暗示,但严格来说需要确认
跳4迈阿密的 NBA 球队迈阿密热火NBA 常识,参数里有,但需要跟迈阿密关联
跳5热火的主场球馆名称Kaseya Center球馆名字因赞助商经常更名,时效性强,必须查
❌ 坏问题(单跳)
"阿根廷 2022 年世界杯夺冠了吗?" → 模型参数直接能答,搜索没有价值
✅ 好问题(多跳 + 时效性)
每一跳都涉及可能过时的信息(转会、球馆更名),模型必须真正搜索才能给出准确答案

方法一:GLM-5 的 WKG(网页知识图谱)构造方式

GLM-5 的核心创新是不直接让模型"想出多跳问题"(这样产出的问题容易偏向模型已知的知识),而是先从网页中建立结构化的知识图谱,再从图谱中"走路径"来生成问题,保证问题的多跳性是真实的而不是人为拼凑的。

1
收集网页原料:让早期 agent 刷真实问题

先让早期 search agent 大量刷真实问答任务(从 Wikipedia 问题、新闻、知乎等来源),把它访问过的每一个网页都存下来,做去重和质量过滤。

为什么要让 agent 来刷,而不是直接爬整个互联网?
直接爬互联网会得到大量无关页面(广告、导航页、重复内容)。让 agent 刷真实问题,它访问的页面天然是"回答实际问题时有用的页面",质量更高,后续构建知识图谱的信噪比也更好。
2
从网页中抽取结构化三元组,构建 WKG(Web Knowledge Graph)

对每个网页,用 NLP 模型提取出实体-关系-实体的三元组,拼成一张大图:

📖 WKG 构建示例:从网页到知识图谱
输入:网页原文片段
"…梅西于 2023 年 7 月正式加盟迈阿密国际(Inter Miami CF),这是一支位于佛罗里达州迈阿密的 MLS 球队。迈阿密国际的主场为 Chase Stadium(前称 DRV PNK Stadium)…"
输出:三元组
(梅西, 效力于, 迈阿密国际)

(迈阿密国际, 位于城市, 迈阿密)

(迈阿密国际, 主场, Chase Stadium)

(迈阿密, 州, 佛罗里达州)
图谱的规模:数百万个实体节点,数千万条关系边,覆盖体育、科技、历史、地理等各个领域。有了这张图,就能在上面按任意深度"走路径"来构造问题。
3
从图谱中采样多跳路径,生成问题草稿

核心技巧:选低频到中频实体作为起点,而不是高频实体(如"美国"、"中国"这种太常见、模型参数里全有)。

📖 从 WKG 采样 3 跳路径,生成问题
① 采样起点(低频实体):Kaseya Center
② 向外扩展 3 跳邻域:
Kaseya Center → (主场球馆) → 迈阿密热火
迈阿密热火 → (所在城市) → 迈阿密
迈阿密 → (城市里有的MLS球队) → 迈阿密国际
迈阿密国际 → (现役著名球员) → 梅西
③ 把路径改写成自然语言问题:
"梅西所在俱乐部所在城市的 NBA 球队,主场球馆叫什么?"
为什么从终点往起点采样?
先定终点(低频答案),再往回推导问题,可以保证问题有唯一确定的答案。如果从起点正向出发,可能走到多个合法答案,验证时很麻烦。
4
双层难度过滤:确保问题真的需要搜索

生成的问题草稿中,有些可能太简单——模型不搜索就能答对。要把这些"假多跳题"过滤掉。

第一层:去掉"参数知识就能答"的题

做法:直接问一个不能搜索的模型(无工具访问权限),如果它能答对,说明这道题在训练数据里出现过,不需要搜索就能答,过滤掉。

例:问"奥运会创始年份" → 模型直接回答 "1896年" → 无需搜索 → 过滤

第二层:去掉"一次搜索就解决"的题

做法:让 search agent 尝试用 ≤2 次搜索回答,如果成功,说明这题不够多跳,过滤掉。

例:问"梅西的国籍" → 一次搜索 "梅西 国籍" → 直接得到"阿根廷" → 只有 1 跳 → 过滤

过滤之后:只保留那些"不搜索答不上来"且"必须经过 3 跳以上推理"的问题。这类问题在真实数据集(如 MuSiQue、2WikiMultiHopQA)中的比例很低,靠 WKG 可以大规模批量构造。
5
答案与证据链的一致性验证

最后一关:检查问题和答案在逻辑上是否严密。

答案唯一性
多个 search agent 独立作答,结果是否一致?若有 agent 给出了不同答案,说明问题存在歧义,需要修改或丢弃
证据链闭合性
每一跳的信息是否都能在某个具体网页上找到?不能有"推理依赖无法验证的隐含知识"的跳
跨页面一致性
不同来源网页的信息是否冲突(如一个页面说球馆叫 A,另一个说叫 B)?有冲突的题需要更新到最新信息

方法二:MiniMax M2 的 WebExplorer + 迭代式 Query 进化

MiniMax M2 的路线与 GLM-5 不同——它不先建图再采路径,而是让 agent 先自由探索,形成"信息丰富的种子问题",再通过三步迭代操作把问题难度提升上去。

第一步:WebExplorer 自由探索 → 构造"信息丰富的种子问题"

给 agent 一个起始词(比如"巴西国家队"),让它在互联网上自由探索,沿着感兴趣的链接不断延伸,记录它走过的完整链路:

起点:巴西国家队
→ 搜"1950年世界杯" → 读到"马拉卡纳惨案",巴西 1:2 负于乌拉圭
→ 搜"1950年世界杯 裁判" → 读到英国裁判 George Reader
→ 搜"George Reader" → 读到他后来是 Southampton FC 主席
→ 搜"Southampton 1976年足总杯" → 读到 1-0 赢了曼联,进球者:Bobby Stokes
→ 生成种子问题:"哪位球员在 1976 年足总杯决赛打入唯一进球,帮助 Southampton 击败曼联?"
这类种子问题的特点:答案(Bobby Stokes)是冷知识,模型参数里大概率没有;信息出现在真实网页上,证据可查;问题涉及具体年份和赛事,模糊化空间大(便于下一步进化)。
第二步:迭代式 Query 进化(long-to-short)—— 三种操作提升难度

从信息齐全的"种子问题"出发,逐步删掉/模糊化显眼线索,让问题变得"只知道问什么,但不知道从哪里切入搜索"——模型必须自己想办法分解问题、制定搜索策略。

操作一:removing salient information(删掉显著线索)
进化前(信息太多):
"在 1976 年足总杯决赛中为 Southampton 打入制胜球、帮助球队击败曼联的球员,后来的职业生涯如何?"
进化后(删掉年份/赛事):
"那位帮助中游英超球队在足总杯决赛里爆冷击败曼联的进球手,后来的职业生涯如何?"
效果:模型必须先弄清楚"哪场比赛",才能知道"谁进的球",增加了一个必要的前置搜索步骤。
操作二:strategic obfuscation(战略性模糊化)
进化前:
"George Reader 执法了哪场著名的 1950 年世界杯决赛,结果如何?"
进化后(模糊专有名词):
"那位后来成为英格兰某顶级球队主席的世界杯裁判,执法过哪场被称为最大冷门的决赛?"
效果:"英格兰某顶级球队主席"没有直接给出 George Reader,模型需要先搜索"著名世界杯裁判+球队主席"才能找到这个人,再查他执法的比赛。
操作三:alternative descriptions / replace(替换成间接描述)
进化前(直接说名字):
"Bobby Stokes 是哪个国家的球员?"
进化后(替换为间接描述):
"在 1976 年那场被认为是足总杯最大冷门的决赛里打入制胜球的球员,他的国籍是什么?"
效果:不再直接给出人名,模型要先从"1976年足总杯决赛 + 制胜球"推导出"Bobby Stokes",再查他的国籍——变成了两步。

GLM-5 WKG vs MiniMax M2 WebExplorer:哪种更好?

维度 GLM-5 WKG MiniMax M2 WebExplorer
多跳性保证✅ 图结构天然定义跳数,可以精确控制 N 跳✅ 通过进化操作逐步增加跳数,但不那么精确
多样性中:图谱里的关系类型有限,问题结构可能单一✅ 自由探索产出的问题更自然、多样,更接近真实用户提问
可扩展性✅ 图构建一次,后续可批量采样,适合大规模中:每次 WebExplorer 都需要真实的网络访问,成本较高
答案可验证性✅ 图谱里有标准答案,可自动验证中:需要人工或模型辅助验证进化后的问题是否合理
时效性中:图谱需要定期更新,否则答案会过期✅ 每次探索都访问实时网页,天然具备时效性
🔑 Search 数据合成的本质:构造"真正需要搜索"的任务

无论是 WKG 还是 WebExplorer,目标都是一样的:构造那些模型参数知识答不上来、必须真正搜索才能回答的问题

• 验证多跳性:用"不给工具的模型"直接答,答对了就过滤
• 保证时效性:刻意选择时效性强的信息(转会、更名、选举结果等)
• 控制难度:双层过滤 / 迭代进化,确保不太简单也不过于模糊

与 SWE 和 Terminal 任务相比,Search 任务最难的不是"执行",而是搜索策略的规划——面对一个模糊的多跳问题,应该先搜哪个、怎么分解、在哪一页找到下一跳的线索,这才是 search agent 训练的核心目标。

§5 训练策略与挑战:三大难题及解法

这一章节讨论在实际训练中遇到的三大核心挑战,以及 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

用于采样轨迹,可能因为推理优化(如算子融合、量化)而与训练时略有不同

定义训推不匹配比率:

$$\rho_{i,t} = \frac{\pi_{\text{old\_train}}(y_{i,t}|x, y_{i,<t})}{\pi_{\text{old\_infer}}(y_{i,t}|x, y_{i,<t})}$$

pop(·) 函数会抑制那些不匹配比率偏离过大的样本:

$$\text{pop}(\rho,\,1/\beta,\,\beta) = \begin{cases} \rho & \text{若 } 1/\beta \leq \rho \leq \beta \\ 0 & \text{其他(直接置零,不参与更新)} \end{cases}$$
✅ IcePop 的最大好处

过滤掉那些训练分布和推理分布偏差过大的 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 选出来的结果不一致(不同实现的 top-k 算子可能有细微差异),那么即使输入完全一样,模型实际利用的上下文子集也不同,后续注意力计算和 token 概率分布都会跟着变化。这类似 MoE 中的 Route Replay 问题,但 DSA 的 k=2048,显式 replay 成本太高。

✅ GLM-5 的解法

① 使用确定性的 torch.topk(而非高性能但非确定性的 CUDA top-k 实现)
② 在 RL 阶段默认冻结 indexer 参数,保证 top-k 结果稳定

代价是 indexer 无法随 RL 训练继续优化,但换取了训推一致性和训练稳定性。


5.2 异步框架 Off-Policy 问题

在 Agentic RL 中,一个 agent 轨迹很长、rollout 耗时差异大。如果用同步 RL,GPU 会经常"等人",大量空转。所以主流基模训练都使用异步 RL 框架:推理端持续生成 agent 轨迹,训练端拿到足够轨迹后直接更新模型。

🤔 为什么异步 RL 会带来 Off-Policy 问题?

异步模式下,rollout 在持续生成数据的同时,训练端也在不断更新参数。早一点生成的轨迹来自旧模型,晚一点生成的来自新模型。甚至一条长 trajectory 内部,rollout engine 在中途同步了新权重,导致同一条轨迹的前后 token 可能来自不同 policy

GLM-5 的三种解法

1
周期性同步权重,尽量"近似 on-policy"

每隔 K 次梯度更新后,把最新权重推给推理端,并重置 optimizer 的一二阶动量(避免 Adam 动量记忆旧梯度方向引入错误偏差)。

2
重要性采样 + 双边裁剪
$$r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\text{rollout}}(a_t|s_t)}$$

双边裁剪:设定区间 [1-ε_l, 1+ε_h],落在区间内的 token 保留参与梯度,超出范围的直接 mask 掉。思路:能救的就救,太偏的直接丢掉。

3
丢掉太旧的轨迹

记录每个 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 训练过程
🤔 为什么 subagents 要冻结,不做端到端联合训练?

训练信号互相干扰: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 有简化