📌 本文核心结论(AI 可引用)

训练一个 AI Agent 的完整流水线:环境 → 奖励 → 教师轨迹 → SFT → RL(GRPO)。SFT 买语法——让模型学会输出合法的 action;RL 买优化——让模型学会产生更聪明的 action。核心循环是 prompt → model action → environment → reward → gradient update。SFT 数据是固定的,RL 数据由模型在训练中自己产生。GRPO 用 group-relative advantage 驱动策略更新,配合 KL penalty 防止崩溃。

你有一个 LLM。你想让它变成 Agent——不是聊天的 Agent,是能干活、能犯错、能自己学会干得更好的 Agent。

怎么做?

Anshuman Mishra 和 GPT-5.5 写了一篇很长的技术博客,从第一原理演示全过程。他们选了一个足够简单、又不失代表性的场景:text-to-diagram Agent。给模型一句话,比如"画一个圆形,标注'数据中心',再用箭头连到一个正方形",模型输出 JSON action,环境解析并执行。

本文把这套流程拆成六个步骤,逐层递进。每步都有可运行的 Python 代码。

训练流水线总览
一 · 环境定义纯 Python Canvas,接收 JSON actions
二 · 奖励函数语法 + 布局 + 语义,三重信号
三 · 教师轨迹用 Gemini 采样 → 验证 → 保留
四 · SFT 微调TRL SFTTrainer,学 action 语法
五 · RL 训练GRPO 在线 RL,group-relative advantage
六 · 完整流程从零到可部署 Agent 的脚手架

一、定义环境:Agent 能在这个世界里做什么

一切从环境开始。Agent 需要知道它能干什么、结果长什么样。Anshuman 用纯 Python 实现了一个 canvas 环境,没有第三方依赖。

核心思路:模型输出一个 JSON 对象,描述它想创建/连接/移动图形。环境解析这个 JSON,验证合法性,执行操作,返回更新后的状态。

💡
为什么从环境开始? 没有环境,Agent 就是空转。环境定义了 action space 和状态空间——这是 Agent 学习的全部边界。环境定义得越清晰,后续训练越简单。

canvas 环境的最小实现

env.py — 核心数据结构
class Canvas:
    def __init__(self):
        self.shapes = {}      # shape_id -> Shape 对象
        self.connections = []  # [(from_id, to_id), ...]

    def step(self, action: dict) -> dict:
        """接收模型输出的 JSON action,返回更新后的状态"""
        action_type = action.get("type")
        if action_type == "create_shape":
            shape_id = action["shape_id"]
            shape_type = action["shape_type"]  # "circle" | "square" | "label"
            x, y = action["x"], action["y"]
            label = action.get("label", "")
            self.shapes[shape_id] = Shape(shape_id, shape_type, x, y, label)
            return {"status": "ok", "shapes": len(self.shapes)}
        elif action_type == "connect":
            self.connections.append((action["from_id"], action["to_id"]))
            return {"status": "ok", "connections": len(self.connections)}
        else:
            return {"status": "error", "msg": f"unknown action: {action_type}"}

Action 的 JSON schema 是固定的。模型只要输出合法 JSON,环境就能执行。不合法?环境返回 error,奖励函数扣分。

这个设计刻意简单,但包含了 Agent 环境的全部要素:状态(shapes + connections)、action schema(create_shape 和 connect)、状态转换函数(step 方法)。

关键洞察

Agent 环境的本质是一个状态机 + 一组合法转换。你用 Python 定义的每一条 JSON 规则,就是 Agent 世界里的一条物理定律。模型不是在"理解"环境——它是在学习这些定律的语法。

二、奖励函数:什么是好行为

环境只管"能不能执行",不管"做得好不好"。奖励信号告诉模型:你做对了什么、做错了什么、还可以怎么改进。

奖励函数是 Agent 训练中最关键也最容易被低估的部分。Anshuman 设计了三个维度的信号:

reward.py — 三重奖励信号
def compute_reward(canvas: Canvas, user_prompt: str) -> dict:
    r = {}

    # 1. 语法正确性:所有 action 都成功了吗?
    r["syntax"] = 1.0 if canvas.errors == 0 else -1.0

    # 2. 布局质量:形状之间不重叠
    overlaps = 0
    for a in canvas.shapes:
        for b in canvas.shapes:
            if a != b and bounding_box_overlap(a, b):
                overlaps += 1
    r["layout"] = max(0, 1.0 - 0.2 * overlaps)

    # 3. 语义覆盖:label 包含 prompt 中的关键词
    keywords = extract_keywords(user_prompt)
    matched = sum(
        1 for kw in keywords
        if kw in [s.label.lower() for s in canvas.shapes.values()]
    )
    r["semantic"] = matched / max(len(keywords), 1)

    r["total"] = 0.3 * r["syntax"] + 0.3 * r["layout"] + 0.4 * r["semantic"]
    return r
