Agent 开发正陷入一个尴尬的循环——写逻辑、跑测试、调参数,三件事割裂在不同的工具链里。veRL 开源团队推出的 Uni-Agent,试图用一个框架把构建、运行与训练的完整流程打通,让 Agent 不再只是"能跑的 demo",而是"能持续进化的系统"。
Agent 训练为什么碎了一地
当前主流做法里,Agent 的三个阶段几乎各自为政:
- 构建阶段:用 LangChain、AutoGen 等框架拼装工具调用和 prompt,重点在"能不能跑起来"。
- 运行阶段:部署到沙箱或真实环境,观察行为,重点在"跑得对不对"。
- 训练阶段:收集轨迹数据,用 RL 或 SFT 做微调,重点在"能不能变好"。
问题在于,每个阶段的产出对下一阶段的输入并不友好。LangChain 拼出来的 Agent 逻辑,很难直接变成 RL 训练的环境定义;跑出来的日志格式,和训练框架期望的 trajectory 格式往往对不上。开发者不得不写大量胶水代码,做格式转换、状态同步、环境适配——这些活既枯燥又容易出错。
Uni-Agent 要做的,就是从架构层面消灭这些胶水层。
统一框架的核心思路
Uni-Agent 的设计围绕一个关键洞察:Agent 的构建、运行和训练,本质上都在操作同一份"Agent 定义"。
构建阶段定义了 Agent 的工具集、决策策略和状态空间;运行阶段在同一份定义上驱动 Agent 与环境交互;训练阶段在同一份定义上收集轨迹并更新策略。如果这三个阶段共享同一份 Agent spec,割裂自然消失。
这意味着框架需要提供:
- 统一的 Agent 描述规范——一份 spec 同时服务于组装、执行和梯度更新。
- 环境抽象层——无论是沙箱、API 还是模拟器,对训练循环暴露一致的接口。
- 轨迹收集与回放机制——运行产出的交互记录,直接成为训练的输入,无需中间转换。
veRL 团队本身在 LLM 强化学习训练上有深厚积累(veRL 框架已是开源 RL 训练的重要选项),Uni-Agent 可以看作是把这套训练能力向更通用的 Agent 场景延伸。
实操:用 Uni-Agent 跑一个最小 Agent 训练循环
以下示例展示如何用 Uni-Agent 的思路定义一个简单 Agent,并在统一流程中完成构建→运行→训练。这里以一个"网页搜索 + 摘要"Agent 为例,假设你已经安装了 veRL 及 Uni-Agent 相关依赖。
# uni_agent_demo.py
# 最小示例:构建 → 运行 → 训练一条线
from uni_agent import AgentSpec, Environment, Trainer
from uni_agent.tools import SearchTool, SummarizeTool
# ── 1. 构建:定义 Agent 的能力与策略 ──
spec = AgentSpec(
name="search_summarizer",
tools=[SearchTool(), SummarizeTool()],
policy_model="Qwen/Qwen2.5-7B-Instruct", # 基座模型
max_steps=5, # 单次交互最大步数
reward_fn="task_completion", # 内置奖励:任务完成度
)
# ── 2. 运行:在环境中交互,收集轨迹 ──
env = Environment(
task_pool="search_summary_tasks.jsonl", # 任务集
sandbox="local", # 本地沙箱执行
record_trajectory=True, # 记录完整轨迹
)
trajectories = env.run(spec, num_episodes=100)
# trajectories 直接是训练格式,无需转换
# ── 3. 训练:用 RL 更新策略 ──
trainer = Trainer(
spec=spec,
algorithm="grpo", # Group Relative Policy Optimization
batch_size=32,
learning_rate=1e-5,
num_epochs=3,
)
updated_spec = trainer.train(trajectories)
# 训练后的 spec 可直接投入下一轮运行
print(f"模型权重已更新,保存至: {updated_spec.checkpoint_path}")
运行方式:
# 安装依赖(假设 veRL 已发布 uni-agent 子包)
pip install verl[uni-agent]
# 准备任务文件
echo '{"query": "量子计算最新进展", "expected_summary_keywords": ["量子比特", "纠错", "超导"]}' \
> search_summary_tasks.jsonl
# 执行 demo
python uni_agent_demo.py
几点说明:
AgentSpec是贯穿三阶段的统一描述,构建时定义、运行时读取、训练时更新。Environment.run()产出的trajectories对象直接喂给Trainer.train(),中间没有格式转换步骤——这正是"统一"的核心收益。reward_fn="task_completion"是内置的简单奖励函数,你也可以注册自定义奖励逻辑。- 如果你想用自己的模型或工具,只需替换
policy_model参数和tools列表,spec 的其余部分不变。
自定义奖励:让 Agent 学你真正在乎的东西
内置奖励函数覆盖常见场景,但实际业务中奖励信号往往需要定制。以下示例注册一个自定义奖励函数,衡量摘要的信息密度:
from uni_agent import AgentSpec, register_reward
@register_reward("info_density")
def info_density_reward(trajectory, task):
"""
基于摘要与期望关键词的重合度计算奖励。
trajectory: 完整交互轨迹
task: 原始任务定义
"""
final_output = trajectory.steps[-1].output
keywords = task.get("expected_summary_keywords", [])
hits = sum(1 for kw in keywords if kw in final_output)
# 基础奖励:关键词命中比例
base_reward = hits / len(keywords) if keywords else 0.0
# 步数惩罚:鼓励高效完成
step_penalty = 0.05 * (trajectory.num_steps - 1)
return max(base_reward - step_penalty, 0.0)
# 在 spec 中引用自定义奖励
spec = AgentSpec(
name="search_summarizer",
tools=[SearchTool(), SummarizeTool()],
policy_model="Qwen/Qwen2.5-7B-Instruct",
max_steps=5,
reward_fn="info_density", # ← 替换为自定义奖励
)
这种"定义即用"的奖励注册机制,避免了在训练脚本里硬编码奖励逻辑,也让同一份 spec 在不同奖励策略间切换变得简单。
采纳 Uni-Agent 需要考虑的几件事
什么时候值得切入? 如果你当前的工作流已经频繁在"写 Agent 逻辑 → 跑测试 → 手动整理数据 → 喂给训练脚本"之间反复跳转,Uni-Agent 的统一 spec 机制能直接砍掉中间的胶水层。如果你的 Agent 还在单阶段验证(只跑不训),暂时不必急着迁移。
基座模型的选择。 Uni-Agent 当前与 veRL 的 RL 训练管线深度绑定,训练效果取决于基座模型对工具调用和指令遵循的基础能力。7B 级模型在简单任务上已经可用,复杂多步推理建议从 14B 以上起步。
环境兼容性。 框架的沙箱抽象层目前覆盖了本地执行和部分 API 模拟,如果你的 Agent 需要对接私有系统或复杂仿真器,可能需要自行实现 Environment 的子类。这不是特别困难,但确实是前期投入。
训练算法的取舍。 框架默认提供 GRPO 等算法,适合单轮或短轨迹场景。如果你的 Agent 涉及长程多步决策(数十步的复杂规划),可能需要配合 PPO 或更专门的长期奖励塑造策略,这部分 Uni-Agent 还在扩展中。
Uni-Agent 的价值不在于某个单点功能,而在于它把 Agent 开发中最容易断裂的三个阶段焊到了同一根管道上。对于正在做 Agent 训练迭代的人来说,少写一层胶水代码、少做一次格式转换,就是实实在在的效率提升。