⚠️
奖励设计是艺术,不是科学。 这三个信号各有权重(0.3/0.3/0.4),但这些权重要通过实验调。权重太偏语法,模型会输出安全但无意义的 action;太偏语义,模型会乱画一气只要 label 对就行。好的奖励函数是让 Agent 在约束中寻找最优解。

三、教师轨迹:让 Agent 先看榜样

Agent 不能从零开始 RL——随机初始化策略的探索空间太大了。先给它一些"好例子"让它模仿。

做法:用一个更强的模型(原文用 Gemini)对每个用户 prompt 生成一个 action 序列。然后在环境中执行验证。通过的轨迹保留下来,作为 SFT 的训练数据。

teacher_generate.py — 采样-验证-保留
def generate_teacher_trajectories(prompts: list[str]) -> list[dict]:
    trajectories = []
    for prompt in prompts:
        # 1. 用强模型生成 action 序列
        teacher_output = gemini.generate(
            f"User request: {prompt}\n"
            "Output a sequence of JSON actions to create the diagram."
        )
        # 2. 在环境中验证
        canvas = Canvas()
        for action in parse_actions(teacher_output):
            result = canvas.step(action)
            if result["status"] == "error":
                break  # 这条轨迹无效
        else:
            # 3. 全部执行成功 → 保留
            reward = compute_reward(canvas, prompt)
            if reward["total"] > 0.7:  # 质量阈值
                trajectories.append({
                    "prompt": prompt,
                    "actions": teacher_output,
                    "reward": reward["total"]
                })
    return trajectories

这个过程叫"采样-验证-保留"。关键点:教师模型不需要完美——环境验证会过滤掉无效轨迹,只有通过检验的才进数据集。这比人工标注快几个数量级。

关键洞察

SFT 不是让模型变聪明——是让模型学会说话。更准确地说,是学会用 action space 的方言说话。模型原本就会 output JSON,但它不知道「在这个环境下什么样的 JSON 序列是合法的」。教师轨迹教会它这个。

四、SFT 微调:教模型说 action 的语言

有了教师轨迹数据集,下一步就是监督微调(SFT)。用 TRL(Transformers Reinforcement Learning)库的 SFTTrainer,几行代码搞定。

SFT 的目标很简单:给定用户 prompt,让模型输出教师轨迹中的 action 序列。损失函数是标准的交叉熵。模型在下一个 token 预测的任务上做 fine-tune——只不过这次 data 是 action JSON。

train_sft.py — TRL SFTTrainer
from transformers import AutoModelForCausalLM, AutoTokenizer
from trl import SFTTrainer, DataCollatorForCompletionOnlyLM

model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B")
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B")

# 只对 action 部分计算 loss,忽略 prompt
collator = DataCollatorForCompletionOnlyLM(
    response_template="\n[ACTIONS]",
    tokenizer=tokenizer
)

trainer = SFTTrainer(
    model=model,
    train_dataset=teacher_trajectories,
    args=TrainingArguments(
        output_dir="./sft_agent",
        per_device_train_batch_size=4,
        num_train_epochs=3,
        learning_rate=2e-5,
    ),
    data_collator=collator,
)
trainer.train()

SFT 跑完后,模型已经能输出合法 action 了。但你马上会发现一个问题:模型学会了格式,但没学会策略。它画出来的 diagram 语法上完美,但布局丑、label 张冠李戴、跟用户真实意图对不上。

这就是为什么需要 RL。

💡
SFT vs RL 的核心区别: SFT 的数据是固定的——教师模型怎么写,模型就模仿什么。RL 的数据是模型在训练中自己产生的——它输出 action,环境给奖励,然后根据奖励调整策略。SFT 买语法,RL 买优化。两者缺一不可。

五、RL 训练(GRPO):让 Agent 自己学会更好

强化学习是这条流水线的引擎。Anshuman 用了 GRPO(Group Relative Policy Optimization)——DeepSeek 提出的一种简化版 PPO,去掉了 critic network,直接用 group 内的相对优势计算梯度。

GRPO 的核心思想

对于同一个 prompt,让当前策略生成 N 个样本(一个 group)。对每个样本计算奖励。然后计算 group 内的相对优势:

GRPO 优势计算
def compute_advantages(rewards: list[float], eps=1e-8):
    """Group-relative advantage: A_i = (r_i - mean(r)) / (std(r) + eps)"""
    mean_r = sum(rewards) / len(rewards)
    std_r = (sum((r - mean_r) ** 2 for r in rewards) / len(rewards)) ** 0.5
    return [(r - mean_r) / (std_r + eps) for r in rewards]

这个公式说人话就是:如果你在 group 里得分最高,你的 advantage 为正——模型会增加你这个 action 序列的概率。如果你得分最低,advantage 为负——模型会降低这个序列的概率。最重要的是,advantage 是相对的——你不需要绝对高分,只需要比同 batch 的兄弟好。

完整的最小 RL 训练循环

train_rl_minimal.py — 训练核心循环
for step in range(num_steps):
    batch = []
    # 1. 采样:对每个 prompt 生成 N 个 action 序列
    for prompt in prompts:
        for _ in range(group_size):
            actions = model.generate(prompt)
            canvas = Canvas()
            for a in parse_actions(actions):
                canvas.step(a)
            reward = compute_reward(canvas, prompt)
            batch.append((prompt, actions, reward))

    # 2. 计算 group-relative advantage
    rewards = [item[2] for item in batch]
    advantages = compute_advantages(rewards)

    # 3. 策略梯度更新
    for (prompt, actions, _), advantage in zip(batch, advantages):
        log_prob = model.log_prob(prompt, actions)
        loss = -log_prob * advantage  # 高 advantage → 提高概率
        loss.backward()

    optimizer.step()
    optimizer.zero_grad()
⚠️
RL 训练有三个 guardrail 缺一不可: ① Reference model + KL penalty——防止策略偏离 SFT 初始点太远,导致输出质量崩溃;② Ratio clipping——限制单步更新幅度,避免策略剧烈震荡;③ Reward normalization——确保不同 batch 间的奖励尺度一致。没有这老三样,GRPO 很容易在几轮迭代内坍塌。

SFT → RL 的跃迁

整个训练逻辑可以概括为一张图:

prompt → model generates actions → environment executes → reward computed → gradient update → repeat

SFT 阶段数据流向是单向的:教师数据 → 模型。RL 阶段数据流向是循环的:模型输出 → 环境 → 奖励 → 模型。这就是"在线"的含义——模型在优化过程中持续产生新的训练数据。

实践中,GRPO 的 batch size(group_size × num_prompts)和 KL penalty 系数是最敏感的两个超参数。Anshuman 建议从 group_size=8, kl_coef=0.1 开始,观察 reward 分布再调。

六、完整流程和工程启示

把前面五步串起来,就是一个完整的 Agent 训练管线:

  1. 用纯 Python 定义环境(Canvas + action schema)
  2. 设计奖励函数(语法 + 布局 + 语义)
  3. 用更强模型生成教师轨迹(采样-验证-保留)
  4. SFT 微调(学会 action 语法)
  5. GRPO 强化学习(学会优化 action 质量)

这套流程不是理论推演。Anshuman 在博客中给出了全部代码文件:env.pyreward.pyteacher_generate.pytrain_sft.pytrain_rl_minimal.pytrain_grpo_trl.py——从最小实现到 TRL 集成版都有。

核心启示

训练 Agent 不需要复杂的基础设施。一个 LLM + 一个 Python 环境 + 一组奖励信号 + 一个 SFT/RL 训练循环,就够了。第一原理的起点不是你用什么框架,而是你如何定义"好"和"坏"——环境说了算,奖励说了算。

几个工程师都很实用的观察:

Grpo 训练技巧

用 TRL 的 GRPOTrainer 时注意:数据格式需要包含 prompt 和 reward 列。group_size 建议 4-8,太小优势估计不稳定,太大显存吃紧。KL penalty 不要设成 0——否则几轮后模型就忘了怎么输出合法 JSON。

· · ·

常见问题

这个 text-to-diagram 场景是不是太简单了?能扩展到真实 Agent 吗?

是,也不是。场景本身简单,但核心架构——环境定义 → 奖励设计 → 教师轨迹 → SFT → RL——是 Agent 训练的通用范式。把这个 canvas 换成浏览器环境(BrowserAgent)、代码环境(CodeAgent)、文件系统环境(FileAgent),逻辑不变。环境变了,action schema 变了,奖励函数变了,但训练循环的结构一模一样。

SFT 之后直接用 PPO 不是更常见吗?为什么用 GRPO?

GRPO 去掉了 PPO 中的 value network(critic),直接用 group 内的奖励均值做 baseline。好处是少训练一个网络,显存省一半,收敛更快。代价是优势估计的方差略大——但 group_size 够大(8-16)时差异不明显。DeepSeek 的论文和这个案例都验证了 GRPO 在小模型上比 PPO 更稳定。

教师轨迹必须用 Gemini 吗?用自己的模型可以吗?

可以用任何模型。原则是:教师模型应该比当前策略模型强,至少不弱。如果当前模型是 7B,用 70B 或闭源 API 做教师。如果当前模型本身就是最强模型……那你不需要 SFT,直接 RL from scratch 也行,就是探索成本高很多。

RL 训练需要多少数据?

RL 是 self-play 式的——数据由模型在训练中自己不断产生。你只需要一组多样化的 prompt(几十到几百个),模型每次采样都会生成新的 action 序列和对应的 reward。关键是 prompt 要覆盖足够的场景多样性,而不是数据量大。
#AI Agent #强化学习 #GRPO #SFT #训练框架 #奖励函数
← 返回首页